diff --git a/Data/ODBC/include/Poco/Data/ODBC/Binder.h b/Data/ODBC/include/Poco/Data/ODBC/Binder.h index 8b428d384..d8696615e 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Binder.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Binder.h @@ -157,13 +157,8 @@ private: /// This is a private no-op in this implementation /// due to security risk. - std::size_t getParamSize(std::size_t pos); - /// Returns parameter size as defined by data source. - /// Used to determine buffer size for variable size out-bound parameters - /// (string and BLOB). - - SQLSMALLINT getParamType(Direction dir) const; - /// Returns ODBC parameter type based on the parameter binding direction + SQLSMALLINT toODBCDirection(Direction dir) const; + /// Returns ODBC parameter direction based on the parameter binding direction /// specified by user. template @@ -184,7 +179,7 @@ private: if (Utility::isError(SQLBindParameter(_rStmt, (SQLUSMALLINT) pos + 1, - getParamType(dir), + toODBCDirection(dir), cDataType, Utility::sqlDataType(cDataType), colSize, diff --git a/Data/ODBC/include/Poco/Data/ODBC/Diagnostics.h b/Data/ODBC/include/Poco/Data/ODBC/Diagnostics.h index 5c28d36e6..352793a0f 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Diagnostics.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Diagnostics.h @@ -57,8 +57,8 @@ template class Diagnostics /// Utility class providing functionality for retrieving ODBC diagnostic /// records. Diagnostics object must be created with corresponding handle - /// as constructor argument. During construction, diagnostic records with corresponding - /// fields are populated and the object is ready for querying on various diagnostic fields. + /// as constructor argument. During construction, diagnostic records fields + /// are populated and the object is ready for querying. { public: @@ -147,12 +147,12 @@ public: return _fields; } - const Iterator begin() const + Iterator begin() const { return _fields.begin(); } - const Iterator end() const + Iterator end() const { return _fields.end(); } diff --git a/Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h b/Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h index 0617f21ca..3b32e32f3 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h +++ b/Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h @@ -132,6 +132,12 @@ private: void fillColumns(); void checkError(SQLRETURN rc, const std::string& msg=""); + + bool isStoredProcedure() const; + /// Returns true if this statement is stored procedure. + /// Only the ODBC CALL escape sequence is supported. + /// The function checks whether trimmed statement + /// text begins with '{' and ends with '}'; const SQLHDBC& _rConnection; const StatementHandle _stmt; @@ -171,7 +177,7 @@ inline Poco::UInt32 ODBCStatementImpl::columnsReturned() const inline bool ODBCStatementImpl::hasData() const { poco_assert_dbg (_pPreparation); - return (_pPreparation->columns() > 0); + return (_pPreparation->columns(!isStoredProcedure()) > 0); } diff --git a/Data/ODBC/include/Poco/Data/ODBC/Preparation.h b/Data/ODBC/include/Poco/Data/ODBC/Preparation.h index d6bc10edc..38831c538 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Preparation.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Preparation.h @@ -150,8 +150,9 @@ public: void prepare(std::size_t pos, const Poco::Any&); /// Prepares an Any. - std::size_t columns() const; + std::size_t columns(bool resize = true) const; /// Returns the number of columns. + /// Resizes the internal storage iff resize is true. Poco::Any& operator [] (std::size_t pos); /// Returns reference to column data. diff --git a/Data/ODBC/src/Binder.cpp b/Data/ODBC/src/Binder.cpp index a8c1ff8c3..e6c73d2a1 100644 --- a/Data/ODBC/src/Binder.cpp +++ b/Data/ODBC/src/Binder.cpp @@ -81,8 +81,26 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir) if (isOutBound(dir)) { - Parameter p(_rStmt, pos); - size = (SQLINTEGER) p.columnSize(); + try + { + Parameter p(_rStmt, pos); + size = (SQLUINTEGER) p.columnSize(); + } + catch (StatementException&) + // some drivers (e.g. MS SQL) refuse to describe output parameters + { + SQLHDESC hIPD = 0; + if (!Utility::isError(SQLGetStmtAttr(_rStmt, SQL_ATTR_IMP_PARAM_DESC, &hIPD, 0, 0))) + { + size = 1024; + SQLUINTEGER sz = size; + if (Utility::isError(SQLSetDescField(hIPD, (SQLSMALLINT) pos + 1, SQL_DESC_LENGTH, &sz, 0))) + throw StatementException(_rStmt, "Could not set output parameter size"); + } + else + throw StatementException(_rStmt, "Could not get statement IPD attribute handle."); + } + char* pChar = (char*) std::calloc(size, sizeof(char)); pVal = (SQLPOINTER) pChar; _outParams.insert(ParamMap::value_type(pVal, size)); @@ -106,7 +124,7 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir) if (Utility::isError(SQLBindParameter(_rStmt, (SQLUSMALLINT) pos + 1, - getParamType(dir), + toODBCDirection(dir), SQL_C_CHAR, SQL_LONGVARCHAR, (SQLUINTEGER) size, @@ -181,7 +199,7 @@ void Binder::bind(std::size_t pos, const Poco::DateTime& val, Direction dir) if (Utility::isError(SQLBindParameter(_rStmt, (SQLUSMALLINT) pos + 1, - getParamType(dir), + toODBCDirection(dir), SQL_C_TIMESTAMP, SQL_TIMESTAMP, colSize, @@ -213,7 +231,7 @@ void Binder::bind(std::size_t pos, const char* const &pVal, Direction dir) } -SQLSMALLINT Binder::getParamType(Direction dir) const +SQLSMALLINT Binder::toODBCDirection(Direction dir) const { bool in = isInBound(dir); bool out = isOutBound(dir); diff --git a/Data/ODBC/src/ODBCStatementImpl.cpp b/Data/ODBC/src/ODBCStatementImpl.cpp index 88f53ad78..32321fe9e 100644 --- a/Data/ODBC/src/ODBCStatementImpl.cpp +++ b/Data/ODBC/src/ODBCStatementImpl.cpp @@ -347,4 +347,13 @@ void ODBCStatementImpl::fillColumns() } +bool ODBCStatementImpl::isStoredProcedure() const +{ + std::string str = toString(); + if (trimInPlace(str).size() < 2) return false; + + return ('{' == str[0] && '}' == str[str.size()-1]); +} + + } } } // namespace Poco::Data::ODBC diff --git a/Data/ODBC/src/Preparation.cpp b/Data/ODBC/src/Preparation.cpp index 74ca8367f..a02ed7dc4 100644 --- a/Data/ODBC/src/Preparation.cpp +++ b/Data/ODBC/src/Preparation.cpp @@ -69,15 +69,13 @@ Preparation::~Preparation() } -std::size_t Preparation::columns() const +std::size_t Preparation::columns(bool resize) const { - if (_pValues.empty()) + if (_pValues.empty() && resize) { SQLSMALLINT nCol = 0; - if (Utility::isError(SQLNumResultCols(_rStmt, &nCol))) - throw StatementException(_rStmt); - - if (nCol) + if (!Utility::isError(SQLNumResultCols(_rStmt, &nCol)) && + 0 != nCol) { _pValues.resize(nCol, 0); _pLengths.resize(nCol, 0); diff --git a/Data/ODBC/testsuite/src/ODBCDB2Test.cpp b/Data/ODBC/testsuite/src/ODBCDB2Test.cpp index fa7f67898..c807b68ef 100644 --- a/Data/ODBC/testsuite/src/ODBCDB2Test.cpp +++ b/Data/ODBC/testsuite/src/ODBCDB2Test.cpp @@ -35,6 +35,7 @@ #include "CppUnit/TestSuite.h" #include "Poco/String.h" #include "Poco/Format.h" +#include "Poco/Tuple.h" #include "Poco/Exception.h" #include "Poco/Data/Common.h" #include "Poco/Data/BLOB.h" @@ -54,6 +55,7 @@ using Poco::Data::ODBC::ConnectionException; using Poco::Data::ODBC::StatementException; using Poco::Data::ODBC::StatementDiagnostics; using Poco::format; +using Poco::Tuple; using Poco::NotFoundException; @@ -859,6 +861,170 @@ void ODBCDB2Test::testInternalStorageType() } +void ODBCDB2Test::testStoredProcedure() +{ + if (!_pSession) fail ("Test not available."); + + for (int k = 0; k < 8;) + { + _pSession->setFeature("autoBind", bindValues[k]); + _pSession->setFeature("autoExtract", bindValues[k+1]); + + dropObject("PROCEDURE", "storedProcedure"); + *_pSession << "CREATE PROCEDURE storedProcedure(OUT outParam INTEGER) " + "BEGIN " + " SET outParam = -1; " + "END" , now; + + int i = 0; + *_pSession << "{call storedProcedure(?)}", out(i), now; + assert(-1 == i); + dropObject("PROCEDURE", "storedProcedure"); + + *_pSession << "CREATE PROCEDURE storedProcedure(inParam INTEGER, OUT outParam INTEGER) " + "BEGIN " + " SET outParam = inParam*inParam; " + "END" , now; + + + i = 2; + int j = 0; + *_pSession << "{call storedProcedure(?, ?)}", in(i), out(j), now; + assert(4 == j); + dropObject("PROCEDURE", "storedProcedure"); + + *_pSession << "CREATE PROCEDURE storedProcedure(INOUT ioParam INTEGER) " + "BEGIN " + " SET ioParam = ioParam*ioParam; " + "END" , now; + + i = 2; + *_pSession << "{call storedProcedure(?)}", io(i), now; + assert(4 == i); + dropObject("PROCEDURE", "storedProcedure"); + + //TIMESTAMP is not supported as stored procedure parameter in DB2 + //(SQL0182N An expression with a datetime value or a labeled duration is not valid.) + + *_pSession << "CREATE PROCEDURE storedProcedure(inParam VARCHAR(1000), OUT outParam VARCHAR(1000)) " + "BEGIN " + " SET outParam = inParam; " + "END" , now; + + std::string inParam = + "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; + std::string outParam; + *_pSession << "{call storedProcedure(?,?)}", in(inParam), out(outParam), now; + assert(inParam == outParam); + dropObject("PROCEDURE", "storedProcedure"); + + k += 2; + } +} + + +void ODBCDB2Test::testStoredFunction() +{ + if (!_pSession) fail ("Test not available."); + + for (int k = 0; k < 8;) + { + _pSession->setFeature("autoBind", bindValues[k]); + _pSession->setFeature("autoExtract", bindValues[k+1]); + + dropObject("PROCEDURE", "storedFunction"); + *_pSession << "CREATE PROCEDURE storedFunction() " + "BEGIN " + " RETURN -1; " + "END" , now; + + int i = 0; + *_pSession << "{? = call storedFunction()}", out(i), now; + assert(-1 == i); + dropObject("PROCEDURE", "storedFunction"); + + *_pSession << "CREATE PROCEDURE storedFunction(inParam INTEGER) " + "BEGIN " + " RETURN inParam*inParam; " + "END" , now; + + i = 2; + int result = 0; + *_pSession << "{? = call storedFunction(?)}", out(result), in(i), now; + assert(4 == result); + dropObject("PROCEDURE", "storedFunction"); + + *_pSession << "CREATE PROCEDURE storedFunction(inParam INTEGER, OUT outParam INTEGER) " + "BEGIN " + " SET outParam = inParam*inParam; " + " RETURN outParam; " + "END" , now; + + i = 2; + int j = 0; + result = 0; + *_pSession << "{? = call storedFunction(?, ?)}", out(result), in(i), out(j), now; + assert(4 == j); + assert(j == result); + dropObject("PROCEDURE", "storedFunction"); + + *_pSession << "CREATE PROCEDURE storedFunction(INOUT param1 INTEGER, INOUT param2 INTEGER) " + "BEGIN " + " DECLARE temp INTEGER;" + " SET temp = param1; " + " SET param1 = param2; " + " SET param2 = temp; " + " RETURN param1 + param2; " + "END" , now; + + i = 1; + j = 2; + result = 0; + *_pSession << "{? = call storedFunction(?, ?)}", out(result), io(i), io(j), now; + assert(1 == j); + assert(2 == i); + assert(3 == result); + + Tuple params(1, 2); + assert(1 == params.get<0>()); + assert(2 == params.get<1>()); + result = 0; + *_pSession << "{? = call storedFunction(?, ?)}", out(result), io(params), now; + assert(1 == params.get<1>()); + assert(2 == params.get<0>()); + assert(3 == result); + + dropObject("PROCEDURE", "storedFunction"); + + _pSession->setFeature("autoBind", true); + + *_pSession << "CREATE PROCEDURE storedFunction(inParam VARCHAR(10), OUT outParam VARCHAR(10)) " + "BEGIN " + " SET outParam = inParam; " + " RETURN LENGTH(outParam);"//DB2 allows only integer as return type + "END" , now; + + std::string inParam = "123456789"; + std::string outParam; + int ret; + *_pSession << "{? = call storedFunction(?,?)}", out(ret), in(inParam), out(outParam), now; + assert(inParam == outParam); + assert(ret == inParam.size()); + dropObject("PROCEDURE", "storedFunction"); + + k += 2; + } +} + + void ODBCDB2Test::dropObject(const std::string& type, const std::string& name) { try @@ -1098,6 +1264,8 @@ CppUnit::Test* ODBCDB2Test::suite() CppUnit_addTest(pSuite, ODBCDB2Test, testTupleVector); CppUnit_addTest(pSuite, ODBCDB2Test, testInternalExtraction); CppUnit_addTest(pSuite, ODBCDB2Test, testInternalStorageType); + CppUnit_addTest(pSuite, ODBCDB2Test, testStoredProcedure); + CppUnit_addTest(pSuite, ODBCDB2Test, testStoredFunction); return pSuite; } diff --git a/Data/ODBC/testsuite/src/ODBCDB2Test.h b/Data/ODBC/testsuite/src/ODBCDB2Test.h index 661022028..b7a6e96c4 100644 --- a/Data/ODBC/testsuite/src/ODBCDB2Test.h +++ b/Data/ODBC/testsuite/src/ODBCDB2Test.h @@ -119,6 +119,9 @@ public: void testInternalExtraction(); void testInternalStorageType(); + void testStoredProcedure(); + void testStoredFunction(); + void setUp(); void tearDown(); diff --git a/Data/ODBC/testsuite/src/ODBCOracleTest.cpp b/Data/ODBC/testsuite/src/ODBCOracleTest.cpp index eb4c4c5d4..649fdd61d 100644 --- a/Data/ODBC/testsuite/src/ODBCOracleTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCOracleTest.cpp @@ -1023,7 +1023,7 @@ void ODBCOracleTest::testStoredFunction() k += 2; } - //string and BLOB for automatic binding only + //string for automatic binding only _pSession->setFeature("autoBind", true); *_pSession << "CREATE OR REPLACE " diff --git a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp index 7fb791c98..5b9edf9ef 100644 --- a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp @@ -51,6 +51,7 @@ using namespace Poco::Data; using Poco::Data::ODBC::Utility; +using Poco::Data::ODBC::ODBCException; using Poco::Data::ODBC::ConnectionException; using Poco::Data::ODBC::StatementException; using Poco::Data::ODBC::StatementDiagnostics; @@ -64,6 +65,11 @@ Poco::SharedPtr ODBCPostgreSQLTest::_pSession = 0; Poco::SharedPtr ODBCPostgreSQLTest::_pExecutor = 0; std::string ODBCPostgreSQLTest::_dbConnString; Poco::Data::ODBC::Utility::DriverMap ODBCPostgreSQLTest::_drivers; +#ifdef POCO_OS_FAMILY_WINDOWS +const std::string ODBCPostgreSQLTest::libDir = "C:\\\\Program Files\\\\PostgreSQL\\\\8.2\\\\lib\\\\"; +#else +const std::string ODBCPostgreSQLTest::libDir = "/usr/local/pgsql/lib/"; +#endif ODBCPostgreSQLTest::ODBCPostgreSQLTest(const std::string& name): @@ -857,6 +863,83 @@ void ODBCPostgreSQLTest::testInternalStorageType() } +void ODBCPostgreSQLTest::testStoredFunction() +{ + configurePLPgSQL(); + + for (int k = 0; k < 8;) + { + _pSession->setFeature("autoBind", bindValues[k]); + _pSession->setFeature("autoExtract", bindValues[k+1]); + + *_pSession << "CREATE FUNCTION storedFunction() RETURNS INTEGER AS '" + "BEGIN " + " return -1; " + "END;'" + "LANGUAGE 'plpgsql'", now; + + int i = 0; + *_pSession << "{? = call storedFunction()}", out(i), now; + assert(-1 == i); + dropObject("FUNCTION", "storedFunction()"); + + *_pSession << "CREATE FUNCTION storedFunction(INTEGER) RETURNS INTEGER AS '" + "BEGIN " + " RETURN $1 * $1; " + "END;'" + "LANGUAGE 'plpgsql'" , now; + + i = 2; + int result = 0; + *_pSession << "{? = call storedFunction(?)}", out(result), in(i), now; + assert(4 == result); + dropObject("FUNCTION", "storedFunction(INTEGER)"); +/*TODO + *_pSession << "CREATE FUNCTION storedFunction(TEXT) RETURNS TEXT AS '" + "BEGIN " + " RETURN $1;" + "END;'" + "LANGUAGE 'plpgsql'" , now; + + std::string param = "123"; + std::string ret; + try { + *_pSession << "{? = call storedFunction(?)}", out(ret), in(param), now; + }catch(StatementException & ex) { std::cout << ex.toString();} + assert(ret == param); + dropObject("FUNCTION", "storedFunction(TEXT)"); +*/ + + k += 2; + } +} + + +void ODBCPostgreSQLTest::configurePLPgSQL() +{ + if (!_pSession) fail ("Test not available."); + + try + { + *_pSession << format("CREATE FUNCTION plpgsql_call_handler () " + "RETURNS OPAQUE " + "AS '%splpgsql.dll' " + "LANGUAGE 'C';", libDir), now; + + *_pSession << "CREATE LANGUAGE 'plpgsql' " + "HANDLER plpgsql_call_handler " + "LANCOMPILER 'PL/pgSQL'", now; + + }catch(StatementException& ex) + { + if (7 != ex.diagnostics().nativeError(0)) + throw; + } + + return; +} + + void ODBCPostgreSQLTest::dropObject(const std::string& type, const std::string& name) { try @@ -1063,7 +1146,7 @@ bool ODBCPostgreSQLTest::init(const std::string& driver, const std::string& dsn) if (_pSession && _pSession->isConnected()) std::cout << "*** Connected to [" << driver << "] test database." << std::endl; - + _pExecutor = new SQLExecutor(driver + " SQL Executor", _pSession); return true; @@ -1073,6 +1156,7 @@ bool ODBCPostgreSQLTest::init(const std::string& driver, const std::string& dsn) CppUnit::Test* ODBCPostgreSQLTest::suite() { if (init("PostgreSQL ANSI", "PocoDataPostgreSQLTest")) + //if (init("Mammoth ODBCng Beta", "Mammoth ODBCng beta")) { CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCPostgreSQLTest"); @@ -1126,6 +1210,7 @@ CppUnit::Test* ODBCPostgreSQLTest::suite() CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testTupleVector); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testInternalExtraction); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testInternalStorageType); + CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testStoredFunction); return pSuite; } diff --git a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.h b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.h index 66a8c9418..80a81df78 100644 --- a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.h +++ b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.h @@ -122,6 +122,8 @@ public: void testInternalExtraction(); void testInternalStorageType(); + void testStoredFunction(); + void setUp(); void tearDown(); @@ -140,12 +142,25 @@ private: static bool init(const std::string& driver, const std::string& dsn); static bool canConnect(const std::string& driver, const std::string& dsn); + + void configurePLPgSQL(); + /// Configures PL/pgSQL in the database. A reasonable defaults + /// for the interpreter location on WIN32 and POSIX platforms are + /// supplied (see installDir member variable). + /// If these do not work, user must determine the proper location, + /// modify the function and recompile. + /// Alternative is direct database configuration for PL/pgSQL usage. static Poco::Data::ODBC::Utility::DriverMap _drivers; static std::string _dbConnString; static Poco::SharedPtr _pSession; static Poco::SharedPtr _pExecutor; static const bool bindValues[8]; + static const std::string libDir; + /// Varible determining the location of the library directory + /// on the database installation system. + /// Used to enable PLpgSQL language programmaticaly when + /// it is not enabled. }; diff --git a/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp b/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp index 91db14144..d9612536d 100644 --- a/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp @@ -907,9 +907,14 @@ void ODBCSQLServerTest::testStoredProcedure() k += 2; } -/*TODO +/*TODO - currently fails with following error: + +[Microsoft][ODBC SQL Server Driver][SQL Server]Invalid parameter +2 (''): Data type 0x23 is a deprecated large object, or LOB, but is marked as output parameter. +Deprecated types are not supported as output parameters. Use current large object types instead. + _pSession->setFeature("autoBind", true); - *_pSession << "CREATE PROCEDURE storedProcedure(@inParam VARCHAR, @outParam VARCHAR OUTPUT) AS " + *_pSession << "CREATE PROCEDURE storedProcedure(@inParam VARCHAR(MAX), @outParam VARCHAR(MAX) OUTPUT) AS " "BEGIN " "SET @outParam = @inParam; " "END;" @@ -1206,6 +1211,7 @@ CppUnit::Test* ODBCSQLServerTest::suite() { #ifdef POCO_OS_FAMILY_WINDOWS if (init("SQL Server", "PocoDataSQLServerTest")) + //if (init("SQL Native Client", "PocoDataSQLServerTest")) #else if (init("FreeTDS", "PocoDataSQLServerTest")) #endif diff --git a/Data/ODBC/testsuite/src/SQLExecutor.cpp b/Data/ODBC/testsuite/src/SQLExecutor.cpp index 10d66fd15..9d673d35f 100644 --- a/Data/ODBC/testsuite/src/SQLExecutor.cpp +++ b/Data/ODBC/testsuite/src/SQLExecutor.cpp @@ -1,7 +1,7 @@ // // SQLExecutor.cpp // -// $Id: //poco/Main/DataConnectors/ODBC/testsuite/src/SQLExecutor.cpp#14 $ +// $Id: //poco/Main/Data/ODBC/testsuite/src/SQLExecutor.cpp#14 $ // // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. // and Contributors. diff --git a/Data/include/Poco/Data/StatementImpl.h b/Data/include/Poco/Data/StatementImpl.h index 8ab1cfd74..c24813360 100644 --- a/Data/include/Poco/Data/StatementImpl.h +++ b/Data/include/Poco/Data/StatementImpl.h @@ -222,6 +222,20 @@ protected: void resetBinding(); /// Resets binding so it can be reused again. + virtual bool isStoredProcedure() const; + /// Returns true if the statement is stored procedure. + /// Used as a help to determine whether to automatically create the + /// internal extractions when no outside extraction is supplied. + /// The reason for this function is to prevent unnecessary internal + /// extraction creation in cases (behavior exhibited by some ODBC drivers) + /// when there is data available from the stored procedure call + /// statement execution but no external extraction is supplied (as is + /// usually the case when stored procedures are called). In such cases + /// no storage is needed because output parameters serve as storage. + /// At the Data framework level, this function always returns false. + /// When connector-specific behavior is desired, it should be overriden + /// by the statement implementation. + private: void compile(); /// Compiles the statement, if not yet compiled. doesn't bind yet @@ -241,6 +255,14 @@ private: void resetExtraction(); /// Resets binding so it can be reused again. + template + InternalExtraction* createExtract(const MetaColumn& mc) + { + C* pData = new C; + Column* pCol = new Column(mc, pData); + return new InternalExtraction(*pData, pCol); + } + template void addInternalExtract(const MetaColumn& mc) /// Creates and adds the internal extraction. @@ -272,23 +294,11 @@ private: if (storage.empty()) storage = VECTOR; if (0 == icompare(VECTOR, storage)) - { - std::vector* pData = new std::vector; - Column >* pCol = new Column >(mc, pData); - addExtract(new InternalExtraction >(*pData, pCol)); - } + addExtract(createExtract >(mc)); else if (0 == icompare(LIST, storage)) - { - std::list* pData = new std::list; - Column >* pCol = new Column >(mc, pData); - addExtract(new InternalExtraction >(*pData, pCol)); - } + addExtract(createExtract >(mc)); else if (0 == icompare(DEQUE, storage)) - { - std::deque* pData = new std::deque; - Column >* pCol = new Column >(mc, pData); - addExtract(new InternalExtraction >(*pData, pCol)); - } + addExtract(createExtract >(mc)); } StatementImpl(const StatementImpl& stmt); @@ -389,6 +399,12 @@ inline std::size_t StatementImpl::extractionCount() const } +inline bool StatementImpl::isStoredProcedure() const +{ + return false; +} + + } } // namespace Poco::Data diff --git a/Data/src/StatementImpl.cpp b/Data/src/StatementImpl.cpp index 88ca3b76b..96d2ab58c 100644 --- a/Data/src/StatementImpl.cpp +++ b/Data/src/StatementImpl.cpp @@ -153,7 +153,7 @@ void StatementImpl::compile() compileImpl(); _state = ST_COMPILED; - if (!extractions().size()) + if (!extractions().size() && !isStoredProcedure()) { Poco::UInt32 cols = columnsReturned(); if (cols) makeExtractors(cols);