From 7064ae3c2dd0fcbb9d1a21c9ff8c8ddc171da44c Mon Sep 17 00:00:00 2001 From: Aleksandar Fabijanic Date: Wed, 24 Jul 2024 06:05:08 -0500 Subject: [PATCH] 4368 fix oracle failing odbc tests (#4611) * chore(CI): enable oracle ODBC tests #4368 * fix(ODBC): Oracle AutoTransaction test case; add explicit ODBC exceptions instantiation #4368 * fix(odbc): oracle test table creation error detection #4368 * enh(Data): enforce sql parsing for transaction tests to avoid select-only transactions #4368 --------- Co-authored-by: cunj123 --- .github/workflows/ci.yml | 30 +++++-------- Data/DataTest/src/SQLExecutor.cpp | 14 ++++++ .../ODBC/include/Poco/Data/ODBC/Diagnostics.h | 27 +++++++++-- Data/ODBC/include/Poco/Data/ODBC/Error.h | 45 +++++++++++++------ .../include/Poco/Data/ODBC/ODBCException.h | 7 +++ .../Poco/Data/ODBC/ODBCStatementImpl.h | 6 +-- Data/ODBC/testsuite/src/ODBCOracleTest.cpp | 21 +++++++-- 7 files changed, 108 insertions(+), 42 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 06a797f35..dd575a1a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -700,12 +700,12 @@ jobs: # POSTGRES_PASSWORD: postgres # ports: # - 5432:5432 - #oracle: - # image: container-registry.oracle.com/database/express:21.3.0-xe - # env: - # ORACLE_PWD: poco - # ports: - # - 1521:1521 + oracle: + image: container-registry.oracle.com/database/express:21.3.0-xe + env: + ORACLE_PWD: poco + ports: + - 1521:1521 sqlserver: image: mcr.microsoft.com/mssql/server:2022-latest env: @@ -716,25 +716,17 @@ jobs: - 1433:1433 steps: - uses: actions/checkout@v4 - - run: sudo apt -y update && sudo apt -y install libssl-dev unixodbc-dev alien libaio1 gnupg2 curl # libmysqlclient-dev mysql-client odbc-postgresql + - 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 -j4 && 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 # wget https://dev.mysql.com/get/Downloads/MySQL-8.2/mysql-community-client-plugins_8.2.0-1ubuntu22.04_amd64.deb # sudo dpkg -i mysql-community-client-plugins_8.2.0-1ubuntu22.04_amd64.deb mysql-connector-odbc_8.2.0-1ubuntu22.04_amd64.deb - # - name: Setup Oracle ODBC connector - # run: | - # wget https://download.oracle.com/otn_software/linux/instantclient/2112000/oracle-instantclient-basic-21.12.0.0.0-1.x86_64.rpm - # wget https://download.oracle.com/otn_software/linux/instantclient/2112000/oracle-instantclient-sqlplus-21.12.0.0.0-1.x86_64.rpm - # wget https://download.oracle.com/otn_software/linux/instantclient/2112000/oracle-instantclient-odbc-21.12.0.0.0-1.x86_64.rpm - # sudo alien --scripts ./oracle-instantclient-basic-21.12.0.0.0-1.x86_64.rpm - # sudo alien --scripts ./oracle-instantclient-sqlplus-21.12.0.0.0-1.x86_64.rpm - # sudo alien --scripts ./oracle-instantclient-odbc-21.12.0.0.0-1.x86_64.rpm - # sudo apt install ./oracle-instantclient-basic_21.12.0.0.0-2_amd64.deb - # sudo apt install ./oracle-instantclient-sqlplus_21.12.0.0.0-2_amd64.deb - # sudo apt install ./oracle-instantclient-odbc_21.12.0.0.0-2_amd64.deb - # sudo /usr/lib/oracle/21/client64/bin/odbc_update_ini.sh / "/usr/lib/oracle/21/client64/lib" "" "" "/etc/odbc.ini" + - name: Setup Oracle ODBC connector + run: | + wget https://www.devart.com/odbc/oracle/devartodbcoracle_amd64.deb + sudo dpkg -i devartodbcoracle_amd64.deb - name: Setup SQL Server ODBC connector run: | curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc diff --git a/Data/DataTest/src/SQLExecutor.cpp b/Data/DataTest/src/SQLExecutor.cpp index 2b30647b0..65f5a7e68 100644 --- a/Data/DataTest/src/SQLExecutor.cpp +++ b/Data/DataTest/src/SQLExecutor.cpp @@ -3857,6 +3857,8 @@ void SQLExecutor::sessionTransaction(const std::string& connector, const std::st } bool autoCommit = session().getFeature("autoCommit"); + bool sqlParse = session().getFeature("sqlParse"); + session().setFeature("sqlParse", true); Session local(connector, connect); @@ -3925,6 +3927,7 @@ void SQLExecutor::sessionTransaction(const std::string& connector, const std::st // end autoCommit = true // restore the original transaction state + session().setFeature("sqlParse", sqlParse); session().setFeature("autoCommit", autoCommit); } @@ -3932,6 +3935,8 @@ void SQLExecutor::sessionTransaction(const std::string& connector, const std::st void SQLExecutor::sessionTransactionNoAutoCommit(const std::string& connector, const std::string& connect) { bool autoCommit = session().getFeature("autoCommit"); + bool sqlParse = session().getFeature("sqlParse"); + session().setFeature("sqlParse", true); Session local(connector, connect); local.setFeature("autoCommit", false); @@ -4019,6 +4024,8 @@ void SQLExecutor::sessionTransactionNoAutoCommit(const std::string& connector, c session().commit(); local.commit(); #endif + + session().setFeature("sqlParse", sqlParse); session().setFeature("autoCommit", autoCommit); } @@ -4033,9 +4040,12 @@ void SQLExecutor::transaction(const std::string& connector, const std::string& c Session local(connector, connect); local.setFeature("autoCommit", true); + local.setFeature("sqlParse", true); bool autoCommit = session().getFeature("autoCommit"); auto ti = session().getTransactionIsolation(); + bool sqlParse = session().getFeature("sqlParse"); + session().setFeature("sqlParse", true); setTransactionIsolation(session(), Session::TRANSACTION_READ_COMMITTED); if (local.hasTransactionIsolation(Session::TRANSACTION_READ_UNCOMMITTED)) @@ -4157,6 +4167,7 @@ void SQLExecutor::transaction(const std::string& connector, const std::string& c session().commit(); // restore the original transaction state + session().setFeature("sqlParse", sqlParse); session().setFeature("autoCommit", autoCommit); setTransactionIsolation(session(), ti); } @@ -4192,6 +4203,8 @@ void SQLExecutor::transactor() session().setFeature("autoCommit", false); session().setTransactionIsolation(Session::TRANSACTION_READ_COMMITTED); + bool sqlParse = session().getFeature("sqlParse"); + session().setFeature("sqlParse", true); TestCommitTransactor ct; Transaction t1(session(), ct); @@ -4249,6 +4262,7 @@ void SQLExecutor::transactor() session().commit(); // restore the original transaction state + session().setFeature("sqlParse", sqlParse); session().setFeature("autoCommit", autoCommit); setTransactionIsolation(session(), ti); } diff --git a/Data/ODBC/include/Poco/Data/ODBC/Diagnostics.h b/Data/ODBC/include/Poco/Data/ODBC/Diagnostics.h index aa919c2be..9b2f3726c 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Diagnostics.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Diagnostics.h @@ -32,6 +32,10 @@ namespace Data { namespace ODBC { +template +class Error; + + template class Diagnostics /// Utility class providing functionality for retrieving ODBC diagnostic @@ -213,6 +217,12 @@ public: return *this; } +protected: + const H& handle() const + { + return _handle; + } + private: Diagnostics(); @@ -226,13 +236,22 @@ private: /// Context handle const H& _handle; + + friend class Error; }; -typedef Diagnostics EnvironmentDiagnostics; -typedef Diagnostics ConnectionDiagnostics; -typedef Diagnostics StatementDiagnostics; -typedef Diagnostics DescriptorDiagnostics; +// explicit instantiation definition +template class Diagnostics; +template class Diagnostics; +template class Diagnostics; +template class Diagnostics; + + +using EnvironmentDiagnostics = Diagnostics; +using ConnectionDiagnostics = Diagnostics; +using StatementDiagnostics = Diagnostics; +using DescriptorDiagnostics = Diagnostics; } } } // namespace Poco::Data::ODBC diff --git a/Data/ODBC/include/Poco/Data/ODBC/Error.h b/Data/ODBC/include/Poco/Data/ODBC/Error.h index 8afc08468..a0f91e046 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Error.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Error.h @@ -41,26 +41,38 @@ class Error /// as well as individual diagnostic records. { public: - explicit Error(const H& handle) : _diagnostics(handle) + explicit Error(const H& handle) : _pDiag(new Diagnostics(handle)) /// Creates the Error. { } + Error(const Error& other) : Error(other.diagnostics().handle()) + /// Creates the Error from another one. + { + } + ~Error() /// Destroys the Error. { } + Error& operator = (const Error& other) + /// Assigns another Error to this one. + { + _pDiag.reset(new Diagnostics(other.diagnostics().handle())); + return *this; + } + const Diagnostics& diagnostics() const /// Returns the associated diagnostics. { - return _diagnostics; + return *_pDiag; } int count() const /// Returns the count of diagnostic records. { - return (int) _diagnostics.count(); + return (int) _pDiag->count(); } std::string& toString(int index, std::string& str) const @@ -76,9 +88,9 @@ public: "===========================\n" "SQLSTATE = %s\nNative Error Code = %ld\n%s\n\n", index + 1, - _diagnostics.sqlState(index), - _diagnostics.nativeError(index), - _diagnostics.message(index)); + _pDiag->sqlState(index), + _pDiag->nativeError(index), + _pDiag->message(index)); str.append(s); @@ -92,8 +104,8 @@ public: Poco::format(str, "Connection:%s\nServer:%s\n", - _diagnostics.connectionName(), - _diagnostics.serverName()); + _pDiag->connectionName(), + _pDiag->serverName()); std::string s; for (int i = 0; i < count(); ++i) @@ -108,14 +120,21 @@ public: private: Error(); - Diagnostics _diagnostics; + std::unique_ptr> _pDiag; }; -typedef Error EnvironmentError; -typedef Error ConnectionError; -typedef Error StatementError; -typedef Error DescriptorError; +// explicit instantiation definition +template class Error; +template class Error; +template class Error; +template class Error; + + +using EnvironmentError = Error; +using ConnectionError = Error; +using StatementError = Error; +using DescriptorError = Error; } } } // namespace Poco::Data::ODBC diff --git a/Data/ODBC/include/Poco/Data/ODBC/ODBCException.h b/Data/ODBC/include/Poco/Data/ODBC/ODBCException.h index 098ffde95..1e909b1be 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/ODBCException.h +++ b/Data/ODBC/include/Poco/Data/ODBC/ODBCException.h @@ -138,6 +138,13 @@ private: }; +// explicit instantiation definition +template class HandleException; +template class HandleException; +template class HandleException; +template class HandleException; + + using EnvironmentException = HandleException; using ConnectionException = HandleException; using StatementException = HandleException; diff --git a/Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h b/Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h index 5eb4e3b19..16290ac95 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h +++ b/Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h @@ -50,6 +50,9 @@ public: ~ODBCStatementImpl(); /// Destroys the ODBCStatementImpl. + std::string nativeSQL(); + /// Returns the SQL string as modified by the driver. + protected: std::size_t columnsReturned() const; /// Returns number of columns returned by query. @@ -91,9 +94,6 @@ protected: void execDirectImpl(const std::string& query); /// Execute query directly impl - std::string nativeSQL(); - /// Returns the SQL string as modified by the driver. - void printErrors(std::ostream& os) const; /// Print errors, if any. diff --git a/Data/ODBC/testsuite/src/ODBCOracleTest.cpp b/Data/ODBC/testsuite/src/ODBCOracleTest.cpp index 4a907915b..f9755b634 100644 --- a/Data/ODBC/testsuite/src/ODBCOracleTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCOracleTest.cpp @@ -649,6 +649,10 @@ void ODBCOracleTest::testAutoTransaction() Session localSession("ODBC", _connectString); bool ac = session().getFeature("autoCommit"); int count = 0; + // enabing SQL parsing is necessary to prevent + // starting a transaction for a simple select + bool sqlParse = session().getFeature("sqlParse"); + session().setFeature("sqlParse", true); recreateIntsTable(); @@ -675,10 +679,19 @@ void ODBCOracleTest::testAutoTransaction() session() << "INSERT INTO Strings VALUES (1)", now; session() << "INSERT INTO Strings VALUES (2)", now; session() << "BAD QUERY", now; - } catch (Poco::Exception&) {} + failmsg("Bad SQL statement must throw"); + } + catch (Poco::Exception&) {} + + assertFalse (session().isTransaction()); session() << "SELECT count(*) FROM Strings", into(count), now; assertTrue (0 == count); + assertFalse (session().isTransaction()); + + session() << "SELECT count(*) FROM Strings", into(count), now; + assertTrue (0 == count); + assertFalse (session().isTransaction()); AutoTransaction at(session()); @@ -694,6 +707,7 @@ void ODBCOracleTest::testAutoTransaction() localSession << "SELECT count(*) FROM Strings", into(count), now; assertTrue (3 == count); + session().setFeature("sqlParse", sqlParse); session().setFeature("autoCommit", ac); } @@ -711,8 +725,9 @@ void ODBCOracleTest::dropObject(const std::string& type, const std::string& name StatementDiagnostics::Iterator it = flds.begin(); for (; it != flds.end(); ++it) { - if (4043 == it->_nativeError || //ORA-04043 (object does not exist) - 942 == it->_nativeError || 1433808584/*DevArt*/== it->_nativeError) //ORA-00942 (table does not exist) + std::string errMsg((char*)it->_message); + if ((errMsg.find("ORA-00942") != std::string::npos) || // table does not exist + (errMsg.find("ORA-04043") != std::string::npos)) // object does not exist { ignoreError = true; break;