From 36c808230c92ac684aa6e83bf16a1a449461c7e6 Mon Sep 17 00:00:00 2001 From: Aleksandar Fabijanic Date: Sat, 19 Oct 2024 14:43:00 -0500 Subject: [PATCH] fix(ODBC): Poco:Data::ODBC - MSSQL (n)varchar(max) length issue #4324 (#4738) * fix(ODBC): Poco:Data::ODBC - MSSQL (n)varchar(max) length issue #4324 * chore(ODBC): remove unused vars; fix SQL Server SDK include path #4324 * fix(ODBC): trim UTF16 string #4324 * chore(ODBC): add compile time big string diagnostics #4324 * chore(ODBC): add SQLServer big string vector test case #4324 * fix(ODBC): detect SQLServer header #4324 * chore: add listing msodbdcsql18 folder #4324 * ci: change odbc drivers installation order #4324 * chore(CMake): Add option ENABLE_DATA_SQL_SERVER_BIG_STRINGS, auto-detection of msodbcsql.h on Linux and macOS * fix(ODBC): detect backend at runtime for string size; add Session::dbmsName() #4324 * fix(ODBC): wrong char to string conversion #4324 --------- Co-authored-by: cunj123 Co-authored-by: Matej Kenda --- .github/workflows/ci.yml | 3 +- CMakeLists.txt | 2 + Data/DataTest/src/SQLExecutor.cpp | 10 +- .../include/Poco/Data/MySQL/SessionImpl.h | 2 + Data/MySQL/src/SessionImpl.cpp | 8 +- Data/ODBC/CMakeLists.txt | 38 +++++ Data/ODBC/ODBC.make | 12 +- Data/ODBC/ODBC_vs170.vcxproj | 152 +++++++++--------- Data/ODBC/include/Poco/Data/ODBC/Binder.h | 9 +- Data/ODBC/include/Poco/Data/ODBC/Handle.h | 6 + Data/ODBC/include/Poco/Data/ODBC/ODBC.h | 16 ++ .../ODBC/include/Poco/Data/ODBC/SessionImpl.h | 3 + Data/ODBC/include/Poco/Data/ODBC/Utility.h | 9 ++ Data/ODBC/src/Binder.cpp | 57 +++++-- Data/ODBC/src/Connector.cpp | 7 + Data/ODBC/src/Extractor.cpp | 75 +++++---- Data/ODBC/src/SessionImpl.cpp | 8 + Data/ODBC/src/Utility.cpp | 15 ++ Data/ODBC/testsuite/TestSuite_vs170.vcxproj | 132 +++++++-------- Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp | 97 ++++++++++- Data/ODBC/testsuite/src/ODBCSQLServerTest.h | 1 + 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 | 30 +++- 32 files changed, 558 insertions(+), 198 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8eda218ad..9c549f418 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -752,7 +752,6 @@ jobs: steps: - uses: actions/checkout@v4 - run: sudo apt -y update && sudo apt -y install libssl-dev unixodbc-dev alien libaio1 gnupg2 curl odbcinst1debian2 libodbc1 odbcinst # libmysqlclient-dev mysql-client odbc-postgresql - - run: ./configure --everything --no-samples --omit=ActiveRecord,ApacheConnector,CppParser,Crypto,Data/MySQL,Data/PostgreSQL,Data/SQLite,Encodings,JSON,JWT,MongoDB,Net,NetSSL_OpenSSL,NetSSL_Win,PDF,PageCompiler,PocoDoc,ProGen,Prometheus,Redis,SevenZip,Util,XML,Zip && make all -s -j6 && sudo make install # - name: Setup MySQL ODBC connector # run: | # wget https://dev.mysql.com/get/Downloads/Connector-ODBC/8.2/mysql-connector-odbc_8.2.0-1ubuntu22.04_amd64.deb @@ -768,6 +767,8 @@ jobs: curl https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/prod.list | sudo tee /etc/apt/sources.list.d/mssql-release.list sudo apt-get update sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18 + ls /opt/microsoft/msodbcsql18/include + - run: ./configure --everything --no-samples --omit=ActiveRecord,ApacheConnector,CppParser,Crypto,Data/MySQL,Data/PostgreSQL,Data/SQLite,Encodings,JSON,JWT,MongoDB,Net,NetSSL_OpenSSL,NetSSL_Win,PDF,PageCompiler,PocoDoc,ProGen,Prometheus,Redis,SevenZip,Util,XML,Zip && make all -s -j6 && sudo make install - uses: ./.github/actions/retry-action with: timeout_minutes: 90 diff --git a/CMakeLists.txt b/CMakeLists.txt index a5a2ee907..718e16cd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,9 +148,11 @@ endif() if(ODBC_FOUND) option(ENABLE_DATA "Enable Data" ON) option(ENABLE_DATA_ODBC "Enable Data ODBC" ON) + option(ENABLE_DATA_SQL_SERVER_BIG_STRINGS "Enable MS SQL Server big strings" ON) else() option(ENABLE_DATA "Enable Data" OFF) option(ENABLE_DATA_ODBC "Enable Data ODBC" OFF) + option(ENABLE_DATA_SQL_SERVER_BIG_STRINGS "Enable MS SQL Server big strings" OFF) endif() # Allow enabling and disabling components diff --git a/Data/DataTest/src/SQLExecutor.cpp b/Data/DataTest/src/SQLExecutor.cpp index 8741bddda..a5df7fb4f 100644 --- a/Data/DataTest/src/SQLExecutor.cpp +++ b/Data/DataTest/src/SQLExecutor.cpp @@ -2426,14 +2426,14 @@ void SQLExecutor::blob(int bigSize, const std::string& blobPlaceholder) catch(DataException& ce) { std::cout << ce.displayText() << std::endl; - fail (__func__, __LINE__, __FILE__); + failmsg (__func__); } try { session() << "SELECT COUNT(*) FROM Person", into(count), now; } catch(DataException& ce) { std::cout << ce.displayText() << std::endl; - fail (__func__, __LINE__, __FILE__); + failmsg (__func__); } assertTrue (count == 1); @@ -2444,7 +2444,7 @@ void SQLExecutor::blob(int bigSize, const std::string& blobPlaceholder) catch(DataException& ce) { std::cout << ce.displayText() << std::endl; - fail (__func__, __LINE__, __FILE__); + failmsg (__func__); } assertTrue (res == img); @@ -2459,7 +2459,7 @@ void SQLExecutor::blob(int bigSize, const std::string& blobPlaceholder) catch(DataException& ce) { std::cout << ce.displayText() << std::endl; - fail (__func__, __LINE__, __FILE__); + failmsg (__func__); } try @@ -2470,7 +2470,7 @@ void SQLExecutor::blob(int bigSize, const std::string& blobPlaceholder) catch(DataException& ce) { std::cout << ce.displayText() << std::endl; - fail (__func__, __LINE__, __FILE__); + failmsg (__func__); } // sometimes throws (intentionally, caught in caller) diff --git a/Data/MySQL/include/Poco/Data/MySQL/SessionImpl.h b/Data/MySQL/include/Poco/Data/MySQL/SessionImpl.h index 985784599..fa7638028 100644 --- a/Data/MySQL/include/Poco/Data/MySQL/SessionImpl.h +++ b/Data/MySQL/include/Poco/Data/MySQL/SessionImpl.h @@ -194,6 +194,8 @@ private: 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 0dcf40a75..c9051144f 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/CMakeLists.txt b/Data/ODBC/CMakeLists.txt index 8b1028520..8c4d6ee23 100644 --- a/Data/ODBC/CMakeLists.txt +++ b/Data/ODBC/CMakeLists.txt @@ -29,6 +29,44 @@ target_include_directories(DataODBC ) target_compile_definitions(DataODBC PUBLIC THREADSAFE) +# Search for MS ODBC header file which is a pre-requisite to enable +# ENABLE_DATA_SQL_SERVER_BIG_STRINGS +find_file( + _msodbc_h + + msodbcsql.h + + HINTS + + /usr/include + #macOS + /opt/homebrew/include/msodbcsql18 + /opt/homebrew/include/msodbcsql17 + #Linux + /opt/microsoft/msodbcsql18/include + /opt/microsoft/msodbcsql17/include/ + /opt/microsoft/msodbcsql/include/ + # Windows? + + NO_CACHE +) + +if (_msodbc_h) + get_filename_component(MSODBC_DIR ${_msodbc_h} DIRECTORY CACHE) + message(STATUS "ODBC: Found msodbcsql.h in: ${MSODBC_DIR}") + target_include_directories(DataODBC PUBLIC "${MSODBC_DIR}") + + if (ENABLE_DATA_SQL_SERVER_BIG_STRINGS) + target_compile_definitions(DataODBC PUBLIC POCO_DATA_SQL_SERVER_BIG_STRINGS=1) + else() + target_compile_definitions(DataODBC PUBLIC POCO_DATA_SQL_SERVER_BIG_STRINGS=0) + endif() +else() + # Disable MS SQL specific setting when msodbcsql.h is not present + set(ENABLE_DATA_SQL_SERVER_BIG_STRINGS OFF CACHE BOOL FORCE "Enable MS SQL Server big strings") +endif() +unset(_msodbc_h) + POCO_INSTALL(DataODBC) POCO_GENERATE_PACKAGE(DataODBC) diff --git a/Data/ODBC/ODBC.make b/Data/ODBC/ODBC.make index 2063fad1c..101decacc 100644 --- a/Data/ODBC/ODBC.make +++ b/Data/ODBC/ODBC.make @@ -12,6 +12,16 @@ POCO_ODBC_INCLUDE = /opt/homebrew/include endif endif +ifndef POCO_MSSQL_INCLUDE +ifeq (0, $(shell test -e /usr/include/msodbcsql.h; echo $$?)) +POCO_MSSQL_INCLUDE = /usr/include +else ifeq (0, $(shell test -e /opt/microsoft/msodbcsql18/include/msodbcsql.h; echo $$?)) +POCO_MSSQL_INCLUDE = /opt/microsoft/msodbcsql18/include +else ifeq (0, $(shell test -e /opt/homebrew/include/msodbcsql18/msodbcsql.h; echo $$?)) +POCO_MSSQL_INCLUDE = /opt/homebrew/include/msodbcsql18 +endif +endif + ifndef POCO_ODBC_LIB ifeq (0, $(shell test -d /usr/lib/$(OSARCH)-linux-gnu; echo $$?)) POCO_ODBC_LIB = /usr/lib/$(OSARCH)-linux-gnu @@ -34,7 +44,7 @@ LIBLINKEXT = $(SHAREDLIBLINKEXT) endif endif -INCLUDE += -I$(POCO_ODBC_INCLUDE) +INCLUDE += -I$(POCO_ODBC_INCLUDE) -I$(POCO_MSSQL_INCLUDE) SYSLIBS += -L$(POCO_ODBC_LIB) ## diff --git a/Data/ODBC/ODBC_vs170.vcxproj b/Data/ODBC/ODBC_vs170.vcxproj index 7a9fd6731..fe8f45f54 100644 --- a/Data/ODBC/ODBC_vs170.vcxproj +++ b/Data/ODBC/ODBC_vs170.vcxproj @@ -1,4 +1,4 @@ - + @@ -81,7 +81,7 @@ ODBC Win32Proj - + StaticLibrary MultiByte @@ -172,63 +172,63 @@ MultiByte v143 - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + <_ProjectFileVersion>17.0.34714.143 PocoDataODBCA64d @@ -306,27 +306,33 @@ ..\..\bin64\ obj64\ODBC\$(Configuration)\ true + C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); ..\..\bin64\ obj64\ODBC\$(Configuration)\ false + C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); ..\..\lib64\ obj64\ODBC\$(Configuration)\ + C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); ..\..\lib64\ obj64\ODBC\$(Configuration)\ + C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); ..\..\lib64\ obj64\ODBC\$(Configuration)\ + C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); ..\..\lib64\ obj64\ODBC\$(Configuration)\ + C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); @@ -340,7 +346,7 @@ true true true - + Level3 ProgramDatabase Default @@ -377,9 +383,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -412,7 +418,7 @@ true true true - + $(OutDir)$(TargetName).pdb Level3 ProgramDatabase @@ -441,9 +447,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -467,7 +473,7 @@ true true true - + $(OutDir)$(TargetName).pdb Level3 ProgramDatabase @@ -496,9 +502,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -522,7 +528,7 @@ true true true - + Level3 ProgramDatabase Default @@ -559,9 +565,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -594,7 +600,7 @@ true true true - + $(OutDir)$(TargetName).pdb Level3 ProgramDatabase @@ -623,9 +629,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -649,7 +655,7 @@ true true true - + $(OutDir)$(TargetName).pdb Level3 ProgramDatabase @@ -678,10 +684,10 @@ true true true - + $(OutDir)$(TargetName).pdb Level3 - + Default /Zc:__cplusplus %(AdditionalOptions) true @@ -705,7 +711,7 @@ true true true - + Level3 ProgramDatabase Default @@ -742,9 +748,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -777,7 +783,7 @@ true true true - + $(OutDir)$(TargetName).pdb Level3 ProgramDatabase @@ -806,9 +812,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -832,7 +838,7 @@ true true true - + $(OutDir)$(TargetName).pdb Level3 ProgramDatabase @@ -861,9 +867,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -876,26 +882,26 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + @@ -1031,6 +1037,6 @@ true - - - + + + \ No newline at end of file diff --git a/Data/ODBC/include/Poco/Data/ODBC/Binder.h b/Data/ODBC/include/Poco/Data/ODBC/Binder.h index ebcba1569..6274d7cff 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Binder.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Binder.h @@ -616,7 +616,7 @@ private: toODBCDirection(dir), SQL_C_CHAR, Utility::sqlDataType(SQL_C_CHAR), - (SQLUINTEGER) size - 1, + getStringColSize(size), 0, _charPtrs[pos], (SQLINTEGER) size, @@ -683,7 +683,7 @@ private: toODBCDirection(dir), SQL_C_WCHAR, Utility::sqlDataType(SQL_C_WCHAR), - (SQLUINTEGER)size - 1, + getStringColSize(size), 0, _utf16CharPtrs[pos], (SQLINTEGER)size, @@ -950,6 +950,11 @@ private: } } + 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 c9e00b672..740d76a20 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Handle.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Handle.h @@ -81,6 +81,12 @@ public: 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 e23f1787b..f415e4fbe 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/ODBC.h +++ b/Data/ODBC/include/Poco/Data/ODBC/ODBC.h @@ -58,6 +58,22 @@ #if __has_include() #include #define POCO_DATA_ODBC_HAVE_SQL_SERVER_EXT + + // To enable varchar(max) > 8000 bytes, set to 1. + // + // Notes: + // + // - this setting works in conjunction with + // the session "maxFieldSize" property, which + // ultimately determines the max string length. + // + // - increasing the "maxFieldSize" property may + // affect performance (more memory preallocated + // for prepared statements in order to safely + // accommodate data returned at execution) + #if !defined(POCO_DATA_SQL_SERVER_BIG_STRINGS) + #define POCO_DATA_SQL_SERVER_BIG_STRINGS 1 + #endif #endif #endif diff --git a/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h b/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h index ba4f1f864..a915f1929 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h +++ b/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h @@ -229,6 +229,9 @@ private: /// 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 28e786535..1cb182523 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Utility.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Utility.h @@ -33,10 +33,15 @@ namespace Data { namespace ODBC { +class ConnectionHandle; + + class ODBC_API Utility /// Various utility functions { public: + inline static const std::string MS_SQL_SERVER_DBMS_NAME = "Microsoft SQL Server"s; + typedef std::map DSNMap; typedef DSNMap DriverMap; @@ -162,6 +167,10 @@ public: 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 1d7d411dd..1f282eb03 100644 --- a/Data/ODBC/src/Binder.cpp +++ b/Data/ODBC/src/Binder.cpp @@ -120,11 +120,11 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir) { std::string tcVal; transcode(val, tcVal); - size = (SQLINTEGER)tcVal.size(); + size = static_cast(tcVal.size()); pTCVal = reinterpret_cast(std::calloc((size_t)size+1, 1)); std::memcpy(pTCVal, tcVal.data(), size); } - else size = (SQLINTEGER)val.size(); + else size = static_cast(val.size()); SQLPOINTER pVal = 0; SQLINTEGER colSize = 0; SQLSMALLINT decDigits = 0; @@ -134,7 +134,7 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir) { getColumnOrParameterSize(pos, size); char* pChar = (char*) std::calloc(size, sizeof(char)); - pVal = (SQLPOINTER) pChar; + pVal = static_cast(pChar); _outParams.insert(ParamMap::value_type(pVal, size)); _strings.insert(StringMap::value_type(pChar, const_cast(&val))); } @@ -161,16 +161,18 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir) _lengthIndicator.push_back(pLenIn); - if (Utility::isError(SQLBindParameter(_rStmt, - (SQLUSMALLINT) pos + 1, + int rc = SQLBindParameter(_rStmt, + (SQLUSMALLINT)pos + 1, toODBCDirection(dir), SQL_C_CHAR, Utility::sqlDataType(SQL_C_CHAR), - (SQLUINTEGER) colSize, + getStringColSize(colSize), 0, pVal, - (SQLINTEGER) size, - _lengthIndicator.back()))) + (SQLINTEGER)size, + _lengthIndicator.back()); + + if (Utility::isError(rc)) { throw StatementException(_rStmt, "ODBC::Binder::bind(string):SQLBindParameter(std::string)"); } @@ -217,7 +219,7 @@ void Binder::bind(std::size_t pos, const UTF16String& val, Direction dir) toODBCDirection(dir), SQL_C_WCHAR, Utility::sqlDataType(SQL_C_WCHAR), - (SQLUINTEGER)colSize, + getStringColSize(colSize), 0, pVal, (SQLINTEGER)size, @@ -504,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()) == Utility::MS_SQL_SERVER_DBMS_NAME) + return SQL_SS_LENGTH_UNLIMITED; +#endif + return columnSize; +} + + void Binder::getColSizeAndPrecision(std::size_t pos, SQLSMALLINT cDataType, SQLINTEGER& colSize, @@ -521,16 +533,37 @@ void Binder::getColSizeAndPrecision(std::size_t pos, Dynamic::Var tmp; bool foundSize(false); bool foundPrec(false); + +// SQLServer driver reports COLUMN_SIZE 8000 for VARCHAR(MAX), +// so the size check must be skipped when big strings are enabled +#ifdef POCO_DATA_ODBC_HAVE_SQL_SERVER_EXT + bool isVarchar(false); + switch (sqlDataType) + { + case SQL_VARCHAR: + case SQL_WVARCHAR: + case SQL_WLONGVARCHAR: + isVarchar = true; + break; + default: break; + } +#endif // POCO_DATA_ODBC_HAVE_SQL_SERVER_EXT + foundSize = _pTypeInfo->tryGetInfo(cDataType, "COLUMN_SIZE", tmp); if (foundSize) colSize = tmp; else foundSize = _pTypeInfo->tryGetInfo(sqlDataType, "COLUMN_SIZE", tmp); if (foundSize) colSize = tmp; - if (actualSize > static_cast(colSize)) + if (actualSize > static_cast(colSize) +#ifdef POCO_DATA_ODBC_HAVE_SQL_SERVER_EXT + && !isVarchar +#endif + ) { throw LengthExceededException(Poco::format("ODBC::Binder::getColSizeAndPrecision();%d: Error binding column %z size=%z, max size=%ld)", __LINE__, pos, actualSize, static_cast(colSize))); } + foundPrec = _pTypeInfo->tryGetInfo(cDataType, "MAXIMUM_SCALE", tmp); if (foundPrec) decDigits = tmp; else foundPrec = _pTypeInfo->tryGetInfo(sqlDataType, "MAXIMUM_SCALE", tmp); @@ -603,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/Connector.cpp b/Data/ODBC/src/Connector.cpp index 9fb71822b..64c491d2a 100644 --- a/Data/ODBC/src/Connector.cpp +++ b/Data/ODBC/src/Connector.cpp @@ -17,6 +17,13 @@ #include "Poco/Data/SessionFactory.h" +#if POCO_DATA_SQL_SERVER_BIG_STRINGS + #pragma message ("MS SQLServer ODBC big string capability ENABLED") +#else + #pragma message ("MS SQLServer ODBC big string capability DISABLED") +#endif + + namespace Poco { namespace Data { namespace ODBC { diff --git a/Data/ODBC/src/Extractor.cpp b/Data/ODBC/src/Extractor.cpp index a872e8010..bec676956 100644 --- a/Data/ODBC/src/Extractor.cpp +++ b/Data/ODBC/src/Extractor.cpp @@ -277,12 +277,9 @@ template<> bool Extractor::extractManualImpl(std::size_t pos, std::string& val, SQLSMALLINT cType) { std::size_t maxSize = _pPreparator->getMaxFieldSize(); - std::size_t fetchedSize = 0; - std::size_t totalSize = 0; SQLLEN len; - const int bufSize = CHUNK_SIZE; - Poco::Buffer apChar(bufSize); + Poco::Buffer apChar(CHUNK_SIZE); char* pChar = apChar.begin(); SQLRETURN rc = 0; @@ -291,15 +288,29 @@ bool Extractor::extractManualImpl(std::size_t pos, std::string& val do { - std::memset(pChar, 0, bufSize); + std::memset(pChar, 0, CHUNK_SIZE); len = 0; rc = SQLGetData(_rStmt, (SQLUSMALLINT) pos + 1, cType, //C data type pChar, //returned value - bufSize, //buffer length + CHUNK_SIZE, //buffer length &len); //length indicator + if (SQL_SUCCESS_WITH_INFO == rc) + { + StatementDiagnostics d(_rStmt); + std::size_t fieldCount = d.fields().size(); + for (int i = 0; i < fieldCount; ++i) + { + if (d.sqlState(i) == "01004"s) + { + if (len == SQL_NO_TOTAL || len > CHUNK_SIZE) // only part of data was returned + len = CHUNK_SIZE-1; // SQLGetData terminates the returned string + } + } + } + if (SQL_NO_DATA != rc && Utility::isError(rc)) throw StatementException(_rStmt, "ODBC::Extractor::extractManualImpl(string):SQLGetData()"); @@ -316,12 +327,11 @@ bool Extractor::extractManualImpl(std::size_t pos, std::string& val break; _lengths[pos] += len; - fetchedSize = _lengths[pos] > CHUNK_SIZE ? CHUNK_SIZE : _lengths[pos]; - totalSize += fetchedSize; - if (totalSize <= maxSize) - val.append(pChar, fetchedSize); + if (_lengths[pos] <= maxSize) + val.append(pChar, len); else - throw DataException(format(FLD_SIZE_EXCEEDED_FMT, fetchedSize, maxSize)); + throw DataException(format(FLD_SIZE_EXCEEDED_FMT, static_cast(_lengths[pos]), maxSize)); + }while (true); return true; @@ -332,12 +342,8 @@ template<> bool Extractor::extractManualImpl(std::size_t pos, UTF16String& val, SQLSMALLINT cType) { std::size_t maxSize = _pPreparator->getMaxFieldSize(); - std::size_t fetchedSize = 0; - std::size_t totalSize = 0; - SQLLEN len; - const int bufSize = CHUNK_SIZE; - Poco::Buffer apChar(bufSize); + Poco::Buffer apChar(CHUNK_SIZE); UTF16String::value_type* pChar = apChar.begin(); SQLRETURN rc = 0; @@ -346,15 +352,29 @@ bool Extractor::extractManualImpl(std::size_t pos, UTF16String& val do { - std::memset(pChar, 0, bufSize); + std::memset(pChar, 0, CHUNK_SIZE); len = 0; rc = SQLGetData(_rStmt, (SQLUSMALLINT)pos + 1, cType, //C data type pChar, //returned value - bufSize, //buffer length + CHUNK_SIZE, //buffer length &len); //length indicator + if (SQL_SUCCESS_WITH_INFO == rc) + { + StatementDiagnostics d(_rStmt); + std::size_t fieldCount = d.fields().size(); + for (int i = 0; i < fieldCount; ++i) + { + if (d.sqlState(i) == "01004"s) + { + if (len == SQL_NO_TOTAL || len > CHUNK_SIZE) // only part of data was returned + len = CHUNK_SIZE - 2; + } + } + } + if (SQL_NO_DATA != rc && Utility::isError(rc)) throw StatementException(_rStmt, "ODBC::Extractor::extractManualImpl(UTF16String):SQLGetData()"); @@ -371,12 +391,10 @@ bool Extractor::extractManualImpl(std::size_t pos, UTF16String& val break; _lengths[pos] += len; - fetchedSize = _lengths[pos] > CHUNK_SIZE ? CHUNK_SIZE : _lengths[pos]; - totalSize += fetchedSize; - if (totalSize <= maxSize) - val.append(pChar, fetchedSize / sizeof(UTF16Char)); + if (_lengths[pos] <= maxSize) + val.append(pChar, len / sizeof(UTF16Char)); else - throw DataException(format(FLD_SIZE_EXCEEDED_FMT, fetchedSize, maxSize)); + throw DataException(format(FLD_SIZE_EXCEEDED_FMT, static_cast(_lengths[pos]), maxSize)); } while (true); return true; @@ -390,7 +408,6 @@ bool Extractor::extractManualImpl(std::size_t pos, { std::size_t maxSize = _pPreparator->getMaxFieldSize(); std::size_t fetchedSize = 0; - std::size_t totalSize = 0; SQLLEN len; const int bufSize = CHUNK_SIZE; @@ -406,14 +423,12 @@ bool Extractor::extractManualImpl(std::size_t pos, std::memset(pChar, 0, bufSize); len = 0; rc = SQLGetData(_rStmt, - (SQLUSMALLINT) pos + 1, + (SQLUSMALLINT)pos + 1, cType, //C data type pChar, //returned value bufSize, //buffer length &len); //length indicator - _lengths[pos] += len; - if (SQL_NO_DATA != rc && Utility::isError(rc)) throw StatementException(_rStmt, "ODBC::Extractor::extractManualImpl(CLOB):SQLGetData()"); @@ -427,13 +442,13 @@ bool Extractor::extractManualImpl(std::size_t pos, break; fetchedSize = len > CHUNK_SIZE ? CHUNK_SIZE : len; - totalSize += fetchedSize; - if (totalSize <= maxSize) + _lengths[pos] += fetchedSize; + if (_lengths[pos] <= maxSize) val.appendRaw(pChar, fetchedSize); else throw DataException(format(FLD_SIZE_EXCEEDED_FMT, fetchedSize, maxSize)); - }while (true); + } while (true); return true; } diff --git a/Data/ODBC/src/SessionImpl.cpp b/Data/ODBC/src/SessionImpl.cpp index 222a41b8f..22dbab73e 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 8d3b4a5e5..acaf47524 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,18 @@ 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(reinterpret_cast(dbmsName), retSize); + } + return "unknown"s; +} + + } } } // namespace Poco::Data::ODBC diff --git a/Data/ODBC/testsuite/TestSuite_vs170.vcxproj b/Data/ODBC/testsuite/TestSuite_vs170.vcxproj index 800b795f8..d2f67b61c 100644 --- a/Data/ODBC/testsuite/TestSuite_vs170.vcxproj +++ b/Data/ODBC/testsuite/TestSuite_vs170.vcxproj @@ -1,4 +1,4 @@ - + @@ -81,7 +81,7 @@ TestSuite Win32Proj - + Application MultiByte @@ -172,63 +172,63 @@ MultiByte v143 - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + <_ProjectFileVersion>17.0.34714.143 TestSuited @@ -314,31 +314,37 @@ bin64\ obj64\TestSuite\$(Configuration)\ true + C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); bin64\ obj64\TestSuite\$(Configuration)\ false + C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); bin64\static_mt\ obj64\TestSuite\$(Configuration)\ true + C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); bin64\static_mt\ obj64\TestSuite\$(Configuration)\ false + C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); bin64\static_md\ obj64\TestSuite\$(Configuration)\ true + C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); bin64\static_md\ obj64\TestSuite\$(Configuration)\ false + C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); @@ -352,7 +358,7 @@ true true true - + Level3 ProgramDatabase Default @@ -388,9 +394,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -421,7 +427,7 @@ true true true - + Level3 ProgramDatabase Default @@ -457,9 +463,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -490,7 +496,7 @@ true true true - + Level3 ProgramDatabase Default @@ -526,9 +532,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -559,7 +565,7 @@ true true true - + Level3 ProgramDatabase Default @@ -595,9 +601,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -628,7 +634,7 @@ true true true - + Level3 ProgramDatabase Default @@ -664,9 +670,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -697,7 +703,7 @@ true true true - + Level3 ProgramDatabase Default @@ -733,9 +739,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -766,7 +772,7 @@ true true true - + Level3 ProgramDatabase Default @@ -802,9 +808,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -835,7 +841,7 @@ true true true - + Level3 ProgramDatabase Default @@ -871,9 +877,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -904,7 +910,7 @@ true true true - + Level3 ProgramDatabase Default @@ -940,9 +946,9 @@ true true true - + Level3 - + Default $(OutDir)$(TargetName).pdb /Zc:__cplusplus %(AdditionalOptions) @@ -962,16 +968,16 @@ - - - - - - - - - - + + + + + + + + + + @@ -1030,6 +1036,6 @@ stdc11 - - - + + + \ No newline at end of file diff --git a/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp b/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp index 14f60f788..d9ce9e581 100644 --- a/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp @@ -66,6 +66,12 @@ using Poco::DateTime; #endif #endif +#if POCO_DATA_SQL_SERVER_BIG_STRINGS + #pragma message ("MS SQLServer ODBC big string capability ENABLED") +#else + #pragma message ("MS SQLServer ODBC big string capability DISABLED") +#endif + #define MS_SQL_SERVER_DSN "PocoDataSQLServerTest" #define MS_SQL_SERVER_SERVER POCO_ODBC_TEST_DATABASE_SERVER #define MS_SQL_SERVER_PORT "1433" @@ -191,7 +197,7 @@ void ODBCSQLServerTest::testBLOB() try { executor().blob(maxFldSize, "CONVERT(VARBINARY(MAX),?)"); - fail (__func__, __LINE__, __FILE__); + failmsg(__func__); } catch (DataException&) { @@ -211,7 +217,7 @@ void ODBCSQLServerTest::testBLOB() try { executor().blob(maxFldSize+1, "CONVERT(VARBINARY(MAX),?)"); - fail (__func__, __LINE__, __FILE__); + failmsg (__func__); } catch (DataException&) { } } @@ -219,8 +225,10 @@ void ODBCSQLServerTest::testBLOB() void ODBCSQLServerTest::testBigString() { - std::string lastName(8000, 'l'); - std::string firstName(8000, 'f'); +#if defined(POCO_DATA_ODBC_HAVE_SQL_SERVER_EXT) && POCO_DATA_SQL_SERVER_BIG_STRINGS + const int limitSize = 8000, overLimitSize = 16002; + std::string lastName(overLimitSize, 'l'); + std::string firstName(limitSize, 'f'); std::string address("Address"); int age = 42; @@ -229,10 +237,87 @@ void ODBCSQLServerTest::testBigString() recreatePersonBigStringTable(); session().setFeature("autoBind", bindValue(i)); session().setFeature("autoExtract", bindValue(i + 1)); + session().setProperty("maxFieldSize", overLimitSize+1); try { + session() << "DELETE FROM Person"s, now; session() << "INSERT INTO Person VALUES (?,?,?,?)"s, use(lastName), use(firstName), use(address), use(age), now; + lastName.clear(); + firstName.clear(); + address.clear(); + age = 0; + + session() << "SELECT LastName /*VARCHAR(MAX)*/, FirstName /*VARCHAR(8000)*/, Address /*VARCHAR(30)*/, Age FROM Person"s, + into(lastName), into(firstName), into(address), into(age), now; + + assertEqual(lastName, std::string(overLimitSize, 'l')); + assertEqual(firstName, std::string(limitSize, 'f')); + assertEqual(address, "Address"s); + assertEqual(age, 42); + } + catch (DataException& ce) + { + std::cout << ce.displayText() << std::endl; + failmsg (__func__); + } + i += 2; + } +#else + std::cout << "SQL Server extensions not enabled."; +#endif // POCO_DATA_ODBC_HAVE_SQL_SERVER_EXT && POCO_DATA_SQL_SERVER_BIG_STRINGS +} + + +void ODBCSQLServerTest::testBigStringVector() +{ +#if defined(POCO_DATA_ODBC_HAVE_SQL_SERVER_EXT) && POCO_DATA_SQL_SERVER_BIG_STRINGS + const int limitSize = 8000, overLimitSize = 16002, entries = 10; + std::string lastName(overLimitSize, 'l'); + std::vector lastNameVec(entries, lastName); + std::string firstName(limitSize, 'f'); + std::vector firstNameVec(entries, firstName); + std::string address("Address"); + std::vector addressVec(entries, address); + int age = 42; + std::vector ageVec(10, age); + + for (int i = 0; i < 8;) + { + recreatePersonBigStringTable(); + session().setFeature("autoBind", bindValue(i)); + session().setFeature("autoExtract", bindValue(i + 1)); + session().setProperty("maxFieldSize", overLimitSize + 1); + try + { + session() << "DELETE FROM Person"s, now; + session() << "INSERT INTO Person VALUES (?,?,?,?)"s, + use(lastNameVec), use(firstNameVec), use(addressVec), use(ageVec), now; + lastNameVec.clear(); + firstNameVec.clear(); + addressVec.clear(); + ageVec.clear(); + + assertEqual(lastNameVec.size(), 0); + assertEqual(firstNameVec.size(), 0); + assertEqual(addressVec.size(), 0); + assertEqual(ageVec.size(), 0); + + session() << "SELECT LastName /*VARCHAR(MAX)*/, FirstName /*VARCHAR(8000)*/, Address /*VARCHAR(30)*/, Age FROM Person"s, + into(lastNameVec), into(firstNameVec), into(addressVec), into(ageVec), now; + + assertEqual(lastNameVec.size(), entries); + assertEqual(firstNameVec.size(), entries); + assertEqual(addressVec.size(), entries); + assertEqual(ageVec.size(), entries); + + for (int i = 0; i < entries; ++i) + { + assertEqual(lastNameVec[i], std::string(overLimitSize, 'l')); + assertEqual(firstNameVec[i], std::string(limitSize, 'f')); + assertEqual(addressVec[i], "Address"s); + assertEqual(ageVec[i], 42); + } } catch (DataException& ce) { @@ -241,6 +326,9 @@ void ODBCSQLServerTest::testBigString() } i += 2; } +#else + std::cout << "SQL Server extensions not enabled."; +#endif // POCO_DATA_ODBC_HAVE_SQL_SERVER_EXT && POCO_DATA_SQL_SERVER_BIG_STRINGS } @@ -1020,6 +1108,7 @@ CppUnit::Test* ODBCSQLServerTest::suite() CppUnit_addTest(pSuite, ODBCSQLServerTest, testEmptyDB); CppUnit_addTest(pSuite, ODBCSQLServerTest, testBLOB); CppUnit_addTest(pSuite, ODBCSQLServerTest, testBigString); + CppUnit_addTest(pSuite, ODBCSQLServerTest, testBigStringVector); CppUnit_addTest(pSuite, ODBCSQLServerTest, testBigBatch); CppUnit_addTest(pSuite, ODBCSQLServerTest, testBLOBContainer); CppUnit_addTest(pSuite, ODBCSQLServerTest, testBLOBStmt); diff --git a/Data/ODBC/testsuite/src/ODBCSQLServerTest.h b/Data/ODBC/testsuite/src/ODBCSQLServerTest.h index 61f95be9b..d1557a171 100644 --- a/Data/ODBC/testsuite/src/ODBCSQLServerTest.h +++ b/Data/ODBC/testsuite/src/ODBCSQLServerTest.h @@ -48,6 +48,7 @@ public: void testBLOB() override; void testBigString(); + void testBigStringVector(); void testBigBatch(); void testNull() override; void testBulk() override; diff --git a/Data/ODBC/testsuite/src/ODBCTestSuite.cpp b/Data/ODBC/testsuite/src/ODBCTestSuite.cpp index 1c80536c4..bf470eb85 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 87cfd1965..edbd10826 100644 --- a/Data/PostgreSQL/include/Poco/Data/PostgreSQL/SessionImpl.h +++ b/Data/PostgreSQL/include/Poco/Data/PostgreSQL/SessionImpl.h @@ -130,6 +130,8 @@ public: const std::string& connectorName() const; /// Returns the name of the connector. + void setName(); + private: std::string _connectorName; mutable SessionHandle _sessionHandle; diff --git a/Data/PostgreSQL/src/SessionImpl.cpp b/Data/PostgreSQL/src/SessionImpl.cpp index 56a22bf9d..ec31fe651 100644 --- a/Data/PostgreSQL/src/SessionImpl.cpp +++ b/Data/PostgreSQL/src/SessionImpl.cpp @@ -79,6 +79,12 @@ SessionImpl::~SessionImpl() } +void SessionImpl::setName() +{ + setDBMSName("PostgreSQL"s); +} + + void SessionImpl::setConnectionTimeout(std::size_t aTimeout) { _timeout = aTimeout; @@ -145,6 +151,8 @@ void SessionImpl::open(const std::string& aConnectionString) addFeature("binaryExtraction", &SessionImpl::setBinaryExtraction, &SessionImpl::isBinaryExtraction); + + setName(); } diff --git a/Data/SQLite/include/Poco/Data/SQLite/SessionImpl.h b/Data/SQLite/include/Poco/Data/SQLite/SessionImpl.h index be768ff87..eeb33c4d8 100644 --- a/Data/SQLite/include/Poco/Data/SQLite/SessionImpl.h +++ b/Data/SQLite/include/Poco/Data/SQLite/SessionImpl.h @@ -139,7 +139,10 @@ protected: void setTransactionType(const std::string &prop, const Poco::Any& value); Poco::Any getTransactionType(const std::string& prop) const; + private: + void setName(); + std::string _connector; sqlite3* _pDB; bool _connected; diff --git a/Data/SQLite/src/SessionImpl.cpp b/Data/SQLite/src/SessionImpl.cpp index 75ca5d0ed..a0df7e530 100644 --- a/Data/SQLite/src/SessionImpl.cpp +++ b/Data/SQLite/src/SessionImpl.cpp @@ -82,6 +82,12 @@ SessionImpl::~SessionImpl() } +void SessionImpl::setName() +{ + setDBMSName("SQLite"s); +} + + Poco::Data::StatementImpl::Ptr SessionImpl::createStatementImpl() { poco_check_ptr (_pDB); @@ -273,17 +279,17 @@ Poco::Any SessionImpl::getConnectionTimeout(const std::string& prop) const return Poco::Any(_timeout/1000); } -void SessionImpl::setTransactionType(TransactionType transactionType) +void SessionImpl::setTransactionType(TransactionType transactionType) { _transactionType = transactionType; } -void SessionImpl::setTransactionType(const std::string &prop, const Poco::Any& value) +void SessionImpl::setTransactionType(const std::string &prop, const Poco::Any& value) { setTransactionType(Poco::RefAnyCast(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 332e99698..d124c463e 100644 --- a/Data/include/Poco/Data/Session.h +++ b/Data/include/Poco/Data/Session.h @@ -204,6 +204,11 @@ public: 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 @@ private: // 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 2d7aef0a0..e5644772e 100644 --- a/Data/include/Poco/Data/SessionImpl.h +++ b/Data/include/Poco/Data/SessionImpl.h @@ -65,6 +65,11 @@ public: 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 @@ public: /// 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 @@ private: SessionImpl(const SessionImpl&); SessionImpl& operator = (const SessionImpl&); + std::string _dbmsName; std::string _connectionString; std::size_t _loginTimeout; }; @@ -213,6 +222,19 @@ private: // // 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 0b7cded41..428eef52a 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 fc89d9f54..00b9228c6 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 198f9960d..36bd98235 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 1788c919f..c3bb3e204 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. @@ -170,11 +177,13 @@ includepath="" librarypath="" odbclib="" odbcinclude="" +mssqlinclude="" unbundled="" 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 @@ -208,6 +217,9 @@ while [ $# -ge 1 ]; do --odbc-include=*) odbcinclude="$(echo "${1}" | awk '{print substr($0,16)}')" ;; + --mssql-include=*) + mssqlinclude="$(echo "${1}" | awk '{print substr($0,16)}')" ;; + --mysql-lib=*) mysqllib="$(echo "${1}" | awk '{print substr($0,13)}')" ;; @@ -255,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)}')" ;; @@ -377,6 +394,9 @@ fi if [ -n "$odbcinclude" ] ; then echo "POCO_ODBC_INCLUDE = $odbcinclude" >>"$build"/config.make fi +if [ -n "$mssqlinclude" ] ; then + echo "POCO_MSSQL_INCLUDE = $mssqlinclude" >>"$build"/config.make +fi if [ -n "$mysqllib" ] ; then echo "POCO_MYSQL_LIB = $mysqllib" >>"$build"/config.make fi @@ -401,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 @@ -428,6 +451,9 @@ fi if [ -n "$odbcinclude" ] ; then echo "export POCO_ODBC_INCLUDE" >>"$build"/config.make fi +if [ -n "$mssqlinclude" ] ; then + echo "export POCO_MSSQL_INCLUDE" >>"$build"/config.make +fi if [ -n "$mysqllib" ] ; then echo "export POCO_MYSQL_LIB" >>"$build"/config.make fi @@ -452,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