diff --git a/Data/DataTest/src/SQLExecutor.cpp b/Data/DataTest/src/SQLExecutor.cpp index a5df7fb4f6..4ccb1b3508 100644 --- a/Data/DataTest/src/SQLExecutor.cpp +++ b/Data/DataTest/src/SQLExecutor.cpp @@ -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" @@ -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 @@ -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; + } } @@ -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 lastNames = {"LN1", "LN2"}; - std::vector firstNames = {"FN1", "FN2"}; - std::vector addresses = {"ADDR1", "ADDR2"}; - std::vector ages = {1, 2}; - int count = 0, locCount = 0; - std::string result; + try + { + std::string tableName("Person"); + std::vector lastNames = {"LN1", "LN2"}; + std::vector firstNames = {"FN1", "FN2"}; + std::vector addresses = {"ADDR1", "ADDR2"}; + std::vector 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 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 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 diff --git a/Data/MySQL/src/Connector.cpp b/Data/MySQL/src/Connector.cpp index b90abab097..b30e991a56 100644 --- a/Data/MySQL/src/Connector.cpp +++ b/Data/MySQL/src/Connector.cpp @@ -44,9 +44,9 @@ const std::string& Connector::name() const Poco::AutoPtr Connector::createSession(const std::string& connectionString, - std::size_t timeout) + std::size_t loginTimeout) { - return Poco::AutoPtr(new SessionImpl(connectionString, timeout)); + return Poco::AutoPtr(new SessionImpl(connectionString, loginTimeout)); } diff --git a/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h b/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h index a915f19290..754b5a0044 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h +++ b/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h @@ -160,14 +160,6 @@ class ODBC_API SessionImpl: public Poco::Data::AbstractSessionImpl 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. @@ -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(value); diff --git a/Data/ODBC/src/ConnectionHandle.cpp b/Data/ODBC/src/ConnectionHandle.cpp index a313b54190..660518469b 100644 --- a/Data/ODBC/src/ConnectionHandle.cpp +++ b/Data/ODBC/src/ConnectionHandle.cpp @@ -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) { @@ -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."); } @@ -77,7 +77,7 @@ void ConnectionHandle::free() if (_pEnvironment) { delete _pEnvironment; - _pEnvironment = 0; + _pEnvironment = SQL_NULL_HENV; } } @@ -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 @@ -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; @@ -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)) @@ -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); } diff --git a/Data/ODBC/src/SessionImpl.cpp b/Data/ODBC/src/SessionImpl.cpp index 22dbab73ef..90fab0a477 100644 --- a/Data/ODBC/src/SessionImpl.cpp +++ b/Data/ODBC/src/SessionImpl.cpp @@ -50,6 +50,7 @@ SessionImpl::SessionImpl(const std::string& connect, // https://github.com/MicrosoftDocs/sql-docs/blob/live/docs/odbc/reference/appendixes/using-the-odbc-cursor-library.md setCursorUse("", ODBC_CURSOR_USE_IF_NEEDED); + _db.setLoginTimeout(loginTimeout); open(); } @@ -76,6 +77,7 @@ SessionImpl::SessionImpl(const std::string& connect, // https://github.com/MicrosoftDocs/sql-docs/blob/live/docs/odbc/reference/appendixes/using-the-odbc-cursor-library.md setCursorUse("", ODBC_CURSOR_USE_IF_NEEDED); + _db.setLoginTimeout(getLoginTimeout()); open(); } @@ -129,10 +131,6 @@ void SessionImpl::addFeatures() &SessionImpl::setMaxFieldSize, &SessionImpl::getMaxFieldSize); - addProperty("loginTimeout", - &SessionImpl::setLoginTimeout, - &SessionImpl::getLoginTimeout); - addProperty("queryTimeout", &SessionImpl::setQueryTimeout, &SessionImpl::getQueryTimeout); @@ -163,7 +161,7 @@ void SessionImpl::open(const std::string& connect) if (connectionString().empty()) throw InvalidArgumentException("SessionImpl::open(): Connection string empty"); - if (_db.connect(connectionString())) + if (_db.connect(connectionString(), static_cast(getLoginTimeout()))) { setProperty("handle", _db.handle()); @@ -266,22 +264,6 @@ std::size_t SessionImpl::getConnectionTimeout() const } -void SessionImpl::setLoginTimeout(const std::string&, const Poco::Any& value) -{ - int timeout = 0; - try - { - timeout = Poco::AnyCast(value); - } - catch(const Poco::BadCastException&) - { - timeout = Poco::AnyCast(value); - } - - _db.setLoginTimeout(timeout); -} - - bool SessionImpl::canTransact() const { if (ODBC_TXN_CAPABILITY_UNKNOWN == _canTransact) diff --git a/Data/SQLite/src/SessionImpl.cpp b/Data/SQLite/src/SessionImpl.cpp index a0df7e530c..333e5325fb 100644 --- a/Data/SQLite/src/SessionImpl.cpp +++ b/Data/SQLite/src/SessionImpl.cpp @@ -52,7 +52,7 @@ const std::string SessionImpl::SQLITE_READ_COMMITTED = "PRAGMA read_uncommitted SessionImpl::SessionImpl(const std::string& fileName, std::size_t loginTimeout): Poco::Data::AbstractSessionImpl(fileName, loginTimeout), _connector(Connector::KEY), - _pDB(0), + _pDB(nullptr), _connected(false), _isTransaction(false), _transactionType(TransactionType::DEFERRED), @@ -213,7 +213,7 @@ void SessionImpl::open(const std::string& connect) while (true) { rc = sqlite3_open_v2(connectionString().c_str(), &_pDB, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI, NULL); + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI, nullptr); if (rc == SQLITE_OK) break; if (!_pDB) throw ConnectionFailedException(std::string(sqlite3_errstr(rc))); @@ -240,7 +240,7 @@ void SessionImpl::close() if (_pDB) { sqlite3_close_v2(_pDB); - _pDB = 0; + _pDB = nullptr; } _connected = false; diff --git a/Data/include/Poco/Data/Session.h b/Data/include/Poco/Data/Session.h index d124c463e4..ad492e98e2 100644 --- a/Data/include/Poco/Data/Session.h +++ b/Data/include/Poco/Data/Session.h @@ -170,12 +170,12 @@ class Data_API Session Session(const std::string& connector, const std::string& connectionString, - std::size_t timeout = LOGIN_TIMEOUT_DEFAULT); + std::size_t loginTimeout = LOGIN_TIMEOUT_DEFAULT); /// Creates a new session, using the given connector (which must have /// been registered), and connectionString. Session(const std::string& connection, - std::size_t timeout = LOGIN_TIMEOUT_DEFAULT); + std::size_t loginTimeout = LOGIN_TIMEOUT_DEFAULT); /// Creates a new session, using the given connection (must be in /// "connection:///connectionString" format). diff --git a/Data/include/Poco/Data/SessionImpl.h b/Data/include/Poco/Data/SessionImpl.h index e5644772e7..5b34689411 100644 --- a/Data/include/Poco/Data/SessionImpl.h +++ b/Data/include/Poco/Data/SessionImpl.h @@ -59,7 +59,7 @@ class Data_API SessionImpl: public Poco::RefCountedObject static const int CURSOR_USE_NEVER = 2; SessionImpl(const std::string& connectionString, - std::size_t timeout = LOGIN_TIMEOUT_DEFAULT); + std::size_t loginTimeout = LOGIN_TIMEOUT_DEFAULT); /// Creates the SessionImpl. virtual ~SessionImpl(); diff --git a/Data/src/PooledSessionImpl.cpp b/Data/src/PooledSessionImpl.cpp index df0afd90ba..0b54697a19 100644 --- a/Data/src/PooledSessionImpl.cpp +++ b/Data/src/PooledSessionImpl.cpp @@ -149,7 +149,7 @@ void PooledSessionImpl::close() } } _pHolder->owner().putBack(_pHolder); - _pHolder = 0; + _pHolder = nullptr; } } diff --git a/Data/src/SQLChannel.cpp b/Data/src/SQLChannel.cpp index 34a747f781..e73e563f19 100644 --- a/Data/src/SQLChannel.cpp +++ b/Data/src/SQLChannel.cpp @@ -648,6 +648,8 @@ void SQLChannel::setProperty(const std::string& name, const std::string& value) { Path d(dir); dir = d.makeDirectory().makeAbsolute().toString(); + File f(dir); + if (!f.exists()) f.createDirectories(); } _directory = dir; } diff --git a/Data/src/Session.cpp b/Data/src/Session.cpp index 50052e3de9..fc47f297c6 100644 --- a/Data/src/Session.cpp +++ b/Data/src/Session.cpp @@ -33,9 +33,9 @@ Session::Session(Poco::AutoPtr pImpl): Session::Session(const std::string& connector, const std::string& connectionString, - std::size_t timeout) + std::size_t loginTimeout) { - Session newSession(SessionFactory::instance().create(connector, connectionString, timeout)); + Session newSession(SessionFactory::instance().create(connector, connectionString, loginTimeout)); swap(newSession); } diff --git a/Data/src/SessionFactory.cpp b/Data/src/SessionFactory.cpp index 3c886c858e..6dfc3a54a8 100644 --- a/Data/src/SessionFactory.cpp +++ b/Data/src/SessionFactory.cpp @@ -61,7 +61,7 @@ void SessionFactory::remove(const std::string& key) Session SessionFactory::create(const std::string& key, const std::string& connectionString, - std::size_t timeout) + std::size_t loginTimeout) { Poco::SharedPtr ptrSI; { @@ -70,16 +70,16 @@ Session SessionFactory::create(const std::string& key, if (_connectors.end() == it) throw Poco::NotFoundException(key); ptrSI = it->second.ptrSI; } - return Session(ptrSI->createSession(connectionString, timeout)); + return Session(ptrSI->createSession(connectionString, loginTimeout)); } Session SessionFactory::create(const std::string& uri, - std::size_t timeout) + std::size_t loginTimeout) { URI u(uri); poco_assert (!u.getPath().empty()); - return create(u.getScheme(), u.getPath().substr(1), timeout); + return create(u.getScheme(), u.getPath().substr(1), loginTimeout); } diff --git a/Data/src/SessionImpl.cpp b/Data/src/SessionImpl.cpp index 428eef52af..2fa2febf76 100644 --- a/Data/src/SessionImpl.cpp +++ b/Data/src/SessionImpl.cpp @@ -20,10 +20,10 @@ namespace Poco { namespace Data { -SessionImpl::SessionImpl(const std::string& connectionString, std::size_t timeout): +SessionImpl::SessionImpl(const std::string& connectionString, std::size_t loginTimeout): _dbmsName("unknown"s), _connectionString(connectionString), - _loginTimeout(timeout) + _loginTimeout(loginTimeout) { }