From 6a148edefa75d091b87edf62b21cd863db469039 Mon Sep 17 00:00:00 2001 From: Alex Fabijanic Date: Sat, 19 Oct 2024 19:13:07 +0200 Subject: [PATCH] fix(ODBC): detect backend at runtime for string size; add Session::dbmsName() #4324 --- .../include/Poco/Data/MySQL/SessionImpl.h | 2 ++ Data/MySQL/src/SessionImpl.cpp | 8 +++++- Data/ODBC/include/Poco/Data/ODBC/Binder.h | 17 +++++------- Data/ODBC/include/Poco/Data/ODBC/Handle.h | 6 +++++ Data/ODBC/include/Poco/Data/ODBC/ODBC.h | 2 +- .../ODBC/include/Poco/Data/ODBC/SessionImpl.h | 3 +++ Data/ODBC/include/Poco/Data/ODBC/Utility.h | 7 +++++ Data/ODBC/src/Binder.cpp | 26 ++++++++++--------- Data/ODBC/src/SessionImpl.cpp | 8 ++++++ Data/ODBC/src/Utility.cpp | 12 +++++++++ Data/ODBC/testsuite/src/ODBCTestSuite.cpp | 3 +-- .../Poco/Data/PostgreSQL/SessionImpl.h | 2 ++ Data/PostgreSQL/src/SessionImpl.cpp | 8 ++++++ .../include/Poco/Data/SQLite/SessionImpl.h | 3 +++ Data/SQLite/src/SessionImpl.cpp | 12 ++++++--- Data/include/Poco/Data/Session.h | 11 ++++++++ Data/include/Poco/Data/SessionImpl.h | 22 ++++++++++++++++ Data/src/SessionImpl.cpp | 1 + Data/testsuite/src/DataTest.cpp | 1 + Data/testsuite/src/SessionImpl.cpp | 1 + configure | 20 +++++++++++++- 21 files changed, 145 insertions(+), 30 deletions(-) diff --git a/Data/MySQL/include/Poco/Data/MySQL/SessionImpl.h b/Data/MySQL/include/Poco/Data/MySQL/SessionImpl.h index 9857845990..fa76380281 100644 --- a/Data/MySQL/include/Poco/Data/MySQL/SessionImpl.h +++ b/Data/MySQL/include/Poco/Data/MySQL/SessionImpl.h @@ -194,6 +194,8 @@ class MySQL_API SessionImpl: public Poco::Data::AbstractSessionImpl return getValue(pResult, val); } + void setName(); + std::string _connector; mutable SessionHandle _handle; bool _reset; diff --git a/Data/MySQL/src/SessionImpl.cpp b/Data/MySQL/src/SessionImpl.cpp index 0dcf40a75f..c9051144f3 100644 --- a/Data/MySQL/src/SessionImpl.cpp +++ b/Data/MySQL/src/SessionImpl.cpp @@ -62,6 +62,12 @@ SessionImpl::SessionImpl(const std::string& connectionString, std::size_t loginT } +void SessionImpl::setName() +{ + setDBMSName("MySQL"s); +} + + void SessionImpl::open(const std::string& connect) { if (connect != connectionString()) @@ -175,7 +181,7 @@ void SessionImpl::open(const std::string& connect) // autocommit is initially on when a session is opened AbstractSessionImpl::setAutoCommit("", true); - + setName(); _connected = true; } diff --git a/Data/ODBC/include/Poco/Data/ODBC/Binder.h b/Data/ODBC/include/Poco/Data/ODBC/Binder.h index ece5708ca9..6274d7cff7 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Binder.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Binder.h @@ -616,11 +616,7 @@ class ODBC_API Binder: public Poco::Data::AbstractBinder toODBCDirection(dir), SQL_C_CHAR, Utility::sqlDataType(SQL_C_CHAR), -#if defined(POCO_DATA_ODBC_HAVE_SQL_SERVER_EXT) && POCO_DATA_SQL_SERVER_BIG_STRINGS - SQL_SS_LENGTH_UNLIMITED, -#else - (SQLUINTEGER)size - 1, -#endif + getStringColSize(size), 0, _charPtrs[pos], (SQLINTEGER) size, @@ -687,11 +683,7 @@ class ODBC_API Binder: public Poco::Data::AbstractBinder toODBCDirection(dir), SQL_C_WCHAR, Utility::sqlDataType(SQL_C_WCHAR), -#if defined(POCO_DATA_ODBC_HAVE_SQL_SERVER_EXT) && POCO_DATA_SQL_SERVER_BIG_STRINGS - SQL_SS_LENGTH_UNLIMITED, -#else - (SQLUINTEGER)size - 1, -#endif + getStringColSize(size), 0, _utf16CharPtrs[pos], (SQLINTEGER)size, @@ -958,6 +950,11 @@ class ODBC_API Binder: public Poco::Data::AbstractBinder } } + SQLUINTEGER getStringColSize(SQLUINTEGER columnSize); + /// Returns the string column size. + /// If the back end is not SQL Server, it returns + /// the `columnSize` passed in. + void getColSizeAndPrecision(std::size_t pos, SQLSMALLINT cDataType, SQLINTEGER& colSize, diff --git a/Data/ODBC/include/Poco/Data/ODBC/Handle.h b/Data/ODBC/include/Poco/Data/ODBC/Handle.h index c9e00b672e..740d76a209 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Handle.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Handle.h @@ -81,6 +81,12 @@ class Handle return _handle; } + const ConnectionHandle& connection() const + /// Returns the connection handle. + { + return _rConnection; + } + private: Handle(const Handle&); const Handle& operator=(const Handle&); diff --git a/Data/ODBC/include/Poco/Data/ODBC/ODBC.h b/Data/ODBC/include/Poco/Data/ODBC/ODBC.h index 6975abb2bf..f415e4fbe3 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/ODBC.h +++ b/Data/ODBC/include/Poco/Data/ODBC/ODBC.h @@ -70,7 +70,7 @@ // - increasing the "maxFieldSize" property may // affect performance (more memory preallocated // for prepared statements in order to safely - // accomodate data returned at execution) + // accommodate data returned at execution) #if !defined(POCO_DATA_SQL_SERVER_BIG_STRINGS) #define POCO_DATA_SQL_SERVER_BIG_STRINGS 1 #endif diff --git a/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h b/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h index ba4f1f8646..a915f19290 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h +++ b/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h @@ -229,6 +229,9 @@ class ODBC_API SessionImpl: public Poco::Data::AbstractSessionImpl /// Sets the transaction isolation level. /// Called internally from getTransactionIsolation() + void setName(); + /// Sets the back end DBMS name. + std::string _connector; mutable ConnectionHandle _db; Poco::Any _maxFieldSize; diff --git a/Data/ODBC/include/Poco/Data/ODBC/Utility.h b/Data/ODBC/include/Poco/Data/ODBC/Utility.h index 28e7865355..c89212c3e2 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Utility.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Utility.h @@ -33,6 +33,9 @@ namespace Data { namespace ODBC { +class ConnectionHandle; + + class ODBC_API Utility /// Various utility functions { @@ -162,6 +165,10 @@ class ODBC_API Utility for (; it != end; ++it, ++tIt) dateTimeSync(*tIt, *it); } + static std::string dbmsName(const ConnectionHandle& db); + /// Returns the back end DBMS name. + /// On error, returns "unknown". + private: static const TypeInfo _dataTypes; /// C <==> SQL data type mapping diff --git a/Data/ODBC/src/Binder.cpp b/Data/ODBC/src/Binder.cpp index c6b3656fe6..6c05403b89 100644 --- a/Data/ODBC/src/Binder.cpp +++ b/Data/ODBC/src/Binder.cpp @@ -166,11 +166,7 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir) toODBCDirection(dir), SQL_C_CHAR, Utility::sqlDataType(SQL_C_CHAR), -#if defined(POCO_DATA_ODBC_HAVE_SQL_SERVER_EXT) && POCO_DATA_SQL_SERVER_BIG_STRINGS - SQL_SS_LENGTH_UNLIMITED, -#else - (SQLUINTEGER)colSize, -#endif + getStringColSize(colSize), 0, pVal, (SQLINTEGER)size, @@ -223,11 +219,7 @@ void Binder::bind(std::size_t pos, const UTF16String& val, Direction dir) toODBCDirection(dir), SQL_C_WCHAR, Utility::sqlDataType(SQL_C_WCHAR), -#if defined(POCO_DATA_ODBC_HAVE_SQL_SERVER_EXT) && POCO_DATA_SQL_SERVER_BIG_STRINGS - SQL_SS_LENGTH_UNLIMITED, -#else - (SQLUINTEGER)colSize, -#endif + getStringColSize(colSize), 0, pVal, (SQLINTEGER)size, @@ -514,6 +506,16 @@ void Binder::reset() } +SQLUINTEGER Binder::getStringColSize(SQLUINTEGER columnSize) +{ +#if defined(POCO_DATA_ODBC_HAVE_SQL_SERVER_EXT) && POCO_DATA_SQL_SERVER_BIG_STRINGS + if (Utility::dbmsName(_rStmt.connection()) == "SQLServer"s) + return SQL_SS_LENGTH_UNLIMITED; +#endif + return columnSize; +} + + void Binder::getColSizeAndPrecision(std::size_t pos, SQLSMALLINT cDataType, SQLINTEGER& colSize, @@ -634,14 +636,14 @@ void Binder::getColumnOrParameterSize(std::size_t pos, SQLINTEGER& size) ODBCMetaColumn col(_rStmt, pos); colSize = col.length(); } - catch (StatementException&) { } + catch (StatementException&){} try { Parameter p(_rStmt, pos); paramSize = p.columnSize(); } - catch (StatementException&) {} + catch (StatementException&){} if (colSize == 0 && paramSize == 0) paramSize = getParamSizeDirect(pos, size); diff --git a/Data/ODBC/src/SessionImpl.cpp b/Data/ODBC/src/SessionImpl.cpp index 222a41b8f3..22dbab73ef 100644 --- a/Data/ODBC/src/SessionImpl.cpp +++ b/Data/ODBC/src/SessionImpl.cpp @@ -99,6 +99,12 @@ SessionImpl::~SessionImpl() } +void SessionImpl::setName() +{ + setDBMSName(Utility::dbmsName(_db)); +} + + Poco::Data::StatementImpl::Ptr SessionImpl::createStatementImpl() { return new ODBCStatementImpl(*this); @@ -173,6 +179,8 @@ void SessionImpl::open(const std::string& connect) else throw ConnectionException(SQL_NULL_HDBC, Poco::format("Connection to '%s' failed.", connectionString())); + + setName(); } diff --git a/Data/ODBC/src/Utility.cpp b/Data/ODBC/src/Utility.cpp index 8d3b4a5e5b..565fcaf805 100644 --- a/Data/ODBC/src/Utility.cpp +++ b/Data/ODBC/src/Utility.cpp @@ -14,6 +14,7 @@ #include "Poco/Data/ODBC/Utility.h" #include "Poco/Data/ODBC/Handle.h" +#include "Poco/Data/ODBC/ConnectionHandle.h" #include "Poco/Data/ODBC/ODBCException.h" #include "Poco/NumberFormatter.h" #include "Poco/DateTime.h" @@ -156,4 +157,15 @@ void Utility::dateTimeSync(SQL_TIMESTAMP_STRUCT& ts, const Poco::DateTime& dt) } +std::string Utility::dbmsName(const ConnectionHandle& db) +{ + const SQLSMALLINT bufSize = 1024; + SQLCHAR dbmsName[bufSize] = {0}; + SQLSMALLINT retSize = 0; + SQLRETURN rc = Poco::Data::ODBC::SQLGetInfo(const_cast(db.handle()), SQL_DBMS_NAME, dbmsName, bufSize, &retSize); + if (!isError(rc)) return std::string(dbmsName[0], retSize); + return "unknown"s; +} + + } } } // namespace Poco::Data::ODBC diff --git a/Data/ODBC/testsuite/src/ODBCTestSuite.cpp b/Data/ODBC/testsuite/src/ODBCTestSuite.cpp index 1c80536c4b..bf470eb85c 100644 --- a/Data/ODBC/testsuite/src/ODBCTestSuite.cpp +++ b/Data/ODBC/testsuite/src/ODBCTestSuite.cpp @@ -37,9 +37,8 @@ CppUnit::Test* ODBCTestSuite::suite() // // For the time being, the workaround is to connect to DB2 after connecting to PostgreSQL and Oracle. - - addTest(pSuite, ODBCSQLServerTest::suite()); addTest(pSuite, ODBCOracleTest::suite()); + addTest(pSuite, ODBCSQLServerTest::suite()); addTest(pSuite, ODBCMySQLTest::suite()); addTest(pSuite, ODBCPostgreSQLTest::suite()); addTest(pSuite, ODBCSQLiteTest::suite()); diff --git a/Data/PostgreSQL/include/Poco/Data/PostgreSQL/SessionImpl.h b/Data/PostgreSQL/include/Poco/Data/PostgreSQL/SessionImpl.h index 87cfd19655..edbd108264 100644 --- a/Data/PostgreSQL/include/Poco/Data/PostgreSQL/SessionImpl.h +++ b/Data/PostgreSQL/include/Poco/Data/PostgreSQL/SessionImpl.h @@ -130,6 +130,8 @@ class PostgreSQL_API SessionImpl: public Poco::Data::AbstractSessionImpl(value)); } -Poco::Any SessionImpl::getTransactionType(const std::string& prop) const +Poco::Any SessionImpl::getTransactionType(const std::string& prop) const { return Poco::Any(_transactionType); } diff --git a/Data/include/Poco/Data/Session.h b/Data/include/Poco/Data/Session.h index 332e99698e..d124c463e4 100644 --- a/Data/include/Poco/Data/Session.h +++ b/Data/include/Poco/Data/Session.h @@ -204,6 +204,11 @@ class Data_API Session return (_statementCreator << t); } + const std::string& dbmsName() const; + /// Returns the DBMS name. The name must be set by the + /// implementation. + /// Defaults to "unknown". + SharedPtr createStatementImpl(); /// Creates a StatementImpl. @@ -351,6 +356,12 @@ class Data_API Session // inlines // +inline const std::string& Session::dbmsName() const +{ + return _pImpl->dbmsName(); +} + + inline bool Session::isAutocommit() const { return _pImpl->isAutocommit(); diff --git a/Data/include/Poco/Data/SessionImpl.h b/Data/include/Poco/Data/SessionImpl.h index 2d7aef0a01..e5644772e7 100644 --- a/Data/include/Poco/Data/SessionImpl.h +++ b/Data/include/Poco/Data/SessionImpl.h @@ -65,6 +65,11 @@ class Data_API SessionImpl: public Poco::RefCountedObject virtual ~SessionImpl(); /// Destroys the SessionImpl. + const std::string& dbmsName() const; + /// Returns the DBMS name. The name must be set by the + /// implementation. + /// Defaults to "unknown". + virtual Poco::SharedPtr createStatementImpl() = 0; /// Creates a StatementImpl. @@ -195,6 +200,9 @@ class Data_API SessionImpl: public Poco::RefCountedObject /// not supported by the underlying implementation. protected: + void setDBMSName(const std::string& name); + /// Sets the DBMS name. + void setConnectionString(const std::string& connectionString); /// Sets the connection string. Should only be called on /// disconnected sessions. Throws InvalidAccessException when called on @@ -205,6 +213,7 @@ class Data_API SessionImpl: public Poco::RefCountedObject SessionImpl(const SessionImpl&); SessionImpl& operator = (const SessionImpl&); + std::string _dbmsName; std::string _connectionString; std::size_t _loginTimeout; }; @@ -213,6 +222,19 @@ class Data_API SessionImpl: public Poco::RefCountedObject // // inlines // + +inline void SessionImpl::setDBMSName(const std::string& name) +{ + _dbmsName = name; +} + + +inline const std::string& SessionImpl::dbmsName() const +{ + return _dbmsName; +} + + inline const std::string& SessionImpl::connectionString() const { return _connectionString; diff --git a/Data/src/SessionImpl.cpp b/Data/src/SessionImpl.cpp index 0b7cded41e..428eef52af 100644 --- a/Data/src/SessionImpl.cpp +++ b/Data/src/SessionImpl.cpp @@ -21,6 +21,7 @@ namespace Data { SessionImpl::SessionImpl(const std::string& connectionString, std::size_t timeout): + _dbmsName("unknown"s), _connectionString(connectionString), _loginTimeout(timeout) { diff --git a/Data/testsuite/src/DataTest.cpp b/Data/testsuite/src/DataTest.cpp index fc89d9f54b..00b9228c67 100644 --- a/Data/testsuite/src/DataTest.cpp +++ b/Data/testsuite/src/DataTest.cpp @@ -72,6 +72,7 @@ void DataTest::testSession() assertTrue (sess.connector() == sess.impl()->connectorName()); assertTrue ("cs" == sess.impl()->connectionString()); assertTrue ("test:///cs" == sess.uri()); + assertTrue ("Test" == sess.dbmsName()); assertTrue (sess.getLoginTimeout() == Session::LOGIN_TIMEOUT_DEFAULT); sess.setLoginTimeout(123); diff --git a/Data/testsuite/src/SessionImpl.cpp b/Data/testsuite/src/SessionImpl.cpp index 198f9960d3..36bd982357 100644 --- a/Data/testsuite/src/SessionImpl.cpp +++ b/Data/testsuite/src/SessionImpl.cpp @@ -31,6 +31,7 @@ SessionImpl::SessionImpl(const std::string& init, std::size_t timeout): addProperty("p1", &SessionImpl::setP, &SessionImpl::getP); addProperty("p2", 0, &SessionImpl::getP); addProperty("p3", &SessionImpl::setP, &SessionImpl::getP); + setDBMSName("Test"); } diff --git a/configure b/configure index bb58d2e0b8..c3bb3e204a 100755 --- a/configure +++ b/configure @@ -82,6 +82,10 @@ $(ls -C "$base"/build/config/) Compile with -DPOCO_DATA_NO_SQL_PARSER Disables compilation of the SQLParser. + --mssql-bigstring + Compile with -DPOCO_DATA_SQL_SERVER_BIG_STRINGS=1 + Enables strings over 8000 bytes on MS SQL Server. + --sqlite-fts= Compile with -DPOCO_DATA_SQLITE_FTS. Compile SQLite with Full Text Search support. @@ -110,6 +114,9 @@ $(ls -C "$base"/build/config/) --odbc-include= Specify the directory where ODBC header files are located. + --mssql-include= + Specify the directory where MS SQL Server ODBC header files are located. + --mysql-lib= Specify the directory where MySQL library is located. @@ -176,6 +183,7 @@ trace="" static="" shared="" nosqlparser= +mssqlbigstring= omitMinimal="Crypto NetSSL_OpenSSL Zip Data Data/SQLite Data/ODBC Data/MySQL Data/PostgreSQL MongoDB Redis PDF DNSSD DNSSD/Avahi DNSSD/Bonjour CppParser PageCompiler" omitTypical="Data/ODBC Data/MySQL Data/PostgreSQL MongoDB Redis PDF DNSSD DNSSD/Avahi DNSSD/Bonjour CppParser" omit=$omitTypical @@ -259,6 +267,11 @@ while [ $# -ge 1 ]; do nosqlparser=1 ;; + --mssql-bigstring) + flags="$flags -DPOCO_DATA_SQL_SERVER_BIG_STRINGS=1" + mssqlbigstring=1 + ;; + --sqlite-thread-safe=*) flags="$flags -DSQLITE_THREADSAFE=$(echo "${1}" | awk '{print substr($0,22)}')" ;; @@ -408,6 +421,9 @@ fi if [ -n "$nosqlparser" ] ; then echo "POCO_DATA_NO_SQL_PARSER = $nosqlparser" >>"$build"/config.make fi +if [ -n "$mssqlbigstring" ] ; then + echo "POCO_DATA_SQL_SERVER_BIG_STRINGS = $mssqlbigstring" >>"$build"/config.make +fi cat <<__EOF__ >>"$build"/config.make export POCO_CONFIG @@ -462,7 +478,9 @@ fi if [ -n "$nosqlparser" ] ; then echo "export POCO_DATA_NO_SQL_PARSER" >>"$build"/config.make fi - +if [ -n "$mssqlbigstring" ] ; then + echo "POCO_DATA_SQL_SERVER_BIG_STRINGS=$mssqlbigstring" >>"$build"/config.make +fi # create config.build echo '# config.build generated by configure script' >"$build"/config.build cat <<__EOF__ >>"$build"/config.build