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 <n.belusic@pta.hr>
This commit is contained in:
Aleksandar Fabijanic 2024-07-24 06:05:08 -05:00 committed by GitHub
parent a6762f51cf
commit 7064ae3c2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 108 additions and 42 deletions

View File

@ -700,12 +700,12 @@ jobs:
# POSTGRES_PASSWORD: postgres # POSTGRES_PASSWORD: postgres
# ports: # ports:
# - 5432:5432 # - 5432:5432
#oracle: oracle:
# image: container-registry.oracle.com/database/express:21.3.0-xe image: container-registry.oracle.com/database/express:21.3.0-xe
# env: env:
# ORACLE_PWD: poco ORACLE_PWD: poco
# ports: ports:
# - 1521:1521 - 1521:1521
sqlserver: sqlserver:
image: mcr.microsoft.com/mssql/server:2022-latest image: mcr.microsoft.com/mssql/server:2022-latest
env: env:
@ -716,25 +716,17 @@ jobs:
- 1433:1433 - 1433:1433
steps: steps:
- uses: actions/checkout@v4 - 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 - 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 # - name: Setup MySQL ODBC connector
# run: | # 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/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 # 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 # 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 - name: Setup Oracle ODBC connector
# run: | 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://www.devart.com/odbc/oracle/devartodbcoracle_amd64.deb
# wget https://download.oracle.com/otn_software/linux/instantclient/2112000/oracle-instantclient-sqlplus-21.12.0.0.0-1.x86_64.rpm sudo dpkg -i devartodbcoracle_amd64.deb
# 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 SQL Server ODBC connector - name: Setup SQL Server ODBC connector
run: | run: |
curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc

View File

@ -3857,6 +3857,8 @@ void SQLExecutor::sessionTransaction(const std::string& connector, const std::st
} }
bool autoCommit = session().getFeature("autoCommit"); bool autoCommit = session().getFeature("autoCommit");
bool sqlParse = session().getFeature("sqlParse");
session().setFeature("sqlParse", true);
Session local(connector, connect); Session local(connector, connect);
@ -3925,6 +3927,7 @@ void SQLExecutor::sessionTransaction(const std::string& connector, const std::st
// end autoCommit = true // end autoCommit = true
// restore the original transaction state // restore the original transaction state
session().setFeature("sqlParse", sqlParse);
session().setFeature("autoCommit", autoCommit); 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) void SQLExecutor::sessionTransactionNoAutoCommit(const std::string& connector, const std::string& connect)
{ {
bool autoCommit = session().getFeature("autoCommit"); bool autoCommit = session().getFeature("autoCommit");
bool sqlParse = session().getFeature("sqlParse");
session().setFeature("sqlParse", true);
Session local(connector, connect); Session local(connector, connect);
local.setFeature("autoCommit", false); local.setFeature("autoCommit", false);
@ -4019,6 +4024,8 @@ void SQLExecutor::sessionTransactionNoAutoCommit(const std::string& connector, c
session().commit(); session().commit();
local.commit(); local.commit();
#endif #endif
session().setFeature("sqlParse", sqlParse);
session().setFeature("autoCommit", autoCommit); session().setFeature("autoCommit", autoCommit);
} }
@ -4033,9 +4040,12 @@ void SQLExecutor::transaction(const std::string& connector, const std::string& c
Session local(connector, connect); Session local(connector, connect);
local.setFeature("autoCommit", true); local.setFeature("autoCommit", true);
local.setFeature("sqlParse", true);
bool autoCommit = session().getFeature("autoCommit"); bool autoCommit = session().getFeature("autoCommit");
auto ti = session().getTransactionIsolation(); auto ti = session().getTransactionIsolation();
bool sqlParse = session().getFeature("sqlParse");
session().setFeature("sqlParse", true);
setTransactionIsolation(session(), Session::TRANSACTION_READ_COMMITTED); setTransactionIsolation(session(), Session::TRANSACTION_READ_COMMITTED);
if (local.hasTransactionIsolation(Session::TRANSACTION_READ_UNCOMMITTED)) if (local.hasTransactionIsolation(Session::TRANSACTION_READ_UNCOMMITTED))
@ -4157,6 +4167,7 @@ void SQLExecutor::transaction(const std::string& connector, const std::string& c
session().commit(); session().commit();
// restore the original transaction state // restore the original transaction state
session().setFeature("sqlParse", sqlParse);
session().setFeature("autoCommit", autoCommit); session().setFeature("autoCommit", autoCommit);
setTransactionIsolation(session(), ti); setTransactionIsolation(session(), ti);
} }
@ -4192,6 +4203,8 @@ void SQLExecutor::transactor()
session().setFeature("autoCommit", false); session().setFeature("autoCommit", false);
session().setTransactionIsolation(Session::TRANSACTION_READ_COMMITTED); session().setTransactionIsolation(Session::TRANSACTION_READ_COMMITTED);
bool sqlParse = session().getFeature("sqlParse");
session().setFeature("sqlParse", true);
TestCommitTransactor ct; TestCommitTransactor ct;
Transaction t1(session(), ct); Transaction t1(session(), ct);
@ -4249,6 +4262,7 @@ void SQLExecutor::transactor()
session().commit(); session().commit();
// restore the original transaction state // restore the original transaction state
session().setFeature("sqlParse", sqlParse);
session().setFeature("autoCommit", autoCommit); session().setFeature("autoCommit", autoCommit);
setTransactionIsolation(session(), ti); setTransactionIsolation(session(), ti);
} }

View File

@ -32,6 +32,10 @@ namespace Data {
namespace ODBC { namespace ODBC {
template <typename H, SQLSMALLINT handleType>
class Error;
template <typename H, SQLSMALLINT handleType> template <typename H, SQLSMALLINT handleType>
class Diagnostics class Diagnostics
/// Utility class providing functionality for retrieving ODBC diagnostic /// Utility class providing functionality for retrieving ODBC diagnostic
@ -213,6 +217,12 @@ public:
return *this; return *this;
} }
protected:
const H& handle() const
{
return _handle;
}
private: private:
Diagnostics(); Diagnostics();
@ -226,13 +236,22 @@ private:
/// Context handle /// Context handle
const H& _handle; const H& _handle;
friend class Error<H, handleType>;
}; };
typedef Diagnostics<SQLHENV, SQL_HANDLE_ENV> EnvironmentDiagnostics; // explicit instantiation definition
typedef Diagnostics<SQLHDBC, SQL_HANDLE_DBC> ConnectionDiagnostics; template class Diagnostics<SQLHENV, SQL_HANDLE_ENV>;
typedef Diagnostics<SQLHSTMT, SQL_HANDLE_STMT> StatementDiagnostics; template class Diagnostics<SQLHDBC, SQL_HANDLE_DBC>;
typedef Diagnostics<SQLHDESC, SQL_HANDLE_DESC> DescriptorDiagnostics; template class Diagnostics<SQLHSTMT, SQL_HANDLE_STMT>;
template class Diagnostics<SQLHDESC, SQL_HANDLE_DESC>;
using EnvironmentDiagnostics = Diagnostics<SQLHENV, SQL_HANDLE_ENV>;
using ConnectionDiagnostics = Diagnostics<SQLHDBC, SQL_HANDLE_DBC>;
using StatementDiagnostics = Diagnostics<SQLHSTMT, SQL_HANDLE_STMT>;
using DescriptorDiagnostics = Diagnostics<SQLHDESC, SQL_HANDLE_DESC>;
} } } // namespace Poco::Data::ODBC } } } // namespace Poco::Data::ODBC

View File

@ -41,26 +41,38 @@ class Error
/// as well as individual diagnostic records. /// as well as individual diagnostic records.
{ {
public: public:
explicit Error(const H& handle) : _diagnostics(handle) explicit Error(const H& handle) : _pDiag(new Diagnostics<H, handleType>(handle))
/// Creates the Error. /// Creates the Error.
{ {
} }
Error(const Error& other) : Error(other.diagnostics().handle())
/// Creates the Error from another one.
{
}
~Error() ~Error()
/// Destroys the Error. /// Destroys the Error.
{ {
} }
Error& operator = (const Error& other)
/// Assigns another Error to this one.
{
_pDiag.reset(new Diagnostics<H, handleType>(other.diagnostics().handle()));
return *this;
}
const Diagnostics<H, handleType>& diagnostics() const const Diagnostics<H, handleType>& diagnostics() const
/// Returns the associated diagnostics. /// Returns the associated diagnostics.
{ {
return _diagnostics; return *_pDiag;
} }
int count() const int count() const
/// Returns the count of diagnostic records. /// Returns the count of diagnostic records.
{ {
return (int) _diagnostics.count(); return (int) _pDiag->count();
} }
std::string& toString(int index, std::string& str) const std::string& toString(int index, std::string& str) const
@ -76,9 +88,9 @@ public:
"===========================\n" "===========================\n"
"SQLSTATE = %s\nNative Error Code = %ld\n%s\n\n", "SQLSTATE = %s\nNative Error Code = %ld\n%s\n\n",
index + 1, index + 1,
_diagnostics.sqlState(index), _pDiag->sqlState(index),
_diagnostics.nativeError(index), _pDiag->nativeError(index),
_diagnostics.message(index)); _pDiag->message(index));
str.append(s); str.append(s);
@ -92,8 +104,8 @@ public:
Poco::format(str, Poco::format(str,
"Connection:%s\nServer:%s\n", "Connection:%s\nServer:%s\n",
_diagnostics.connectionName(), _pDiag->connectionName(),
_diagnostics.serverName()); _pDiag->serverName());
std::string s; std::string s;
for (int i = 0; i < count(); ++i) for (int i = 0; i < count(); ++i)
@ -108,14 +120,21 @@ public:
private: private:
Error(); Error();
Diagnostics<H, handleType> _diagnostics; std::unique_ptr<Diagnostics<H, handleType>> _pDiag;
}; };
typedef Error<SQLHENV, SQL_HANDLE_ENV> EnvironmentError; // explicit instantiation definition
typedef Error<SQLHDBC, SQL_HANDLE_DBC> ConnectionError; template class Error<SQLHENV, SQL_HANDLE_ENV>;
typedef Error<SQLHSTMT, SQL_HANDLE_STMT> StatementError; template class Error<SQLHDBC, SQL_HANDLE_DBC>;
typedef Error<SQLHSTMT, SQL_HANDLE_DESC> DescriptorError; template class Error<SQLHSTMT, SQL_HANDLE_STMT>;
template class Error<SQLHDESC, SQL_HANDLE_DESC>;
using EnvironmentError = Error<SQLHENV, SQL_HANDLE_ENV>;
using ConnectionError = Error<SQLHDBC, SQL_HANDLE_DBC>;
using StatementError = Error<SQLHSTMT, SQL_HANDLE_STMT>;
using DescriptorError = Error<SQLHSTMT, SQL_HANDLE_DESC>;
} } } // namespace Poco::Data::ODBC } } } // namespace Poco::Data::ODBC

View File

@ -138,6 +138,13 @@ private:
}; };
// explicit instantiation definition
template class HandleException<SQLHENV, SQL_HANDLE_ENV>;
template class HandleException<SQLHDBC, SQL_HANDLE_DBC>;
template class HandleException<SQLHSTMT, SQL_HANDLE_STMT>;
template class HandleException<SQLHDESC, SQL_HANDLE_DESC>;
using EnvironmentException = HandleException<SQLHENV, SQL_HANDLE_ENV>; using EnvironmentException = HandleException<SQLHENV, SQL_HANDLE_ENV>;
using ConnectionException = HandleException<SQLHDBC, SQL_HANDLE_DBC>; using ConnectionException = HandleException<SQLHDBC, SQL_HANDLE_DBC>;
using StatementException = HandleException<SQLHSTMT, SQL_HANDLE_STMT>; using StatementException = HandleException<SQLHSTMT, SQL_HANDLE_STMT>;

View File

@ -50,6 +50,9 @@ public:
~ODBCStatementImpl(); ~ODBCStatementImpl();
/// Destroys the ODBCStatementImpl. /// Destroys the ODBCStatementImpl.
std::string nativeSQL();
/// Returns the SQL string as modified by the driver.
protected: protected:
std::size_t columnsReturned() const; std::size_t columnsReturned() const;
/// Returns number of columns returned by query. /// Returns number of columns returned by query.
@ -91,9 +94,6 @@ protected:
void execDirectImpl(const std::string& query); void execDirectImpl(const std::string& query);
/// Execute query directly impl /// Execute query directly impl
std::string nativeSQL();
/// Returns the SQL string as modified by the driver.
void printErrors(std::ostream& os) const; void printErrors(std::ostream& os) const;
/// Print errors, if any. /// Print errors, if any.

View File

@ -649,6 +649,10 @@ void ODBCOracleTest::testAutoTransaction()
Session localSession("ODBC", _connectString); Session localSession("ODBC", _connectString);
bool ac = session().getFeature("autoCommit"); bool ac = session().getFeature("autoCommit");
int count = 0; 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(); recreateIntsTable();
@ -675,10 +679,19 @@ void ODBCOracleTest::testAutoTransaction()
session() << "INSERT INTO Strings VALUES (1)", now; session() << "INSERT INTO Strings VALUES (1)", now;
session() << "INSERT INTO Strings VALUES (2)", now; session() << "INSERT INTO Strings VALUES (2)", now;
session() << "BAD QUERY", 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; session() << "SELECT count(*) FROM Strings", into(count), now;
assertTrue (0 == count); assertTrue (0 == count);
assertFalse (session().isTransaction());
session() << "SELECT count(*) FROM Strings", into(count), now;
assertTrue (0 == count);
assertFalse (session().isTransaction());
AutoTransaction at(session()); AutoTransaction at(session());
@ -694,6 +707,7 @@ void ODBCOracleTest::testAutoTransaction()
localSession << "SELECT count(*) FROM Strings", into(count), now; localSession << "SELECT count(*) FROM Strings", into(count), now;
assertTrue (3 == count); assertTrue (3 == count);
session().setFeature("sqlParse", sqlParse);
session().setFeature("autoCommit", ac); session().setFeature("autoCommit", ac);
} }
@ -711,8 +725,9 @@ void ODBCOracleTest::dropObject(const std::string& type, const std::string& name
StatementDiagnostics::Iterator it = flds.begin(); StatementDiagnostics::Iterator it = flds.begin();
for (; it != flds.end(); ++it) for (; it != flds.end(); ++it)
{ {
if (4043 == it->_nativeError || //ORA-04043 (object does not exist) std::string errMsg((char*)it->_message);
942 == it->_nativeError || 1433808584/*DevArt*/== it->_nativeError) //ORA-00942 (table does not exist) 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; ignoreError = true;
break; break;