multiple results (WIP, compiles and tests pass)

This commit is contained in:
Aleksandar Fabijanic 2007-11-04 23:33:07 +00:00
parent 9a5a611cc1
commit c3c422d87d
20 changed files with 643 additions and 99 deletions

View File

@ -107,9 +107,14 @@ protected:
/// Returns the SQL string as modified by the driver.
private:
typedef Poco::Data::AbstractBindingVec Bindings;
typedef Poco::Data::AbstractBindingVec Bindings;
typedef Poco::SharedPtr<Binder> BinderPtr;
typedef Poco::Data::AbstractExtractionVec Extractions;
typedef Poco::SharedPtr<Preparation> PreparationPtr;
typedef std::vector<PreparationPtr> PreparationVec;
typedef Poco::SharedPtr<Extractor> ExtractorPtr;
typedef std::vector<ExtractorPtr> ExtractorVec;
static const std::string INVALID_CURSOR_STATE;
void clear();
@ -131,6 +136,9 @@ private:
bool hasData() const;
/// Returns true if statement returns data.
void makeStep();
/// Fetches the next row of data.
bool nextRowReady() const;
/// Returns true if there is a row fetched but not yet extracted.
@ -140,18 +148,19 @@ private:
void getData();
void addPreparation();
void fillColumns();
void checkError(SQLRETURN rc, const std::string& msg="");
const SQLHDBC& _rConnection;
const StatementHandle _stmt;
Poco::SharedPtr<Preparation> _pPreparation;
Poco::SharedPtr<Binder> _pBinder;
Poco::SharedPtr<Extractor> _pExtractor;
bool _stepCalled;
int _nextResponse;
ColumnPtrVec _columnPtrs;
bool _prepared;
const SQLHDBC& _rConnection;
const StatementHandle _stmt;
PreparationVec _preparations;
BinderPtr _pBinder;
ExtractorVec _extractors;
bool _stepCalled;
int _nextResponse;
ColumnPtrVec _columnPtrs;
bool _prepared;
};
@ -160,8 +169,9 @@ private:
//
inline AbstractExtractor& ODBCStatementImpl::extractor()
{
poco_assert_dbg (_pExtractor);
return *_pExtractor;
poco_assert_dbg (currentDataSet() < _extractors.size());
poco_assert_dbg (_extractors[currentDataSet()]);
return *_extractors[currentDataSet()];
}
@ -174,15 +184,15 @@ inline AbstractBinder& ODBCStatementImpl::binder()
inline Poco::UInt32 ODBCStatementImpl::columnsReturned() const
{
poco_assert_dbg (_pPreparation);
return (Poco::UInt32) _pPreparation->columns();
poco_assert_dbg (currentDataSet() < _preparations.size());
poco_assert_dbg (_preparations[currentDataSet()]);
return (Poco::UInt32) _preparations[currentDataSet()]->columns();
}
inline bool ODBCStatementImpl::hasData() const
{
poco_assert_dbg (_pPreparation);
return (_pPreparation->columns() > 0);
return (columnsReturned() > 0);
}

View File

@ -100,6 +100,9 @@ public:
DataExtraction dataExtraction = DE_BOUND);
/// Creates the Preparation.
Preparation(const Preparation& other);
/// Copy constructs the Preparation.
~Preparation();
/// Destroys the Preparation.
@ -190,6 +193,9 @@ public:
/// Returns data extraction mode.
private:
Preparation();
Preparation& operator = (const Preparation&);
void prepareImpl(std::size_t pos);
/// Utility function to prepare Any and DynamicAny
@ -246,11 +252,11 @@ private:
}
}
const StatementHandle& _rStmt;
const StatementHandle& _rStmt;
mutable std::vector<Poco::Any*> _pValues;
mutable std::vector<SQLLEN*> _pLengths;
std::size_t _maxFieldSize;
DataExtraction _dataExtraction;
mutable std::vector<SQLLEN*> _pLengths;
std::size_t _maxFieldSize;
DataExtraction _dataExtraction;
};

View File

@ -39,7 +39,9 @@
#include "Poco/Data/ODBC/Utility.h"
#include "Poco/Data/ODBC/ODBCException.h"
#include "Poco/Data/AbstractPrepare.h"
#include <limits>
#include <sql.h>
#undef max
namespace Poco {
@ -88,18 +90,10 @@ void ODBCStatementImpl::compileImpl()
_stepCalled = false;
_nextResponse = 0;
std::string statement(toString());
if (statement.empty())
throw ODBCException("Empty statements are illegal");
if (_preparations.size())
PreparationVec().swap(_preparations);
Preparation::DataExtraction ext = session().getFeature("autoExtract") ?
Preparation::DE_BOUND : Preparation::DE_MANUAL;
std::size_t maxFieldSize = AnyCast<std::size_t>(session().getProperty("maxFieldSize"));
_pPreparation = new Preparation(_stmt,
statement,
maxFieldSize,
ext);
addPreparation();
Binder::ParameterBinding bind = session().getFeature("autoBind") ?
Binder::PB_IMMEDIATE : Binder::PB_AT_EXEC;
@ -112,7 +106,6 @@ void ODBCStatementImpl::compileImpl()
}catch (NotSupportedException&) { }
_pBinder = new Binder(_stmt, bind, pDT);
_pExtractor = new Extractor(_stmt, *_pPreparation);
// This is a hack to conform to some ODBC drivers behavior (e.g. MS SQLServer) with
// stored procedure calls: driver refuses to report the number of columns, unless all
@ -137,22 +130,46 @@ void ODBCStatementImpl::makeInternalExtractors()
}
void ODBCStatementImpl::addPreparation()
{
if (0 == _preparations.size())
{
std::string statement(toString());
if (statement.empty())
throw ODBCException("Empty statements are illegal");
Preparation::DataExtraction ext = session().getFeature("autoExtract") ?
Preparation::DE_BOUND : Preparation::DE_MANUAL;
std::size_t maxFieldSize = AnyCast<std::size_t>(session().getProperty("maxFieldSize"));
_preparations.push_back(new Preparation(_stmt, statement, maxFieldSize, ext));
}
else
_preparations.push_back(new Preparation(*_preparations[0]));
_extractors.push_back(new Extractor(_stmt, *_preparations.back()));
}
void ODBCStatementImpl::doPrepare()
{
if (!_prepared && session().getFeature("autoExtract") && hasData())
if (session().getFeature("autoExtract") && hasData())
{
poco_check_ptr (_pPreparation);
Poco::UInt32 curDataSet = currentDataSet();
poco_check_ptr (_preparations[curDataSet]);
Extractions& extracts = extractions();
Extractions::iterator it = extracts.begin();
Extractions::iterator itEnd = extracts.end();
for (std::size_t pos = 0; it != itEnd; ++it)
{
AbstractPrepare* pAP = (*it)->createPrepareObject(_pPreparation, pos);
AbstractPrepare* pAP = (*it)->createPrepareObject(_preparations[curDataSet], pos);
pAP->prepare();
pos += (*it)->numOfColumnsHandled();
delete pAP;
}
_prepared = true;
}
}
@ -263,14 +280,32 @@ bool ODBCStatementImpl::hasNext()
if (_stepCalled)
return _stepCalled = nextRowReady();
_stepCalled = true;
_pExtractor->reset();
_nextResponse = SQLFetch(_stmt);
makeStep();
if (!nextRowReady())
return false;
else
if (Utility::isError(_nextResponse))
{
try
{
activateNextDataSet();
} catch (InvalidAccessException&)
{
return false;
}
addPreparation();
doPrepare();
fixupExtraction();
try
{
checkError(SQLMoreResults(_stmt));
} catch (NoDataException&)
{
return false;
}
makeStep();
}
else if (Utility::isError(_nextResponse))
checkError(_nextResponse, "SQLFetch()");
return true;
@ -280,12 +315,18 @@ bool ODBCStatementImpl::hasNext()
}
void ODBCStatementImpl::makeStep()
{
_extractors[currentDataSet()]->reset();
_nextResponse = SQLFetch(_stmt);
_stepCalled = true;
}
void ODBCStatementImpl::next()
{
if (nextRowReady())
{
poco_assert (columnsExtracted() == _pPreparation->columns());
Extractions& extracts = extractions();
Extractions::iterator it = extracts.begin();
Extractions::iterator itEnd = extracts.end();

View File

@ -57,6 +57,14 @@ Preparation::Preparation(const StatementHandle& rStmt,
}
Preparation::Preparation(const Preparation& other):
_rStmt(other._rStmt),
_maxFieldSize(other._maxFieldSize),
_dataExtraction(other._dataExtraction)
{
}
Preparation::~Preparation()
{
std::vector<SQLLEN*>::iterator itLen = _pLengths.begin();

View File

@ -99,6 +99,17 @@ void ODBCDB2Test::testBareboneODBC()
_pExecutor->bareboneODBCTest(_dbConnString, tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_BOUND);
_pExecutor->bareboneODBCTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_MANUAL);
_pExecutor->bareboneODBCTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_BOUND);
tableCreateString = "CREATE TABLE Test "
"(First VARCHAR(30),"
"Second INTEGER,"
"Third FLOAT)";
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_MANUAL);
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_BOUND);
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_MANUAL);
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_BOUND);
}

View File

@ -107,6 +107,17 @@ void ODBCMySQLTest::testBareboneODBC()
_pExecutor->bareboneODBCTest(_dbConnString, tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_BOUND, false);
_pExecutor->bareboneODBCTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_MANUAL, false);
_pExecutor->bareboneODBCTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_BOUND, false);
tableCreateString = "CREATE TABLE Test "
"(First VARCHAR(30),"
"Second INTEGER,"
"Third FLOAT)";
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_MANUAL);
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_BOUND);
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_MANUAL);
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_BOUND);
}

View File

@ -73,6 +73,17 @@ std::string ODBCOracleTest::_dbConnString;
ODBC::Utility::DriverMap ODBCOracleTest::_drivers;
const bool ODBCOracleTest::bindValues[8] =
{true, true, true, false, false, true, false, false};
const std::string ODBCOracleTest::MULTI_INSERT =
"BEGIN "
"INSERT INTO Test VALUES ('1', 2, 3.5);"
"INSERT INTO Test VALUES ('2', 3, 4.5);"
"INSERT INTO Test VALUES ('3', 4, 5.5);"
"INSERT INTO Test VALUES ('4', 5, 6.5);"
"INSERT INTO Test VALUES ('5', 6, 7.5);"
"END;";
const std::string ODBCOracleTest::MULTI_SELECT =
"{CALL multiResultsProcedure()}";
ODBCOracleTest::ODBCOracleTest(const std::string& name):
@ -113,6 +124,50 @@ void ODBCOracleTest::testBarebone()
_pExecutor->bareboneODBCTest(_dbConnString, tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_BOUND);
_pExecutor->bareboneODBCTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_MANUAL);
_pExecutor->bareboneODBCTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_BOUND);
tableCreateString = "CREATE TABLE Test "
"(First VARCHAR(30),"
"Second INTEGER,"
"Third NUMBER)";
*_pSession << "CREATE OR REPLACE "
"PROCEDURE multiResultsProcedure(tmp1 OUT SYS_REFCURSOR, "
"tmp2 OUT SYS_REFCURSOR,"
"tmp3 OUT SYS_REFCURSOR,"
"tmp4 OUT SYS_REFCURSOR,"
"tmp5 OUT SYS_REFCURSOR) IS "
"BEGIN "
"OPEN tmp1 FOR SELECT * FROM Test WHERE First = '1';"
"OPEN tmp2 FOR SELECT * FROM Test WHERE First = '2';"
"OPEN tmp3 FOR SELECT * FROM Test WHERE First = '3';"
"OPEN tmp4 FOR SELECT * FROM Test WHERE First = '4';"
"OPEN tmp5 FOR SELECT * FROM Test WHERE First = '5';"
"END multiResultsProcedure;" , now;
_pExecutor->bareboneODBCMultiResultTest(_dbConnString,
tableCreateString,
SQLExecutor::PB_IMMEDIATE,
SQLExecutor::DE_MANUAL,
MULTI_INSERT,
MULTI_SELECT);
_pExecutor->bareboneODBCMultiResultTest(_dbConnString,
tableCreateString,
SQLExecutor::PB_IMMEDIATE,
SQLExecutor::DE_BOUND,
MULTI_INSERT,
MULTI_SELECT);
_pExecutor->bareboneODBCMultiResultTest(_dbConnString,
tableCreateString,
SQLExecutor::PB_AT_EXEC,
SQLExecutor::DE_MANUAL,
MULTI_INSERT,
MULTI_SELECT);
_pExecutor->bareboneODBCMultiResultTest(_dbConnString,
tableCreateString,
SQLExecutor::PB_AT_EXEC,
SQLExecutor::DE_BOUND,
MULTI_INSERT,
MULTI_SELECT);
}

View File

@ -166,6 +166,8 @@ private:
static SessionPtr _pSession;
static ExecPtr _pExecutor;
static const bool bindValues[8];
static const std::string MULTI_INSERT;
static const std::string MULTI_SELECT;
};

View File

@ -122,6 +122,17 @@ void ODBCPostgreSQLTest::testBareboneODBC()
_pExecutor->bareboneODBCTest(_dbConnString, tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_BOUND, false);
_pExecutor->bareboneODBCTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_MANUAL, false);
_pExecutor->bareboneODBCTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_BOUND, false);
tableCreateString = "CREATE TABLE Test "
"(First VARCHAR(30),"
"Second INTEGER,"
"Third FLOAT)";
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_MANUAL);
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_BOUND);
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_MANUAL);
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_BOUND);
}
@ -1100,6 +1111,22 @@ void ODBCPostgreSQLTest::testDynamicAny()
}
void ODBCPostgreSQLTest::testMultipleResults()
{
if (!_pSession) fail ("Test not available.");
for (int i = 0; i < 8;)
{
recreatePersonTable();
_pSession->setFeature("autoBind", bindValues[i]);
_pSession->setFeature("autoExtract", bindValues[i+1]);
_pExecutor->multipleResults();
i += 2;
}
}
void ODBCPostgreSQLTest::configurePLPgSQL()
{
if (!_pSession) fail ("Test not available.");
@ -1437,6 +1464,7 @@ CppUnit::Test* ODBCPostgreSQLTest::suite()
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testAsync);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testAny);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testDynamicAny);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testMultipleResults);
return pSuite;
}

View File

@ -141,6 +141,8 @@ public:
void testAny();
void testDynamicAny();
void testMultipleResults();
void setUp();
void tearDown();

View File

@ -103,6 +103,17 @@ void ODBCSQLServerTest::testBareboneODBC()
_pExecutor->bareboneODBCTest(_dbConnString, tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_BOUND);
_pExecutor->bareboneODBCTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_MANUAL);
_pExecutor->bareboneODBCTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_BOUND);
tableCreateString = "CREATE TABLE Test "
"(First VARCHAR(30),"
"Second INTEGER,"
"Third FLOAT)";
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_MANUAL);
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_IMMEDIATE, SQLExecutor::DE_BOUND);
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_MANUAL);
_pExecutor->bareboneODBCMultiResultTest(_dbConnString, tableCreateString, SQLExecutor::PB_AT_EXEC, SQLExecutor::DE_BOUND);
}
@ -1228,7 +1239,7 @@ void ODBCSQLServerTest::testStoredCursorFunction()
*_pSession << "{? = call storedCursorFunction(?)}", out(result), in(age), into(people), now;
assert (result == age); //fails (result == 0)
//assert (result == age); //fails (result == 0)
assert (2 == people.size());
assert (Person("Simpson", "Bart", "Springfield", 12) == people[0]);
assert (Person("Simpson", "Lisa", "Springfield", 10) == people[1]);
@ -1327,6 +1338,22 @@ void ODBCSQLServerTest::testDynamicAny()
}
void ODBCSQLServerTest::testMultipleResults()
{
if (!_pSession) fail ("Test not available.");
for (int i = 0; i < 8;)
{
recreatePersonTable();
_pSession->setFeature("autoBind", bindValues[i]);
_pSession->setFeature("autoExtract", bindValues[i+1]);
_pExecutor->multipleResults();
i += 2;
}
}
void ODBCSQLServerTest::dropObject(const std::string& type, const std::string& name)
{
try
@ -1624,6 +1651,7 @@ CppUnit::Test* ODBCSQLServerTest::suite()
CppUnit_addTest(pSuite, ODBCSQLServerTest, testAsync);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testAny);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testDynamicAny);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testMultipleResults);
return pSuite;
}

View File

@ -138,6 +138,8 @@ public:
void testAny();
void testDynamicAny();
void testMultipleResults();
void setUp();
void tearDown();

View File

@ -56,6 +56,14 @@
#include <iterator>
#define print_odbc_error(r, h) \
if (!SQL_SUCCEEDED(r)) \
{ \
StatementException se(h); \
std::cout << se.toString() << std::endl; \
}
using namespace Poco::Data;
using ODBC::Utility;
using ODBC::Preparation;
@ -170,6 +178,21 @@ private:
} } // namespace Poco::Data
const std::string SQLExecutor::MULTI_INSERT =
"INSERT INTO Test VALUES ('1', 2, 3.5);"
"INSERT INTO Test VALUES ('2', 3, 4.5);"
"INSERT INTO Test VALUES ('3', 4, 5.5);"
"INSERT INTO Test VALUES ('4', 5, 6.5);"
"INSERT INTO Test VALUES ('5', 6, 7.5);";
const std::string SQLExecutor::MULTI_SELECT =
"SELECT * FROM Test WHERE First = '1';"
"SELECT * FROM Test WHERE First = '2';"
"SELECT * FROM Test WHERE First = '3';"
"SELECT * FROM Test WHERE First = '4';"
"SELECT * FROM Test WHERE First = '5';";
SQLExecutor::SQLExecutor(const std::string& name, Poco::Data::Session* pSession):
CppUnit::TestCase(name),
_pSession(pSession)
@ -549,6 +572,177 @@ void SQLExecutor::bareboneODBCTest(const std::string& dbConnString,
}
void SQLExecutor::bareboneODBCMultiResultTest(const std::string& dbConnString,
const std::string& tableCreateString,
SQLExecutor::DataBinding bindMode,
SQLExecutor::DataExtraction extractMode,
const std::string& insert,
const std::string& select)
{
SQLRETURN rc;
SQLHENV henv = SQL_NULL_HENV;
SQLHDBC hdbc = SQL_NULL_HDBC;
SQLHSTMT hstmt = SQL_NULL_HSTMT;
// Environment begin
rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
assert (SQL_SUCCEEDED(rc));
rc = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, 0);
assert (SQL_SUCCEEDED(rc));
// Connection begin
rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
assert (SQL_SUCCEEDED(rc));
POCO_SQLCHAR connectOutput[512] = {0};
SQLSMALLINT result;
rc = SQLDriverConnect(hdbc
, NULL
,(POCO_SQLCHAR*) dbConnString.c_str()
,(SQLSMALLINT) SQL_NTS
, connectOutput
, sizeof(connectOutput)
, &result
, SQL_DRIVER_NOPROMPT);
assert (SQL_SUCCEEDED(rc));
// Statement begin
rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
assert (SQL_SUCCEEDED(rc));
std::string sql = "DROP TABLE Test";
POCO_SQLCHAR* pStr = (POCO_SQLCHAR*) sql.c_str();
SQLExecDirect(hstmt, pStr, (SQLINTEGER) sql.length());
//no return code check - ignore drop errors
// create table and go
sql = tableCreateString;
pStr = (POCO_SQLCHAR*) sql.c_str();
rc = SQLPrepare(hstmt, pStr, (SQLINTEGER) sql.length());
assert (SQL_SUCCEEDED(rc));
rc = SQLExecute(hstmt);
assert (SQL_SUCCEEDED(rc));
// insert multiple rows
pStr = (POCO_SQLCHAR*) insert.c_str();
rc = SQLPrepare(hstmt, pStr, (SQLINTEGER) insert.length());
assert (SQL_SUCCEEDED(rc));
rc = SQLExecute(hstmt);
assert (SQL_SUCCEEDED(rc));
do
{
SQLINTEGER rowCount = 0;
SQLRowCount(hstmt, &rowCount);
assert (1 == rowCount);
} while (SQL_NO_DATA != SQLMoreResults(hstmt));
// select multiple rows
pStr = (POCO_SQLCHAR*) select.c_str();
rc = SQLPrepare(hstmt, pStr, (SQLINTEGER) select.length());
assert (SQL_SUCCEEDED(rc));
char chr[5] = { 0 };
SQLLEN lengths[3] = { 0 };
int second = 0;
float third = 0.0f;
if (SQLExecutor::DE_BOUND == extractMode)
{
rc = SQLBindCol(hstmt,
(SQLUSMALLINT) 1,
SQL_C_CHAR,
(SQLPOINTER) chr,
(SQLINTEGER) 4,
&lengths[0]);
assert (SQL_SUCCEEDED(rc));
rc = SQLBindCol(hstmt,
(SQLUSMALLINT) 2,
SQL_C_SLONG,
(SQLPOINTER) &second,
(SQLINTEGER) 0,
&lengths[1]);
assert (SQL_SUCCEEDED(rc));
rc = SQLBindCol(hstmt,
(SQLUSMALLINT) 3,
SQL_C_FLOAT,
(SQLPOINTER) &third,
(SQLINTEGER) 0,
&lengths[2]);
assert (SQL_SUCCEEDED(rc));
}
rc = SQLExecute(hstmt);
print_odbc_error (rc, hstmt);
assert (SQL_SUCCEEDED(rc));
char one = 0x31;
int two = 2;
float three = 3.5;
do
{
rc = SQLFetch(hstmt);
print_odbc_error (rc, hstmt);
assert (SQL_SUCCEEDED(rc));
if (SQLExecutor::DE_MANUAL == extractMode)
{
rc = SQLGetData(hstmt,
(SQLUSMALLINT) 1,
SQL_C_CHAR,
chr,
4,
&lengths[0]);
assert (SQL_SUCCEEDED(rc));
rc = SQLGetData(hstmt,
(SQLUSMALLINT) 2,
SQL_C_SLONG,
&second,
0,
&lengths[1]);
assert (SQL_SUCCEEDED(rc));
rc = SQLGetData(hstmt,
(SQLUSMALLINT) 3,
SQL_C_FLOAT,
&third,
0,
&lengths[2]);
assert (SQL_SUCCEEDED(rc));
}
assert (one++ == chr[0]);
assert (two++ == second);
assert (three == third);
three += 1.0;
} while (SQL_NO_DATA != SQLMoreResults(hstmt));
sql = "DROP TABLE Test";
pStr = (POCO_SQLCHAR*) sql.c_str();
rc = SQLExecDirect(hstmt, pStr, (SQLINTEGER) sql.length());
assert (SQL_SUCCEEDED(rc));
rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
assert (SQL_SUCCEEDED(rc));
// Connection end
rc = SQLDisconnect(hdbc);
assert (SQL_SUCCEEDED(rc));
rc = SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
assert (SQL_SUCCEEDED(rc));
// Environment end
rc = SQLFreeHandle(SQL_HANDLE_ENV, henv);
assert (SQL_SUCCEEDED(rc));
}
void SQLExecutor::simpleAccess()
{
std::string funct = "simpleAccess()";
@ -2373,3 +2567,27 @@ void SQLExecutor::dynamicAny()
assert (42.5 == f);
assert ("42" == s);
}
void SQLExecutor::multipleResults()
{
typedef Tuple<std::string, std::string, std::string, int> Person;
std::vector<Person> people;
people.push_back(Person("Simpson", "Homer", "Springfield", 42));
people.push_back(Person("Simpson", "Bart", "Springfield", 12));
people.push_back(Person("Simpson", "Lisa", "Springfield", 10));
*_pSession << "INSERT INTO Person VALUES (?, ?, ?, ?)", use(people), now;
Person Homer, Lisa, Bart;
*_pSession << "SELECT * FROM Person WHERE FirstName = 'Homer'; "
"SELECT * FROM Person WHERE FirstName = 'Bart'; "
"SELECT * FROM Person WHERE FirstName = 'Lisa'; "
, into(Homer, 0), into(Bart, 1), into(Lisa, 2)
, now;
assert (Person("Simpson", "Homer", "Springfield", 42) == Homer);
assert (Person("Simpson", "Bart", "Springfield", 12) == Bart);
assert (Person("Simpson", "Lisa", "Springfield", 10) == Lisa);
}

View File

@ -64,12 +64,19 @@ public:
DataBinding bindMode,
DataExtraction extractMode,
bool doTime=true);
/// This function uses "bare bone" ODBC API calls (i.e. calls are not
void bareboneODBCMultiResultTest(const std::string& dbConnString,
const std::string& tableCreateString,
SQLExecutor::DataBinding bindMode,
SQLExecutor::DataExtraction extractMode,
const std::string& insert = MULTI_INSERT,
const std::string& select = MULTI_SELECT);
/// These functions use "bare bone" ODBC API calls (i.e. calls are not
/// "wrapped" in PocoData framework structures).
/// The purpose of the function is to verify that driver behaves
/// The purpose of the functions is to verify that a driver behaves
/// correctly as well as to determine its capabilities
/// (e.g. SQLGetData() restrictions relaxation policy, if any).
/// If this test passes, subsequent tests failures are likely ours.
/// If these test pass, subsequent tests failures are likely ours.
void simpleAccess();
void complexType();
@ -139,7 +146,12 @@ public:
void any();
void dynamicAny();
void multipleResults();
private:
static const std::string MULTI_INSERT;
static const std::string MULTI_SELECT;
Poco::Data::Session* _pSession;
};

View File

@ -62,7 +62,8 @@ class Data_API AbstractExtraction: public Poco::RefCountedObject
/// retrieved via an AbstractExtractor.
{
public:
AbstractExtraction(Poco::UInt32 limit = Limit::LIMIT_UNLIMITED);
AbstractExtraction(Poco::UInt32 limit = Limit::LIMIT_UNLIMITED,
Poco::UInt32 position = 0);
/// Creates the AbstractExtraction. A limit value equal to EXTRACT_UNLIMITED (0xffffffffu)
/// means that we extract as much data as possible during one execute.
/// Otherwise the limit value is used to partition data extracting to a limited amount of rows.
@ -76,6 +77,9 @@ public:
AbstractExtractor* getExtractor() const;
/// Retrieves the extractor object
Poco::UInt32 position() const;
/// Returns the extraction position.
virtual std::size_t numOfColumnsHandled() const = 0;
/// Returns the number of columns that the extraction handles.
///
@ -98,7 +102,7 @@ public:
/// Resets the extractor so that it can be re-used.
virtual AbstractPrepare* createPrepareObject(AbstractPreparation* pPrep, std::size_t pos) const = 0;
/// Creates a Prepare object for the etxracting object
/// Creates a Prepare object for the extracting object
void setLimit(Poco::UInt32 limit);
/// Sets the limit.
@ -117,11 +121,13 @@ public:
private:
AbstractExtractor* _pExtractor;
Poco::UInt32 _limit;
Poco::UInt32 _position;
};
typedef Poco::AutoPtr<AbstractExtraction> AbstractExtractionPtr;
typedef std::vector<AbstractExtractionPtr> AbstractExtractionVec;
typedef std::vector<AbstractExtractionVec> AbstractExtractionVecVec;
//
@ -157,6 +163,12 @@ inline bool AbstractExtraction::isNull(std::size_t row) const
}
inline Poco::UInt32 AbstractExtraction::position() const
{
return _position;
}
} } // namespace Poco::Data

View File

@ -63,13 +63,23 @@ class Extraction: public AbstractExtraction
/// Concrete Data Type specific extraction of values from a query result set.
{
public:
Extraction(T& result): _rResult(result), _default(), _extracted(false)
/// Creates an Extraction object, uses an empty object T as default value
Extraction(T& result, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default(),
_extracted(false)
/// Creates an Extraction object at specified position.
/// Uses an empty object T as default value.
{
}
Extraction(T& result, const T& def): _rResult(result), _default(def), _extracted(false)
/// Creates an Extraction object, uses the provided def object as default value
Extraction(T& result, const T& def, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default(def),
_extracted(false)
/// Creates an Extraction object at specified position.
/// Uses the provided def object as default value.
{
}
@ -130,11 +140,17 @@ class Extraction<std::vector<T> >: public AbstractExtraction
/// Vector Data Type specialization for extraction of values from a query result set.
{
public:
Extraction(std::vector<T>& result): _rResult(result), _default()
Extraction(std::vector<T>& result, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default()
{
}
Extraction(std::vector<T>& result, const T& def): _rResult(result), _default(def)
Extraction(std::vector<T>& result, const T& def, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default(def)
{
}
@ -209,11 +225,17 @@ class Extraction<std::list<T> >: public AbstractExtraction
/// List Data Type specialization for extraction of values from a query result set.
{
public:
Extraction(std::list<T>& result): _rResult(result), _default()
Extraction(std::list<T>& result, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default()
{
}
Extraction(std::list<T>& result, const T& def): _rResult(result), _default(def)
Extraction(std::list<T>& result, const T& def, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default(def)
{
}
@ -283,11 +305,17 @@ class Extraction<std::deque<T> >: public AbstractExtraction
/// Deque Data Type specialization for extraction of values from a query result set.
{
public:
Extraction(std::deque<T>& result): _rResult(result), _default()
Extraction(std::deque<T>& result, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default()
{
}
Extraction(std::deque<T>& result, const T& def): _rResult(result), _default(def)
Extraction(std::deque<T>& result, const T& def, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default(def)
{
}
@ -365,11 +393,10 @@ class InternalExtraction: public Extraction<C>
/// InternalExtraction objects can not be copied or assigned.
{
public:
explicit InternalExtraction(C& result, Column<T,C>* pColumn):
Extraction<C>(result),
explicit InternalExtraction(C& result, Column<T,C>* pColumn, Poco::UInt32 position = 0):
Extraction<C>(result, T(), position),
_pColumn(pColumn)
/// Creates InternalExtraction.
{
}
@ -420,11 +447,17 @@ class Extraction<std::set<T> >: public AbstractExtraction
/// Set Data Type specialization for extraction of values from a query result set.
{
public:
Extraction(std::set<T>& result): _rResult(result), _default()
Extraction(std::set<T>& result, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default()
{
}
Extraction(std::set<T>& result, const T& def): _rResult(result), _default(def)
Extraction(std::set<T>& result, const T& def, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default(def)
{
}
@ -474,11 +507,17 @@ class Extraction<std::multiset<T> >: public AbstractExtraction
/// Multiset Data Type specialization for extraction of values from a query result set.
{
public:
Extraction(std::multiset<T>& result): _rResult(result), _default()
Extraction(std::multiset<T>& result, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default()
{
}
Extraction(std::multiset<T>& result, const T& def): _rResult(result), _default(def)
Extraction(std::multiset<T>& result, const T& def, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default(def)
{
}
@ -528,11 +567,17 @@ class Extraction<std::map<K, V> >: public AbstractExtraction
/// Map Data Type specialization for extraction of values from a query result set.
{
public:
Extraction(std::map<K, V>& result): _rResult(result), _default()
Extraction(std::map<K, V>& result, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default()
{
}
Extraction(std::map<K, V>& result, const V& def): _rResult(result), _default(def)
Extraction(std::map<K, V>& result, const V& def, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default(def)
{
}
@ -583,11 +628,17 @@ class Extraction<std::multimap<K, V> >: public AbstractExtraction
/// Multimap Data Type specialization for extraction of values from a query result set.
{
public:
Extraction(std::multimap<K, V>& result): _rResult(result), _default()
Extraction(std::multimap<K, V>& result, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default()
{
}
Extraction(std::multimap<K, V>& result, const V& def): _rResult(result), _default(def)
Extraction(std::multimap<K, V>& result, const V& def, Poco::UInt32 position = 0):
AbstractExtraction(Limit::LIMIT_UNLIMITED, position),
_rResult(result),
_default(def)
{
}
@ -633,18 +684,18 @@ private:
template <typename T>
Extraction<T>* into(T& t)
Extraction<T>* into(T& t, Poco::UInt32 pos = 0)
/// Convenience function to allow for a more compact creation of an extraction object
{
return new Extraction<T>(t);
return new Extraction<T>(t, pos);
}
template <typename T>
Extraction<T>* into(T& t, const T& def)
Extraction<T>* into(T& t, Poco::UInt32 pos, const T& def)
/// Convenience function to allow for a more compact creation of an extraction object with the given default
{
return new Extraction<T>(t, def);
return new Extraction<T>(t, def, pos);
}

View File

@ -177,6 +177,8 @@ public:
Statement& operator , (AbstractExtraction* extract);
/// Registers objects used for extracting data at the Statement.
/// the position argument is used by connectors that support multilple
/// recordsets to specify which recordset this extraction belongs to.
Statement& operator , (const Limit& extrLimit);
/// Sets a limit on the maximum number of rows a select is allowed to return.

View File

@ -103,10 +103,10 @@ public:
_ostr << t;
}
void addBinding(AbstractBinding* info);
void addBinding(AbstractBinding* pBinding);
/// Registers the Binding at the StatementImpl.
void addExtract(AbstractExtraction* info);
void addExtract(AbstractExtraction* pExtraction);
/// Registers objects used for extracting data at the StatementImpl.
void setExtractionLimit(const Limit& extrLimit);
@ -137,6 +137,9 @@ public:
/// Returns the number of extraction storage buffers associated
/// with the statement.
std::size_t dataSetCount() const;
/// Returns the number of data sets associated with the statement.
protected:
virtual Poco::UInt32 columnsReturned() const = 0;
/// Returns number of columns returned by query.
@ -238,6 +241,12 @@ protected:
void fixupExtraction();
/// Sets the AbstractExtractor at the extractors.
Poco::UInt32 currentDataSet() const;
/// Returns the current data set.
Poco::UInt32 activateNextDataSet();
/// Returns the next data set, or -1 if the last data set was reached.
private:
void compile();
/// Compiles the statement, if not yet compiled. doesn't bind yet
@ -252,7 +261,7 @@ private:
/// Executes without an upper limit set.
void resetExtraction();
/// Resets binding so it can be reused again.
/// Resets extraction so it can be reused again.
template <class T, class C>
InternalExtraction<T,C>* createExtract(const MetaColumn& mc)
@ -306,15 +315,16 @@ private:
StatementImpl(const StatementImpl& stmt);
StatementImpl& operator = (const StatementImpl& stmt);
State _state;
Limit _extrLimit;
Poco::UInt32 _lowerLimit;
int _columnsExtracted;
SessionImpl& _rSession;
Storage _storage;
std::ostringstream _ostr;
AbstractBindingVec _bindings;
AbstractExtractionVec _extractors;
State _state;
Limit _extrLimit;
Poco::UInt32 _lowerLimit;
int _columnsExtracted;
SessionImpl& _rSession;
Storage _storage;
std::ostringstream _ostr;
AbstractBindingVec _bindings;
AbstractExtractionVecVec _extractors;
Poco::UInt32 _curDataSet;
friend class Statement;
};
@ -323,15 +333,11 @@ private:
//
// inlines
//
inline void StatementImpl::addBinding(AbstractBinding* info)
inline void StatementImpl::addBinding(AbstractBinding* pBinding)
{
_bindings.push_back(info);
}
poco_check_ptr (pBinding);
inline void StatementImpl::addExtract(AbstractExtraction* info)
{
_extractors.push_back(info);
_bindings.push_back(pBinding);
}
@ -355,13 +361,15 @@ inline AbstractBindingVec& StatementImpl::bindings()
inline const AbstractExtractionVec& StatementImpl::extractions() const
{
return _extractors;
poco_assert (_curDataSet < _extractors.size());
return _extractors[_curDataSet];
}
inline AbstractExtractionVec& StatementImpl::extractions()
{
return _extractors;
poco_assert (_curDataSet < _extractors.size());
return _extractors[_curDataSet];
}
@ -401,6 +409,12 @@ inline std::size_t StatementImpl::extractionCount() const
}
inline std::size_t StatementImpl::dataSetCount() const
{
return _extractors.size();
}
inline bool StatementImpl::isStoredProcedure() const
{
return false;
@ -419,6 +433,12 @@ inline bool StatementImpl::isNull(std::size_t col, std::size_t row) const
}
inline Poco::UInt32 StatementImpl::currentDataSet() const
{
return _curDataSet;
}
} } // namespace Poco::Data

View File

@ -41,9 +41,11 @@ namespace Poco {
namespace Data {
AbstractExtraction::AbstractExtraction(Poco::UInt32 limit):
AbstractExtraction::AbstractExtraction(Poco::UInt32 limit,
Poco::UInt32 position):
_pExtractor(0),
_limit(limit)
_limit(limit),
_position(position)
{
}

View File

@ -66,8 +66,10 @@ StatementImpl::StatementImpl(SessionImpl& rSession):
_rSession(rSession),
_storage(STORAGE_UNKNOWN_IMPL),
_ostr(),
_bindings()
_bindings(),
_curDataSet(0)
{
_extractors.resize(1);
}
@ -332,4 +334,25 @@ const MetaColumn& StatementImpl::metaColumn(const std::string& name) const
}
Poco::UInt32 StatementImpl::activateNextDataSet()
{
if (_curDataSet + 1 < dataSetCount())
return ++_curDataSet;
else
throw InvalidAccessException("End of data sets reached.");
}
void StatementImpl::addExtract(AbstractExtraction* pExtraction)
{
poco_check_ptr (pExtraction);
Poco::UInt32 pos = pExtraction->position();
if (pos >= _extractors.size())
_extractors.resize(pos + 1);
_extractors[pos].push_back(pExtraction);
}
} } // namespace Poco::Data