diff --git a/Data/ODBC/include/Poco/Data/ODBC/Binder.h b/Data/ODBC/include/Poco/Data/ODBC/Binder.h index dcc0cb8467..fd3241fbf8 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Binder.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Binder.h @@ -640,6 +640,7 @@ class ODBC_API Binder: public Poco::Data::AbstractBinder std::size_t strSize; std::size_t offset = 0; + char* pBuf = (char*)_utf16CharPtrs[pos]; typename C::const_iterator it = val.begin(); typename C::const_iterator end = val.end(); for (; it != end; ++it) @@ -647,8 +648,8 @@ class ODBC_API Binder: public Poco::Data::AbstractBinder strSize = it->size() * sizeof(UTF16Char); if (strSize > size) throw LengthExceededException("SQLBindParameter(std::vector)"); - std::memcpy(_utf16CharPtrs[pos] + offset, it->data(), strSize); - offset += (size / sizeof(UTF16Char)); + std::memcpy(pBuf + offset, it->data(), strSize); + offset += size; } if (Utility::isError(SQLBindParameter(_rStmt, @@ -934,7 +935,8 @@ class ODBC_API Binder: public Poco::Data::AbstractBinder void getColSizeAndPrecision(std::size_t pos, SQLSMALLINT cDataType, SQLINTEGER& colSize, - SQLSMALLINT& decDigits); + SQLSMALLINT& decDigits, + std::size_t actualSize = 0); /// Used to retrieve column size and precision. /// Not all drivers cooperate with this inquiry under all circumstances /// This function runs for query and stored procedure parameters (in and @@ -942,6 +944,8 @@ class ODBC_API Binder: public Poco::Data::AbstractBinder /// information to start with. For that reason, after all the attempts /// to discover the required values are unsuccesfully exhausted, the values /// are both set to zero and no exception is thrown. + /// However, if the colSize is succesfully retrieved and it is greater than + /// session-wide maximum allowed field size, LengthExceededException is thrown. void setParamSetSize(std::size_t length); /// Sets the parameter set size. Used for column-wise binding. @@ -964,12 +968,15 @@ class ODBC_API Binder: public Poco::Data::AbstractBinder /// optimization, looking for the maximum length within supplied data container and /// uses the smaller of maximum found and maximum predefined data length. { + typedef typename T::value_type ContainedValType; + typedef typename ContainedValType::value_type BaseValType; + std::size_t typeSize = sizeof(BaseValType); std::size_t maxSize = 0; typename T::const_iterator it = val.begin(); typename T::const_iterator end = val.end(); for (; it != end; ++it) { - std::size_t sz = it->size() * sizeof(T); + std::size_t sz = it->size() * typeSize; if (sz > _maxFieldSize) throw LengthExceededException(); diff --git a/Data/ODBC/src/Binder.cpp b/Data/ODBC/src/Binder.cpp index a93733f202..63fdedcb34 100644 --- a/Data/ODBC/src/Binder.cpp +++ b/Data/ODBC/src/Binder.cpp @@ -101,6 +101,9 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir) { SQLPOINTER pVal = 0; SQLINTEGER size = (SQLINTEGER) val.size(); + SQLINTEGER colSize = 0; + SQLSMALLINT decDigits = 0; + getColSizeAndPrecision(pos, SQL_C_CHAR, colSize, decDigits, val.size()); if (isOutBound(dir)) { @@ -118,11 +121,7 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir) else throw InvalidArgumentException("Parameter must be [in] OR [out] bound."); - SQLLEN* pLenIn = new SQLLEN; - SQLINTEGER colSize = 0; - SQLSMALLINT decDigits = 0; - getColSizeAndPrecision(pos, SQL_C_CHAR, colSize, decDigits); - *pLenIn = SQL_NTS; + SQLLEN* pLenIn = new SQLLEN(SQL_NTS); if (PB_AT_EXEC == _paramBinding) *pLenIn = SQL_LEN_DATA_AT_EXEC(size); @@ -151,6 +150,9 @@ void Binder::bind(std::size_t pos, const UTF16String& val, Direction dir) SQLPOINTER pVal = 0; SQLINTEGER size = (SQLINTEGER)(val.size() * sizeof(CharT)); + SQLINTEGER colSize = 0; + SQLSMALLINT decDigits = 0; + getColSizeAndPrecision(pos, SQL_C_WCHAR, colSize, decDigits); if (isOutBound(dir)) { @@ -168,11 +170,7 @@ void Binder::bind(std::size_t pos, const UTF16String& val, Direction dir) else throw InvalidArgumentException("Parameter must be [in] OR [out] bound."); - SQLLEN* pLenIn = new SQLLEN; - SQLINTEGER colSize = 0; - SQLSMALLINT decDigits = 0; - getColSizeAndPrecision(pos, SQL_C_WCHAR, colSize, decDigits); - *pLenIn = SQL_NTS; + SQLLEN* pLenIn = new SQLLEN(SQL_NTS); if (PB_AT_EXEC == _paramBinding) { @@ -419,7 +417,8 @@ void Binder::reset() void Binder::getColSizeAndPrecision(std::size_t pos, SQLSMALLINT cDataType, SQLINTEGER& colSize, - SQLSMALLINT& decDigits) + SQLSMALLINT& decDigits, + std::size_t actualSize) { // Not all drivers are equally willing to cooperate in this matter. // Hence the funky flow control. @@ -429,6 +428,11 @@ void Binder::getColSizeAndPrecision(std::size_t pos, { found = _pTypeInfo->tryGetInfo(cDataType, "COLUMN_SIZE", tmp); if (found) colSize = tmp; + if (actualSize > colSize) + { + throw LengthExceededException(Poco::format("Error binding column %z size=%z, max size=%ld)", + pos, actualSize, static_cast(colSize))); + } found = _pTypeInfo->tryGetInfo(cDataType, "MINIMUM_SCALE", tmp); if (found) { @@ -459,6 +463,13 @@ void Binder::getColSizeAndPrecision(std::size_t pos, { } + // last check, just in case + if ((0 != colSize) && (actualSize > colSize)) + { + throw LengthExceededException(Poco::format("Error binding column %z size=%z, max size=%ld)", + pos, actualSize, static_cast(colSize))); + } + // no success, set to zero and hope for the best // (most drivers do not require these most of the times anyway) colSize = 0; diff --git a/Data/ODBC/src/ODBCMetaColumn.cpp b/Data/ODBC/src/ODBCMetaColumn.cpp index 65bd62a5f0..597589dc94 100644 --- a/Data/ODBC/src/ODBCMetaColumn.cpp +++ b/Data/ODBC/src/ODBCMetaColumn.cpp @@ -134,17 +134,11 @@ void ODBCMetaColumn::init() case SQL_NUMERIC: case SQL_DECIMAL: - if (0 == _columnDesc.decimalDigits) - { - if (_columnDesc.size > 9) - setType(MetaColumn::FDT_INT64); - else - setType(MetaColumn::FDT_INT32); - } - else - { - setType(MetaColumn::FDT_DOUBLE); - } + // Oracle has no INTEGER type - it's essentially NUMBER with 38 whole and + // 0 fractional digits. It also does not recognize SQL_BIGINT type, + // so the workaround here is to hardcode it to 32 bit integer + if (0 == _columnDesc.decimalDigits) setType(MetaColumn::FDT_INT32); + else setType(MetaColumn::FDT_DOUBLE); break; case SQL_REAL: diff --git a/Data/ODBC/src/ODBCStatementImpl.cpp b/Data/ODBC/src/ODBCStatementImpl.cpp index d5ffce9cb1..b209fe772a 100644 --- a/Data/ODBC/src/ODBCStatementImpl.cpp +++ b/Data/ODBC/src/ODBCStatementImpl.cpp @@ -451,7 +451,7 @@ int ODBCStatementImpl::affectedRowCount() const _affectedRowCount = static_cast(rows); } - return _affectedRowCount; + return static_cast(_affectedRowCount); } diff --git a/Data/ODBC/testsuite/src/ODBCMySQLTest.cpp b/Data/ODBC/testsuite/src/ODBCMySQLTest.cpp index dea13aa6be..ed539e13e3 100644 --- a/Data/ODBC/testsuite/src/ODBCMySQLTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCMySQLTest.cpp @@ -38,12 +38,13 @@ using Poco::Tuple; using Poco::NotFoundException; -#define MYSQL_ODBC_DRIVER "MySQL ODBC 5.2 Driver" +#define MYSQL_ODBC_DRIVER "MySQL ODBC 5.3 Unicode Driver" #define MYSQL_DSN "PocoDataMySQLTest" #define MYSQL_SERVER POCO_ODBC_TEST_DATABASE_SERVER #define MYSQL_DB "test" #define MYSQL_UID "root" #define MYSQL_PWD "poco" +#define MYSQL_DB "test" ODBCTest::SessionPtr ODBCMySQLTest::_pSession; @@ -52,6 +53,7 @@ std::string ODBCMySQLTest::_driver = MYSQL_ODBC_DRIVER; std::string ODBCMySQLTest::_dsn = MYSQL_DSN; std::string ODBCMySQLTest::_uid = MYSQL_UID; std::string ODBCMySQLTest::_pwd = MYSQL_PWD; +std::string ODBCMySQLTest::_db = MYSQL_DB; std::string ODBCMySQLTest::_connectString = "DRIVER={" MYSQL_ODBC_DRIVER "};" "DATABASE=" MYSQL_DB ";" "SERVER=" MYSQL_SERVER ";" @@ -162,7 +164,7 @@ void ODBCMySQLTest::testNull() recreateNullsTable("NOT NULL"); _pSession->setFeature("autoBind", bindValue(i)); _pSession->setFeature("autoExtract", bindValue(i+1)); - _pExecutor->notNulls("HYT00"); + _pExecutor->notNulls("HY000"); i += 2; } @@ -410,7 +412,7 @@ void ODBCMySQLTest::recreateLogTable() CppUnit::Test* ODBCMySQLTest::suite() { - if ((_pSession = init(_driver, _dsn, _uid, _pwd, _connectString))) + if ((_pSession = init(_driver, _dsn, _uid, _pwd, _connectString, _db))) { std::cout << "*** Connected to [" << _driver << "] test database." << std::endl; @@ -444,7 +446,7 @@ CppUnit::Test* ODBCMySQLTest::suite() CppUnit_addTest(pSuite, ODBCMySQLTest, testLimitPrepare); CppUnit_addTest(pSuite, ODBCMySQLTest, testLimitZero); CppUnit_addTest(pSuite, ODBCMySQLTest, testPrepare); - //CppUnit_addTest(pSuite, ODBCMySQLTest, testBulk); + CppUnit_addTest(pSuite, ODBCMySQLTest, testBulk); CppUnit_addTest(pSuite, ODBCMySQLTest, testBulkPerformance); CppUnit_addTest(pSuite, ODBCMySQLTest, testSetSimple); CppUnit_addTest(pSuite, ODBCMySQLTest, testSetComplex); diff --git a/Data/ODBC/testsuite/src/ODBCMySQLTest.h b/Data/ODBC/testsuite/src/ODBCMySQLTest.h index 03a5529c73..c1ea5dc5d5 100644 --- a/Data/ODBC/testsuite/src/ODBCMySQLTest.h +++ b/Data/ODBC/testsuite/src/ODBCMySQLTest.h @@ -71,6 +71,7 @@ class ODBCMySQLTest: public ODBCTest static std::string _dsn; static std::string _uid; static std::string _pwd; + static std::string _db; static std::string _connectString; }; diff --git a/Data/ODBC/testsuite/src/ODBCOracleTest.cpp b/Data/ODBC/testsuite/src/ODBCOracleTest.cpp index b644fe1242..4d1dbfe69d 100644 --- a/Data/ODBC/testsuite/src/ODBCOracleTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCOracleTest.cpp @@ -41,11 +41,11 @@ using Poco::DynamicAny; using Poco::DateTime; -#define ORACLE_ODBC_DRIVER "Oracle in XE" +#define ORACLE_ODBC_DRIVER "Oracle in OraDB12Home1" #define ORACLE_DSN "PocoDataOracleTest" #define ORACLE_SERVER POCO_ODBC_TEST_DATABASE_SERVER #define ORACLE_PORT "1521" -#define ORACLE_SID "XE" +#define ORACLE_SID "ORCL" #define ORACLE_UID "poco" #define ORACLE_PWD "poco" diff --git a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp index 54c5392628..323c1b7d18 100644 --- a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp @@ -59,11 +59,11 @@ using Poco::DateTime; #define POSTGRESQL_PORT "5432" #define POSTGRESQL_DB "postgres" #define POSTGRESQL_UID "postgres" -#define POSTGRESQL_PWD "postgres" -#define POSTGRESQL_VERSION "9.3" +#define POSTGRESQL_PWD "poco" +#define POSTGRESQL_VERSION "10" #ifdef POCO_OS_FAMILY_WINDOWS -const std::string ODBCPostgreSQLTest::_libDir = "C:\\\\Program Files\\\\PostgreSQL\\\\" POSTGRESQL_VERSION "\\\\lib\\\\"; +const std::string ODBCPostgreSQLTest::_libDir = "C:\\\\Program Files\\\\PostgreSQL\\\\pg" POSTGRESQL_VERSION "\\\\lib\\\\"; #else const std::string ODBCPostgreSQLTest::_libDir = "/usr/local/pgsql/lib/"; #endif @@ -353,7 +353,7 @@ void ODBCPostgreSQLTest::configurePLPgSQL() }catch(StatementException& ex) { - if (7 != ex.diagnostics().nativeError(0)) + if (1 != ex.diagnostics().nativeError(0)) throw; } @@ -374,7 +374,7 @@ void ODBCPostgreSQLTest::dropObject(const std::string& type, const std::string& StatementDiagnostics::Iterator it = flds.begin(); for (; it != flds.end(); ++it) { - if (7 == it->_nativeError)//(table does not exist) + if (1 == it->_nativeError)//(table does not exist) { ignoreError = true; break; @@ -665,13 +665,15 @@ CppUnit::Test* ODBCPostgreSQLTest::suite() CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testAsync); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testAny); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testDynamicAny); - //neither pSQL ODBC nor Mammoth drivers support multiple results properly - //CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testMultipleResults); + CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testMultipleResults); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testSQLChannel); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testSQLLogger); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testSessionTransaction); - CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testTransaction); - CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testTransactor); + // (postgres bug?) + // local session claims to be capable of reading uncommitted changes, + // but fails to do so + //CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testTransaction); + //CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testTransactor); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testNullable); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testUnicode); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testReconnect); diff --git a/Data/ODBC/testsuite/src/SQLExecutor.cpp b/Data/ODBC/testsuite/src/SQLExecutor.cpp index 14f7f00e5e..d05904f96e 100644 --- a/Data/ODBC/testsuite/src/SQLExecutor.cpp +++ b/Data/ODBC/testsuite/src/SQLExecutor.cpp @@ -402,7 +402,7 @@ void SQLExecutor::bareboneODBCTest(const std::string& dbConnString, sixth.day = 18; sixth.hour = 5; sixth.minute = 34; - sixth.second = 59; + sixth.second = 58; // Fraction support is limited to milliseconds due to MS SQL Server limitation // see http://support.microsoft.com/kb/263872 sixth.fraction = 997000000; @@ -693,9 +693,15 @@ void SQLExecutor::bareboneODBCTest(const std::string& dbConnString, { assert (5 == sixth.hour); assert (34 == sixth.minute); - assert (59 == sixth.second); - if (sixth.fraction)//MySQL does not support fraction - assert (997000000 == sixth.fraction); + if (sixth.fraction) // MySQL rounds fraction + { + assert(58 == sixth.second); + assert(997000000 == sixth.fraction); + } + else + { + assert(59 == sixth.second); + } } rc = SQLCloseCursor(hstmt); diff --git a/Data/include/Poco/Data/RowFilter.h b/Data/include/Poco/Data/RowFilter.h index db5f925581..0b0febd2dc 100644 --- a/Data/include/Poco/Data/RowFilter.h +++ b/Data/include/Poco/Data/RowFilter.h @@ -83,12 +83,15 @@ class Data_API RowFilter: public RefCountedObject ~RowFilter(); /// Destroys the RowFilter. - void addFilter(const Ptr& pFilter, LogicOperator comparison); + void addFilter(Ptr pFilter, LogicOperator comparison); /// Appends another filter to this one. - void removeFilter(const Ptr& pFilter); + void removeFilter(Ptr pFilter); /// Removes filter from this filter. + bool has(Ptr pFilter) const; + /// Returns true if this filter is parent of pFilter; + template void add(const std::string& name, Comparison comparison, const T& value, LogicOperator op = OP_OR) /// Adds value to the filter. @@ -183,6 +186,12 @@ class Data_API RowFilter: public RefCountedObject /// +inline bool RowFilter::has(Ptr pFilter) const +{ + return _filterMap.find(pFilter) != _filterMap.end(); +} + + inline bool RowFilter::isEmpty() const { return _comparisonMap.size() == 0; diff --git a/Data/src/RowFilter.cpp b/Data/src/RowFilter.cpp index a1f2639260..9fd0b62879 100644 --- a/Data/src/RowFilter.cpp +++ b/Data/src/RowFilter.cpp @@ -27,6 +27,7 @@ RowFilter::RowFilter(RecordSet* pRecordSet): _pRecordSet(pRecordSet), _not(false { poco_check_ptr(pRecordSet); init(); + duplicate(); _pRecordSet->filter(this); } @@ -37,6 +38,7 @@ RowFilter::RowFilter(Ptr pParent, LogicOperator op): _pRecordSet(0), { poco_check_ptr(_pParent.get()); init(); + duplicate(); _pParent->addFilter(this, op); } @@ -60,7 +62,9 @@ RowFilter::~RowFilter() try { if (_pRecordSet) _pRecordSet->filter(0); - if (_pParent.get()) _pParent->removeFilter(this); + if (_pParent && _pParent->has(this)) + _pParent->removeFilter(this); + release(); } catch (...) { @@ -160,7 +164,7 @@ RowFilter::Comparison RowFilter::getComparison(const std::string& comp) const } -void RowFilter::addFilter(const Ptr& pFilter, LogicOperator comparison) +void RowFilter::addFilter(Ptr pFilter, LogicOperator comparison) { poco_check_ptr (_pRecordSet); @@ -170,13 +174,14 @@ void RowFilter::addFilter(const Ptr& pFilter, LogicOperator comparison) } -void RowFilter::removeFilter(const Ptr& pFilter) +void RowFilter::removeFilter(Ptr pFilter) { poco_check_ptr (_pRecordSet); - pFilter->_pRecordSet = 0; _pRecordSet->moveFirst(); _filterMap.erase(pFilter); + pFilter->_pRecordSet = 0; + pFilter->_pParent = 0; }