Skip to content

Commit

Permalink
fix(Data::ODBC): use connection and login timeouts in ODBC session im…
Browse files Browse the repository at this point in the history
…plementation (#4721)

* fix(Data::ODBC): use connection and login timeouts in ODBC session implementation (#4366)

* fix(Data::ODBC): use only connection timeout in ODBC session implementation (#4366)

* fix(ODBC): consolidate login timeout; create temp directory if it doesn't exist #4366

---------

Co-authored-by: Alex Fabijanic <[email protected]>
  • Loading branch information
matejk and aleks-f authored Oct 30, 2024
1 parent 82c17ea commit 7df5ec4
Show file tree
Hide file tree
Showing 13 changed files with 150 additions and 147 deletions.
204 changes: 117 additions & 87 deletions Data/DataTest/src/SQLExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@


#include "CppUnit/TestCase.h"
#include "CppUnit/CppUnitException.h"
#include "Poco/Data/Test/SQLExecutor.h"
#include "Poco/String.h"
#include "Poco/Format.h"
Expand Down Expand Up @@ -3833,6 +3834,8 @@ void SQLExecutor::autoCommit()

void SQLExecutor::transactionIsolation()
{
try
{
auto ti = session().getTransactionIsolation();

// these are just calls to check the transactional capabilities of the session
Expand All @@ -3843,6 +3846,15 @@ void SQLExecutor::transactionIsolation()
setTransactionIsolation(session(), Session::TRANSACTION_READ_COMMITTED);

setTransactionIsolation(session(), ti);
}
catch(const Poco::Exception& ex)
{
std::cerr << ex.displayText() << std::endl;
}
catch(const std::exception& ex)
{
std::cerr << ex.what() << std::endl;
}
}


Expand Down Expand Up @@ -4051,117 +4063,135 @@ void SQLExecutor::transaction(const std::string& connector, const std::string& c
else if (local.hasTransactionIsolation(Session::TRANSACTION_READ_COMMITTED))
setTransactionIsolation(local, Session::TRANSACTION_READ_COMMITTED);

std::string tableName("Person");
std::vector<std::string> lastNames = {"LN1", "LN2"};
std::vector<std::string> firstNames = {"FN1", "FN2"};
std::vector<std::string> addresses = {"ADDR1", "ADDR2"};
std::vector<int> ages = {1, 2};
int count = 0, locCount = 0;
std::string result;
try
{
std::string tableName("Person");
std::vector<std::string> lastNames = {"LN1", "LN2"};
std::vector<std::string> firstNames = {"FN1", "FN2"};
std::vector<std::string> addresses = {"ADDR1", "ADDR2"};
std::vector<int> ages = {1, 2};
int count = 0, locCount = 0;
std::string result;

session().setFeature("autoCommit", true);
assertTrue (!session().isTransaction());
session().setFeature("autoCommit", false);
assertTrue (!session().isTransaction());
session().setTransactionIsolation(Session::TRANSACTION_READ_COMMITTED);
session().setFeature("autoCommit", true);
assertTrue (!session().isTransaction());
session().setFeature("autoCommit", false);
assertTrue (!session().isTransaction());
session().setTransactionIsolation(Session::TRANSACTION_READ_COMMITTED);

{
Transaction trans(session());
assertTrue (trans.isActive());
assertTrue (session().isTransaction());
{
Transaction trans(session());
assertTrue (trans.isActive());
assertTrue (session().isTransaction());

session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastNames), use(firstNames), use(addresses), use(ages), now;
session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastNames), use(firstNames), use(addresses), use(ages), now;

assertTrue (session().isTransaction());
assertTrue (trans.isActive());
assertTrue (session().isTransaction());
assertTrue (trans.isActive());

session() << "SELECT COUNT(*) FROM Person", into(count), now;
assertTrue (2 == count);
assertTrue (session().isTransaction());
assertTrue (trans.isActive());
}
assertTrue (!session().isTransaction());
session() << "SELECT COUNT(*) FROM Person", into(count), now;
assertEqual (2, count);
assertTrue (session().isTransaction());
assertTrue (trans.isActive());
}
assertTrue (!session().isTransaction());

session() << "SELECT count(*) FROM Person", into(count), now;
assertTrue (0 == count);
assertTrue (!(session().impl()->shouldParse() && session().isTransaction()));
session().commit();
session() << "SELECT count(*) FROM Person", into(count), now;
assertEqual (0, count);
assertTrue (!(session().impl()->shouldParse() && session().isTransaction()));
session().commit();

{
Transaction trans(session());
session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastNames), use(firstNames), use(addresses), use(ages), now;
{
Transaction trans(session());
session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastNames), use(firstNames), use(addresses), use(ages), now;

Statement stmt1 = (local << "SELECT COUNT(*) FROM Person", into(locCount), async, now);
Statement stmt1 = (local << "SELECT COUNT(*) FROM Person", into(locCount), async, now);

assertTrue (session().isTransaction());
assertTrue (trans.isActive());
trans.commit();
assertTrue (!session().isTransaction());
assertTrue (!trans.isActive());
assertTrue (session().isTransaction());
assertTrue (trans.isActive());
trans.commit();
assertTrue (!session().isTransaction());
assertTrue (!trans.isActive());

stmt1.wait();
assertTrue (2 == locCount);
}
stmt1.wait();
assertEqual (2, locCount);
}

session() << "SELECT count(*) FROM Person", into(count), now;
assertTrue (2 == count);
session() << "SELECT count(*) FROM Person", into(count), now;
assertEqual (2, count);

session() << "DELETE FROM Person", now;
session() << "DELETE FROM Person", now;

Statement stmt1 = (local << "SELECT count(*) FROM Person", into(locCount), async, now);
Statement stmt1 = (local << "SELECT count(*) FROM Person", into(locCount), async, now);

session() << "SELECT count(*) FROM Person", into(count), now;
assertTrue (0 == count);
try
{
stmt1.wait(5000);
if (readUncommitted &&
local.hasTransactionIsolation(Session::TRANSACTION_READ_UNCOMMITTED) &&
local.getTransactionIsolation() == Session::TRANSACTION_READ_UNCOMMITTED)
assertTrue (0 == locCount);
} catch (TimeoutException&)
{ std::cerr << '[' << name() << ']' << " Warning: async query timed out." << std::endl; }
session().commit();
// repeat for those that don't support uncommitted read isolation
if (local.getTransactionIsolation() == Session::TRANSACTION_READ_COMMITTED)
{
stmt1.wait();
local << "SELECT count(*) FROM Person", into(locCount), now;
assertTrue (0 == locCount);
}
session() << "SELECT count(*) FROM Person", into(count), now;
assertEqual (0, count);
try
{
stmt1.wait(5000);
if (readUncommitted &&
local.hasTransactionIsolation(Session::TRANSACTION_READ_UNCOMMITTED) &&
local.getTransactionIsolation() == Session::TRANSACTION_READ_UNCOMMITTED)
assertEqual (0, locCount);
}
catch (TimeoutException&)
{
std::cerr << '[' << name() << ']' << " Warning: async query timed out." << std::endl;
}
catch (CppUnit::CppUnitException& ex)
{
std::cerr << " Warning: " << ex.what() << std::endl;
}
catch (std::exception& ex)
{
std::cerr << " Warning: " << ex.what() << std::endl;
}
session().commit();
// repeat for those that don't support uncommitted read isolation
if (local.getTransactionIsolation() == Session::TRANSACTION_READ_COMMITTED)
{
stmt1.wait();
local << "SELECT count(*) FROM Person", into(locCount), now;
assertEqual (0, locCount);
}

std::string sql1 = format("INSERT INTO Person VALUES ('%s','%s','%s',%d)", lastNames[0], firstNames[0], addresses[0], ages[0]);
std::string sql2 = format("INSERT INTO Person VALUES ('%s','%s','%s',%d)", lastNames[1], firstNames[1], addresses[1], ages[1]);
std::vector<std::string> sql;
sql.push_back(sql1);
sql.push_back(sql2);
std::string sql1 = format("INSERT INTO Person VALUES ('%s','%s','%s',%d)", lastNames[0], firstNames[0], addresses[0], ages[0]);
std::string sql2 = format("INSERT INTO Person VALUES ('%s','%s','%s',%d)", lastNames[1], firstNames[1], addresses[1], ages[1]);
std::vector<std::string> sql;
sql.push_back(sql1);
sql.push_back(sql2);

Transaction trans(session());
Transaction trans(session());

trans.execute(sql1, false);
session() << "SELECT count(*) FROM Person", into(count), now;
assertTrue (1 == count);
trans.execute(sql2, false);
session() << "SELECT count(*) FROM Person", into(count), now;
assertTrue (2 == count);
trans.execute(sql1, false);
session() << "SELECT count(*) FROM Person", into(count), now;
assertEqual (1, count);
trans.execute(sql2, false);
session() << "SELECT count(*) FROM Person", into(count), now;
assertEqual (2, count);

Statement stmt2 = (local << "SELECT COUNT(*) FROM Person", into(locCount), async, now);
Statement stmt2 = (local << "SELECT COUNT(*) FROM Person", into(locCount), async, now);

trans.rollback();
trans.rollback();

stmt2.wait();
assertTrue (0 == locCount);
stmt2.wait();
assertEqual (0, locCount);

session() << "SELECT count(*) FROM Person", into(count), now;
assertTrue (0 == count);
session() << "SELECT count(*) FROM Person", into(count), now;
assertEqual (0, count);

trans.execute(sql);
trans.execute(sql);

Statement stmt3 = (local << "SELECT COUNT(*) FROM Person", into(locCount), now);
assertTrue (2 == locCount);
Statement stmt3 = (local << "SELECT COUNT(*) FROM Person", into(locCount), now);
assertEqual (2, locCount);

session() << "SELECT count(*) FROM Person", into(count), now;
assertTrue (2 == count);
session() << "SELECT count(*) FROM Person", into(count), now;
assertEqual (2, count);
}
catch (std::exception& ex)
{
std::cerr << " Warning: " << ex.what() << std::endl;
}
session().commit();

// restore the original transaction state
Expand Down
4 changes: 2 additions & 2 deletions Data/MySQL/src/Connector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ const std::string& Connector::name() const


Poco::AutoPtr<Poco::Data::SessionImpl> Connector::createSession(const std::string& connectionString,
std::size_t timeout)
std::size_t loginTimeout)
{
return Poco::AutoPtr<Poco::Data::SessionImpl>(new SessionImpl(connectionString, timeout));
return Poco::AutoPtr<Poco::Data::SessionImpl>(new SessionImpl(connectionString, loginTimeout));
}


Expand Down
14 changes: 0 additions & 14 deletions Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,6 @@ class ODBC_API SessionImpl: public Poco::Data::AbstractSessionImpl<SessionImpl>
int maxStatementLength() const;
/// Returns maximum length of SQL statement allowed by driver.

void setLoginTimeout(const std::string&, const Poco::Any& value);
/// Sets the timeout (in seconds) for the session login.
/// Value must be of type (unsigned) int.
/// It must be set prior to logging in.

Poco::Any getLoginTimeout(const std::string&) const;
/// Returns the timeout (in seconds) for the session login.

void setQueryTimeout(const std::string&, const Poco::Any& value);
/// Sets the timeout (in seconds) for queries.
/// Value must be of type int.
Expand Down Expand Up @@ -322,12 +314,6 @@ inline bool SessionImpl::isTransactionIsolation(Poco::UInt32 ti) const
}


inline Poco::Any SessionImpl::getLoginTimeout(const std::string&) const
{
return _db.getLoginTimeout();
}


inline void SessionImpl::setQueryTimeout(const std::string&, const Poco::Any& value)
{
_queryTimeout = Poco::AnyCast<int>(value);
Expand Down
19 changes: 11 additions & 8 deletions Data/ODBC/src/ConnectionHandle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const std::string ConnectionHandle::CANT_SET_ATTR_SQLSTATE = "HY011";


ConnectionHandle::ConnectionHandle(const std::string& connectString, SQLULEN loginTimeout, SQLULEN timeout):
_pEnvironment(nullptr),
_pEnvironment(SQL_NULL_HENV),
_hdbc(SQL_NULL_HDBC),
_connectString(connectString)
{
Expand Down Expand Up @@ -59,7 +59,7 @@ void ConnectionHandle::alloc()
if (Utility::isError(SQLAllocHandle(SQL_HANDLE_DBC, _pEnvironment->handle(), &_hdbc)))
{
delete _pEnvironment;
_pEnvironment = nullptr;
_pEnvironment = SQL_NULL_HENV;
_hdbc = SQL_NULL_HDBC;
throw ODBCException("ODBC: Could not allocate connection handle.");
}
Expand All @@ -77,7 +77,7 @@ void ConnectionHandle::free()
if (_pEnvironment)
{
delete _pEnvironment;
_pEnvironment = 0;
_pEnvironment = SQL_NULL_HENV;
}
}

Expand Down Expand Up @@ -122,8 +122,8 @@ bool ConnectionHandle::connect(const std::string& connectString, SQLULEN loginTi

setTimeouts(loginTimeout, timeout);

if (Utility::isError(Poco::Data::ODBC::SQLDriverConnect(_hdbc
, NULL
if (*this && Utility::isError(Poco::Data::ODBC::SQLDriverConnect(_hdbc
, nullptr
,(SQLCHAR*) _connectString.c_str()
,(SQLSMALLINT) SQL_NTS
, connectOutput
Expand Down Expand Up @@ -172,7 +172,10 @@ void ConnectionHandle::setTimeoutImpl(SQLULEN timeout, SQLINTEGER attribute)
if (attribute != SQL_ATTR_LOGIN_TIMEOUT && attribute != SQL_ATTR_CONNECTION_TIMEOUT)
throw InvalidArgumentException(Poco::format("ODBC::ConnectionHandle::setTimeoutImpl(%d)", attribute));

if (Utility::isError(SQLSetConnectAttr(_hdbc, attribute, (SQLPOINTER) timeout, 0)))
if (attribute == SQL_ATTR_CONNECTION_TIMEOUT && !isConnected()) // can't set this on not connected session
return;

if (*this && Utility::isError(SQLSetConnectAttr(_hdbc, attribute, (SQLPOINTER) timeout, 0)))
{
ConnectionError e(_hdbc);
std::string name;
Expand Down Expand Up @@ -201,7 +204,7 @@ void ConnectionHandle::setTimeoutImpl(SQLULEN timeout, SQLINTEGER attribute)
int ConnectionHandle::getTimeoutImpl(SQLINTEGER attribute) const
{
SQLUINTEGER timeout = 0;
if (Utility::isError(SQLGetConnectAttr(_hdbc, attribute, &timeout, sizeof(timeout), 0)))
if (*this && Utility::isError(SQLGetConnectAttr(_hdbc, attribute, &timeout, sizeof(timeout), nullptr)))
{
ConnectionError e(_hdbc);
if (isUnsupported(e))
Expand Down Expand Up @@ -268,7 +271,7 @@ bool ConnectionHandle::isConnected() const
SQL_ATTR_CONNECTION_DEAD,
&value,
sizeof(value),
0))) return false;
nullptr))) return false;

return (SQL_CD_FALSE == value);
}
Expand Down
Loading

0 comments on commit 7df5ec4

Please sign in to comment.