From d11f007d2385a92a8205d3d852a066ddf471619e Mon Sep 17 00:00:00 2001 From: Aleksandar Fabijanic Date: Sun, 8 Feb 2009 23:14:54 +0000 Subject: [PATCH] [SF 2580108] Improve transaction handling --- Data/Data_VS71.vcproj | 9 + Data/Data_VS80.vcproj | 12 +- Data/Data_VS90.vcproj | 12 +- Data/Makefile | 2 +- Data/MySQL/MySQL_VS90.sln | 3 + Data/MySQL/MySQL_VS90.vcproj | 2 +- .../include/Poco/Data/MySQL/SessionHandle.h | 4 +- .../include/Poco/Data/MySQL/SessionImpl.h | 351 +- .../Poco/Data/MySQL/StatementExecutor.h | 4 +- Data/MySQL/src/MySQLStatementImpl.cpp | 33 +- Data/MySQL/src/SessionHandle.cpp | 60 +- Data/MySQL/src/SessionImpl.cpp | 504 +-- Data/MySQL/src/StatementExecutor.cpp | 71 +- Data/MySQL/testsuite/TestSuite_VS90.vcproj | 2 +- Data/MySQL/testsuite/src/MySQLTest.cpp | 41 +- Data/MySQL/testsuite/src/MySQLTest.h | 3 + Data/MySQL/testsuite/src/SQLExecutor.cpp | 3325 +++++++++-------- Data/MySQL/testsuite/src/SQLExecutor.h | 5 + .../ODBC/include/Poco/Data/ODBC/SessionImpl.h | 51 +- Data/ODBC/src/SessionImpl.cpp | 159 +- Data/ODBC/testsuite/src/ODBCDB2Test.cpp | 3 + Data/ODBC/testsuite/src/ODBCMySQLTest.cpp | 3 + Data/ODBC/testsuite/src/ODBCOracleTest.cpp | 3 + .../ODBC/testsuite/src/ODBCPostgreSQLTest.cpp | 3 + Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp | 3 + Data/ODBC/testsuite/src/ODBCSQLiteTest.cpp | 3 + Data/ODBC/testsuite/src/ODBCTest.cpp | 46 + Data/ODBC/testsuite/src/ODBCTest.h | 5 +- Data/ODBC/testsuite/src/SQLExecutor.cpp | 328 ++ Data/ODBC/testsuite/src/SQLExecutor.h | 7 +- .../include/Poco/Data/SQLite/SessionImpl.h | 25 +- Data/SQLite/src/SessionImpl.cpp | 29 + Data/include/Poco/Data/AutoTransaction.h | 41 +- Data/include/Poco/Data/PooledSessionImpl.h | 5 + Data/include/Poco/Data/Session.h | 52 + Data/include/Poco/Data/SessionImpl.h | 17 + Data/include/Poco/Data/Transaction.h | 242 ++ Data/src/PooledSessionImpl.cpp | 30 + .../{AutoTransaction.cpp => Transaction.cpp} | 82 +- Data/testsuite/src/SessionImpl.cpp | 29 + Data/testsuite/src/SessionImpl.h | 17 + 41 files changed, 3484 insertions(+), 2142 deletions(-) create mode 100644 Data/include/Poco/Data/Transaction.h rename Data/src/{AutoTransaction.cpp => Transaction.cpp} (57%) diff --git a/Data/Data_VS71.vcproj b/Data/Data_VS71.vcproj index 39d2916be..2dcae00a9 100644 --- a/Data/Data_VS71.vcproj +++ b/Data/Data_VS71.vcproj @@ -270,6 +270,9 @@ + + @@ -366,6 +369,9 @@ + + @@ -454,6 +460,9 @@ + + + + @@ -537,10 +541,6 @@ RelativePath=".\src\AbstractPreparator.cpp" > - - @@ -625,6 +625,10 @@ RelativePath=".\src\Time.cpp" > + + + + @@ -542,10 +546,6 @@ RelativePath=".\src\AbstractPreparator.cpp" > - - @@ -630,6 +630,10 @@ RelativePath=".\src\Time.cpp" > + + - /// Implements SessionImpl interface -{ -public: - - SessionImpl(const std::string& connectionString); - /// Creates the SessionImpl. Opens a connection to the database - /// - /// Connection string format: - /// == | ';' - /// == '=' - /// == 'host' | 'port' | 'user' | 'password' | 'db' } 'compress' | 'auto-reconnect' - /// == [~;]* - /// - /// for compress and auto-reconnect correct values are true/false - /// for port - numeric in decimal notation - /// - - ~SessionImpl(); - /// Destroys the SessionImpl. - - virtual Poco::Data::StatementImpl* createStatementImpl(); - /// Returns an MySQL StatementImpl - - virtual void begin(); - /// Starts a transaction - - virtual void commit(); - /// Commits and ends a transaction - - virtual void rollback(); - /// Aborts a transaction - - virtual void close(); - /// Closes the connection - - virtual bool isConnected(); - /// Returns true iff session is connected. - - virtual bool isTransaction(); - /// Returns true iff a transaction is in progress. - - void setInsertId(const std::string&, const Poco::Any&); - /// Try to set insert id - do nothing. - - Poco::Any getInsertId(const std::string&); - /// Get insert id - - SessionHandle& handle(); - // Get handle - +// +// SessionImpl.h +// +// $Id: //poco/1.4/Data/MySQL/include/Poco/Data/MySQL/SessionImpl.h#1 $ +// +// Library: Data +// Package: MySQL +// Module: SessionImpl +// +// Definition of the SessionImpl class. +// +// Copyright (c) 2008, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + + +#ifndef Data_MySQL_SessionImpl_INCLUDED +#define Data_MySQL_SessionImpl_INCLUDED + + +#include "Poco/Data/MySQL/MySQL.h" +#include "Poco/Data/AbstractSessionImpl.h" +#include "Poco/Data/MySQL/SessionHandle.h" +#include "Poco/Mutex.h" + + +namespace Poco { +namespace Data { +namespace MySQL { + + +class MySQL_API SessionImpl: public Poco::Data::AbstractSessionImpl + /// Implements SessionImpl interface +{ +public: + static const std::string MYSQL_READ_UNCOMMITTED; + static const std::string MYSQL_READ_COMMITTED; + static const std::string MYSQL_REPEATABLE_READ; + static const std::string MYSQL_SERIALIZABLE; + + SessionImpl(const std::string& connectionString); + /// Creates the SessionImpl. Opens a connection to the database + /// + /// Connection string format: + /// == | ';' + /// == '=' + /// == 'host' | 'port' | 'user' | 'password' | 'db' } 'compress' | 'auto-reconnect' + /// == [~;]* + /// + /// for compress and auto-reconnect correct values are true/false + /// for port - numeric in decimal notation + /// + + ~SessionImpl(); + /// Destroys the SessionImpl. + + Poco::Data::StatementImpl* createStatementImpl(); + /// Returns an MySQL StatementImpl + + void begin(); + /// Starts a transaction + + void commit(); + /// Commits and ends a transaction + + void rollback(); + /// Aborts a transaction + + void close(); + /// Closes the connection + + bool isConnected(); + /// Returns true if connected, false otherwise. + + bool canTransact(); + /// Returns true if session has transaction capabilities. + + bool isTransaction(); + /// Returns true iff a transaction is a transaction is in progress, false otherwise. + + void setTransactionIsolation(Poco::UInt32 ti); + /// Sets the transaction isolation level. + + Poco::UInt32 getTransactionIsolation(); + /// Returns the transaction isolation level. + + bool hasTransactionIsolation(Poco::UInt32 ti); + /// Returns true iff the transaction isolation level corresponding + /// to the supplied bitmask is supported. + + bool isTransactionIsolation(Poco::UInt32 ti); + /// Returns true iff the transaction isolation level corresponds + /// to the supplied bitmask. + + void autoCommit(const std::string&, bool val); + /// Sets autocommit property for the session. + + bool isAutoCommit(const std::string& name=""); + /// Returns autocommit property value. + + void setInsertId(const std::string&, const Poco::Any&); + /// Try to set insert id - do nothing. + + Poco::Any getInsertId(const std::string&); + /// Get insert id + + SessionHandle& handle(); + // Get handle + const std::string& connectorName(); /// Returns the name of the connector. - -private: - - std::string _connector; - SessionHandle _mysql; - bool _connected; - int _inTransaction; -}; - - -// -// inlines -// - -inline void SessionImpl::setInsertId(const std::string&, const Poco::Any&) -{ -} - - -inline Poco::Any SessionImpl::getInsertId(const std::string&) -{ - return Poco::Any(Poco::UInt64(mysql_insert_id(_mysql))); -} - - -inline SessionHandle& SessionImpl::handle() -{ - return _mysql; + +private: + + template + inline T& getValue(MYSQL_BIND* pResult, T& val) + { + return val = *((T*) pResult->buffer); + } + + template + T& getSetting(const std::string& name, T& val) + /// Returns required setting. + /// Limited to one setting at a time. + { + StatementExecutor ex(_handle); + ResultMetadata metadata; + metadata.reset(); + ex.prepare(Poco::format("SELECT @@%s", name)); + metadata.init(ex); + + if (metadata.columnsReturned() > 0) + ex.bindResult(metadata.row()); + else + throw InvalidArgumentException("No data returned."); + + ex.execute(); ex.fetch(); + MYSQL_BIND* pResult = metadata.row(); + return getValue(pResult, val); + } + + std::string _connector; + SessionHandle _handle; + bool _connected; + bool _inTransaction; + Poco::FastMutex _mutex; +}; + + +// +// inlines +// +inline bool SessionImpl::canTransact() +{ + return true; +} + + +inline void SessionImpl::setInsertId(const std::string&, const Poco::Any&) +{ +} + + +inline Poco::Any SessionImpl::getInsertId(const std::string&) +{ + return Poco::Any(Poco::UInt64(mysql_insert_id(_handle))); +} + + +inline SessionHandle& SessionImpl::handle() +{ + return _handle; } inline const std::string& SessionImpl::connectorName() { return _connector; -} - - -} } } // namespace Poco::Data::MySQL - - -#endif // Data_MySQL_SessionImpl_INCLUDED +} + + +inline bool SessionImpl::isTransactionIsolation(Poco::UInt32 ti) +{ + return getTransactionIsolation() == ti; +} + + +template <> +inline std::string& SessionImpl::getValue(MYSQL_BIND* pResult, std::string& val) +{ + val.assign((char*) pResult->buffer, pResult->buffer_length); + return val; +} + + +} } } // namespace Poco::Data::MySQL + + +#endif // Data_MySQL_SessionImpl_INCLUDED diff --git a/Data/MySQL/include/Poco/Data/MySQL/StatementExecutor.h b/Data/MySQL/include/Poco/Data/MySQL/StatementExecutor.h index b02016cf6..15e4c9beb 100644 --- a/Data/MySQL/include/Poco/Data/MySQL/StatementExecutor.h +++ b/Data/MySQL/include/Poco/Data/MySQL/StatementExecutor.h @@ -96,7 +96,7 @@ private: private: - MYSQL_STMT* h; + MYSQL_STMT* _pHandle; int _state; std::string _query; }; @@ -108,7 +108,7 @@ private: inline StatementExecutor::operator MYSQL_STMT* () { - return h; + return _pHandle; } diff --git a/Data/MySQL/src/MySQLStatementImpl.cpp b/Data/MySQL/src/MySQLStatementImpl.cpp index f119e86d3..2bf08860b 100644 --- a/Data/MySQL/src/MySQLStatementImpl.cpp +++ b/Data/MySQL/src/MySQLStatementImpl.cpp @@ -144,43 +144,24 @@ void MySQLStatementImpl::compileImpl() _metadata.init(_stmt); if (_metadata.columnsReturned() > 0) - { _stmt.bindResult(_metadata.row()); - } } void MySQLStatementImpl::bindImpl() { - // - // Bind all bindings - // - + Poco::Data::AbstractBindingVec& binds = bindings(); + size_t pos = 0; + Poco::Data::AbstractBindingVec::iterator it = binds.begin(); + Poco::Data::AbstractBindingVec::iterator itEnd = binds.end(); + for (; it != itEnd && (*it)->canBind(); ++it) { - Poco::Data::AbstractBindingVec& binds = bindings(); - size_t pos = 0; - Poco::Data::AbstractBindingVec::iterator it = binds.begin(); - Poco::Data::AbstractBindingVec::iterator itEnd = binds.end(); - - for (; it != itEnd && (*it)->canBind(); ++it) - { - (*it)->bind(pos); - pos += (*it)->numOfColumnsHandled(); - } + (*it)->bind(pos); + pos += (*it)->numOfColumnsHandled(); } - // - // And bind them to statement - // - _stmt.bindParams(_binder.getBindArray(), _binder.size()); - - // - // And execute - // - _stmt.execute(); - _hasNext = NEXT_DONTKNOW; } diff --git a/Data/MySQL/src/SessionHandle.cpp b/Data/MySQL/src/SessionHandle.cpp index 78fc6c11b..3c1a0173d 100644 --- a/Data/MySQL/src/SessionHandle.cpp +++ b/Data/MySQL/src/SessionHandle.cpp @@ -44,12 +44,8 @@ namespace MySQL { SessionHandle::SessionHandle(MYSQL* mysql) { - h = mysql_init(mysql); - - if (!h) - { + if (!(_pHandle = mysql_init(mysql))) throw ConnectionException("mysql_init error"); - } } @@ -61,74 +57,54 @@ SessionHandle::~SessionHandle() void SessionHandle::options(mysql_option opt) { - int res = mysql_options(h, opt, 0); - - if (res != 0) - { - throw ConnectionException("mysql_options error", h); - } + if (mysql_options(_pHandle, opt, 0) != 0) + throw ConnectionException("mysql_options error", _pHandle); } void SessionHandle::options(mysql_option opt, bool b) { my_bool tmp = b; - int res = mysql_options(h, opt, &tmp); - - if (res != 0) - { - throw ConnectionException("mysql_options error", h); - } + if (mysql_options(_pHandle, opt, &tmp) != 0) + throw ConnectionException("mysql_options error", _pHandle); } void SessionHandle::connect(const char* host, const char* user, const char* password, const char* db, unsigned int port) { - if (!mysql_real_connect(h, host, user, password, db, port, 0, 0)) - { - throw ConnectionException("create session: mysql_real_connect error", h); - } + if (!mysql_real_connect(_pHandle, host, user, password, db, port, 0, 0)) + throw ConnectionException("create session: mysql_real_connect error", _pHandle); } void SessionHandle::close() { - if (h) + if (_pHandle) { - mysql_close(h); - h = 0; + mysql_close(_pHandle); + _pHandle = 0; } } void SessionHandle::startTransaction() { - int res = mysql_autocommit(h, false); - - if (res != 0) - { - throw TransactionException("Start transaction failed.", h); - } + if (mysql_autocommit(_pHandle, false) != 0) + throw TransactionException("Start transaction failed.", _pHandle); } + void SessionHandle::commit() { - int res = mysql_commit(h); - - if (res != 0) - { - throw TransactionException("Commit failed.", h); - } + if (mysql_commit(_pHandle) != 0) + throw TransactionException("Commit failed.", _pHandle); } + void SessionHandle::rollback() { - int res = mysql_rollback(h); - - if (res != 0) - { - throw TransactionException("Rollback failed.", h); - } + if (mysql_rollback(_pHandle) != 0) + throw TransactionException("Rollback failed.", _pHandle); } diff --git a/Data/MySQL/src/SessionImpl.cpp b/Data/MySQL/src/SessionImpl.cpp index 10c025a7b..f5878b84b 100644 --- a/Data/MySQL/src/SessionImpl.cpp +++ b/Data/MySQL/src/SessionImpl.cpp @@ -1,233 +1,271 @@ -// -// MySQLException.cpp -// -// $Id: //poco/1.4/Data/MySQL/src/SessionImpl.cpp#1 $ -// -// Library: Data -// Package: MySQL -// Module: SessionImpl -// -// Copyright (c) 2008, Applied Informatics Software Engineering GmbH. -// and Contributors. -// -// Permission is hereby granted, free of charge, to any person or organization -// obtaining a copy of the software and accompanying documentation covered by -// this license (the "Software") to use, reproduce, display, distribute, -// execute, and transmit the Software, and to prepare derivative works of the -// Software, and to permit third-parties to whom the Software is furnished to -// do so, all subject to the following: -// -// The copyright notices in the Software and this entire statement, including -// the above license grant, this restriction and the following disclaimer, -// must be included in all copies of the Software, in whole or in part, and -// all derivative works of the Software, unless such copies or derivative -// works are solely in the form of machine-executable object code generated by -// a source language processor. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -// - - -#include "Poco/Data/MySQL/SessionImpl.h" -#include "Poco/Data/MySQL/MySQLStatementImpl.h" -#include "Poco/NumberParser.h" -#include "Poco/String.h" - - -namespace -{ - std::string copyStripped(std::string::const_iterator from, std::string::const_iterator to) - { - // skip leading spaces - while ((from != to) && isspace(*from)) from++; - // skip trailing spaces - while ((from != to) && isspace(*(to - 1))) to--; - - return std::string(from, to); - } -} - - -namespace Poco { -namespace Data { -namespace MySQL { - - -SessionImpl::SessionImpl(const std::string& connectionString) : - Poco::Data::AbstractSessionImpl(toLower(connectionString)), - _mysql(0), - _connected(false), - _inTransaction(0) -{ - addProperty("insertId", - &SessionImpl::setInsertId, - &SessionImpl::getInsertId); - - std::map options; - - // Default values - options["host"] = "localhost"; - options["port"] = "3306"; - options["user"] = ""; - options["password"] = ""; - options["db"] = ""; - options["compress"] = ""; - options["auto-reconnect"] = ""; - - // - // Parse string - // - - for (std::string::const_iterator start = connectionString.begin();;) - { - // find next ';' - std::string::const_iterator finish = std::find(start, connectionString.end(), ';'); - - // find '=' - std::string::const_iterator middle = std::find(start, finish, '='); - - if (middle == finish) - { - throw MySQLException("create session: bad connection string format, can not find '='"); - } - - // Parse name and value, skip all spaces - options[copyStripped(start, middle)] = copyStripped(middle + 1, finish); - - if ((finish == connectionString.end()) || (finish + 1 == connectionString.end())) - { - // end of parse - break; - } - - // move start position after ';' - start = finish + 1; - } - - // - // Checking - // - - if (options["user"] == "") - { - throw MySQLException("create session: specify user name"); - } - - if (options["db"] == "") - { - throw MySQLException("create session: specify database"); - } - - unsigned int port = 0; - if (!NumberParser::tryParseUnsigned(options["port"], port) || 0 == port || port > 65535) - { - throw MySQLException("create session: specify correct port (numeric in decimal notation)"); - } - - // - // Options - // - - if (options["compress"] == "true") - { - _mysql.options(MYSQL_OPT_COMPRESS); - } - else if (options["compress"] == "false") - { - // do nothing - } - else if (options["compress"] != "") - { - throw MySQLException("create session: specify correct compress option (true or false) or skip it"); - } - - if (options["auto-reconnect"] == "true") - { - _mysql.options(MYSQL_OPT_RECONNECT, true); - } - else if (options["auto-reconnect"] == "false") - { - _mysql.options(MYSQL_OPT_RECONNECT, false); - } - else if (options["auto-reconnect"] != "") - { - throw MySQLException("create session: specify correct auto-reconnect option (true or false) or skip it"); - } - - // - // Real connect - // - - _mysql.connect( - options["host"].c_str(), - options["user"].c_str(), - options["password"].c_str(), - options["db"].c_str(), - port); - - _connected = true; -} - - -SessionImpl::~SessionImpl() -{ - close(); -} - - -Poco::Data::StatementImpl* SessionImpl::createStatementImpl() -{ - return new MySQLStatementImpl(*this); -} - - -void SessionImpl::begin() -{ - _mysql.startTransaction(); - _inTransaction++; -} - - -void SessionImpl::commit() -{ - _mysql.commit(); - _inTransaction--; -} - - -void SessionImpl::rollback() -{ - _mysql.rollback(); - _inTransaction--; -} - - -void SessionImpl::close() -{ - if (_connected) - { - _mysql.close(); - _connected = false; - } -} - - -bool SessionImpl::isConnected() -{ - return _connected; -} - - -bool SessionImpl::isTransaction() -{ - return (_inTransaction > 0); -} - - -}}} +// +// MySQLException.cpp +// +// $Id: //poco/1.4/Data/MySQL/src/SessionImpl.cpp#1 $ +// +// Library: Data +// Package: MySQL +// Module: SessionImpl +// +// Copyright (c) 2008, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + + +#include "Poco/Data/MySQL/SessionImpl.h" +#include "Poco/Data/MySQL/MySQLStatementImpl.h" +#include "Poco/Data/MySQL/StatementExecutor.h" +#include "Poco/Data/Session.h" +#include "Poco/NumberParser.h" +#include "Poco/String.h" + + +namespace +{ + std::string copyStripped(std::string::const_iterator from, std::string::const_iterator to) + { + // skip leading spaces + while ((from != to) && isspace(*from)) from++; + // skip trailing spaces + while ((from != to) && isspace(*(to - 1))) to--; + + return std::string(from, to); + } +} + + +namespace Poco { +namespace Data { +namespace MySQL { + + +const std::string SessionImpl::MYSQL_READ_UNCOMMITTED = "READ UNCOMMITTED"; +const std::string SessionImpl::MYSQL_READ_COMMITTED = "READ COMMITTED"; +const std::string SessionImpl::MYSQL_REPEATABLE_READ = "REPEATABLE READ"; +const std::string SessionImpl::MYSQL_SERIALIZABLE = "SERIALIZABLE"; + + +SessionImpl::SessionImpl(const std::string& connectionString) : + Poco::Data::AbstractSessionImpl(toLower(connectionString)), + _handle(0), + _connected(false), + _inTransaction(false) +{ + addProperty("insertId", + &SessionImpl::setInsertId, + &SessionImpl::getInsertId); + + std::map options; + + // Default values + options["host"] = "localhost"; + options["port"] = "3306"; + options["user"] = ""; + options["password"] = ""; + options["db"] = ""; + options["compress"] = ""; + options["auto-reconnect"] = ""; + + for (std::string::const_iterator start = connectionString.begin();;) + { + std::string::const_iterator finish = std::find(start, connectionString.end(), ';'); + std::string::const_iterator middle = std::find(start, finish, '='); + + if (middle == finish) + throw MySQLException("create session: bad connection string format, can not find '='"); + + options[copyStripped(start, middle)] = copyStripped(middle + 1, finish); + + if ((finish == connectionString.end()) || (finish + 1 == connectionString.end())) break; + + start = finish + 1; + } + + if (options["user"] == "") + throw MySQLException("create session: specify user name"); + + if (options["db"] == "") + throw MySQLException("create session: specify database"); + + unsigned int port = 0; + if (!NumberParser::tryParseUnsigned(options["port"], port) || 0 == port || port > 65535) + throw MySQLException("create session: specify correct port (numeric in decimal notation)"); + + if (options["compress"] == "true") + _handle.options(MYSQL_OPT_COMPRESS); + else if (options["compress"] == "false") + ; + else if (options["compress"] != "") + throw MySQLException("create session: specify correct compress option (true or false) or skip it"); + + if (options["auto-reconnect"] == "true") + _handle.options(MYSQL_OPT_RECONNECT, true); + else if (options["auto-reconnect"] == "false") + _handle.options(MYSQL_OPT_RECONNECT, false); + else if (options["auto-reconnect"] != "") + throw MySQLException("create session: specify correct auto-reconnect option (true or false) or skip it"); + + // Real connect + _handle.connect( + options["host"].c_str(), + options["user"].c_str(), + options["password"].c_str(), + options["db"].c_str(), + port); + + addFeature("autoCommit", + &SessionImpl::autoCommit, + &SessionImpl::isAutoCommit); + + _connected = true; +} + + +SessionImpl::~SessionImpl() +{ + close(); +} + + +Poco::Data::StatementImpl* SessionImpl::createStatementImpl() +{ + return new MySQLStatementImpl(*this); +} + + +void SessionImpl::begin() +{ + Poco::FastMutex::ScopedLock l(_mutex); + + if (_inTransaction) + throw Poco::InvalidAccessException("Already in transaction."); + + _handle.startTransaction(); + _inTransaction = true; +} + + +void SessionImpl::commit() +{ + _handle.commit(); + _inTransaction = false; +} + + +void SessionImpl::rollback() +{ + _handle.rollback(); + _inTransaction = false; +} + + +void SessionImpl::autoCommit(const std::string&, bool val) +{ + StatementExecutor ex(_handle); + ex.prepare(Poco::format("SET autocommit=%d", val ? 1 : 0)); + ex.execute(); +} + + +bool SessionImpl::isAutoCommit(const std::string&) +{ + int ac = 0; + return 1 == getSetting("autocommit", ac); +} + + +void SessionImpl::setTransactionIsolation(Poco::UInt32 ti) +{ + std::string isolation; + switch (ti) + { + case Session::TRANSACTION_READ_UNCOMMITTED: + isolation = MYSQL_READ_UNCOMMITTED; break; + case Session::TRANSACTION_READ_COMMITTED: + isolation = MYSQL_READ_COMMITTED; break; + case Session::TRANSACTION_REPEATABLE_READ: + isolation = MYSQL_REPEATABLE_READ; break; + case Session::TRANSACTION_SERIALIZABLE: + isolation = MYSQL_SERIALIZABLE; break; + default: + throw Poco::InvalidArgumentException("setTransactionIsolation()"); + } + + StatementExecutor ex(_handle); + ex.prepare(Poco::format("SET SESSION TRANSACTION ISOLATION LEVEL %s", isolation)); + ex.execute(); +} + + +Poco::UInt32 SessionImpl::getTransactionIsolation() +{ + std::string isolation; + getSetting("tx_isolation", isolation); + Poco::replaceInPlace(isolation, "-", " "); + if (MYSQL_READ_UNCOMMITTED == isolation) + return Session::TRANSACTION_READ_UNCOMMITTED; + else if (MYSQL_READ_COMMITTED == isolation) + return Session::TRANSACTION_READ_COMMITTED; + else if (MYSQL_REPEATABLE_READ == isolation) + return Session::TRANSACTION_REPEATABLE_READ; + else if (MYSQL_SERIALIZABLE == isolation) + return Session::TRANSACTION_SERIALIZABLE; + + throw InvalidArgumentException("getTransactionIsolation()"); +} + + +bool SessionImpl::hasTransactionIsolation(Poco::UInt32 ti) +{ + return Session::TRANSACTION_READ_UNCOMMITTED == ti || + Session::TRANSACTION_READ_COMMITTED == ti || + Session::TRANSACTION_REPEATABLE_READ == ti || + Session::TRANSACTION_SERIALIZABLE == ti; +} + + +void SessionImpl::close() +{ + if (_connected) + { + _handle.close(); + _connected = false; + } +} + + +bool SessionImpl::isConnected() +{ + return _connected; +} + + +bool SessionImpl::isTransaction() +{ + return _inTransaction; +} + + +}}} diff --git a/Data/MySQL/src/StatementExecutor.cpp b/Data/MySQL/src/StatementExecutor.cpp index 5da236ed7..47630ff39 100644 --- a/Data/MySQL/src/StatementExecutor.cpp +++ b/Data/MySQL/src/StatementExecutor.cpp @@ -46,12 +46,8 @@ namespace MySQL { StatementExecutor::StatementExecutor(MYSQL* mysql) { - h = mysql_stmt_init(mysql); - - if (!h) - { + if (!(_pHandle = mysql_stmt_init(mysql))) throw StatementException("mysql_stmt_init error"); - } _state = STMT_INITED; } @@ -59,7 +55,7 @@ StatementExecutor::StatementExecutor(MYSQL* mysql) StatementExecutor::~StatementExecutor() { - mysql_stmt_close(h); + mysql_stmt_close(_pHandle); } @@ -75,16 +71,10 @@ void StatementExecutor::prepare(const std::string& query) { _state = STMT_COMPILED; return; - //throw StatementException("Satement is already compiled"); } - // compile - int res = mysql_stmt_prepare(h, query.c_str(), static_cast(query.length())); - - if (res != 0) - { - throw StatementException("mysql_stmt_prepare error", h, query); - } + if (mysql_stmt_prepare(_pHandle, query.c_str(), static_cast(query.length())) != 0) + throw StatementException("mysql_stmt_prepare error", _pHandle, query); _query = query; _state = STMT_COMPILED; @@ -94,58 +84,35 @@ void StatementExecutor::prepare(const std::string& query) void StatementExecutor::bindParams(MYSQL_BIND* params, size_t count) { if (_state < STMT_COMPILED) - { throw StatementException("Satement is not compiled yet"); - } - if (count != mysql_stmt_param_count(h)) - { + if (count != mysql_stmt_param_count(_pHandle)) throw StatementException("wrong bind parameters count", 0, _query); - } - if (count == 0) - { - return; - } + if (count == 0) return; - int res = mysql_stmt_bind_param(h, params); - - if (res != 0) - { - throw StatementException("mysql_stmt_bind_param() error ", h, _query); - } + if (mysql_stmt_bind_param(_pHandle, params) != 0) + throw StatementException("mysql_stmt_bind_param() error ", _pHandle, _query); } void StatementExecutor::bindResult(MYSQL_BIND* result) { if (_state < STMT_COMPILED) - { throw StatementException("Satement is not compiled yet"); - } - int res = mysql_stmt_bind_result(h, result); - - if (res != 0) - { - throw StatementException("mysql_stmt_bind_result error ", h, _query); - } + if (mysql_stmt_bind_result(_pHandle, result) != 0) + throw StatementException("mysql_stmt_bind_result error ", _pHandle, _query); } void StatementExecutor::execute() { if (_state < STMT_COMPILED) - { throw StatementException("Satement is not compiled yet"); - } - int res = mysql_stmt_execute(h); - - if (res != 0) - { - throw StatementException("mysql_stmt_execute error", h, _query); - } + if (mysql_stmt_execute(_pHandle) != 0) + throw StatementException("mysql_stmt_execute error", _pHandle, _query); _state = STMT_EXECUTED; } @@ -154,16 +121,12 @@ void StatementExecutor::execute() bool StatementExecutor::fetch() { if (_state < STMT_EXECUTED) - { throw StatementException("Satement is not executed yet"); - } - int res = mysql_stmt_fetch(h); + int res = mysql_stmt_fetch(_pHandle); if ((res != 0) && (res != MYSQL_NO_DATA)) - { - throw StatementException("mysql_stmt_fetch error", h, _query); - } + throw StatementException("mysql_stmt_fetch error", _pHandle, _query); return (res == 0); } @@ -172,17 +135,15 @@ bool StatementExecutor::fetch() bool StatementExecutor::fetchColumn(size_t n, MYSQL_BIND *bind) { if (_state < STMT_EXECUTED) - { throw StatementException("Satement is not executed yet"); - } - int res = mysql_stmt_fetch_column(h, bind, static_cast(n), 0); + int res = mysql_stmt_fetch_column(_pHandle, bind, static_cast(n), 0); if ((res != 0) && (res != MYSQL_NO_DATA)) { std::ostringstream msg; msg << "mysql_stmt_fetch_column(" << n << ") error"; - throw StatementException(msg.str(), h, _query); + throw StatementException(msg.str(), _pHandle, _query); } return (res == 0); diff --git a/Data/MySQL/testsuite/TestSuite_VS90.vcproj b/Data/MySQL/testsuite/TestSuite_VS90.vcproj index 76e8fa303..42f4624fc 100644 --- a/Data/MySQL/testsuite/TestSuite_VS90.vcproj +++ b/Data/MySQL/testsuite/TestSuite_VS90.vcproj @@ -41,7 +41,7 @@ isConnected()) - std::cout << "*** Connected to " << '(' << _dbConnString << ')' << std::endl; - if (!_pExecutor) _pExecutor = new SQLExecutor("MySQL SQL Executor", _pSession); - } - - beenHere = true;*/ } @@ -510,6 +489,24 @@ void MySQLTest::testNull() } +void MySQLTest::testSessionTransaction() +{ + if (!_pSession) fail ("Test not available."); + + recreatePersonBLOBTable(); + _pExecutor->sessionTransaction(_dbConnString); +} + + +void MySQLTest::testTransaction() +{ + if (!_pSession) fail ("Test not available."); + + recreatePersonBLOBTable(); + _pExecutor->transaction(_dbConnString); +} + + void MySQLTest::testNullableInt() { if (!_pSession) fail ("Test not available."); @@ -814,6 +811,8 @@ CppUnit::Test* MySQLTest::suite() CppUnit_addTest(pSuite, MySQLTest, testNullableInt); CppUnit_addTest(pSuite, MySQLTest, testNullableString); CppUnit_addTest(pSuite, MySQLTest, testTupleWithNullable); + CppUnit_addTest(pSuite, MySQLTest, testSessionTransaction); + CppUnit_addTest(pSuite, MySQLTest, testTransaction); return pSuite; } diff --git a/Data/MySQL/testsuite/src/MySQLTest.h b/Data/MySQL/testsuite/src/MySQLTest.h index cf3b55d82..a3b75bff6 100644 --- a/Data/MySQL/testsuite/src/MySQLTest.h +++ b/Data/MySQL/testsuite/src/MySQLTest.h @@ -113,6 +113,9 @@ public: void testNullableString(); void testTupleWithNullable(); + void testSessionTransaction(); + void testTransaction(); + void setUp(); void tearDown(); diff --git a/Data/MySQL/testsuite/src/SQLExecutor.cpp b/Data/MySQL/testsuite/src/SQLExecutor.cpp index 420830c69..576f5e20a 100644 --- a/Data/MySQL/testsuite/src/SQLExecutor.cpp +++ b/Data/MySQL/testsuite/src/SQLExecutor.cpp @@ -1,1520 +1,1805 @@ -// -// SQLExecutor.cpp -// -// $Id: //poco/1.4/Data/MySQL/testsuite/src/SQLExecutor.cpp#1 $ -// -// Copyright (c) 2008, Applied Informatics Software Engineering GmbH. -// and Contributors. -// -// Permission is hereby granted, free of charge, to any person or organization -// obtaining a copy of the software and accompanying documentation covered by -// this license (the "Software") to use, reproduce, display, distribute, -// execute, and transmit the Software, and to prepare derivative works of the -// Software, and to permit third-parties to whom the Software is furnished to -// do so, all subject to the following: -// -// The copyright notices in the Software and this entire statement, including -// the above license grant, this restriction and the following disclaimer, -// must be included in all copies of the Software, in whole or in part, and -// all derivative works of the Software, unless such copies or derivative -// works are solely in the form of machine-executable object code generated by -// a source language processor. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -// - - -#include "CppUnit/TestCase.h" -#include "SQLExecutor.h" -#include "Poco/String.h" -#include "Poco/Format.h" -#include "Poco/Tuple.h" -#include "Poco/Any.h" -#include "Poco/Exception.h" -#include "Poco/Data/LOB.h" -#include "Poco/Data/StatementImpl.h" -#include "Poco/Data/RecordSet.h" -#include "Poco/Data/MySQL/Connector.h" -#include "Poco/Data/MySQL/MySQLException.h" - -#ifdef _WIN32 -#include -#endif - -#include -#include - - -using namespace Poco::Data; -using namespace Poco::Data::Keywords; -using Poco::Data::MySQL::ConnectionException; -using Poco::Data::MySQL::StatementException; -using Poco::format; -using Poco::Tuple; -using Poco::Any; -using Poco::AnyCast; -using Poco::NotFoundException; -using Poco::InvalidAccessException; -using Poco::BadCastException; -using Poco::RangeException; - - -struct Person -{ - std::string lastName; - std::string firstName; - std::string address; - int age; - Person(){age = 0;} - Person(const std::string& ln, const std::string& fn, const std::string& adr, int a):lastName(ln), firstName(fn), address(adr), age(a) - { - } - bool operator==(const Person& other) const - { - return lastName == other.lastName && firstName == other.firstName && address == other.address && age == other.age; - } - - bool operator < (const Person& p) const - { - if (age < p.age) - return true; - if (lastName < p.lastName) - return true; - if (firstName < p.firstName) - return true; - return (address < p.address); - } - - const std::string& operator () () const - /// This method is required so we can extract data to a map! - { - // we choose the lastName as examplary key - return lastName; - } -}; - - -namespace Poco { -namespace Data { - - -template <> -class TypeHandler -{ -public: - static void bind(std::size_t pos, const Person& obj, AbstractBinder* pBinder, AbstractBinder::Direction dir) - { - // the table is defined as Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3)) - poco_assert_dbg (pBinder != 0); - pBinder->bind(pos++, obj.lastName, dir); - pBinder->bind(pos++, obj.firstName, dir); - pBinder->bind(pos++, obj.address, dir); - pBinder->bind(pos++, obj.age, dir); - } - - static void prepare(std::size_t pos, Person& obj, AbstractPreparator* pPrepare) - { - // the table is defined as Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3)) - poco_assert_dbg (pPrepare != 0); - pPrepare->prepare(pos++, obj.lastName); - pPrepare->prepare(pos++, obj.firstName); - pPrepare->prepare(pos++, obj.address); - pPrepare->prepare(pos++, obj.age); - } - - static std::size_t size() - { - return 4; - } - - static void extract(std::size_t pos, Person& obj, const Person& defVal, AbstractExtractor* pExt) - { - poco_assert_dbg (pExt != 0); - if (!pExt->extract(pos++, obj.lastName)) - obj.lastName = defVal.lastName; - if (!pExt->extract(pos++, obj.firstName)) - obj.firstName = defVal.firstName; - if (!pExt->extract(pos++, obj.address)) - obj.address = defVal.address; - if (!pExt->extract(pos++, obj.age)) - obj.age = defVal.age; - } - -private: - TypeHandler(); - ~TypeHandler(); - TypeHandler(const TypeHandler&); - TypeHandler& operator=(const TypeHandler&); -}; - - -} } // namespace Poco::Data - - -SQLExecutor::SQLExecutor(const std::string& name, Poco::Data::Session* pSession): - CppUnit::TestCase(name), - _pSession(pSession) -{ -} - - -SQLExecutor::~SQLExecutor() -{ -} - - -void SQLExecutor::bareboneMySQLTest(const char* host, const char* user, const char* pwd, const char* db, int port, const char* tableCreateString) -{ - int rc; - MYSQL* hsession = mysql_init(0); - assert (hsession != 0); - - MYSQL* tmp = mysql_real_connect(hsession, host, user, pwd, db, port, 0, 0); - assert(tmp == hsession); - - MYSQL_STMT* hstmt = mysql_stmt_init(hsession); - assert(hstmt != 0); - - std::string sql = "DROP TABLE Test"; - mysql_real_query(hsession, sql.c_str(), static_cast(sql.length())); - - sql = tableCreateString; - rc = mysql_stmt_prepare(hstmt, sql.c_str(), static_cast(sql.length())); - assert(rc == 0); - - rc = mysql_stmt_execute(hstmt); - assert(rc == 0); - - sql = "INSERT INTO Test VALUES (?,?,?,?,?)"; - rc = mysql_stmt_prepare(hstmt, sql.c_str(), static_cast(sql.length())); - assert(rc == 0); - - std::string str[3] = { "111", "222", "333" }; - int fourth = 4; - float fifth = 1.5; - - MYSQL_BIND bind_param[5] = {{0}}; - - bind_param[0].buffer = const_cast(str[0].c_str()); - bind_param[0].buffer_length = static_cast(str[0].length()); - bind_param[0].buffer_type = MYSQL_TYPE_STRING; - - bind_param[1].buffer = const_cast(str[1].c_str()); - bind_param[1].buffer_length = static_cast(str[1].length()); - bind_param[1].buffer_type = MYSQL_TYPE_STRING; - - bind_param[2].buffer = const_cast(str[2].c_str()); - bind_param[2].buffer_length = static_cast(str[2].length()); - bind_param[2].buffer_type = MYSQL_TYPE_STRING; - - bind_param[3].buffer = &fourth; - bind_param[3].buffer_type = MYSQL_TYPE_LONG; - - bind_param[4].buffer = &fifth; - bind_param[4].buffer_type = MYSQL_TYPE_FLOAT; - - rc = mysql_stmt_bind_param(hstmt, bind_param); - assert (rc == 0); - - rc = mysql_stmt_execute(hstmt); - assert (rc == 0); - - sql = "SELECT * FROM Test"; - rc = mysql_stmt_prepare(hstmt, sql.c_str(), static_cast(sql.length())); - assert (rc == 0); - - char chr[3][5] = {{ 0 }}; - unsigned long lengths[5] = { 0 }; - fourth = 0; - fifth = 0.0f; - - MYSQL_BIND bind_result[5] = {{0}}; - - bind_result[0].buffer = chr[0]; - bind_result[0].buffer_length = sizeof(chr[0]); - bind_result[0].buffer_type = MYSQL_TYPE_STRING; - bind_result[0].length = &lengths[0]; - - bind_result[1].buffer = chr[1]; - bind_result[1].buffer_length = sizeof(chr[1]); - bind_result[1].buffer_type = MYSQL_TYPE_STRING; - bind_result[1].length = &lengths[1]; - - bind_result[2].buffer = chr[2]; - bind_result[2].buffer_length = sizeof(chr[2]); - bind_result[2].buffer_type = MYSQL_TYPE_STRING; - bind_result[2].length = &lengths[2]; - - bind_result[3].buffer = &fourth; - bind_result[3].buffer_type = MYSQL_TYPE_LONG; - bind_result[3].length = &lengths[3]; - - bind_result[4].buffer = &fifth; - bind_result[4].buffer_type = MYSQL_TYPE_FLOAT; - bind_result[4].length = &lengths[4]; - - rc = mysql_stmt_bind_result(hstmt, bind_result); - assert (rc == 0); - - rc = mysql_stmt_execute(hstmt); - assert (rc == 0); - rc = mysql_stmt_fetch(hstmt); - assert (rc == 0); - - assert (0 == std::strncmp("111", chr[0], 3)); - assert (0 == std::strncmp("222", chr[1], 3)); - assert (0 == std::strncmp("333", chr[2], 3)); - assert (4 == fourth); - assert (1.5 == fifth); - - rc = mysql_stmt_close(hstmt); - assert(rc == 0); - - sql = "DROP TABLE Test"; - rc = mysql_real_query(hsession, sql.c_str(), static_cast(sql.length())); - assert(rc == 0); - - mysql_close(hsession); -} - - -void SQLExecutor::simpleAccess() -{ - std::string funct = "simpleAccess()"; - std::string lastName = "lastName"; - std::string firstName("firstName"); - std::string address("Address"); - int age = 133132; - int count = 0; - std::string result; - - count = 0; - try - { - Statement stmt(*_pSession); - stmt << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(age);//, now; - stmt.execute(); - } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - assert (count == 1); - - try { *_pSession << "SELECT LastName FROM Person", into(result), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (lastName == result); - - try { *_pSession << "SELECT Age FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == age); -} - - -void SQLExecutor::complexType() -{ - std::string funct = "complexType()"; - Person p1("LN1", "FN1", "ADDR1", 1); - Person p2("LN2", "FN2", "ADDR2", 2); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(p1), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(p2), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - - Person c1; - Person c2; - try { *_pSession << "SELECT * FROM Person WHERE LastName = 'LN1'", into(c1), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (c1 == p1); -} - - -void SQLExecutor::simpleAccessVector() -{ - std::string funct = "simpleAccessVector()"; - std::vector lastNames; - std::vector firstNames; - std::vector addresses; - std::vector ages; - std::string tableName("Person"); - lastNames.push_back("LN1"); - lastNames.push_back("LN2"); - firstNames.push_back("FN1"); - firstNames.push_back("FN2"); - addresses.push_back("ADDR1"); - addresses.push_back("ADDR2"); - ages.push_back(1); - ages.push_back(2); - int count = 0; - std::string result; - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - - std::vector lastNamesR; - std::vector firstNamesR; - std::vector addressesR; - std::vector agesR; - try { *_pSession << "SELECT * FROM Person", into(lastNamesR), into(firstNamesR), into(addressesR), into(agesR), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (ages == agesR); - assert (lastNames == lastNamesR); - assert (firstNames == firstNamesR); - assert (addresses == addressesR); -} - - -void SQLExecutor::complexTypeVector() -{ - std::string funct = "complexTypeVector()"; - std::vector people; - people.push_back(Person("LN1", "FN1", "ADDR1", 1)); - people.push_back(Person("LN2", "FN2", "ADDR2", 2)); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - - std::vector result; - try { *_pSession << "SELECT * FROM Person", into(result), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (result == people); -} - - -void SQLExecutor::insertVector() -{ - std::string funct = "insertVector()"; - std::vector str; - str.push_back("s1"); - str.push_back("s2"); - str.push_back("s3"); - str.push_back("s3"); - int count = 100; - - { - Statement stmt((*_pSession << "INSERT INTO Strings VALUES (?)", use(str))); - try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 0); - - try { stmt.execute(); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 4); - } - count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 4); -} - - -void SQLExecutor::insertEmptyVector() -{ - std::string funct = "insertEmptyVector()"; - std::vector str; - - try - { - *_pSession << "INSERT INTO Strings VALUES (?)", use(str), now; - fail("empty collections should not work"); - } - catch (Poco::Exception&) - { - } -} - - -void SQLExecutor::insertSingleBulk() -{ - std::string funct = "insertSingleBulk()"; - int x = 0; - Statement stmt((*_pSession << "INSERT INTO Strings VALUES (?)", use(x))); - - for (x = 0; x < 100; ++x) - { - int i = stmt.execute(); - assert (i == 0); - } - - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 100); - - try { *_pSession << "SELECT SUM(str) FROM Strings", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == ((0+99)*100/2)); -} - - -void SQLExecutor::floats() -{ - std::string funct = "floats()"; - float data = 1.5f; - float ret = 0.0f; - - try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 1); - - try { *_pSession << "SELECT str FROM Strings", into(ret), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (ret == data); -} - - -void SQLExecutor::doubles() -{ - std::string funct = "floats()"; - double data = 1.5; - double ret = 0.0; - - try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 1); - - try { *_pSession << "SELECT str FROM Strings", into(ret), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (ret == data); -} - - -void SQLExecutor::insertSingleBulkVec() -{ - std::string funct = "insertSingleBulkVec()"; - std::vector data; - - for (int x = 0; x < 100; ++x) - data.push_back(x); - - Statement stmt((*_pSession << "INSERT INTO Strings VALUES (?)", use(data))); - stmt.execute(); - - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - assert (count == 100); - try { *_pSession << "SELECT SUM(str) FROM Strings", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == ((0+99)*100/2)); -} - - -void SQLExecutor::limits() -{ - std::string funct = "limit()"; - std::vector data; - for (int x = 0; x < 100; ++x) - { - data.push_back(x); - } - - try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - std::vector retData; - try { *_pSession << "SELECT * FROM Strings", into(retData), limit(50), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (retData.size() == 50); - for (int x = 0; x < 50; ++x) - { - assert(data[x] == retData[x]); - } -} - - -void SQLExecutor::limitZero() -{ - std::string funct = "limitZero()"; - std::vector data; - for (int x = 0; x < 100; ++x) - { - data.push_back(x); - } - - try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - std::vector retData; - try { *_pSession << "SELECT * FROM Strings", into(retData), limit(0), now; }// stupid test, but at least we shouldn't crash - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (retData.size() == 0); -} - - -void SQLExecutor::limitOnce() -{ - std::string funct = "limitOnce()"; - std::vector data; - for (int x = 0; x < 101; ++x) - { - data.push_back(x); - } - - try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - std::vector retData; - Statement stmt = (*_pSession << "SELECT * FROM Strings", into(retData), limit(50), now); - assert (!stmt.done()); - assert (retData.size() == 50); - stmt.execute(); - assert (!stmt.done()); - assert (retData.size() == 100); - stmt.execute(); - assert (stmt.done()); - assert (retData.size() == 101); - - for (int x = 0; x < 101; ++x) - { - assert(data[x] == retData[x]); - } -} - - -void SQLExecutor::limitPrepare() -{ - std::string funct = "limitPrepare()"; - std::vector data; - for (int x = 0; x < 100; ++x) - { - data.push_back(x); - } - - try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - std::vector retData; - Statement stmt = (*_pSession << "SELECT * FROM Strings", into(retData), limit(50)); - assert (retData.size() == 0); - assert (!stmt.done()); - - try { stmt.execute(); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (!stmt.done()); - assert (retData.size() == 50); - - try { stmt.execute(); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (stmt.done()); - assert (retData.size() == 100); - - try { stmt.execute(); }// will restart execution! - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (!stmt.done()); - assert (retData.size() == 150); - for (int x = 0; x < 150; ++x) - { - assert(data[x%100] == retData[x]); - } -} - - - -void SQLExecutor::prepare() -{ - std::string funct = "prepare()"; - std::vector data; - for (int x = 0; x < 100; x += 2) - { - data.push_back(x); - } - - { - Statement stmt((*_pSession << "INSERT INTO Strings VALUES (?)", use(data))); - } - // stmt should not have been executed when destroyed - int count = 100; - try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 0); -} - - -void SQLExecutor::setSimple() -{ - std::string funct = "setSimple()"; - std::set lastNames; - std::set firstNames; - std::set addresses; - std::set ages; - std::string tableName("Person"); - lastNames.insert("LN1"); - lastNames.insert("LN2"); - firstNames.insert("FN1"); - firstNames.insert("FN2"); - addresses.insert("ADDR1"); - addresses.insert("ADDR2"); - ages.insert(1); - ages.insert(2); - int count = 0; - std::string result; - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - - std::set lastNamesR; - std::set firstNamesR; - std::set addressesR; - std::set agesR; - try { *_pSession << "SELECT * FROM Person", into(lastNamesR), into(firstNamesR), into(addressesR), into(agesR), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (ages == agesR); - assert (lastNames == lastNamesR); - assert (firstNames == firstNamesR); - assert (addresses == addressesR); -} - - -void SQLExecutor::setComplex() -{ - std::string funct = "setComplex()"; - std::set people; - people.insert(Person("LN1", "FN1", "ADDR1", 1)); - people.insert(Person("LN2", "FN2", "ADDR2", 2)); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - - std::set result; - try { *_pSession << "SELECT * FROM Person", into(result), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (result == people); -} - - -void SQLExecutor::setComplexUnique() -{ - std::string funct = "setComplexUnique()"; - std::vector people; - Person p1("LN1", "FN1", "ADDR1", 1); - people.push_back(p1); - people.push_back(p1); - people.push_back(p1); - people.push_back(p1); - Person p2("LN2", "FN2", "ADDR2", 2); - people.push_back(p2); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 5); - - std::set result; - try { *_pSession << "SELECT * FROM Person", into(result), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (result.size() == 2); - assert (*result.begin() == p1); - assert (*++result.begin() == p2); -} - -void SQLExecutor::multiSetSimple() -{ - std::string funct = "multiSetSimple()"; - std::multiset lastNames; - std::multiset firstNames; - std::multiset addresses; - std::multiset ages; - std::string tableName("Person"); - lastNames.insert("LN1"); - lastNames.insert("LN2"); - firstNames.insert("FN1"); - firstNames.insert("FN2"); - addresses.insert("ADDR1"); - addresses.insert("ADDR2"); - ages.insert(1); - ages.insert(2); - int count = 0; - std::string result; - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - - std::multiset lastNamesR; - std::multiset firstNamesR; - std::multiset addressesR; - std::multiset agesR; - try { *_pSession << "SELECT * FROM Person", into(lastNamesR), into(firstNamesR), into(addressesR), into(agesR), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (ages.size() == agesR.size()); - assert (lastNames.size() == lastNamesR.size()); - assert (firstNames.size() == firstNamesR.size()); - assert (addresses.size() == addressesR.size()); -} - - -void SQLExecutor::multiSetComplex() -{ - std::string funct = "multiSetComplex()"; - std::multiset people; - Person p1("LN1", "FN1", "ADDR1", 1); - people.insert(p1); - people.insert(p1); - people.insert(p1); - people.insert(p1); - Person p2("LN2", "FN2", "ADDR2", 2); - people.insert(p2); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 5); - - std::multiset result; - try { *_pSession << "SELECT * FROM Person", into(result), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (result.size() == people.size()); -} - - -void SQLExecutor::mapComplex() -{ - std::string funct = "mapComplex()"; - std::map people; - Person p1("LN1", "FN1", "ADDR1", 1); - Person p2("LN2", "FN2", "ADDR2", 2); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN2", p2)); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - - std::map result; - try { *_pSession << "SELECT * FROM Person", into(result), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (result == people); -} - - -void SQLExecutor::mapComplexUnique() -{ - std::string funct = "mapComplexUnique()"; - std::multimap people; - Person p1("LN1", "FN1", "ADDR1", 1); - Person p2("LN2", "FN2", "ADDR2", 2); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN2", p2)); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 5); - - std::map result; - try { *_pSession << "SELECT * FROM Person", into(result), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (result.size() == 2); -} - - -void SQLExecutor::multiMapComplex() -{ - std::string funct = "multiMapComplex()"; - std::multimap people; - Person p1("LN1", "FN1", "ADDR1", 1); - Person p2("LN2", "FN2", "ADDR2", 2); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN2", p2)); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 5); - - std::multimap result; - try { *_pSession << "SELECT * FROM Person", into(result), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (result.size() == people.size()); -} - - -void SQLExecutor::selectIntoSingle() -{ - std::string funct = "selectIntoSingle()"; - std::multimap people; - Person p1("LN1", "FN1", "ADDR1", 1); - Person p2("LN2", "FN2", "ADDR2", 2); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p2)); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - Person result; - try { *_pSession << "SELECT * FROM Person", into(result), limit(1), now; }// will return 1 object into one single result - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (result == p1); -} - - -void SQLExecutor::selectIntoSingleStep() -{ - std::string funct = "selectIntoSingleStep()"; - std::multimap people; - Person p1("LN1", "FN1", "ADDR1", 1); - Person p2("LN2", "FN2", "ADDR2", 2); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p2)); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - Person result; - Statement stmt = (*_pSession << "SELECT * FROM Person", into(result), limit(1)); - stmt.execute(); - assert (result == p1); - assert (!stmt.done()); - stmt.execute(); - assert (result == p2); - assert (stmt.done()); -} - - -void SQLExecutor::selectIntoSingleFail() -{ - std::string funct = "selectIntoSingleFail()"; - std::multimap people; - Person p1("LN1", "FN1", "ADDR1", 1); - Person p2("LN2", "FN2", "ADDR2", 2); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p2)); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), limit(2, true), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - Person result; - try - { - *_pSession << "SELECT * FROM Person", into(result), limit(1, true), now; // will fail now - fail("hardLimit is set: must fail"); - } - catch(Poco::Data::LimitException&) - { - } -} - - -void SQLExecutor::lowerLimitOk() -{ - std::string funct = "lowerLimitOk()"; - std::multimap people; - Person p1("LN1", "FN1", "ADDR1", 1); - Person p2("LN2", "FN2", "ADDR2", 2); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p2)); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - Person result; - try - { - *_pSession << "SELECT * FROM Person", into(result), lowerLimit(2), now; // will return 2 objects into one single result but only room for one! - fail("Not enough space for results"); - } - catch(Poco::Exception&) - { - } -} - - -void SQLExecutor::singleSelect() -{ - std::string funct = "singleSelect()"; - std::multimap people; - Person p1("LN1", "FN1", "ADDR1", 1); - Person p2("LN2", "FN2", "ADDR2", 2); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p2)); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - Person result; - Statement stmt = (*_pSession << "SELECT * FROM Person", into(result), limit(1)); - stmt.execute(); - assert (result == p1); - assert (!stmt.done()); - stmt.execute(); - assert (result == p2); - assert (stmt.done()); -} - - -void SQLExecutor::lowerLimitFail() -{ - std::string funct = "lowerLimitFail()"; - std::multimap people; - Person p1("LN1", "FN1", "ADDR1", 1); - Person p2("LN2", "FN2", "ADDR2", 2); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p2)); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - Person result; - try - { - *_pSession << "SELECT * FROM Person", into(result), lowerLimit(3), now; // will fail - fail("should fail. not enough data"); - } - catch(Poco::Exception&) - { - } -} - - -void SQLExecutor::combinedLimits() -{ - std::string funct = "combinedLimits()"; - std::multimap people; - Person p1("LN1", "FN1", "ADDR1", 1); - Person p2("LN2", "FN2", "ADDR2", 2); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p2)); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - std::vector result; - try { *_pSession << "SELECT * FROM Person", into(result), lowerLimit(2), upperLimit(2), now; }// will return 2 objects - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (result.size() == 2); - assert (result[0] == p1); - assert (result[1] == p2); -} - - - -void SQLExecutor::ranges() -{ - std::string funct = "range()"; - std::multimap people; - Person p1("LN1", "FN1", "ADDR1", 1); - Person p2("LN2", "FN2", "ADDR2", 2); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p2)); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - std::vector result; - try { *_pSession << "SELECT * FROM Person", into(result), range(2, 2), now; }// will return 2 objects - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (result.size() == 2); - assert (result[0] == p1); - assert (result[1] == p2); -} - - -void SQLExecutor::combinedIllegalLimits() -{ - std::string funct = "combinedIllegalLimits()"; - std::multimap people; - Person p1("LN1", "FN1", "ADDR1", 1); - Person p2("LN2", "FN2", "ADDR2", 2); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p2)); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - Person result; - try - { - *_pSession << "SELECT * FROM Person", into(result), lowerLimit(3), upperLimit(2), now; - fail("lower > upper is not allowed"); - } - catch(LimitException&) - { - } -} - - -void SQLExecutor::illegalRange() -{ - std::string funct = "illegalRange()"; - std::multimap people; - Person p1("LN1", "FN1", "ADDR1", 1); - Person p2("LN2", "FN2", "ADDR2", 2); - people.insert(std::make_pair("LN1", p1)); - people.insert(std::make_pair("LN1", p2)); - - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 2); - Person result; - try - { - *_pSession << "SELECT * FROM Person", into(result), range(3, 2), now; - fail("lower > upper is not allowed"); - } - catch(LimitException&) - { - } -} - - -void SQLExecutor::emptyDB() -{ - std::string funct = "emptyDB()"; - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 0); - - Person result; - Statement stmt = (*_pSession << "SELECT * FROM Person", into(result), limit(1)); - stmt.execute(); - assert (result.firstName.empty()); - assert (stmt.done()); -} - - -void SQLExecutor::blob(int bigSize) -{ - std::string funct = "blob()"; - std::string lastName("lastname"); - std::string firstName("firstname"); - std::string address("Address"); - - Poco::Data::CLOB img("0123456789", 10); - int count = 0; - try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(img), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 1); - - Poco::Data::CLOB res; - assert (res.size() == 0); - try { *_pSession << "SELECT Image FROM Person", into(res), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (res == img); - - Poco::Data::CLOB big; - std::vector v(bigSize, 'x'); - big.assignRaw(&v[0], v.size()); - - assert (big.size() == (size_t)bigSize); - - try { *_pSession << "DELETE FROM Person", now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - try { *_pSession << "INSERT INTO Person VALUES(?,?,?,?)", use(lastName), use(firstName), use(address), use(big), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - try { *_pSession << "SELECT Image FROM Person", into(res), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (res == big); -} - -void SQLExecutor::blobStmt() -{ - std::string funct = "blobStmt()"; - std::string lastName("lastname"); - std::string firstName("firstname"); - std::string address("Address"); - Poco::Data::CLOB blob("0123456789", 10); - - int count = 0; - Statement ins = (*_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(blob)); - ins.execute(); - try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 1); - - Poco::Data::CLOB res; - poco_assert (res.size() == 0); - Statement stmt = (*_pSession << "SELECT Image FROM Person", into(res)); - try { stmt.execute(); } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - poco_assert (res == blob); -} - -void SQLExecutor::tuples() -{ - typedef Tuple TupleType; - std::string funct = "tuples()"; - TupleType t(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19); - - try { *_pSession << "INSERT INTO Tuples VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", use(t), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - TupleType ret(-10,-11,-12,-13,-14,-15,-16,-17,-18,-19); - assert (ret != t); - try { *_pSession << "SELECT * FROM Tuples", into(ret), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (ret == t); -} - -void SQLExecutor::tupleVector() -{ - typedef Tuple TupleType; - std::string funct = "tupleVector()"; - TupleType t(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19); - Tuple - t10(10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29); - TupleType t100(100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119); - std::vector v; - v.push_back(t); - v.push_back(t10); - v.push_back(t100); - - try { *_pSession << "INSERT INTO Tuples VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", use(v), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Tuples", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (v.size() == (size_t)count); - - std::vector > ret; - try { *_pSession << "SELECT * FROM Tuples", into(ret), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (ret == v); -} - - -void SQLExecutor::internalExtraction() -{ - /*std::string funct = "internalExtraction()"; - std::vector > v; - v.push_back(Tuple(1, 1.5f, "3")); - v.push_back(Tuple(2, 2.5f, "4")); - v.push_back(Tuple(3, 3.5f, "5")); - v.push_back(Tuple(4, 4.5f, "6")); - - try { *_pSession << "INSERT INTO Vectors VALUES (?,?,?)", use(v), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - - try - { - Statement stmt = (*_pSession << "SELECT * FROM Vectors", now); - RecordSet rset(stmt); - - assert (3 == rset.columnCount()); - assert (4 == rset.rowCount()); - - int curVal = 3; - do - { - assert (rset["str0"] == curVal); - ++curVal; - } while (rset.moveNext()); - - rset.moveFirst(); - assert (rset["str0"] == "3"); - rset.moveLast(); - assert (rset["str0"] == "6"); - - RecordSet rset2(rset); - assert (3 == rset2.columnCount()); - assert (4 == rset2.rowCount()); - - int i = rset.value(0,0); - assert (1 == i); - - std::string s = rset.value(0,0); - assert ("1" == s); - - int a = rset.value(0,2); - assert (3 == a); - - try - { - double d = rset.value(1,1); - assert (2.5 == d); - } - catch (BadCastException&) - { - float f = rset.value(1,1); - assert (2.5 == f); - } - - s = rset.value(2,2); - assert ("5" == s); - i = rset.value("str0", 2); - assert (5 == i); - - const Column& col = rset.column(0); - Column::Iterator it = col.begin(); - Column::Iterator end = col.end(); - for (int i = 1; it != end; ++it, ++i) - assert (*it == i); - - rset = (*_pSession << "SELECT COUNT(*) AS cnt FROM Vectors", now); - - //various results for COUNT(*) are received from different drivers - try - { - //this is what most drivers will return - int i = rset.value(0,0); - assert (4 == i); - } - catch(BadCastException&) - { - try - { - //this is for Oracle - double i = rset.value(0,0); - assert (4 == int(i)); - } - catch(BadCastException&) - { - //this is for PostgreSQL - Poco::Int64 big = rset.value(0,0); - assert (4 == big); - } - } - - s = rset.value("cnt", 0).convert(); - assert ("4" == s); - - try { const Column& col1 = rset.column(100); fail ("must fail"); } - catch (RangeException&) { } - - try { rset.value(0,0); fail ("must fail"); } - catch (BadCastException&) { } - - stmt = (*_pSession << "DELETE FROM Vectors", now); - rset = stmt; - - try { const Column& col1 = rset.column(0); fail ("must fail"); } - catch (RangeException&) { } - } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } -*/ -} - - -void SQLExecutor::doNull() -{ - std::string funct = "null()"; - - *_pSession << "INSERT INTO Vectors VALUES (?, ?, ?)", - use(Poco::Data::Keywords::null), - use(Poco::Data::Keywords::null), - use(Poco::Data::Keywords::null), now; - - int count = 0; - try { *_pSession << "SELECT COUNT(*) FROM Vectors", into(count), now; } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - assert (count == 1); - - int i0 = 0; - Statement stmt1 = (*_pSession << "SELECT i0 FROM Vectors", into(i0, Poco::Data::Position(0), -1)); - try { stmt1.execute(); } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - poco_assert (i0 == -1); - - float flt0 = 0; - Statement stmt2 = (*_pSession << "SELECT flt0 FROM Vectors", into(flt0, Poco::Data::Position(0), 3.25f)); - try { stmt2.execute(); } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - poco_assert (flt0 == 3.25); - - std::string str0("string"); - Statement stmt3 = (*_pSession << "SELECT str0 FROM Vectors", into(str0, Poco::Data::Position(0), std::string("DEFAULT"))); - try { stmt3.execute(); } - catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } - catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } - poco_assert (str0 == "DEFAULT"); -} +// +// SQLExecutor.cpp +// +// $Id: //poco/1.4/Data/MySQL/testsuite/src/SQLExecutor.cpp#1 $ +// +// Copyright (c) 2008, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + + +#include "CppUnit/TestCase.h" +#include "SQLExecutor.h" +#include "Poco/String.h" +#include "Poco/Format.h" +#include "Poco/Tuple.h" +#include "Poco/Any.h" +#include "Poco/Exception.h" +#include "Poco/Data/LOB.h" +#include "Poco/Data/StatementImpl.h" +#include "Poco/Data/RecordSet.h" +#include "Poco/Data/Transaction.h" +#include "Poco/Data/MySQL/Connector.h" +#include "Poco/Data/MySQL/MySQLException.h" + +#ifdef _WIN32 +#include +#endif + +#include +#include + + +using namespace Poco::Data; +using namespace Poco::Data::Keywords; +using Poco::Data::MySQL::ConnectionException; +using Poco::Data::MySQL::StatementException; +using Poco::format; +using Poco::Tuple; +using Poco::Any; +using Poco::AnyCast; +using Poco::NotFoundException; +using Poco::InvalidAccessException; +using Poco::BadCastException; +using Poco::RangeException; + + +struct Person +{ + std::string lastName; + std::string firstName; + std::string address; + int age; + Person(){age = 0;} + Person(const std::string& ln, const std::string& fn, const std::string& adr, int a):lastName(ln), firstName(fn), address(adr), age(a) + { + } + bool operator==(const Person& other) const + { + return lastName == other.lastName && firstName == other.firstName && address == other.address && age == other.age; + } + + bool operator < (const Person& p) const + { + if (age < p.age) + return true; + if (lastName < p.lastName) + return true; + if (firstName < p.firstName) + return true; + return (address < p.address); + } + + const std::string& operator () () const + /// This method is required so we can extract data to a map! + { + // we choose the lastName as examplary key + return lastName; + } +}; + + +namespace Poco { +namespace Data { + + +template <> +class TypeHandler +{ +public: + static void bind(std::size_t pos, const Person& obj, AbstractBinder* pBinder, AbstractBinder::Direction dir) + { + // the table is defined as Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3)) + poco_assert_dbg (pBinder != 0); + pBinder->bind(pos++, obj.lastName, dir); + pBinder->bind(pos++, obj.firstName, dir); + pBinder->bind(pos++, obj.address, dir); + pBinder->bind(pos++, obj.age, dir); + } + + static void prepare(std::size_t pos, Person& obj, AbstractPreparator* pPrepare) + { + // the table is defined as Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3)) + poco_assert_dbg (pPrepare != 0); + pPrepare->prepare(pos++, obj.lastName); + pPrepare->prepare(pos++, obj.firstName); + pPrepare->prepare(pos++, obj.address); + pPrepare->prepare(pos++, obj.age); + } + + static std::size_t size() + { + return 4; + } + + static void extract(std::size_t pos, Person& obj, const Person& defVal, AbstractExtractor* pExt) + { + poco_assert_dbg (pExt != 0); + if (!pExt->extract(pos++, obj.lastName)) + obj.lastName = defVal.lastName; + if (!pExt->extract(pos++, obj.firstName)) + obj.firstName = defVal.firstName; + if (!pExt->extract(pos++, obj.address)) + obj.address = defVal.address; + if (!pExt->extract(pos++, obj.age)) + obj.age = defVal.age; + } + +private: + TypeHandler(); + ~TypeHandler(); + TypeHandler(const TypeHandler&); + TypeHandler& operator=(const TypeHandler&); +}; + + +} } // namespace Poco::Data + + +SQLExecutor::SQLExecutor(const std::string& name, Poco::Data::Session* pSession): + CppUnit::TestCase(name), + _pSession(pSession) +{ +} + + +SQLExecutor::~SQLExecutor() +{ +} + + +void SQLExecutor::bareboneMySQLTest(const char* host, const char* user, const char* pwd, const char* db, int port, const char* tableCreateString) +{ + int rc; + MYSQL* hsession = mysql_init(0); + assert (hsession != 0); + + MYSQL* tmp = mysql_real_connect(hsession, host, user, pwd, db, port, 0, 0); + assert(tmp == hsession); + + MYSQL_STMT* hstmt = mysql_stmt_init(hsession); + assert(hstmt != 0); + + std::string sql = "DROP TABLE Test"; + mysql_real_query(hsession, sql.c_str(), static_cast(sql.length())); + + sql = tableCreateString; + rc = mysql_stmt_prepare(hstmt, sql.c_str(), static_cast(sql.length())); + assert(rc == 0); + + rc = mysql_stmt_execute(hstmt); + assert(rc == 0); + + sql = "INSERT INTO Test VALUES (?,?,?,?,?)"; + rc = mysql_stmt_prepare(hstmt, sql.c_str(), static_cast(sql.length())); + assert(rc == 0); + + std::string str[3] = { "111", "222", "333" }; + int fourth = 4; + float fifth = 1.5; + + MYSQL_BIND bind_param[5] = {{0}}; + + bind_param[0].buffer = const_cast(str[0].c_str()); + bind_param[0].buffer_length = static_cast(str[0].length()); + bind_param[0].buffer_type = MYSQL_TYPE_STRING; + + bind_param[1].buffer = const_cast(str[1].c_str()); + bind_param[1].buffer_length = static_cast(str[1].length()); + bind_param[1].buffer_type = MYSQL_TYPE_STRING; + + bind_param[2].buffer = const_cast(str[2].c_str()); + bind_param[2].buffer_length = static_cast(str[2].length()); + bind_param[2].buffer_type = MYSQL_TYPE_STRING; + + bind_param[3].buffer = &fourth; + bind_param[3].buffer_type = MYSQL_TYPE_LONG; + + bind_param[4].buffer = &fifth; + bind_param[4].buffer_type = MYSQL_TYPE_FLOAT; + + rc = mysql_stmt_bind_param(hstmt, bind_param); + assert (rc == 0); + + rc = mysql_stmt_execute(hstmt); + assert (rc == 0); + + sql = "SELECT * FROM Test"; + rc = mysql_stmt_prepare(hstmt, sql.c_str(), static_cast(sql.length())); + assert (rc == 0); + + char chr[3][5] = {{ 0 }}; + unsigned long lengths[5] = { 0 }; + fourth = 0; + fifth = 0.0f; + + MYSQL_BIND bind_result[5] = {{0}}; + + bind_result[0].buffer = chr[0]; + bind_result[0].buffer_length = sizeof(chr[0]); + bind_result[0].buffer_type = MYSQL_TYPE_STRING; + bind_result[0].length = &lengths[0]; + + bind_result[1].buffer = chr[1]; + bind_result[1].buffer_length = sizeof(chr[1]); + bind_result[1].buffer_type = MYSQL_TYPE_STRING; + bind_result[1].length = &lengths[1]; + + bind_result[2].buffer = chr[2]; + bind_result[2].buffer_length = sizeof(chr[2]); + bind_result[2].buffer_type = MYSQL_TYPE_STRING; + bind_result[2].length = &lengths[2]; + + bind_result[3].buffer = &fourth; + bind_result[3].buffer_type = MYSQL_TYPE_LONG; + bind_result[3].length = &lengths[3]; + + bind_result[4].buffer = &fifth; + bind_result[4].buffer_type = MYSQL_TYPE_FLOAT; + bind_result[4].length = &lengths[4]; + + rc = mysql_stmt_bind_result(hstmt, bind_result); + assert (rc == 0); + + rc = mysql_stmt_execute(hstmt); + assert (rc == 0); + rc = mysql_stmt_fetch(hstmt); + assert (rc == 0); + + assert (0 == std::strncmp("111", chr[0], 3)); + assert (0 == std::strncmp("222", chr[1], 3)); + assert (0 == std::strncmp("333", chr[2], 3)); + assert (4 == fourth); + assert (1.5 == fifth); + + rc = mysql_stmt_close(hstmt); + assert(rc == 0); + + sql = "DROP TABLE Test"; + rc = mysql_real_query(hsession, sql.c_str(), static_cast(sql.length())); + assert(rc == 0); + + mysql_close(hsession); +} + + +void SQLExecutor::simpleAccess() +{ + std::string funct = "simpleAccess()"; + std::string lastName = "lastName"; + std::string firstName("firstName"); + std::string address("Address"); + int age = 133132; + int count = 0; + std::string result; + + count = 0; + try + { + Statement stmt(*_pSession); + stmt << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(age);//, now; + stmt.execute(); + } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + assert (count == 1); + + try { *_pSession << "SELECT LastName FROM Person", into(result), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (lastName == result); + + try { *_pSession << "SELECT Age FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == age); +} + + +void SQLExecutor::complexType() +{ + std::string funct = "complexType()"; + Person p1("LN1", "FN1", "ADDR1", 1); + Person p2("LN2", "FN2", "ADDR2", 2); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(p1), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(p2), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + + Person c1; + Person c2; + try { *_pSession << "SELECT * FROM Person WHERE LastName = 'LN1'", into(c1), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (c1 == p1); +} + + +void SQLExecutor::simpleAccessVector() +{ + std::string funct = "simpleAccessVector()"; + std::vector lastNames; + std::vector firstNames; + std::vector addresses; + std::vector ages; + std::string tableName("Person"); + lastNames.push_back("LN1"); + lastNames.push_back("LN2"); + firstNames.push_back("FN1"); + firstNames.push_back("FN2"); + addresses.push_back("ADDR1"); + addresses.push_back("ADDR2"); + ages.push_back(1); + ages.push_back(2); + int count = 0; + std::string result; + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + + std::vector lastNamesR; + std::vector firstNamesR; + std::vector addressesR; + std::vector agesR; + try { *_pSession << "SELECT * FROM Person", into(lastNamesR), into(firstNamesR), into(addressesR), into(agesR), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (ages == agesR); + assert (lastNames == lastNamesR); + assert (firstNames == firstNamesR); + assert (addresses == addressesR); +} + + +void SQLExecutor::complexTypeVector() +{ + std::string funct = "complexTypeVector()"; + std::vector people; + people.push_back(Person("LN1", "FN1", "ADDR1", 1)); + people.push_back(Person("LN2", "FN2", "ADDR2", 2)); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + + std::vector result; + try { *_pSession << "SELECT * FROM Person", into(result), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (result == people); +} + + +void SQLExecutor::insertVector() +{ + std::string funct = "insertVector()"; + std::vector str; + str.push_back("s1"); + str.push_back("s2"); + str.push_back("s3"); + str.push_back("s3"); + int count = 100; + + { + Statement stmt((*_pSession << "INSERT INTO Strings VALUES (?)", use(str))); + try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 0); + + try { stmt.execute(); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 4); + } + count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 4); +} + + +void SQLExecutor::insertEmptyVector() +{ + std::string funct = "insertEmptyVector()"; + std::vector str; + + try + { + *_pSession << "INSERT INTO Strings VALUES (?)", use(str), now; + fail("empty collections should not work"); + } + catch (Poco::Exception&) + { + } +} + + +void SQLExecutor::insertSingleBulk() +{ + std::string funct = "insertSingleBulk()"; + int x = 0; + Statement stmt((*_pSession << "INSERT INTO Strings VALUES (?)", use(x))); + + for (x = 0; x < 100; ++x) + { + int i = stmt.execute(); + assert (i == 0); + } + + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 100); + + try { *_pSession << "SELECT SUM(str) FROM Strings", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == ((0+99)*100/2)); +} + + +void SQLExecutor::floats() +{ + std::string funct = "floats()"; + float data = 1.5f; + float ret = 0.0f; + + try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 1); + + try { *_pSession << "SELECT str FROM Strings", into(ret), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (ret == data); +} + + +void SQLExecutor::doubles() +{ + std::string funct = "floats()"; + double data = 1.5; + double ret = 0.0; + + try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 1); + + try { *_pSession << "SELECT str FROM Strings", into(ret), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (ret == data); +} + + +void SQLExecutor::insertSingleBulkVec() +{ + std::string funct = "insertSingleBulkVec()"; + std::vector data; + + for (int x = 0; x < 100; ++x) + data.push_back(x); + + Statement stmt((*_pSession << "INSERT INTO Strings VALUES (?)", use(data))); + stmt.execute(); + + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + assert (count == 100); + try { *_pSession << "SELECT SUM(str) FROM Strings", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == ((0+99)*100/2)); +} + + +void SQLExecutor::limits() +{ + std::string funct = "limit()"; + std::vector data; + for (int x = 0; x < 100; ++x) + { + data.push_back(x); + } + + try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + std::vector retData; + try { *_pSession << "SELECT * FROM Strings", into(retData), limit(50), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (retData.size() == 50); + for (int x = 0; x < 50; ++x) + { + assert(data[x] == retData[x]); + } +} + + +void SQLExecutor::limitZero() +{ + std::string funct = "limitZero()"; + std::vector data; + for (int x = 0; x < 100; ++x) + { + data.push_back(x); + } + + try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + std::vector retData; + try { *_pSession << "SELECT * FROM Strings", into(retData), limit(0), now; }// stupid test, but at least we shouldn't crash + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (retData.size() == 0); +} + + +void SQLExecutor::limitOnce() +{ + std::string funct = "limitOnce()"; + std::vector data; + for (int x = 0; x < 101; ++x) + { + data.push_back(x); + } + + try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + std::vector retData; + Statement stmt = (*_pSession << "SELECT * FROM Strings", into(retData), limit(50), now); + assert (!stmt.done()); + assert (retData.size() == 50); + stmt.execute(); + assert (!stmt.done()); + assert (retData.size() == 100); + stmt.execute(); + assert (stmt.done()); + assert (retData.size() == 101); + + for (int x = 0; x < 101; ++x) + { + assert(data[x] == retData[x]); + } +} + + +void SQLExecutor::limitPrepare() +{ + std::string funct = "limitPrepare()"; + std::vector data; + for (int x = 0; x < 100; ++x) + { + data.push_back(x); + } + + try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + std::vector retData; + Statement stmt = (*_pSession << "SELECT * FROM Strings", into(retData), limit(50)); + assert (retData.size() == 0); + assert (!stmt.done()); + + try { stmt.execute(); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (!stmt.done()); + assert (retData.size() == 50); + + try { stmt.execute(); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (stmt.done()); + assert (retData.size() == 100); + + try { stmt.execute(); }// will restart execution! + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (!stmt.done()); + assert (retData.size() == 150); + for (int x = 0; x < 150; ++x) + { + assert(data[x%100] == retData[x]); + } +} + + + +void SQLExecutor::prepare() +{ + std::string funct = "prepare()"; + std::vector data; + for (int x = 0; x < 100; x += 2) + { + data.push_back(x); + } + + { + Statement stmt((*_pSession << "INSERT INTO Strings VALUES (?)", use(data))); + } + // stmt should not have been executed when destroyed + int count = 100; + try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 0); +} + + +void SQLExecutor::setSimple() +{ + std::string funct = "setSimple()"; + std::set lastNames; + std::set firstNames; + std::set addresses; + std::set ages; + std::string tableName("Person"); + lastNames.insert("LN1"); + lastNames.insert("LN2"); + firstNames.insert("FN1"); + firstNames.insert("FN2"); + addresses.insert("ADDR1"); + addresses.insert("ADDR2"); + ages.insert(1); + ages.insert(2); + int count = 0; + std::string result; + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + + std::set lastNamesR; + std::set firstNamesR; + std::set addressesR; + std::set agesR; + try { *_pSession << "SELECT * FROM Person", into(lastNamesR), into(firstNamesR), into(addressesR), into(agesR), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (ages == agesR); + assert (lastNames == lastNamesR); + assert (firstNames == firstNamesR); + assert (addresses == addressesR); +} + + +void SQLExecutor::setComplex() +{ + std::string funct = "setComplex()"; + std::set people; + people.insert(Person("LN1", "FN1", "ADDR1", 1)); + people.insert(Person("LN2", "FN2", "ADDR2", 2)); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + + std::set result; + try { *_pSession << "SELECT * FROM Person", into(result), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (result == people); +} + + +void SQLExecutor::setComplexUnique() +{ + std::string funct = "setComplexUnique()"; + std::vector people; + Person p1("LN1", "FN1", "ADDR1", 1); + people.push_back(p1); + people.push_back(p1); + people.push_back(p1); + people.push_back(p1); + Person p2("LN2", "FN2", "ADDR2", 2); + people.push_back(p2); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 5); + + std::set result; + try { *_pSession << "SELECT * FROM Person", into(result), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (result.size() == 2); + assert (*result.begin() == p1); + assert (*++result.begin() == p2); +} + +void SQLExecutor::multiSetSimple() +{ + std::string funct = "multiSetSimple()"; + std::multiset lastNames; + std::multiset firstNames; + std::multiset addresses; + std::multiset ages; + std::string tableName("Person"); + lastNames.insert("LN1"); + lastNames.insert("LN2"); + firstNames.insert("FN1"); + firstNames.insert("FN2"); + addresses.insert("ADDR1"); + addresses.insert("ADDR2"); + ages.insert(1); + ages.insert(2); + int count = 0; + std::string result; + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + + std::multiset lastNamesR; + std::multiset firstNamesR; + std::multiset addressesR; + std::multiset agesR; + try { *_pSession << "SELECT * FROM Person", into(lastNamesR), into(firstNamesR), into(addressesR), into(agesR), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (ages.size() == agesR.size()); + assert (lastNames.size() == lastNamesR.size()); + assert (firstNames.size() == firstNamesR.size()); + assert (addresses.size() == addressesR.size()); +} + + +void SQLExecutor::multiSetComplex() +{ + std::string funct = "multiSetComplex()"; + std::multiset people; + Person p1("LN1", "FN1", "ADDR1", 1); + people.insert(p1); + people.insert(p1); + people.insert(p1); + people.insert(p1); + Person p2("LN2", "FN2", "ADDR2", 2); + people.insert(p2); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 5); + + std::multiset result; + try { *_pSession << "SELECT * FROM Person", into(result), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (result.size() == people.size()); +} + + +void SQLExecutor::mapComplex() +{ + std::string funct = "mapComplex()"; + std::map people; + Person p1("LN1", "FN1", "ADDR1", 1); + Person p2("LN2", "FN2", "ADDR2", 2); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN2", p2)); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + + std::map result; + try { *_pSession << "SELECT * FROM Person", into(result), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (result == people); +} + + +void SQLExecutor::mapComplexUnique() +{ + std::string funct = "mapComplexUnique()"; + std::multimap people; + Person p1("LN1", "FN1", "ADDR1", 1); + Person p2("LN2", "FN2", "ADDR2", 2); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN2", p2)); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 5); + + std::map result; + try { *_pSession << "SELECT * FROM Person", into(result), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (result.size() == 2); +} + + +void SQLExecutor::multiMapComplex() +{ + std::string funct = "multiMapComplex()"; + std::multimap people; + Person p1("LN1", "FN1", "ADDR1", 1); + Person p2("LN2", "FN2", "ADDR2", 2); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN2", p2)); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 5); + + std::multimap result; + try { *_pSession << "SELECT * FROM Person", into(result), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (result.size() == people.size()); +} + + +void SQLExecutor::selectIntoSingle() +{ + std::string funct = "selectIntoSingle()"; + std::multimap people; + Person p1("LN1", "FN1", "ADDR1", 1); + Person p2("LN2", "FN2", "ADDR2", 2); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p2)); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + Person result; + try { *_pSession << "SELECT * FROM Person", into(result), limit(1), now; }// will return 1 object into one single result + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (result == p1); +} + + +void SQLExecutor::selectIntoSingleStep() +{ + std::string funct = "selectIntoSingleStep()"; + std::multimap people; + Person p1("LN1", "FN1", "ADDR1", 1); + Person p2("LN2", "FN2", "ADDR2", 2); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p2)); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + Person result; + Statement stmt = (*_pSession << "SELECT * FROM Person", into(result), limit(1)); + stmt.execute(); + assert (result == p1); + assert (!stmt.done()); + stmt.execute(); + assert (result == p2); + assert (stmt.done()); +} + + +void SQLExecutor::selectIntoSingleFail() +{ + std::string funct = "selectIntoSingleFail()"; + std::multimap people; + Person p1("LN1", "FN1", "ADDR1", 1); + Person p2("LN2", "FN2", "ADDR2", 2); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p2)); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), limit(2, true), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + Person result; + try + { + *_pSession << "SELECT * FROM Person", into(result), limit(1, true), now; // will fail now + fail("hardLimit is set: must fail"); + } + catch(Poco::Data::LimitException&) + { + } +} + + +void SQLExecutor::lowerLimitOk() +{ + std::string funct = "lowerLimitOk()"; + std::multimap people; + Person p1("LN1", "FN1", "ADDR1", 1); + Person p2("LN2", "FN2", "ADDR2", 2); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p2)); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + Person result; + try + { + *_pSession << "SELECT * FROM Person", into(result), lowerLimit(2), now; // will return 2 objects into one single result but only room for one! + fail("Not enough space for results"); + } + catch(Poco::Exception&) + { + } +} + + +void SQLExecutor::singleSelect() +{ + std::string funct = "singleSelect()"; + std::multimap people; + Person p1("LN1", "FN1", "ADDR1", 1); + Person p2("LN2", "FN2", "ADDR2", 2); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p2)); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + Person result; + Statement stmt = (*_pSession << "SELECT * FROM Person", into(result), limit(1)); + stmt.execute(); + assert (result == p1); + assert (!stmt.done()); + stmt.execute(); + assert (result == p2); + assert (stmt.done()); +} + + +void SQLExecutor::lowerLimitFail() +{ + std::string funct = "lowerLimitFail()"; + std::multimap people; + Person p1("LN1", "FN1", "ADDR1", 1); + Person p2("LN2", "FN2", "ADDR2", 2); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p2)); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + Person result; + try + { + *_pSession << "SELECT * FROM Person", into(result), lowerLimit(3), now; // will fail + fail("should fail. not enough data"); + } + catch(Poco::Exception&) + { + } +} + + +void SQLExecutor::combinedLimits() +{ + std::string funct = "combinedLimits()"; + std::multimap people; + Person p1("LN1", "FN1", "ADDR1", 1); + Person p2("LN2", "FN2", "ADDR2", 2); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p2)); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + std::vector result; + try { *_pSession << "SELECT * FROM Person", into(result), lowerLimit(2), upperLimit(2), now; }// will return 2 objects + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (result.size() == 2); + assert (result[0] == p1); + assert (result[1] == p2); +} + + + +void SQLExecutor::ranges() +{ + std::string funct = "range()"; + std::multimap people; + Person p1("LN1", "FN1", "ADDR1", 1); + Person p2("LN2", "FN2", "ADDR2", 2); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p2)); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + std::vector result; + try { *_pSession << "SELECT * FROM Person", into(result), range(2, 2), now; }// will return 2 objects + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (result.size() == 2); + assert (result[0] == p1); + assert (result[1] == p2); +} + + +void SQLExecutor::combinedIllegalLimits() +{ + std::string funct = "combinedIllegalLimits()"; + std::multimap people; + Person p1("LN1", "FN1", "ADDR1", 1); + Person p2("LN2", "FN2", "ADDR2", 2); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p2)); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + Person result; + try + { + *_pSession << "SELECT * FROM Person", into(result), lowerLimit(3), upperLimit(2), now; + fail("lower > upper is not allowed"); + } + catch(LimitException&) + { + } +} + + +void SQLExecutor::illegalRange() +{ + std::string funct = "illegalRange()"; + std::multimap people; + Person p1("LN1", "FN1", "ADDR1", 1); + Person p2("LN2", "FN2", "ADDR2", 2); + people.insert(std::make_pair("LN1", p1)); + people.insert(std::make_pair("LN1", p2)); + + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 2); + Person result; + try + { + *_pSession << "SELECT * FROM Person", into(result), range(3, 2), now; + fail("lower > upper is not allowed"); + } + catch(LimitException&) + { + } +} + + +void SQLExecutor::emptyDB() +{ + std::string funct = "emptyDB()"; + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 0); + + Person result; + Statement stmt = (*_pSession << "SELECT * FROM Person", into(result), limit(1)); + stmt.execute(); + assert (result.firstName.empty()); + assert (stmt.done()); +} + + +void SQLExecutor::blob(int bigSize) +{ + std::string funct = "blob()"; + std::string lastName("lastname"); + std::string firstName("firstname"); + std::string address("Address"); + + Poco::Data::CLOB img("0123456789", 10); + int count = 0; + try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(img), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 1); + + Poco::Data::CLOB res; + assert (res.size() == 0); + try { *_pSession << "SELECT Image FROM Person", into(res), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (res == img); + + Poco::Data::CLOB big; + std::vector v(bigSize, 'x'); + big.assignRaw(&v[0], v.size()); + + assert (big.size() == (size_t)bigSize); + + try { *_pSession << "DELETE FROM Person", now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + try { *_pSession << "INSERT INTO Person VALUES(?,?,?,?)", use(lastName), use(firstName), use(address), use(big), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + try { *_pSession << "SELECT Image FROM Person", into(res), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (res == big); +} + +void SQLExecutor::blobStmt() +{ + std::string funct = "blobStmt()"; + std::string lastName("lastname"); + std::string firstName("firstname"); + std::string address("Address"); + Poco::Data::CLOB blob("0123456789", 10); + + int count = 0; + Statement ins = (*_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(blob)); + ins.execute(); + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 1); + + Poco::Data::CLOB res; + poco_assert (res.size() == 0); + Statement stmt = (*_pSession << "SELECT Image FROM Person", into(res)); + try { stmt.execute(); } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + poco_assert (res == blob); +} + +void SQLExecutor::tuples() +{ + typedef Tuple TupleType; + std::string funct = "tuples()"; + TupleType t(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19); + + try { *_pSession << "INSERT INTO Tuples VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", use(t), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + TupleType ret(-10,-11,-12,-13,-14,-15,-16,-17,-18,-19); + assert (ret != t); + try { *_pSession << "SELECT * FROM Tuples", into(ret), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (ret == t); +} + +void SQLExecutor::tupleVector() +{ + typedef Tuple TupleType; + std::string funct = "tupleVector()"; + TupleType t(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19); + Tuple + t10(10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29); + TupleType t100(100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119); + std::vector v; + v.push_back(t); + v.push_back(t10); + v.push_back(t100); + + try { *_pSession << "INSERT INTO Tuples VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", use(v), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Tuples", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (v.size() == (size_t)count); + + std::vector > ret; + try { *_pSession << "SELECT * FROM Tuples", into(ret), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (ret == v); +} + + +void SQLExecutor::internalExtraction() +{ + /*std::string funct = "internalExtraction()"; + std::vector > v; + v.push_back(Tuple(1, 1.5f, "3")); + v.push_back(Tuple(2, 2.5f, "4")); + v.push_back(Tuple(3, 3.5f, "5")); + v.push_back(Tuple(4, 4.5f, "6")); + + try { *_pSession << "INSERT INTO Vectors VALUES (?,?,?)", use(v), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + try + { + Statement stmt = (*_pSession << "SELECT * FROM Vectors", now); + RecordSet rset(stmt); + + assert (3 == rset.columnCount()); + assert (4 == rset.rowCount()); + + int curVal = 3; + do + { + assert (rset["str0"] == curVal); + ++curVal; + } while (rset.moveNext()); + + rset.moveFirst(); + assert (rset["str0"] == "3"); + rset.moveLast(); + assert (rset["str0"] == "6"); + + RecordSet rset2(rset); + assert (3 == rset2.columnCount()); + assert (4 == rset2.rowCount()); + + int i = rset.value(0,0); + assert (1 == i); + + std::string s = rset.value(0,0); + assert ("1" == s); + + int a = rset.value(0,2); + assert (3 == a); + + try + { + double d = rset.value(1,1); + assert (2.5 == d); + } + catch (BadCastException&) + { + float f = rset.value(1,1); + assert (2.5 == f); + } + + s = rset.value(2,2); + assert ("5" == s); + i = rset.value("str0", 2); + assert (5 == i); + + const Column& col = rset.column(0); + Column::Iterator it = col.begin(); + Column::Iterator end = col.end(); + for (int i = 1; it != end; ++it, ++i) + assert (*it == i); + + rset = (*_pSession << "SELECT COUNT(*) AS cnt FROM Vectors", now); + + //various results for COUNT(*) are received from different drivers + try + { + //this is what most drivers will return + int i = rset.value(0,0); + assert (4 == i); + } + catch(BadCastException&) + { + try + { + //this is for Oracle + double i = rset.value(0,0); + assert (4 == int(i)); + } + catch(BadCastException&) + { + //this is for PostgreSQL + Poco::Int64 big = rset.value(0,0); + assert (4 == big); + } + } + + s = rset.value("cnt", 0).convert(); + assert ("4" == s); + + try { const Column& col1 = rset.column(100); fail ("must fail"); } + catch (RangeException&) { } + + try { rset.value(0,0); fail ("must fail"); } + catch (BadCastException&) { } + + stmt = (*_pSession << "DELETE FROM Vectors", now); + rset = stmt; + + try { const Column& col1 = rset.column(0); fail ("must fail"); } + catch (RangeException&) { } + } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } +*/ +} + + +void SQLExecutor::doNull() +{ + std::string funct = "null()"; + + *_pSession << "INSERT INTO Vectors VALUES (?, ?, ?)", + use(Poco::Data::Keywords::null), + use(Poco::Data::Keywords::null), + use(Poco::Data::Keywords::null), now; + + int count = 0; + try { *_pSession << "SELECT COUNT(*) FROM Vectors", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (count == 1); + + int i0 = 0; + Statement stmt1 = (*_pSession << "SELECT i0 FROM Vectors", into(i0, Poco::Data::Position(0), -1)); + try { stmt1.execute(); } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + poco_assert (i0 == -1); + + float flt0 = 0; + Statement stmt2 = (*_pSession << "SELECT flt0 FROM Vectors", into(flt0, Poco::Data::Position(0), 3.25f)); + try { stmt2.execute(); } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + poco_assert (flt0 == 3.25); + + std::string str0("string"); + Statement stmt3 = (*_pSession << "SELECT str0 FROM Vectors", into(str0, Poco::Data::Position(0), std::string("DEFAULT"))); + try { stmt3.execute(); } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + poco_assert (str0 == "DEFAULT"); +} + + +void SQLExecutor::setTransactionIsolation(Session& session, Poco::UInt32 ti) +{ + if (session.hasTransactionIsolation(ti)) + { + std::string funct = "setTransactionIsolation()"; + + try + { + Transaction t(session, false); + t.setIsolation(ti); + + assert (ti == t.getIsolation()); + assert (t.isIsolation(ti)); + + assert (ti == session.getTransactionIsolation()); + assert (session.isTransactionIsolation(ti)); + } + catch(Poco::Exception& e){ std::cout << funct << ':' << e.displayText() << std::endl;} + } + else + { + std::cout << "Transaction isolation not supported: "; + switch (ti) + { + case Session::TRANSACTION_READ_COMMITTED: + std::cout << "READ COMMITTED"; break; + case Session::TRANSACTION_READ_UNCOMMITTED: + std::cout << "READ UNCOMMITTED"; break; + case Session::TRANSACTION_REPEATABLE_READ: + std::cout << "REPEATABLE READ"; break; + case Session::TRANSACTION_SERIALIZABLE: + std::cout << "SERIALIZABLE"; break; + default: + std::cout << "UNKNOWN"; break; + } + std::cout << std::endl; + } +} + + +void SQLExecutor::sessionTransaction(const std::string& connect) +{ + if (!_pSession->canTransact()) + { + std::cout << "Session not capable of transactions." << std::endl; + return; + } + + Session local("mysql", connect); + local.setFeature("autoCommit", true); + + std::string funct = "transaction()"; + std::vector lastNames; + std::vector firstNames; + std::vector addresses; + std::vector ages; + std::string tableName("Person"); + lastNames.push_back("LN1"); + lastNames.push_back("LN2"); + firstNames.push_back("FN1"); + firstNames.push_back("FN2"); + addresses.push_back("ADDR1"); + addresses.push_back("ADDR2"); + ages.push_back(1); + ages.push_back(2); + int count = 0, locCount = 0; + std::string result; + + bool autoCommit = _pSession->getFeature("autoCommit"); + + _pSession->setFeature("autoCommit", true); + assert (!_pSession->isTransaction()); + _pSession->setFeature("autoCommit", false); + assert (!_pSession->isTransaction()); + + setTransactionIsolation((*_pSession), Session::TRANSACTION_READ_UNCOMMITTED); + setTransactionIsolation((*_pSession), Session::TRANSACTION_REPEATABLE_READ); + setTransactionIsolation((*_pSession), Session::TRANSACTION_SERIALIZABLE); + + setTransactionIsolation((*_pSession), Session::TRANSACTION_READ_COMMITTED); + + _pSession->begin(); + assert (_pSession->isTransaction()); + try { (*_pSession) << "INSERT INTO PERSON VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (_pSession->isTransaction()); + + local << "SELECT COUNT(*) FROM PERSON", into(locCount), now; + assert (0 == locCount); + + try { (*_pSession) << "SELECT COUNT(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (2 == count); + assert (_pSession->isTransaction()); + _pSession->rollback(); + assert (!_pSession->isTransaction()); + + local << "SELECT COUNT(*) FROM PERSON", into(locCount), now; + assert (0 == locCount); + + try { (*_pSession) << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (0 == count); + assert (!_pSession->isTransaction()); + + _pSession->begin(); + try { (*_pSession) << "INSERT INTO PERSON VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (_pSession->isTransaction()); + + local << "SELECT COUNT(*) FROM PERSON", into(locCount), now; + assert (0 == locCount); + + _pSession->commit(); + assert (!_pSession->isTransaction()); + + local << "SELECT COUNT(*) FROM PERSON", into(locCount), now; + assert (2 == locCount); + + try { (*_pSession) << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (2 == count); + + _pSession->setFeature("autoCommit", autoCommit); +} + + +void SQLExecutor::transaction(const std::string& connect) +{ + if (!_pSession->canTransact()) + { + std::cout << "Session not transaction-capable." << std::endl; + return; + } + + Session local("mysql", connect); + local.setFeature("autoCommit", true); + + setTransactionIsolation((*_pSession), Session::TRANSACTION_READ_COMMITTED); + setTransactionIsolation(local, Session::TRANSACTION_READ_COMMITTED); + + std::string funct = "transaction()"; + std::vector lastNames; + std::vector firstNames; + std::vector addresses; + std::vector ages; + std::string tableName("Person"); + lastNames.push_back("LN1"); + lastNames.push_back("LN2"); + firstNames.push_back("FN1"); + firstNames.push_back("FN2"); + addresses.push_back("ADDR1"); + addresses.push_back("ADDR2"); + ages.push_back(1); + ages.push_back(2); + int count = 0, locCount = 0; + std::string result; + + bool autoCommit = _pSession->getFeature("autoCommit"); + + _pSession->setFeature("autoCommit", true); + assert (!_pSession->isTransaction()); + _pSession->setFeature("autoCommit", false); + assert (!_pSession->isTransaction()); + _pSession->setTransactionIsolation(Session::TRANSACTION_READ_COMMITTED); + + { + Transaction trans((*_pSession)); + assert (trans.isActive()); + assert (_pSession->isTransaction()); + + try { (*_pSession) << "INSERT INTO PERSON VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + assert (_pSession->isTransaction()); + assert (trans.isActive()); + + try { (*_pSession) << "SELECT COUNT(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (2 == count); + assert (_pSession->isTransaction()); + assert (trans.isActive()); + } + assert (!_pSession->isTransaction()); + + try { (*_pSession) << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (0 == count); + assert (!_pSession->isTransaction()); + + { + Transaction trans((*_pSession)); + try { (*_pSession) << "INSERT INTO PERSON VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + local << "SELECT COUNT(*) FROM PERSON", into(locCount), now; + assert (0 == locCount); + + assert (_pSession->isTransaction()); + assert (trans.isActive()); + trans.commit(); + assert (!_pSession->isTransaction()); + assert (!trans.isActive()); + local << "SELECT COUNT(*) FROM PERSON", into(locCount), now; + assert (2 == locCount); + } + + try { (*_pSession) << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (2 == count); + + _pSession->begin(); + try { (*_pSession) << "DELETE FROM PERSON", now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + + local << "SELECT COUNT(*) FROM PERSON", into(locCount), now; + assert (2 == locCount); + + try { (*_pSession) << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (0 == count); + _pSession->commit(); + + local << "SELECT COUNT(*) FROM PERSON", into(locCount), now; + assert (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); + + Transaction trans((*_pSession)); + + trans.execute(sql1, false); + try { (*_pSession) << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (1 == count); + trans.execute(sql2, false); + try { (*_pSession) << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (2 == count); + + local << "SELECT COUNT(*) FROM PERSON", into(locCount), now; + assert (0 == locCount); + + trans.rollback(); + + local << "SELECT COUNT(*) FROM PERSON", into(locCount), now; + assert (0 == locCount); + + try { (*_pSession) << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (0 == count); + + trans.execute(sql); + + local << "SELECT COUNT(*) FROM PERSON", into(locCount), now; + assert (2 == locCount); + + try { (*_pSession) << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assert (2 == count); + + _pSession->setFeature("autoCommit", autoCommit); +} diff --git a/Data/MySQL/testsuite/src/SQLExecutor.h b/Data/MySQL/testsuite/src/SQLExecutor.h index 22449b803..a8cbfc977 100644 --- a/Data/MySQL/testsuite/src/SQLExecutor.h +++ b/Data/MySQL/testsuite/src/SQLExecutor.h @@ -110,7 +110,12 @@ public: void internalExtraction(); void doNull(); + void sessionTransaction(const std::string& connect); + void transaction(const std::string& connect); + private: + void setTransactionIsolation(Poco::Data::Session& session, Poco::UInt32 ti); + Poco::Data::Session* _pSession; }; diff --git a/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h b/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h index fd3269794..fceb742fe 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h +++ b/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h @@ -47,7 +47,8 @@ #include "Poco/Data/ODBC/Handle.h" #include "Poco/Data/ODBC/ODBCException.h" #include "Poco/Data/AbstractSessionImpl.h" -#include "Poco/SharedPtr.h" +#include "Poco/SharedPtr.h" +#include "Poco/Mutex.h" #ifdef POCO_OS_FAMILY_WINDOWS #include #endif @@ -63,6 +64,13 @@ class ODBC_API SessionImpl: public Poco::Data::AbstractSessionImpl /// Implements SessionImpl interface { public: + enum TransactionCapability + { + ODBC_TXN_CAPABILITY_UNKNOWN = -1, + ODBC_TXN_CAPABILITY_FALSE = 0, + ODBC_TXN_CAPABILITY_TRUE = 1 + }; + SessionImpl(const std::string& connect, Poco::Any maxFieldSize = std::size_t(1024), bool enforceCapability=false, @@ -100,6 +108,20 @@ public: bool canTransact(); /// Returns true if connection is transaction-capable. + void setTransactionIsolation(Poco::UInt32 ti); + /// Sets the transaction isolation level. + + Poco::UInt32 getTransactionIsolation(); + /// Returns the transaction isolation level. + + bool hasTransactionIsolation(Poco::UInt32); + /// Returns true iff the transaction isolation level corresponding + /// to the supplied bitmask is supported. + + bool isTransactionIsolation(Poco::UInt32); + /// Returns true iff the transaction isolation level corresponds + /// to the supplied bitmask. + void autoCommit(const std::string&, bool val); /// Sets autocommit property for the session. @@ -144,12 +166,19 @@ private: void checkError(SQLRETURN rc, const std::string& msg=""); + Poco::UInt32 getDefaultTransactionIsolation(); + + Poco::UInt32 transactionIsolation(SQLUINTEGER isolation); + std::string _connector; const ConnectionHandle _db; Poco::Any _maxFieldSize; bool _autoBind; bool _autoExtract; TypeInfo _dataTypes; + char _canTransact; + bool _inTransaction; + Poco::FastMutex _mutex; }; @@ -169,20 +198,6 @@ inline const ConnectionHandle& SessionImpl::dbc() const } -inline void SessionImpl::commit() -{ - if (!isAutoCommit()) - checkError(SQLEndTran(SQL_HANDLE_DBC, _db, SQL_COMMIT)); -} - - -inline void SessionImpl::rollback() -{ - if (!isAutoCommit()) - checkError(SQLEndTran(SQL_HANDLE_DBC, _db, SQL_ROLLBACK)); -} - - inline void SessionImpl::setMaxFieldSize(const std::string& rName, const Poco::Any& rValue) { _maxFieldSize = rValue; @@ -237,6 +252,12 @@ inline const std::string& SessionImpl::connectorName() } +inline bool SessionImpl::isTransactionIsolation(Poco::UInt32 ti) +{ + return 0 != (ti & getTransactionIsolation()); +} + + } } } // namespace Poco::Data::ODBC diff --git a/Data/ODBC/src/SessionImpl.cpp b/Data/ODBC/src/SessionImpl.cpp index 268eae5c7..f618422cf 100644 --- a/Data/ODBC/src/SessionImpl.cpp +++ b/Data/ODBC/src/SessionImpl.cpp @@ -39,6 +39,7 @@ #include "Poco/Data/ODBC/ODBCStatementImpl.h" #include "Poco/Data/ODBC/Error.h" #include "Poco/Data/ODBC/ODBCException.h" +#include "Poco/Data/Session.h" #include "Poco/String.h" #include @@ -56,7 +57,9 @@ SessionImpl::SessionImpl(const std::string& connect, _connector(toLower(Connector::KEY)), _maxFieldSize(maxFieldSize), _autoBind(autoBind), - _autoExtract(autoExtract) + _autoExtract(autoExtract), + _canTransact(ODBC_TXN_CAPABILITY_UNKNOWN), + _inTransaction(false) { setFeature("bulk", true); open(); @@ -65,7 +68,14 @@ SessionImpl::SessionImpl(const std::string& connect, SessionImpl::~SessionImpl() { - close(); + if (isTransaction() && !getFeature("autoCommit")) + { + try { rollback(); } + catch (...) { } + } + + try { close(); } + catch (...) { } } @@ -75,11 +85,6 @@ Poco::Data::StatementImpl* SessionImpl::createStatementImpl() } -void SessionImpl::begin() -{ -} - - void SessionImpl::open() { SQLCHAR connectOutput[512] = {0}; @@ -128,11 +133,101 @@ void SessionImpl::open() bool SessionImpl::canTransact() { - SQLUSMALLINT ret; - checkError(Poco::Data::ODBC::SQLGetInfo(_db, SQL_TXN_CAPABLE, &ret, 0, 0), - "Failed to obtain transaction capability info."); + if (ODBC_TXN_CAPABILITY_UNKNOWN == _canTransact) + { + SQLUSMALLINT ret; + checkError(Poco::Data::ODBC::SQLGetInfo(_db, SQL_TXN_CAPABLE, &ret, 0, 0), + "Failed to obtain transaction capability info."); - return (SQL_TC_NONE != ret); + _canTransact = (SQL_TC_NONE != ret) ? + ODBC_TXN_CAPABILITY_TRUE : + ODBC_TXN_CAPABILITY_FALSE; + } + + return ODBC_TXN_CAPABILITY_TRUE == _canTransact; +} + + +void SessionImpl::setTransactionIsolation(Poco::UInt32 ti) +{ + Poco::UInt32 isolation = 0; + + if (ti & Session::TRANSACTION_READ_UNCOMMITTED) + isolation |= SQL_TXN_READ_UNCOMMITTED; + + if (ti & Session::TRANSACTION_READ_COMMITTED) + isolation |= SQL_TXN_READ_COMMITTED; + + if (ti & Session::TRANSACTION_REPEATABLE_READ) + isolation |= SQL_TXN_REPEATABLE_READ; + + if (ti & Session::TRANSACTION_SERIALIZABLE) + isolation |= SQL_TXN_SERIALIZABLE; + + checkError(SQLSetConnectAttr(_db, SQL_ATTR_TXN_ISOLATION, (SQLPOINTER) isolation, 0)); +} + + +Poco::UInt32 SessionImpl::getTransactionIsolation() +{ + SQLUINTEGER isolation = 0; + checkError(SQLGetConnectAttr(_db, SQL_ATTR_TXN_ISOLATION, + &isolation, + 0, + 0)); + + return transactionIsolation(isolation); +} + + +bool SessionImpl::hasTransactionIsolation(Poco::UInt32 ti) +{ + if (isTransaction()) throw InvalidAccessException(); + + bool retval = true; + Poco::UInt32 old = getTransactionIsolation(); + try { setTransactionIsolation(ti); } + catch (Poco::Exception&) { retval = false; } + setTransactionIsolation(old); + return retval; +} + + +Poco::UInt32 SessionImpl::getDefaultTransactionIsolation() +{ + SQLUINTEGER isolation = 0; + checkError(SQLGetInfo(_db, SQL_DEFAULT_TXN_ISOLATION, + &isolation, + 0, + 0)); + + return transactionIsolation(isolation); +} + + +Poco::UInt32 SessionImpl::transactionIsolation(SQLUINTEGER isolation) +{ + if (0 == isolation) + throw InvalidArgumentException("transactionIsolation(SQLUINTEGER)"); + + Poco::UInt32 ret = 0; + + if (isolation & SQL_TXN_READ_UNCOMMITTED) + ret |= Session::TRANSACTION_READ_UNCOMMITTED; + + if (isolation & SQL_TXN_READ_COMMITTED) + ret |= Session::TRANSACTION_READ_COMMITTED; + + if (isolation & SQL_TXN_REPEATABLE_READ) + ret |= Session::TRANSACTION_REPEATABLE_READ; + + if (isolation & SQL_TXN_SERIALIZABLE) + ret |= Session::TRANSACTION_SERIALIZABLE; + + if (0 == ret) + throw InvalidArgumentException("transactionIsolation(SQLUINTEGER)"); + + return ret; } @@ -171,21 +266,57 @@ bool SessionImpl::isConnected() 0))) return false; - return (0 == value); + return (SQL_CD_FALSE == value); } bool SessionImpl::isTransaction() { - Poco::UInt32 value = 0; + if (!canTransact()) return false; + Poco::UInt32 value = 0; checkError(Poco::Data::ODBC::SQLGetConnectAttr(_db, SQL_ATTR_AUTOCOMMIT, &value, 0, 0)); - return (0 == value); + if (0 == value) return _inTransaction; + else return false; +} + + +void SessionImpl::begin() +{ + if (isAutoCommit()) + throw InvalidAccessException("Session in auto commit mode."); + + { + Poco::FastMutex::ScopedLock l(_mutex); + + if (_inTransaction) + throw InvalidAccessException("Transaction in progress."); + + _inTransaction = true; + } +} + + +void SessionImpl::commit() +{ + if (!isAutoCommit()) + checkError(SQLEndTran(SQL_HANDLE_DBC, _db, SQL_COMMIT)); + + _inTransaction = false; +} + + +void SessionImpl::rollback() +{ + if (!isAutoCommit()) + checkError(SQLEndTran(SQL_HANDLE_DBC, _db, SQL_ROLLBACK)); + + _inTransaction = false; } diff --git a/Data/ODBC/testsuite/src/ODBCDB2Test.cpp b/Data/ODBC/testsuite/src/ODBCDB2Test.cpp index 347f4acdb..ac2994779 100644 --- a/Data/ODBC/testsuite/src/ODBCDB2Test.cpp +++ b/Data/ODBC/testsuite/src/ODBCDB2Test.cpp @@ -678,6 +678,9 @@ CppUnit::Test* ODBCDB2Test::suite() CppUnit_addTest(pSuite, ODBCDB2Test, testMultipleResults); CppUnit_addTest(pSuite, ODBCDB2Test, testSQLChannel); CppUnit_addTest(pSuite, ODBCDB2Test, testSQLLogger); + CppUnit_addTest(pSuite, ODBCDB2Test, testSessionTransaction); + CppUnit_addTest(pSuite, ODBCDB2Test, testTransaction); + CppUnit_addTest(pSuite, ODBCDB2Test, testTransactor); return pSuite; } diff --git a/Data/ODBC/testsuite/src/ODBCMySQLTest.cpp b/Data/ODBC/testsuite/src/ODBCMySQLTest.cpp index 2f432d209..dc33929bd 100644 --- a/Data/ODBC/testsuite/src/ODBCMySQLTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCMySQLTest.cpp @@ -494,6 +494,9 @@ CppUnit::Test* ODBCMySQLTest::suite() CppUnit_addTest(pSuite, ODBCMySQLTest, testMultipleResults); CppUnit_addTest(pSuite, ODBCMySQLTest, testSQLChannel); CppUnit_addTest(pSuite, ODBCMySQLTest, testSQLLogger); + CppUnit_addTest(pSuite, ODBCMySQLTest, testSessionTransaction); + CppUnit_addTest(pSuite, ODBCMySQLTest, testTransaction); + CppUnit_addTest(pSuite, ODBCMySQLTest, testTransactor); return pSuite; } diff --git a/Data/ODBC/testsuite/src/ODBCOracleTest.cpp b/Data/ODBC/testsuite/src/ODBCOracleTest.cpp index 64f8906cd..43a27fbb9 100644 --- a/Data/ODBC/testsuite/src/ODBCOracleTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCOracleTest.cpp @@ -919,6 +919,9 @@ CppUnit::Test* ODBCOracleTest::suite() CppUnit_addTest(pSuite, ODBCOracleTest, testSQLChannel); CppUnit_addTest(pSuite, ODBCOracleTest, testSQLLogger); CppUnit_addTest(pSuite, ODBCOracleTest, testAutoTransaction); + CppUnit_addTest(pSuite, ODBCOracleTest, testSessionTransaction); + CppUnit_addTest(pSuite, ODBCOracleTest, testTransaction); + CppUnit_addTest(pSuite, ODBCOracleTest, testTransactor); return pSuite; } diff --git a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp index 9fda63066..0b942ee14 100644 --- a/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp @@ -656,6 +656,9 @@ CppUnit::Test* ODBCPostgreSQLTest::suite() //CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testMultipleResults); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testSQLChannel); CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testSQLLogger); + CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testSessionTransaction); + CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testTransaction); + CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testTransactor); return pSuite; } diff --git a/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp b/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp index ae27bf5b1..28d577b0b 100644 --- a/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp @@ -806,6 +806,9 @@ CppUnit::Test* ODBCSQLServerTest::suite() CppUnit_addTest(pSuite, ODBCSQLServerTest, testMultipleResults); CppUnit_addTest(pSuite, ODBCSQLServerTest, testSQLChannel); CppUnit_addTest(pSuite, ODBCSQLServerTest, testSQLLogger); + CppUnit_addTest(pSuite, ODBCSQLServerTest, testSessionTransaction); + CppUnit_addTest(pSuite, ODBCSQLServerTest, testTransaction); + CppUnit_addTest(pSuite, ODBCSQLServerTest, testTransactor); return pSuite; } diff --git a/Data/ODBC/testsuite/src/ODBCSQLiteTest.cpp b/Data/ODBC/testsuite/src/ODBCSQLiteTest.cpp index 692ebd090..a4b2c56f4 100644 --- a/Data/ODBC/testsuite/src/ODBCSQLiteTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCSQLiteTest.cpp @@ -397,6 +397,9 @@ CppUnit::Test* ODBCSQLiteTest::suite() CppUnit_addTest(pSuite, ODBCSQLiteTest, testDynamicAny); CppUnit_addTest(pSuite, ODBCSQLiteTest, testSQLChannel); CppUnit_addTest(pSuite, ODBCSQLiteTest, testSQLLogger); + CppUnit_addTest(pSuite, ODBCSQLiteTest, testSessionTransaction); + CppUnit_addTest(pSuite, ODBCSQLiteTest, testTransaction); + CppUnit_addTest(pSuite, ODBCSQLiteTest, testTransactor); return pSuite; } diff --git a/Data/ODBC/testsuite/src/ODBCTest.cpp b/Data/ODBC/testsuite/src/ODBCTest.cpp index 8a8b5f953..bb38edcda 100644 --- a/Data/ODBC/testsuite/src/ODBCTest.cpp +++ b/Data/ODBC/testsuite/src/ODBCTest.cpp @@ -88,6 +88,7 @@ ODBCTest::ODBCTest(const std::string& name, _rPwd(rPwd), _rConnectString(rConnectString) { + _pSession->setFeature("autoCommit", true); } @@ -1141,6 +1142,51 @@ void ODBCTest::testSQLLogger() } +void ODBCTest::testSessionTransaction() +{ + if (!_pSession) fail ("Test not available."); + + for (int i = 0; i < 8;) + { + recreatePersonTable(); + _pSession->setFeature("autoBind", bindValue(i)); + _pSession->setFeature("autoExtract", bindValue(i+1)); + _pExecutor->sessionTransaction(_rConnectString); + i += 2; + } +} + + +void ODBCTest::testTransaction() +{ + if (!_pSession) fail ("Test not available."); + + for (int i = 0; i < 8;) + { + recreatePersonTable(); + _pSession->setFeature("autoBind", bindValue(i)); + _pSession->setFeature("autoExtract", bindValue(i+1)); + _pExecutor->transaction(_rConnectString); + i += 2; + } +} + + +void ODBCTest::testTransactor() +{ + if (!_pSession) fail ("Test not available."); + + for (int i = 0; i < 8;) + { + recreatePersonTable(); + _pSession->setFeature("autoBind", bindValue(i)); + _pSession->setFeature("autoExtract", bindValue(i+1)); + _pExecutor->transactor(); + i += 2; + } +} + + bool ODBCTest::canConnect(const std::string& driver, std::string& dsn, std::string& uid, diff --git a/Data/ODBC/testsuite/src/ODBCTest.h b/Data/ODBC/testsuite/src/ODBCTest.h index a04c08e84..ff33c5fba 100644 --- a/Data/ODBC/testsuite/src/ODBCTest.h +++ b/Data/ODBC/testsuite/src/ODBCTest.h @@ -161,9 +161,12 @@ public: virtual void testMultipleResults(); virtual void testSQLChannel(); - virtual void testSQLLogger(); + virtual void testSessionTransaction(); + virtual void testTransaction(); + virtual void testTransactor(); + protected: typedef Poco::Data::ODBC::Utility::DriverMap Drivers; diff --git a/Data/ODBC/testsuite/src/SQLExecutor.cpp b/Data/ODBC/testsuite/src/SQLExecutor.cpp index 19c7cf253..d3b74444b 100644 --- a/Data/ODBC/testsuite/src/SQLExecutor.cpp +++ b/Data/ODBC/testsuite/src/SQLExecutor.cpp @@ -57,6 +57,7 @@ #include "Poco/Data/BulkExtraction.h" #include "Poco/Data/BulkBinding.h" #include "Poco/Data/SQLChannel.h" +#include "Poco/Data/Transaction.h" #include "Poco/Data/ODBC/Connector.h" #include "Poco/Data/ODBC/Utility.h" #include "Poco/Data/ODBC/Diagnostics.h" @@ -83,6 +84,7 @@ using Poco::Data::BindingException; using Poco::Data::CLOB; using Poco::Data::Date; using Poco::Data::Time; +using Poco::Data::Transaction; using Poco::Data::ODBC::Utility; using Poco::Data::ODBC::Preparator; using Poco::Data::ODBC::ConnectionException; @@ -3376,3 +3378,329 @@ void SQLExecutor::sqlLogger(const std::string& connect) catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("sqlChannel()"); } catch(StatementException& se){ std::cout << se.toString() << std::endl; fail ("sqlChannel()"); } } + + +void SQLExecutor::setTransactionIsolation(Session& session, Poco::UInt32 ti) +{ + if (session.hasTransactionIsolation(ti)) + { + std::string funct = "setTransactionIsolation()"; + + try + { + Transaction t(session, false); + t.setIsolation(ti); + + assert (ti == t.getIsolation()); + assert (t.isIsolation(ti)); + + assert (ti == session.getTransactionIsolation()); + assert (session.isTransactionIsolation(ti)); + } + catch(Poco::Exception& e){ std::cout << funct << ':' << e.displayText() << std::endl;} + } + else + { + std::cout << "Transaction isolation not supported: "; + switch (ti) + { + case Session::TRANSACTION_READ_COMMITTED: + std::cout << "READ COMMITTED"; break; + case Session::TRANSACTION_READ_UNCOMMITTED: + std::cout << "READ UNCOMMITTED"; break; + case Session::TRANSACTION_REPEATABLE_READ: + std::cout << "REPEATABLE READ"; break; + case Session::TRANSACTION_SERIALIZABLE: + std::cout << "SERIALIZABLE"; break; + default: + std::cout << "UNKNOWN"; break; + } + std::cout << std::endl; + } +} + + +void SQLExecutor::sessionTransaction(const std::string& connect) +{ + if (!session().canTransact()) + { + std::cout << "Session not capable of transactions." << std::endl; + return; + } + + Session local("odbc", connect); + local.setFeature("autoCommit", true); + + std::string funct = "transaction()"; + std::vector lastNames; + std::vector firstNames; + std::vector addresses; + std::vector ages; + std::string tableName("Person"); + lastNames.push_back("LN1"); + lastNames.push_back("LN2"); + firstNames.push_back("FN1"); + firstNames.push_back("FN2"); + addresses.push_back("ADDR1"); + addresses.push_back("ADDR2"); + ages.push_back(1); + ages.push_back(2); + int count = 0, locCount = 0; + std::string result; + + bool autoCommit = session().getFeature("autoCommit"); + + session().setFeature("autoCommit", true); + assert (!session().isTransaction()); + session().setFeature("autoCommit", false); + assert (!session().isTransaction()); + + setTransactionIsolation(session(), Session::TRANSACTION_READ_UNCOMMITTED); + setTransactionIsolation(session(), Session::TRANSACTION_REPEATABLE_READ); + setTransactionIsolation(session(), Session::TRANSACTION_SERIALIZABLE); + + setTransactionIsolation(session(), Session::TRANSACTION_READ_COMMITTED); + + session().begin(); + assert (session().isTransaction()); + try { session() << "INSERT INTO PERSON VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + assert (session().isTransaction()); + + Statement stmt = (local << "SELECT COUNT(*) FROM PERSON", into(locCount), async, now); + + try { session() << "SELECT COUNT(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + assert (2 == count); + assert (session().isTransaction()); + session().rollback(); + assert (!session().isTransaction()); + + stmt.wait(); + assert (0 == locCount); + + try { session() << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + assert (0 == count); + assert (!session().isTransaction()); + + session().begin(); + try { session() << "INSERT INTO PERSON VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + assert (session().isTransaction()); + + Statement stmt1 = (local << "SELECT COUNT(*) FROM PERSON", into(locCount), async, now); + + session().commit(); + assert (!session().isTransaction()); + + stmt1.wait(); + assert (2 == locCount); + + try { session() << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + assert (2 == count); + + session().setFeature("autoCommit", autoCommit); +} + + +void SQLExecutor::transaction(const std::string& connect) +{ + if (!session().canTransact()) + { + std::cout << "Session not transaction-capable." << std::endl; + return; + } + + Session local("odbc", connect); + local.setFeature("autoCommit", true); + + setTransactionIsolation(session(), Session::TRANSACTION_READ_COMMITTED); + setTransactionIsolation(local, Session::TRANSACTION_READ_COMMITTED); + + std::string funct = "transaction()"; + std::vector lastNames; + std::vector firstNames; + std::vector addresses; + std::vector ages; + std::string tableName("Person"); + lastNames.push_back("LN1"); + lastNames.push_back("LN2"); + firstNames.push_back("FN1"); + firstNames.push_back("FN2"); + addresses.push_back("ADDR1"); + addresses.push_back("ADDR2"); + ages.push_back(1); + ages.push_back(2); + int count = 0, locCount = 0; + std::string result; + + bool autoCommit = session().getFeature("autoCommit"); + + session().setFeature("autoCommit", true); + assert (!session().isTransaction()); + session().setFeature("autoCommit", false); + assert (!session().isTransaction()); + session().setTransactionIsolation(Session::TRANSACTION_READ_COMMITTED); + + { + Transaction trans(session()); + assert (trans.isActive()); + assert (session().isTransaction()); + + try { session() << "INSERT INTO PERSON VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + + assert (session().isTransaction()); + assert (trans.isActive()); + + try { session() << "SELECT COUNT(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + assert (2 == count); + assert (session().isTransaction()); + assert (trans.isActive()); + } + assert (!session().isTransaction()); + + try { session() << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + assert (0 == count); + assert (!session().isTransaction()); + + { + Transaction trans(session()); + try { session() << "INSERT INTO PERSON VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + + Statement stmt1 = (local << "SELECT COUNT(*) FROM PERSON", into(locCount), async, now); + + assert (session().isTransaction()); + assert (trans.isActive()); + trans.commit(); + assert (!session().isTransaction()); + assert (!trans.isActive()); + + stmt1.wait(); + assert (2 == locCount); + } + + try { session() << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + assert (2 == count); + + try { session() << "DELETE FROM PERSON", now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + + Statement stmt1 = (local << "SELECT COUNT(*) FROM PERSON", into(locCount), async, now); + + try { session() << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + assert (0 == count); + session().commit(); + + stmt1.wait(); + assert (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); + + Transaction trans(session()); + + trans.execute(sql1, false); + try { session() << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + assert (1 == count); + trans.execute(sql2, false); + try { session() << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + assert (2 == count); + + Statement stmt2 = (local << "SELECT COUNT(*) FROM PERSON", into(locCount), async, now); + + trans.rollback(); + + stmt2.wait(); + assert (0 == locCount); + + try { session() << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + assert (0 == count); + + trans.execute(sql); + + Statement stmt3 = (local << "SELECT COUNT(*) FROM PERSON", into(locCount), now); + assert (2 == locCount); + + try { session() << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + assert (2 == count); + + session().setFeature("autoCommit", autoCommit); +} + + +struct TestCommitTransaction +{ + void operator () (Session& session) const + { + session << "INSERT INTO PERSON VALUES (?,?,?,?)", bind("lastName"), bind("firstName"), bind("address"), bind("age"), now; + } +}; + + +struct TestRollbackTransaction +{ + void operator () (Session& session) const + { + session << "INSERT INTO PERSON VALUES (?,?,?,?)", bind("lastName"), bind("firstName"), bind("address"), bind("age"), now; + throw Poco::Exception("test"); + } +}; + + +void SQLExecutor::transactor() +{ + std::string funct = "transaction()"; + int count = 0; + + TestCommitTransaction ct; + Transaction t1(session()); + t1.transact(ct); + + try { session() << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + assert (1 == count); + + try + { + TestRollbackTransaction rt; + Transaction t2(session()); + t2.transact(rt); + } catch (Poco::Exception&) { } + + try { session() << "SELECT count(*) FROM PERSON", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); } + assert (0 == count); +} \ No newline at end of file diff --git a/Data/ODBC/testsuite/src/SQLExecutor.h b/Data/ODBC/testsuite/src/SQLExecutor.h index 3e3f20e2a..61a98d184 100644 --- a/Data/ODBC/testsuite/src/SQLExecutor.h +++ b/Data/ODBC/testsuite/src/SQLExecutor.h @@ -514,12 +514,17 @@ public: void sqlChannel(const std::string& connect); void sqlLogger(const std::string& connect); + void sessionTransaction(const std::string& connect); + void transaction(const std::string& connect); + void transactor(); + private: static const std::string MULTI_INSERT; static const std::string MULTI_SELECT; - Poco::Data::Session& session(); + void setTransactionIsolation(Poco::Data::Session& session, Poco::UInt32 ti); + Poco::Data::Session& session(); Poco::Data::Session* _pSession; }; diff --git a/Data/SQLite/include/Poco/Data/SQLite/SessionImpl.h b/Data/SQLite/include/Poco/Data/SQLite/SessionImpl.h index 0ed67dd2e..6b24ee973 100644 --- a/Data/SQLite/include/Poco/Data/SQLite/SessionImpl.h +++ b/Data/SQLite/include/Poco/Data/SQLite/SessionImpl.h @@ -83,8 +83,25 @@ public: bool isConnected(); /// Returns true if connected, false otherwise. + bool canTransact(); + /// Returns true if session has transaction capabilities. + bool isTransaction(); - /// Returns true iff a transaction is in progress. + /// Returns true iff a transaction is a transaction is in progress, false otherwise. + + void setTransactionIsolation(Poco::UInt32 ti); + /// Sets the transaction isolation level. + + Poco::UInt32 getTransactionIsolation(); + /// Returns the transaction isolation level. + + bool hasTransactionIsolation(Poco::UInt32 ti); + /// Returns true iff the transaction isolation level corresponding + /// to the supplied bitmask is supported. + + bool isTransactionIsolation(Poco::UInt32 ti); + /// Returns true iff the transaction isolation level corresponds + /// to the supplied bitmask. const std::string& connectorName(); /// Returns the name of the connector. @@ -107,6 +124,12 @@ private: // // inlines // +inline bool SessionImpl::canTransact() +{ + return true; +} + + inline bool SessionImpl::isTransaction() { return _isTransaction; diff --git a/Data/SQLite/src/SessionImpl.cpp b/Data/SQLite/src/SessionImpl.cpp index 11688d2fb..db1008c18 100644 --- a/Data/SQLite/src/SessionImpl.cpp +++ b/Data/SQLite/src/SessionImpl.cpp @@ -38,7 +38,9 @@ #include "Poco/Data/SQLite/Utility.h" #include "Poco/Data/SQLite/SQLiteStatementImpl.h" #include "Poco/Data/SQLite/Connector.h" +#include "Poco/Data/Session.h" #include "Poco/String.h" +#include "Poco/Exception.h" #include "sqlite3.h" #include @@ -104,6 +106,33 @@ void SessionImpl::rollback() } +void SessionImpl::setTransactionIsolation(Poco::UInt32 ti) +{ + if (ti != Session::TRANSACTION_READ_COMMITTED) + throw Poco::InvalidArgumentException("setTransactionIsolation()"); +} + + +Poco::UInt32 SessionImpl::getTransactionIsolation() +{ + return Session::TRANSACTION_READ_COMMITTED; +} + + +bool SessionImpl::hasTransactionIsolation(Poco::UInt32 ti) +{ + if (ti == Session::TRANSACTION_READ_COMMITTED) return true; + return false; +} + + +bool SessionImpl::isTransactionIsolation(Poco::UInt32 ti) +{ + if (ti == Session::TRANSACTION_READ_COMMITTED) return true; + return false; +} + + void SessionImpl::open() { int rc = sqlite3_open(connectionString().c_str(), &_pDB); diff --git a/Data/include/Poco/Data/AutoTransaction.h b/Data/include/Poco/Data/AutoTransaction.h index f5b5436fe..1c78f4f18 100644 --- a/Data/include/Poco/Data/AutoTransaction.h +++ b/Data/include/Poco/Data/AutoTransaction.h @@ -7,7 +7,7 @@ // Package: Core // Module: AutoTransaction // -// Definition of the AutoTransaction class. +// Forward header for the Transaction class. // // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. // and Contributors. @@ -41,49 +41,14 @@ #define Data_AutoTransaction_INCLUDED -#include "Poco/Data/Data.h" -#include "Poco/Data/Session.h" -#include "Poco/Logger.h" +#include "Poco/Data/Transaction.h" namespace Poco { namespace Data { -class Data_API AutoTransaction - /// AutoTransaction helps with transactions in domain logic. - /// When an AutoTransaction object is created, it first checks whether a - /// transaction is in progress. If not, a new transaction is created. - /// When the AutoTransaction is destroyed, and commit() has been called, - /// nothing is done. Otherwise, the current transaction is rolled back. -{ -public: - AutoTransaction(Poco::Data::Session& session, Poco::Logger* pLogger = 0); - /// Creates the AutoTransaction, using the given database session and logger. - - ~AutoTransaction(); - /// Destroys the AutoTransaction. - /// Rolls back the current database transaction if it has not been commited - /// (by calling commit()), or rolled back (by calling rollback()). - /// - /// If an exception is thrown during rollback, the exception is logged - /// and no further action is taken. - - void commit(); - /// Commits the current transaction. - - void rollback(); - /// Rolls back the current transaction. - -private: - AutoTransaction(); - AutoTransaction(const AutoTransaction&); - AutoTransaction& operator = (const AutoTransaction&); - - Session& _session; - Logger* _pLogger; - bool _mustRollback; -}; +typedef Transaction AutoTransaction; } } // namespace Poco::Data diff --git a/Data/include/Poco/Data/PooledSessionImpl.h b/Data/include/Poco/Data/PooledSessionImpl.h index 592f2607c..49e7188be 100644 --- a/Data/include/Poco/Data/PooledSessionImpl.h +++ b/Data/include/Poco/Data/PooledSessionImpl.h @@ -72,7 +72,12 @@ public: void rollback(); void close(); bool isConnected(); + bool canTransact(); bool isTransaction(); + void setTransactionIsolation(Poco::UInt32); + Poco::UInt32 getTransactionIsolation(); + bool hasTransactionIsolation(Poco::UInt32); + bool isTransactionIsolation(Poco::UInt32); const std::string& connectorName(); void setFeature(const std::string& name, bool state); bool getFeature(const std::string& name); diff --git a/Data/include/Poco/Data/Session.h b/Data/include/Poco/Data/Session.h index a07120f3b..5e0bf3176 100644 --- a/Data/include/Poco/Data/Session.h +++ b/Data/include/Poco/Data/Session.h @@ -173,6 +173,11 @@ class Data_API Session /// For complete list of supported data types with their respective specifications, see the documentation for format in Foundation. { public: + static const Poco::UInt32 TRANSACTION_READ_UNCOMMITTED = 0x00000001L; + static const Poco::UInt32 TRANSACTION_READ_COMMITTED = 0x00000002L; + static const Poco::UInt32 TRANSACTION_REPEATABLE_READ = 0x00000004L; + static const Poco::UInt32 TRANSACTION_SERIALIZABLE = 0x00000008L; + Session(Poco::AutoPtr ptrImpl); /// Creates the Session. @@ -221,9 +226,26 @@ public: bool isConnected(); /// Returns true iff session is connected, false otherwise. + bool canTransact(); + /// Returns true if session has transaction capabilities. + bool isTransaction(); /// Returns true iff a transaction is in progress, false otherwise. + void setTransactionIsolation(Poco::UInt32); + /// Sets the transaction isolation level. + + Poco::UInt32 getTransactionIsolation(); + /// Returns the transaction isolation level. + + bool hasTransactionIsolation(Poco::UInt32 ti); + /// Returns true iff the transaction isolation level corresponding + /// to the supplied bitmask is supported. + + bool isTransactionIsolation(Poco::UInt32 ti); + /// Returns true iff the transaction isolation level corresponds + /// to the supplied bitmask. + std::string uri(); /// Returns the URI for this session. @@ -318,12 +340,42 @@ inline bool Session::isConnected() } +inline bool Session::canTransact() +{ + return _ptrImpl->canTransact(); +} + + inline bool Session::isTransaction() { return _ptrImpl->isTransaction(); } +inline void Session::setTransactionIsolation(Poco::UInt32 ti) +{ + _ptrImpl->setTransactionIsolation(ti); +} + + +inline Poco::UInt32 Session::getTransactionIsolation() +{ + return _ptrImpl->getTransactionIsolation(); +} + + +inline bool Session::hasTransactionIsolation(Poco::UInt32 ti) +{ + return _ptrImpl->hasTransactionIsolation(ti); +} + + +inline bool Session::isTransactionIsolation(Poco::UInt32 ti) +{ + return _ptrImpl->isTransactionIsolation(ti); +} + + inline std::string Session::uri(const std::string& connector, const std::string& connectionString) { diff --git a/Data/include/Poco/Data/SessionImpl.h b/Data/include/Poco/Data/SessionImpl.h index fa532dd2d..368d5ac39 100644 --- a/Data/include/Poco/Data/SessionImpl.h +++ b/Data/include/Poco/Data/SessionImpl.h @@ -83,9 +83,26 @@ public: virtual bool isConnected() = 0; /// Returns true if session is connected, false otherwise. + virtual bool canTransact() = 0; + /// Returns true if session has transaction capabilities. + virtual bool isTransaction() = 0; /// Returns true iff a transaction is a transaction is in progress, false otherwise. + virtual void setTransactionIsolation(Poco::UInt32) = 0; + /// Sets the transaction isolation level. + + virtual Poco::UInt32 getTransactionIsolation() = 0; + /// Returns the transaction isolation level. + + virtual bool hasTransactionIsolation(Poco::UInt32) = 0; + /// Returns true iff the transaction isolation level corresponding + /// to the supplied bitmask is supported. + + virtual bool isTransactionIsolation(Poco::UInt32) = 0; + /// Returns true iff the transaction isolation level corresponds + /// to the supplied bitmask. + virtual const std::string& connectorName() = 0; /// Returns the name of the connector. diff --git a/Data/include/Poco/Data/Transaction.h b/Data/include/Poco/Data/Transaction.h new file mode 100644 index 000000000..ffb9d9ba1 --- /dev/null +++ b/Data/include/Poco/Data/Transaction.h @@ -0,0 +1,242 @@ +// +// Transaction.h +// +// $Id: //poco/Main/Data/include/Poco/Data/Transaction.h#2 $ +// +// Library: Data +// Package: Core +// Module: Transaction +// +// Definition of the Transaction class. +// +// Copyright (c) 2006, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + + + +#ifndef Data_Transaction_INCLUDED +#define Data_Transaction_INCLUDED + + +#include "Poco/Data/Data.h" +#include "Poco/Data/Session.h" +#include "Poco/Logger.h" + + +namespace Poco { +namespace Data { + + +class Data_API Transaction + /// Transaction helps with transactions in domain logic. + /// When an Transaction object is created, it first checks whether a + /// transaction is in progress. If not, a new transaction is created. + /// When the Transaction is destroyed, and commit() has been called, + /// nothing is done. Otherwise, the current transaction is rolled back. + /// See Transaction for more detaisl nad purpose of this template. +{ +public: + template + class Transactor + /// Transactor is a helper functor template. + /// It is used to consolidate the C++ code that participates in + /// the transaction. + /// + /// Example usage: + /// + /// struct ATransaction + /// { + /// void operator () (Session& session) const + /// { + /// // do something ... + /// } + /// }; + /// + /// ATransaction t; + /// Transaction at(session, t); // commits, if successful + /// + /// See Transaction for more details on how to use Transactor. + { + public: + Transactor(T& transactor): _transactor(transactor) + /// Creates the Transactor + { + } + + inline void operator () (Poco::Data::Session& session) + { + _transactor(session); + } + + inline void operator () (Poco::Data::Session& session) const + { + _transactor(session); + } + + private: + Transactor(); + Transactor(const Transactor&); + Transactor& operator = (const Transactor&); + + T& _transactor; + }; + + Transaction(Poco::Data::Session& session, Poco::Logger* pLogger = 0); + /// Creates the Transaction and starts it, using the given database session and logger. + + Transaction(Poco::Data::Session& session, bool start); + /// Creates the Transaction, using the given database session. + /// If start is true, transaction is started, otherwise begin() must be called + /// to start the transaction. + + template + Transaction(Poco::Data::Session& rSession, T& t, Poco::Logger* pLogger = 0): + _rSession(rSession), + _pLogger(pLogger) + /// Creates the Transaction, using the given database session and logger. + /// The type for the second argument must be Transactor-compatible, i.e. + /// provide the overload for the operator (). + /// When transaction is created using this constructor, it is executed and + /// commited automatically. If no error occurs, rollback is disabled and does + /// not occur at destruction time. + { + begin(); + execute(t); + } + + ~Transaction(); + /// Destroys the Transaction. + /// Rolls back the current database transaction if it has not been commited + /// (by calling commit()), or rolled back (by calling rollback()). + /// + /// If an exception is thrown during rollback, the exception is logged + /// and no further action is taken. + + void setIsolation(Poco::UInt32 ti); + /// Sets the transaction isolation level. + + Poco::UInt32 getIsolation(); + /// Returns the transaction isolation level. + + bool hasIsolation(Poco::UInt32 ti); + /// Returns true iff the transaction isolation level corresponding + /// to the supplied bitmask is supported. + + bool isIsolation(Poco::UInt32 ti); + /// Returns true iff the transaction isolation level corresponds + /// to the supplied bitmask. + + void execute(const std::string& sql, bool doCommit = true); + /// Executes and, if doCommit is true, commits the transaction. + /// Passing true value for commit disables rollback during destruction + /// of this Transaction object. + + void execute(const std::vector& sql); + /// Executes all the SQL statements supplied in the vector and, after the last + /// one is sucesfully executed, commits the transaction. + /// If an error occurs during execution, transaction is rolled back. + /// Passing true value for commit disables rollback during destruction + /// of this Transaction object. + + template + void transact(T& t) + /// Executes the transactor and, if doCommit is true, commits the transaction. + /// Passing true value for commit disables rollback during destruction + /// of this Transaction object. + { + Transactor transactor(t); + transactor(_rSession); + commit(); + } + + void commit(); + /// Commits the current transaction. + + void rollback(); + /// Rolls back the current transaction. + + bool isActive(); + /// Returns false after the transaction has been committed or rolled back, + /// true if the transaction is ongoing. + + void setLogger(Poco::Logger* pLogger); + /// Sets the logger for this transaction. + /// Transaction does not take the ownership of the pointer. + +private: + Transaction(); + Transaction(const Transaction&); + Transaction& operator = (const Transaction&); + + void begin(); + /// Begins the transaction if the session is already not in transaction. + /// Otherwise does nothing. + + Session _rSession; + Logger* _pLogger; +}; + + +inline bool Transaction::isActive() +{ + return _rSession.isTransaction(); +} + + +inline void Transaction::setIsolation(Poco::UInt32 ti) +{ + _rSession.setTransactionIsolation(ti); +} + + +inline Poco::UInt32 Transaction::getIsolation() +{ + return _rSession.getTransactionIsolation(); +} + + +inline bool Transaction::hasIsolation(Poco::UInt32 ti) +{ + return _rSession.isTransactionIsolation(ti); +} + + +inline bool Transaction::isIsolation(Poco::UInt32 ti) +{ + return _rSession.isTransactionIsolation(ti); +} + + +inline void Transaction::setLogger(Poco::Logger* pLogger) +{ + _pLogger = pLogger; +} + + +} } // namespace Poco::Data + + +#endif // Data_Transaction_INCLUDED diff --git a/Data/src/PooledSessionImpl.cpp b/Data/src/PooledSessionImpl.cpp index 452aba24f..9eb08458e 100644 --- a/Data/src/PooledSessionImpl.cpp +++ b/Data/src/PooledSessionImpl.cpp @@ -80,12 +80,42 @@ bool PooledSessionImpl::isConnected() } +bool PooledSessionImpl::canTransact() +{ + return access()->canTransact(); +} + + bool PooledSessionImpl::isTransaction() { return access()->isTransaction(); } +void PooledSessionImpl::setTransactionIsolation(Poco::UInt32 ti) +{ + access()->setTransactionIsolation(ti); +} + + +Poco::UInt32 PooledSessionImpl::getTransactionIsolation() +{ + return access()->getTransactionIsolation(); +} + + +bool PooledSessionImpl::hasTransactionIsolation(Poco::UInt32 ti) +{ + return access()->hasTransactionIsolation(ti); +} + + +bool PooledSessionImpl::isTransactionIsolation(Poco::UInt32 ti) +{ + return access()->isTransactionIsolation(ti); +} + + void PooledSessionImpl::rollback() { return access()->rollback(); diff --git a/Data/src/AutoTransaction.cpp b/Data/src/Transaction.cpp similarity index 57% rename from Data/src/AutoTransaction.cpp rename to Data/src/Transaction.cpp index 5258d671e..e62500bf4 100644 --- a/Data/src/AutoTransaction.cpp +++ b/Data/src/Transaction.cpp @@ -1,11 +1,11 @@ // -// AutoTransaction.cpp +// Transaction.cpp // -// $Id: //poco/Main/Data/src/AutoTransaction.cpp#1 $ +// $Id: //poco/Main/Data/src/Transaction.cpp#1 $ // // Library: Data // Package: DataCore -// Module: AutoTransaction +// Module: Transaction // // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. // and Contributors. @@ -34,35 +34,40 @@ // -#include "Poco/Data/AutoTransaction.h" +#include "Poco/Data/Transaction.h" +#include "Poco/Exception.h" namespace Poco { namespace Data { -AutoTransaction::AutoTransaction(Poco::Data::Session& session, Poco::Logger* pLogger): - _session(session), - _pLogger(pLogger), - _mustRollback(true) +Transaction::Transaction(Poco::Data::Session& rSession, Poco::Logger* pLogger): + _rSession(rSession), + _pLogger(pLogger) { - if (!_session.isTransaction()) - { - _session.begin(); - } + begin(); +} + + +Transaction::Transaction(Poco::Data::Session& rSession, bool start): + _rSession(rSession), + _pLogger(0) +{ + if (start) begin(); } -AutoTransaction::~AutoTransaction() +Transaction::~Transaction() { - if (_mustRollback) + if (_rSession.isTransaction()) { try { if (_pLogger) _pLogger->debug("Rolling back transaction."); - _session.rollback(); + _rSession.rollback(); } catch (Poco::Exception& exc) { @@ -72,24 +77,57 @@ AutoTransaction::~AutoTransaction() } } - -void AutoTransaction::commit() + +void Transaction::begin() +{ + if (!_rSession.isTransaction()) + _rSession.begin(); + else + throw InvalidAccessException("Transaction in progress."); +} + + +void Transaction::execute(const std::string& sql, bool doCommit) +{ + if (!_rSession.isTransaction()) _rSession.begin(); + _rSession << sql, Keywords::now; + if (doCommit) commit(); +} + + +void Transaction::execute(const std::vector& sql) +{ + try + { + std::vector::const_iterator it = sql.begin(); + std::vector::const_iterator end = sql.end(); + for (; it != end; ++it) execute(*it, it + 1 == end ? true : false); + return; + } + catch (Exception& ex) + { + if (_pLogger) _pLogger->error(ex.displayText()); + } + + rollback(); +} + + +void Transaction::commit() { if (_pLogger) _pLogger->debug("Committing transaction."); - _session.commit(); - _mustRollback = false; + _rSession.commit(); } -void AutoTransaction::rollback() +void Transaction::rollback() { if (_pLogger) _pLogger->debug("Rolling back transaction."); - _session.rollback(); - _mustRollback = false; + _rSession.rollback(); } diff --git a/Data/testsuite/src/SessionImpl.cpp b/Data/testsuite/src/SessionImpl.cpp index 0debb88ca..af74d68d7 100644 --- a/Data/testsuite/src/SessionImpl.cpp +++ b/Data/testsuite/src/SessionImpl.cpp @@ -93,12 +93,41 @@ bool SessionImpl::isConnected() } +bool SessionImpl::canTransact() +{ + return false; +} + + bool SessionImpl::isTransaction() { return false; } +void SessionImpl::setTransactionIsolation(Poco::UInt32) +{ +} + + +Poco::UInt32 SessionImpl::getTransactionIsolation() +{ + return 0; +} + + +bool SessionImpl::hasTransactionIsolation(Poco::UInt32) +{ + return false; +} + + +bool SessionImpl::isTransactionIsolation(Poco::UInt32) +{ + return false; +} + + const std::string& SessionImpl::connectorName() { return Connector::KEY; diff --git a/Data/testsuite/src/SessionImpl.h b/Data/testsuite/src/SessionImpl.h index 906fe477e..804366619 100644 --- a/Data/testsuite/src/SessionImpl.h +++ b/Data/testsuite/src/SessionImpl.h @@ -75,9 +75,26 @@ public: /// Returns true if session is connected to the database, /// false otherwise. + bool canTransact(); + /// Returns true if session has transaction capabilities. + bool isTransaction(); /// Returns true iff a transaction is a transaction is in progress, false otherwise. + void setTransactionIsolation(Poco::UInt32); + /// Sets the transaction isolation level. + + Poco::UInt32 getTransactionIsolation(); + /// Returns the transaction isolation level. + + bool hasTransactionIsolation(Poco::UInt32); + /// Returns true iff the transaction isolation level corresponding + /// to the supplied bitmask is supported. + + bool isTransactionIsolation(Poco::UInt32); + /// Returns true iff the transaction isolation level corresponds + /// to the supplied bitmask. + const std::string& connectorName(); /// Returns the name of the connector.