Browse Source

multiple results (WIP, compiles and tests pass)

Aleksandar Fabijanic 18 years ago
parent
commit
c3c422d87d

+ 27 - 17
Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h

@@ -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);
 }
 
 

+ 10 - 4
Data/ODBC/include/Poco/Data/ODBC/Preparation.h

@@ -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;
 };
 
 

+ 64 - 23
Data/ODBC/src/ODBCStatementImpl.cpp

@@ -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();

+ 8 - 0
Data/ODBC/src/Preparation.cpp

@@ -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();

+ 11 - 0
Data/ODBC/testsuite/src/ODBCDB2Test.cpp

@@ -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);
 }
 
 

+ 11 - 0
Data/ODBC/testsuite/src/ODBCMySQLTest.cpp

@@ -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);
 }
 
 

+ 55 - 0
Data/ODBC/testsuite/src/ODBCOracleTest.cpp

@@ -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);
 }
 
 

+ 2 - 0
Data/ODBC/testsuite/src/ODBCOracleTest.h

@@ -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;
 };
 
 

+ 28 - 0
Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp

@@ -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;
 	}

+ 2 - 0
Data/ODBC/testsuite/src/ODBCPostgreSQLTest.h

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

+ 29 - 1
Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp

@@ -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;
 	}

+ 2 - 0
Data/ODBC/testsuite/src/ODBCSQLServerTest.h

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

+ 218 - 0
Data/ODBC/testsuite/src/SQLExecutor.cpp

@@ -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);
+}
+

+ 15 - 3
Data/ODBC/testsuite/src/SQLExecutor.h

@@ -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;
 };
 

+ 14 - 2
Data/include/Poco/Data/AbstractExtraction.h

@@ -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
 
 

+ 76 - 25
Data/include/Poco/Data/Extraction.h

@@ -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);
 }
 
 

+ 2 - 0
Data/include/Poco/Data/Statement.h

@@ -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.

+ 41 - 21
Data/include/Poco/Data/StatementImpl.h

@@ -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
 
 

+ 4 - 2
Data/src/AbstractExtraction.cpp

@@ -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)
 {
 }
 

+ 24 - 1
Data/src/StatementImpl.cpp

@@ -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