mirror of
				https://github.com/pocoproject/poco.git
				synced 2025-10-30 21:50:47 +01:00 
			
		
		
		
	multiple results (WIP, compiles and tests pass)
This commit is contained in:
		| @@ -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); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| }; | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| }; | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| 	} | ||||
|   | ||||
| @@ -141,6 +141,8 @@ public: | ||||
| 	void testAny(); | ||||
| 	void testDynamicAny(); | ||||
|  | ||||
| 	void testMultipleResults(); | ||||
|  | ||||
| 	void setUp(); | ||||
| 	void tearDown(); | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| 	} | ||||
|   | ||||
| @@ -138,6 +138,8 @@ public: | ||||
| 	void testAny(); | ||||
| 	void testDynamicAny(); | ||||
|  | ||||
| 	void testMultipleResults(); | ||||
|  | ||||
| 	void setUp(); | ||||
| 	void tearDown(); | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
| { | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Aleksandar Fabijanic
					Aleksandar Fabijanic