feat(Data::AbstractSessionImpl): add autoCommit property and tests #4261 (#4262)

* fix(Data::AbstracSessionImpl): protect autocommit feature handlers #4261

* chore(CI): re-enable mysql

* MySQL SessionImpl: make sure autocommit mode is on when session is openend or reset.

* PostgreSQL SessionImpl: reuse autocommit flag of AbstractSessionImpl.

* Github workflow: re-activated linux-gcc-make-postgres

* Fixed indentation in ci.yml

* Fix for DataTest SQLExecutor: use connector

* Data::Session: when parser is not used and autocommit mode is off, assume any SQL statement begins a transaction.

* PostgreSQL: don't use SQL parser (it currently cannot handle placeholders).

* PostgreSQL: added test sessionTransactionNoAutoCommit

* PostgreSQL test suite: removed reference to generic SQLExecutor

* PostgreSQL: fixes for sessionTransactionNoAutoCommit.

* MySQL: added test sessionPoolAndUnicode (from #2801)

* Fixed #define in sql-parser

* Data generic testsuite: support numbered placeholders

* PostgreSQL test suite: added missing include directory to Makefile.

* Attempt to fix PostgreSQL Makefiles

* PostgreSQL testsuite: added include path to Makefile

* PostgreSQL testsuite: added PocoDataTest library to Makefile

* DataTest SQLExecutor::formatSQL: don't use string_view

* PostgreSQL test suite: delegated most tests to Poco::Data::Test

* Makefile: added dependencies on Data-Tests

* Weaken assumptions about async in generic transaction tests

* Makefile: added dependency for Prometheus samples

* Fix deadlock in DataTest SQLExecutor

* PostgreSQL tests SQLExecutor: cleanup

* feat(Data::AbstractSessionImpl): add autoCommit property and tests #4261

* Brought MySQL backend in line with _autoCommit flag of AbstractSessionImpl.

---------

Co-authored-by: Friedrich Wilckens <frwilckens@gmail.com>
Co-authored-by: Friedrich Wilckens <friedrich.wilckens@ingramcontent.com>
This commit is contained in:
Aleksandar Fabijanic 2023-12-22 09:27:34 +01:00 committed by GitHub
parent 80cde9e3e6
commit 86084cb7b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 441 additions and 1340 deletions

View File

@ -420,55 +420,55 @@ jobs:
# cd cmake-build; # cd cmake-build;
# ctest --output-on-failure -E "(DataMySQL)|(DataODBC)|(Redis)|(MongoDB)" -C Debug # ctest --output-on-failure -E "(DataMySQL)|(DataODBC)|(Redis)|(MongoDB)" -C Debug
# linux-gcc-make-mysql: linux-gcc-make-mysql:
# runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
# services: services:
# mysql: mysql:
# image: mysql:8.1.0 image: mysql:8.1.0
# env: env:
# MYSQL_ALLOW_EMPTY_PASSWORD: yes MYSQL_ALLOW_EMPTY_PASSWORD: yes
# MYSQL_USER: pocotest MYSQL_USER: pocotest
# MYSQL_PASSWORD: pocotest MYSQL_PASSWORD: pocotest
# MYSQL_DATABASE: pocotest MYSQL_DATABASE: pocotest
# ports: ports:
# - 3306:3306 - 3306:3306
# steps: steps:
# - uses: actions/checkout@v3 - uses: actions/checkout@v3
# - run: sudo apt -y update && sudo apt -y install libssl-dev unixodbc-dev libmysqlclient-dev mysql-client - run: sudo apt -y update && sudo apt -y install libssl-dev unixodbc-dev libmysqlclient-dev mysql-client
# - run: ./configure --everything --no-samples --omit=ActiveRecord,ApacheConnector,CppParser,Crypto,Data/PostgreSQL,Data/SQLite,Data/ODBC,Encodings,JSON,JWT,MongoDB,Net,NetSSL_OpenSSL,NetSSL_Win,PDF,PageCompiler,PocoDoc,ProGen,Prometheus,Redis,SevenZip,Util,XML,Zip && make all -s -j4 && sudo make install - run: ./configure --everything --no-samples --omit=ActiveRecord,ApacheConnector,CppParser,Crypto,Data/PostgreSQL,Data/SQLite,Data/ODBC,Encodings,JSON,JWT,MongoDB,Net,NetSSL_OpenSSL,NetSSL_Win,PDF,PageCompiler,PocoDoc,ProGen,Prometheus,Redis,SevenZip,Util,XML,Zip && make all -s -j4 && sudo make install
# - uses: ./.github/actions/retry-action - uses: ./.github/actions/retry-action
# with: with:
# timeout_minutes: 90 timeout_minutes: 90
# max_attempts: 3 max_attempts: 3
# retry_on: any retry_on: any
# command: >- command: >-
# sudo -s sudo -s
# EXCLUDE_TESTS="ActiveRecord ApacheConnector CppParser CppUnit Crypto Data Data/PostgreSQL Data/ODBC Data/SQLite Encodings Foundation JSON JWT MongoDB Net NetSSL_OpenSSL NetSSL_Win PDF PageCompiler PocoDoc ProGen Prometheus Redis SevenZip Util XML Zip" EXCLUDE_TESTS="ActiveRecord ApacheConnector CppParser CppUnit Crypto Data Data/PostgreSQL Data/ODBC Data/SQLite Encodings Foundation JSON JWT MongoDB Net NetSSL_OpenSSL NetSSL_Win PDF PageCompiler PocoDoc ProGen Prometheus Redis SevenZip Util XML Zip"
# ./ci/runtests.sh ./ci/runtests.sh
# TODO tests sometimes failling on testTransaction and testReconnect # TODO tests sometimes failing on testTransaction and testReconnect
# linux-gcc-make-postgres: linux-gcc-make-postgres:
# runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
# services: services:
# postgres: postgres:
# image: postgres:16.0 image: postgres:16.0
# env: env:
# POSTGRES_PASSWORD: postgres POSTGRES_PASSWORD: postgres
# ports: ports:
# - 5432:5432 - 5432:5432
# steps: steps:
# - uses: actions/checkout@v3 - uses: actions/checkout@v3
# - run: sudo apt -y update && sudo apt -y install libssl-dev unixodbc-dev libmysqlclient-dev odbc-postgresql - run: sudo apt -y update && sudo apt -y install libssl-dev unixodbc-dev libmysqlclient-dev odbc-postgresql
# - run: ./configure --everything --no-samples --omit=ActiveRecord,ApacheConnector,CppParser,Crypto,Data/MySQL,Data/ODBC,Data/SQLite,Encodings,JSON,JWT,MongoDB,Net,NetSSL_OpenSSL,NetSSL_Win,PDF,PageCompiler,PocoDoc,ProGen,Prometheus,Redis,SevenZip,Util,XML,Zip && make all -s -j4 && sudo make install - run: ./configure --everything --no-samples --omit=ActiveRecord,ApacheConnector,CppParser,Crypto,Data/MySQL,Data/ODBC,Data/SQLite,Encodings,JSON,JWT,MongoDB,Net,NetSSL_OpenSSL,NetSSL_Win,PDF,PageCompiler,PocoDoc,ProGen,Prometheus,Redis,SevenZip,Util,XML,Zip && make all -s -j4 && sudo make install
# - uses: ./.github/actions/retry-action - uses: ./.github/actions/retry-action
# with: with:
# timeout_minutes: 90 timeout_minutes: 90
# max_attempts: 3 max_attempts: 3
# retry_on: any retry_on: any
# command: >- command: >-
# sudo -s sudo -s
# EXCLUDE_TESTS="ActiveRecord ApacheConnector CppParser CppUnit Crypto Data Data/ODBC Data/MySQL Data/SQLite Encodings Foundation JSON JWT MongoDB Net NetSSL_OpenSSL NetSSL_Win PDF PageCompiler PocoDoc ProGen Prometheus Redis SevenZip Util XML Zip" EXCLUDE_TESTS="ActiveRecord ApacheConnector CppParser CppUnit Crypto Data Data/ODBC Data/MySQL Data/SQLite Encodings Foundation JSON JWT MongoDB Net NetSSL_OpenSSL NetSSL_Win PDF PageCompiler PocoDoc ProGen Prometheus Redis SevenZip Util XML Zip"
# ./ci/runtests.sh ./ci/runtests.sh
linux-gcc-make-redis: linux-gcc-make-redis:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04

View File

@ -67,6 +67,9 @@ public:
void rollback(); void rollback();
/// Rollback transaction /// Rollback transaction
void autoCommit(bool val);
/// Set autocommit mode
void reset(); void reset();
/// Reset connection with dababase and clears session state, but without disconnecting /// Reset connection with dababase and clears session state, but without disconnecting

View File

@ -181,6 +181,13 @@ void SessionHandle::rollback()
} }
void SessionHandle::autoCommit(bool val)
{
if (mysql_autocommit(_pHandle, val) != 0)
throw TransactionException("Setting autocommit mode failed.", _pHandle);
}
void SessionHandle::reset() void SessionHandle::reset()
{ {
#if ((defined (MYSQL_VERSION_ID)) && (MYSQL_VERSION_ID >= 50700)) || ((defined (MARIADB_PACKAGE_VERSION_ID)) && (MARIADB_PACKAGE_VERSION_ID >= 30000)) #if ((defined (MYSQL_VERSION_ID)) && (MYSQL_VERSION_ID >= 50700)) || ((defined (MARIADB_PACKAGE_VERSION_ID)) && (MARIADB_PACKAGE_VERSION_ID >= 30000))

View File

@ -173,6 +173,9 @@ void SessionImpl::open(const std::string& connect)
&SessionImpl::autoCommit, &SessionImpl::autoCommit,
&SessionImpl::isAutoCommit); &SessionImpl::isAutoCommit);
// autocommit is initially on when a session is opened
AbstractSessionImpl::setAutoCommit("", true);
_connected = true; _connected = true;
} }
@ -215,18 +218,18 @@ void SessionImpl::rollback()
} }
void SessionImpl::autoCommit(const std::string&, bool val) void SessionImpl::autoCommit(const std::string& s, bool val)
{ {
StatementExecutor ex(_handle); if (val != getAutoCommit(s)) {
ex.prepare(Poco::format("SET autocommit=%d", val ? 1 : 0)); _handle.autoCommit(val);
ex.execute(); AbstractSessionImpl::setAutoCommit(s, val);
}
} }
bool SessionImpl::isAutoCommit(const std::string&) const bool SessionImpl::isAutoCommit(const std::string& s) const
{ {
int ac = 0; return AbstractSessionImpl::getAutoCommit(s);
return 1 == getSetting("autocommit", ac);
} }
@ -306,6 +309,7 @@ void SessionImpl::reset()
if (_connected && _reset) if (_connected && _reset)
{ {
_handle.reset(); _handle.reset();
AbstractSessionImpl::setAutoCommit("", true);
} }
} }

View File

@ -727,6 +727,15 @@ void MySQLTest::testTupleWithNullable()
} }
void MySQLTest::testSessionPoolAndUnicode()
{
if (!_pSession) fail ("Test not available.");
recreateStringsTable();
_pExecutor->sessionPoolAndUnicode(_dbConnString);
}
void MySQLTest::dropTable(const std::string& tableName) void MySQLTest::dropTable(const std::string& tableName)
{ {
try { *_pSession << format("DROP TABLE IF EXISTS %s", tableName), now; } try { *_pSession << format("DROP TABLE IF EXISTS %s", tableName), now; }
@ -993,6 +1002,6 @@ CppUnit::Test* MySQLTest::suite()
CppUnit_addTest(pSuite, MySQLTest, testSessionTransaction); CppUnit_addTest(pSuite, MySQLTest, testSessionTransaction);
CppUnit_addTest(pSuite, MySQLTest, testTransaction); CppUnit_addTest(pSuite, MySQLTest, testTransaction);
CppUnit_addTest(pSuite, MySQLTest, testReconnect); CppUnit_addTest(pSuite, MySQLTest, testReconnect);
CppUnit_addTest(pSuite, MySQLTest, testSessionPoolAndUnicode);
return pSuite; return pSuite;
} }

View File

@ -107,6 +107,7 @@ public:
void testTransaction(); void testTransaction();
void testReconnect(); void testReconnect();
void testSessionPoolAndUnicode();
void setUp(); void setUp();
void tearDown(); void tearDown();

View File

@ -21,6 +21,7 @@
#include "Poco/Data/Time.h" #include "Poco/Data/Time.h"
#include "Poco/Data/StatementImpl.h" #include "Poco/Data/StatementImpl.h"
#include "Poco/Data/RecordSet.h" #include "Poco/Data/RecordSet.h"
#include "Poco/Data/SessionPool.h"
#include "Poco/Data/Transaction.h" #include "Poco/Data/Transaction.h"
#include "Poco/Data/MySQL/Connector.h" #include "Poco/Data/MySQL/Connector.h"
#include "Poco/Data/MySQL/MySQLException.h" #include "Poco/Data/MySQL/MySQLException.h"
@ -1372,7 +1373,7 @@ void SQLExecutor::timestamp()
std::string firstName("Simpson"); std::string firstName("Simpson");
std::string address("Springfield"); std::string address("Springfield");
DateTime birthday(1980, 4, 1, 5, 45, 12, 354, 879); DateTime birthday(1980, 4, 1, 5, 45, 12, 354, 879);
int count = 0; int count = 0;
try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(birthday), now; } try { *_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(birthday), now; }
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); }
@ -1381,14 +1382,14 @@ void SQLExecutor::timestamp()
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); }
assertTrue (count == 1); assertTrue (count == 1);
DateTime bd; DateTime bd;
assertTrue (bd != birthday); assertTrue (bd != birthday);
try { *_pSession << "SELECT Birthday FROM Person", into(bd), now; } try { *_pSession << "SELECT Birthday FROM Person", into(bd), now; }
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); }
assertTrue (bd == birthday); assertTrue (bd == birthday);
std::cout << std::endl << RecordSet(*_pSession, "SELECT * FROM Person"); std::cout << std::endl << RecordSet(*_pSession, "SELECT * FROM Person");
} }
@ -2066,3 +2067,31 @@ void SQLExecutor::reconnect()
assertTrue (count == age); assertTrue (count == age);
assertTrue (_pSession->isConnected()); assertTrue (_pSession->isConnected());
} }
void SQLExecutor::sessionPoolAndUnicode(const std::string& connString)
{
std::string funct = "unicode()";
std::string text = "ěščřžťďůň";
std::string text2;
// Test uses session from SessionPool instead of _pSession to prove session
// obtained and returned into pool is valid.
// Min/Max 1 session - ensures that when get() is called, same session should be returned
Poco::SharedPtr<Poco::Data::SessionPool> sp = new Poco::Data::SessionPool(MySQL::Connector::KEY, connString, 1, 1);
{
Poco::Data::Session session = sp->get();
try { session << "INSERT INTO Strings VALUES (?)", use(text), now; }
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); }
} // parentheses to ensure session is returned into pool
Poco::Data::Session session2 = sp->get();
try { session2 << "SELECT str FROM Strings", into(text2), now; }
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); }
assertTrue (text == text2);
}

View File

@ -88,7 +88,7 @@ public:
void longText(); void longText();
#ifdef POCO_MYSQL_JSON #ifdef POCO_MYSQL_JSON
void json(); void json();
#endif #endif
void unsignedInts(); void unsignedInts();
void floats(); void floats();
void doubles(); void doubles();
@ -103,6 +103,7 @@ public:
void transaction(const std::string& connect); void transaction(const std::string& connect);
void reconnect(); void reconnect();
void sessionPoolAndUnicode(const std::string& connString);
private: private:
void setTransactionIsolation(Poco::Data::Session& session, Poco::UInt32 ti); void setTransactionIsolation(Poco::Data::Session& session, Poco::UInt32 ti);

View File

@ -12,10 +12,13 @@ objects = Extractor BinaryExtractor Binder SessionImpl Connector \
PostgreSQLStatementImpl PostgreSQLException \ PostgreSQLStatementImpl PostgreSQLException \
SessionHandle StatementExecutor PostgreSQLTypes Utility SessionHandle StatementExecutor PostgreSQLTypes Utility
target_includes = $(POCO_BASE)/Data/testsuite/include
ifndef POCO_DATA_NO_SQL_PARSER ifndef POCO_DATA_NO_SQL_PARSER
target_includes = $(POCO_BASE)/Data/src target_includes += $(POCO_BASE)/Data/src
endif endif
target = PocoDataPostgreSQL target = PocoDataPostgreSQL
target_version = $(LIBVERSION) target_version = $(LIBVERSION)
target_libs = PocoData PocoFoundation target_libs = PocoData PocoFoundation

View File

@ -118,7 +118,7 @@ public:
bool isAutoCommit() const; bool isAutoCommit() const;
/// is the connection in auto commit mode? /// is the connection in auto commit mode?
void setAutoCommit(bool aShouldAutoCommit = true); void autoCommit(bool val);
/// is the connection in auto commit mode? /// is the connection in auto commit mode?
bool isAsynchronousCommit() const; bool isAsynchronousCommit() const;
@ -193,7 +193,6 @@ private:
PGconn* _pConnection; PGconn* _pConnection;
std::string _connectionString; std::string _connectionString;
bool _inTransaction; bool _inTransaction;
bool _isAutoCommit;
bool _isAsynchronousCommit; bool _isAsynchronousCommit;
Poco::UInt32 _tranactionIsolationLevel; Poco::UInt32 _tranactionIsolationLevel;
std::vector <std::string> _preparedStatementsToBeDeallocated; std::vector <std::string> _preparedStatementsToBeDeallocated;
@ -306,12 +305,6 @@ inline bool SessionHandle::isTransaction() const
} }
inline bool SessionHandle::isAutoCommit() const
{
return _isAutoCommit;
}
inline bool SessionHandle::isAsynchronousCommit() const inline bool SessionHandle::isAsynchronousCommit() const
{ {
return _isAsynchronousCommit; return _isAsynchronousCommit;

View File

@ -99,7 +99,7 @@ public:
/// Returns true iff the transaction isolation level corresponds /// Returns true iff the transaction isolation level corresponds
/// to the supplied bitmask. /// to the supplied bitmask.
void setAutoCommit(const std::string&, bool aValue); void autoCommit(const std::string&, bool aValue);
/// Sets autocommit property for the session. /// Sets autocommit property for the session.
bool isAutoCommit(const std::string& aName = std::string()) const; bool isAutoCommit(const std::string& aName = std::string()) const;

View File

@ -36,7 +36,6 @@ const std::string SessionHandle::POSTGRESQL_SERIALIZABLE = "SERIALIZABLE";
SessionHandle::SessionHandle(): SessionHandle::SessionHandle():
_pConnection(0), _pConnection(0),
_inTransaction(false), _inTransaction(false),
_isAutoCommit(true),
_isAsynchronousCommit(false), _isAsynchronousCommit(false),
_tranactionIsolationLevel(Session::TRANSACTION_READ_COMMITTED) _tranactionIsolationLevel(Session::TRANSACTION_READ_COMMITTED)
{ {
@ -157,7 +156,6 @@ void SessionHandle::disconnect()
_connectionString = std::string(); _connectionString = std::string();
_inTransaction= false; _inTransaction= false;
_isAutoCommit = true;
_isAsynchronousCommit = false; _isAsynchronousCommit = false;
_tranactionIsolationLevel = Session::TRANSACTION_READ_COMMITTED; _tranactionIsolationLevel = Session::TRANSACTION_READ_COMMITTED;
} }
@ -280,21 +278,13 @@ void SessionHandle::rollback()
} }
void SessionHandle::setAutoCommit(bool aShouldAutoCommit) void SessionHandle::autoCommit(bool val)
{ {
// There is no PostgreSQL API call to switch autocommit (unchained) mode off. // There is no PostgreSQL API call to switch autocommit (unchained) mode off.
if (aShouldAutoCommit == _isAutoCommit) if (isTransaction())
{ {
return; throw Poco::InvalidAccessException();
} }
if (aShouldAutoCommit)
{
if (isTransaction())
commit(); // end any in process transaction
}
_isAutoCommit = aShouldAutoCommit;
} }

View File

@ -60,6 +60,7 @@ SessionImpl::SessionImpl(const std::string& aConnectionString, std::size_t aLogi
Poco::Data::AbstractSessionImpl<SessionImpl>(aConnectionString, aLoginTimeout), Poco::Data::AbstractSessionImpl<SessionImpl>(aConnectionString, aLoginTimeout),
_connectorName("postgresql") _connectorName("postgresql")
{ {
setFeature("sqlParse", false); // the parse currently cannot handle the PostgreSQL placeholders $1, $2, etc.
setProperty("handle", static_cast<SessionHandle*>(&_sessionHandle)); setProperty("handle", static_cast<SessionHandle*>(&_sessionHandle));
setConnectionTimeout(CONNECTION_TIMEOUT_DEFAULT); setConnectionTimeout(CONNECTION_TIMEOUT_DEFAULT);
open(); open();
@ -134,7 +135,7 @@ void SessionImpl::open(const std::string& aConnectionString)
_sessionHandle.connect(createConnectionStringFromOptionsMap(optionsMap)); _sessionHandle.connect(createConnectionStringFromOptionsMap(optionsMap));
addFeature("autoCommit", addFeature("autoCommit",
&SessionImpl::setAutoCommit, &SessionImpl::autoCommit,
&SessionImpl::isAutoCommit); &SessionImpl::isAutoCommit);
addFeature("asynchronousCommit", addFeature("asynchronousCommit",
@ -206,15 +207,18 @@ void SessionImpl::rollback()
} }
void SessionImpl::setAutoCommit(const std::string&, bool aValue) void SessionImpl::autoCommit(const std::string& s, bool val)
{ {
_sessionHandle.setAutoCommit(aValue); if (val != getAutoCommit(s)) {
_sessionHandle.autoCommit(val);
AbstractSessionImpl::setAutoCommit(s, val);
}
} }
bool SessionImpl::isAutoCommit(const std::string&) const bool SessionImpl::isAutoCommit(const std::string& s) const
{ {
return _sessionHandle.isAutoCommit(); return AbstractSessionImpl::getAutoCommit(s);
} }

View File

@ -14,11 +14,12 @@ SYSLIBS += -lpq -lz -lpthread -ldl
objects = PostgreSQLTestSuite Driver PostgreSQLTest SQLExecutor objects = PostgreSQLTestSuite Driver PostgreSQLTest SQLExecutor
ifndef POCO_DATA_NO_SQL_PARSER ifndef POCO_DATA_NO_SQL_PARSER
target_includes = $(POCO_BASE)/Data/src target_includes += $(POCO_BASE)/Data/src
endif endif
target = testrunner target = testrunner
target_version = 1 target_version = 1
target_libs = PocoDataPostgreSQL PocoData PocoFoundation CppUnit target_libs = PocoDataPostgreSQL PocoDataTest PocoData PocoFoundation CppUnit
target_includes += $(POCO_BASE)/Data/testsuite/DataTest/include
include $(POCO_BASE)/build/rules/exec include $(POCO_BASE)/build/rules/exec

View File

@ -774,6 +774,15 @@ void PostgreSQLTest::testSessionTransaction()
} }
void PostgreSQLTest::testSessionTransactionNoAutoCommit()
{
if (!_pSession) fail ("Test not available.");
recreatePersonTable();
_pExecutor->sessionTransactionNoAutoCommit(_dbConnString);
}
void PostgreSQLTest::testTransaction() void PostgreSQLTest::testTransaction()
{ {
if (!_pSession) fail ("Test not available."); if (!_pSession) fail ("Test not available.");
@ -1328,6 +1337,7 @@ CppUnit::Test* PostgreSQLTest::suite()
CppUnit_addTest(pSuite, PostgreSQLTest, testBinaryBLOBStmt); CppUnit_addTest(pSuite, PostgreSQLTest, testBinaryBLOBStmt);
CppUnit_addTest(pSuite, PostgreSQLTest, testSessionTransaction); CppUnit_addTest(pSuite, PostgreSQLTest, testSessionTransaction);
CppUnit_addTest(pSuite, PostgreSQLTest, testSessionTransactionNoAutoCommit);
CppUnit_addTest(pSuite, PostgreSQLTest, testTransaction); CppUnit_addTest(pSuite, PostgreSQLTest, testTransaction);
CppUnit_addTest(pSuite, PostgreSQLTest, testReconnect); CppUnit_addTest(pSuite, PostgreSQLTest, testReconnect);

View File

@ -110,8 +110,8 @@ public:
void testBinaryCLOBStmt(); void testBinaryCLOBStmt();
void testSessionTransaction(); void testSessionTransaction();
void testSessionTransactionNoAutoCommit();
void testTransaction(); void testTransaction();
void testReconnect(); void testReconnect();
void testSqlState(); void testSqlState();

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@
#include "Poco/Data/PostgreSQL/PostgreSQL.h" #include "Poco/Data/PostgreSQL/PostgreSQL.h"
#include "Poco/Data/Session.h" #include "Poco/Data/Session.h"
#include "Poco/Data/Test/SQLExecutor.h"
#include <libpq-fe.h> #include <libpq-fe.h>
@ -97,14 +98,17 @@ public:
void doNull(); void doNull();
void sessionTransaction(const std::string& connect); void sessionTransaction(const std::string& connect);
void sessionTransactionNoAutoCommit(const std::string& connect);
void transaction(const std::string& connect); void transaction(const std::string& connect);
void reconnect(); void reconnect();
private: private:
void setTransactionIsolation(Poco::Data::Session& session, Poco::UInt32 ti); void setTransactionIsolation(Poco::Data::Session& session, Poco::UInt32 ti);
Poco::Data::Session* _pSession; Poco::Data::Session* _pSession;
Poco::Data::Test::SQLExecutor _dataExecutor;
}; };

View File

@ -56,7 +56,8 @@ public:
_bulk(false), _bulk(false),
_emptyStringIsNull(false), _emptyStringIsNull(false),
_forceEmptyString(false), _forceEmptyString(false),
_sqlParse(true) _sqlParse(true),
_autoCommit(true)
/// Creates the AbstractSessionImpl. /// Creates the AbstractSessionImpl.
/// ///
/// Adds "storage" property and sets the default internal storage container /// Adds "storage" property and sets the default internal storage container
@ -113,6 +114,10 @@ public:
addFeature("sqlParse", addFeature("sqlParse",
&AbstractSessionImpl<C>::setSQLParse, &AbstractSessionImpl<C>::setSQLParse,
&AbstractSessionImpl<C>::getSQLParse); &AbstractSessionImpl<C>::getSQLParse);
addFeature("autoCommit",
&AbstractSessionImpl<C>::setAutoCommit,
&AbstractSessionImpl<C>::getAutoCommit);
} }
~AbstractSessionImpl() ~AbstractSessionImpl()
@ -327,6 +332,32 @@ protected:
_properties[name] = property; _properties[name] = property;
} }
// most, if not all, back ends support the autocommit feature
// these handlers are added in this class by default,
// but an implementation can easily replace them by registering
// early its own handlers with:
// addFeature("autoCommit", setter, getter)");
//
// these are here to be used by any back end DBMS client that
// does not provide its own autocommit get/set capabilities
void setAutoCommit(const std::string&, bool autoCommit)
/// Enables automatic commit. When this feature is true,
/// every query is automatically commited. When false,
/// every query starts a transaction, except SELECT queries
/// (if properly detected by parser, see set/getSQLParse() and
/// Statement::checkBeginTransaction() documentation).
{
_autoCommit = autoCommit;
}
bool getAutoCommit(const std::string& name = "") const
/// Returns the value of the automatic commit flag.
/// See setAutoCommit() documentation for more details.
{
return _autoCommit;
}
private: private:
struct Feature struct Feature
{ {
@ -350,6 +381,7 @@ private:
bool _emptyStringIsNull; bool _emptyStringIsNull;
bool _forceEmptyString; bool _forceEmptyString;
bool _sqlParse; bool _sqlParse;
bool _autoCommit;
Poco::Any _handle; Poco::Any _handle;
}; };

View File

@ -235,11 +235,16 @@ void Statement::formatQuery()
void Statement::checkBeginTransaction() void Statement::checkBeginTransaction()
{ {
SessionImpl& session = _pImpl->session(); SessionImpl& session = _pImpl->session();
if (!session.isAutocommit() && !session.isTransaction() && session.shouldParse()) if (!session.isTransaction() && !session.isAutocommit()) {
{ if (session.shouldParse())
auto result = parse(); {
if (result.isSpecified() && result.value() && !isSelect().value()) auto result = parse();
if (result.isSpecified() && result.value() && !isSelect().value())
session.begin();
} else
{
session.begin(); session.begin();
}
} }
} }

View File

@ -22,6 +22,7 @@
#include "Poco/String.h" #include "Poco/String.h"
#include "Poco/Exception.h" #include "Poco/Exception.h"
#include <iostream> #include <iostream>
#include <string_view>
namespace Poco { namespace Poco {
@ -53,7 +54,7 @@ public:
DE_BOUND DE_BOUND
}; };
SQLExecutor(const std::string& name, Poco::Data::Session* pSession, Poco::Data::Session* pEncSession = 0); SQLExecutor(const std::string& name, Poco::Data::Session* pSession, Poco::Data::Session* pEncSession = nullptr, bool numberedPlaceHolders = false);
~SQLExecutor(); ~SQLExecutor();
template <typename C> template <typename C>
@ -232,6 +233,9 @@ private:
Poco::Data::Session* _pSession; Poco::Data::Session* _pSession;
Poco::Data::Session* _pEncSession; Poco::Data::Session* _pEncSession;
bool _numberedPlaceHolders = false;
std::string formatSQL(const std::string& s) const;
}; };

View File

@ -292,10 +292,11 @@ const std::string SQLExecutor::MULTI_SELECT =
"SELECT * FROM Test WHERE First = '5';"; "SELECT * FROM Test WHERE First = '5';";
SQLExecutor::SQLExecutor(const std::string& name, Poco::Data::Session* pSession, Poco::Data::Session* pEncSession): SQLExecutor::SQLExecutor(const std::string& name, Poco::Data::Session* pSession, Poco::Data::Session* pEncSession, bool numberedPlaceHolders):
CppUnit::TestCase(name), CppUnit::TestCase(name),
_pSession(pSession), _pSession(pSession),
_pEncSession(pEncSession) _pEncSession(pEncSession),
_numberedPlaceHolders(numberedPlaceHolders)
{ {
} }
@ -565,7 +566,7 @@ void SQLExecutor::simpleAccess()
int count = 0; int count = 0;
std::string result; std::string result;
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(age), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastName), use(firstName), use(address), use(age), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -608,7 +609,7 @@ void SQLExecutor::complexType()
Person p1("LN1", "FN1", "ADDR1", 1); Person p1("LN1", "FN1", "ADDR1", 1);
Person p2("LN2", "FN2", "ADDR2", 2); Person p2("LN2", "FN2", "ADDR2", 2);
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(p1), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(p1), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -616,7 +617,7 @@ void SQLExecutor::complexType()
} }
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(p2), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(p2), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -652,7 +653,7 @@ void SQLExecutor::complexTypeTuple()
Person p2("LN2", "FN2", "ADDR2", 2); Person p2("LN2", "FN2", "ADDR2", 2);
Tuple<Person,Person> t(p1,p2); Tuple<Person,Person> t(p1,p2);
try { *_pSession << "INSERT INTO Person VALUES(?,?,?,?,?,?,?,?)", use(t), now; } try { *_pSession << formatSQL("INSERT INTO Person VALUES(?,?,?,?,?,?,?,?)"), use(t), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -691,7 +692,7 @@ void SQLExecutor::simpleAccessVector()
int count = 0; int count = 0;
std::string result; std::string result;
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastNames), use(firstNames), use(addresses), use(ages), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -732,7 +733,7 @@ void SQLExecutor::complexTypeVector()
people.push_back(Person("LN1", "FN1", "ADDR1", 1)); people.push_back(Person("LN1", "FN1", "ADDR1", 1));
people.push_back(Person("LN2", "FN2", "ADDR2", 2)); people.push_back(Person("LN2", "FN2", "ADDR2", 2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -768,7 +769,7 @@ void SQLExecutor::sharedPtrComplexTypeVector()
people.push_back(new Person("LN1", "FN1", "ADDR1", 1)); people.push_back(new Person("LN1", "FN1", "ADDR1", 1));
people.push_back(new Person("LN2", "FN2", "ADDR2", 2)); people.push_back(new Person("LN2", "FN2", "ADDR2", 2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -806,7 +807,7 @@ void SQLExecutor::autoPtrComplexTypeVector()
people.push_back(new RefCountedPerson("LN1", "FN1", "ADDR1", 1)); people.push_back(new RefCountedPerson("LN1", "FN1", "ADDR1", 1));
people.push_back(new RefCountedPerson("LN2", "FN2", "ADDR2", 2)); people.push_back(new RefCountedPerson("LN2", "FN2", "ADDR2", 2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -854,7 +855,7 @@ void SQLExecutor::insertVector()
int count = 100; int count = 100;
{ {
Statement stmt((session() << "INSERT INTO Strings VALUES (?)", use(str))); Statement stmt((session() << formatSQL("INSERT INTO Strings VALUES (?)"), use(str)));
try { session() << "SELECT COUNT(*) FROM Strings", into(count), now; } try { session() << "SELECT COUNT(*) FROM Strings", into(count), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
@ -899,7 +900,7 @@ void SQLExecutor::insertEmptyVector()
try try
{ {
session() << "INSERT INTO Strings VALUES (?)", use(str), now; session() << formatSQL("INSERT INTO Strings VALUES (?)"), use(str), now;
fail("empty collections should not work", __LINE__, __FILE__); fail("empty collections should not work", __LINE__, __FILE__);
} catch (Poco::Exception&) } catch (Poco::Exception&)
{ {
@ -925,7 +926,7 @@ void SQLExecutor::simpleAccessList()
int count = 0; int count = 0;
std::string result; std::string result;
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastNames), use(firstNames), use(addresses), use(ages), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -966,7 +967,7 @@ void SQLExecutor::complexTypeList()
people.push_back(Person("LN1", "FN1", "ADDR1", 1)); people.push_back(Person("LN1", "FN1", "ADDR1", 1));
people.push_back(Person("LN2", "FN2", "ADDR2", 2)); people.push_back(Person("LN2", "FN2", "ADDR2", 2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -1006,7 +1007,7 @@ void SQLExecutor::insertList()
int count = 100; int count = 100;
{ {
Statement stmt((session() << "INSERT INTO Strings VALUES (?)", use(str))); Statement stmt((session() << formatSQL("INSERT INTO Strings VALUES (?)"), use(str)));
try { session() << "SELECT COUNT(*) FROM Strings", into(count), now; } try { session() << "SELECT COUNT(*) FROM Strings", into(count), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
@ -1050,7 +1051,7 @@ void SQLExecutor::insertEmptyList()
try try
{ {
session() << "INSERT INTO Strings VALUES (?)", use(str), now; session() << formatSQL("INSERT INTO Strings VALUES (?)"), use(str), now;
fail("empty collections should not work"); fail("empty collections should not work");
} catch (Poco::Exception&) } catch (Poco::Exception&)
{ {
@ -1076,7 +1077,7 @@ void SQLExecutor::simpleAccessDeque()
int count = 0; int count = 0;
std::string result; std::string result;
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastNames), use(firstNames), use(addresses), use(ages), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -1116,7 +1117,7 @@ void SQLExecutor::complexTypeDeque()
people.push_back(Person("LN1", "FN1", "ADDR1", 1)); people.push_back(Person("LN1", "FN1", "ADDR1", 1));
people.push_back(Person("LN2", "FN2", "ADDR2", 2)); people.push_back(Person("LN2", "FN2", "ADDR2", 2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -1156,7 +1157,7 @@ void SQLExecutor::insertDeque()
int count = 100; int count = 100;
{ {
Statement stmt((session() << "INSERT INTO Strings VALUES (?)", use(str))); Statement stmt((session() << formatSQL("INSERT INTO Strings VALUES (?)"), use(str)));
try { session() << "SELECT COUNT(*) FROM Strings", into(count), now; } try { session() << "SELECT COUNT(*) FROM Strings", into(count), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
@ -1217,7 +1218,7 @@ void SQLExecutor::affectedRows(const std::string& whereClause)
str.push_back("s3"); str.push_back("s3");
int count = 100; int count = 100;
Statement stmt1((session() << "INSERT INTO Strings VALUES(?)", use(str))); Statement stmt1((session() << formatSQL("INSERT INTO Strings VALUES(?)"), use(str)));
session() << "SELECT COUNT(*) FROM Strings", into(count), now; session() << "SELECT COUNT(*) FROM Strings", into(count), now;
assertTrue (count == 0); assertTrue (count == 0);
assertTrue (4 == stmt1.execute()); assertTrue (4 == stmt1.execute());
@ -1232,7 +1233,7 @@ void SQLExecutor::affectedRows(const std::string& whereClause)
std::string sql; std::string sql;
format(sql, "DELETE FROM Strings %s", whereClause); format(sql, "DELETE FROM Strings %s", whereClause);
Statement stmt4(session() << sql); Statement stmt4(session() << formatSQL(sql));
assertTrue (3 == stmt4.execute()); assertTrue (3 == stmt4.execute());
} }
@ -1240,7 +1241,7 @@ void SQLExecutor::affectedRows(const std::string& whereClause)
void SQLExecutor::insertSingleBulk() void SQLExecutor::insertSingleBulk()
{ {
int x = 0; int x = 0;
Statement stmt((session() << "INSERT INTO Strings VALUES (?)", use(x))); Statement stmt((session() << formatSQL("INSERT INTO Strings VALUES (?)"), use(x)));
for (x = 0; x < 100; ++x) for (x = 0; x < 100; ++x)
{ {
@ -1273,7 +1274,7 @@ void SQLExecutor::floats()
float data = 1.5f; float data = 1.5f;
float ret = 0.0f; float ret = 0.0f;
try { session() << "INSERT INTO Strings VALUES (?)", use(data), now; } try { session() << formatSQL("INSERT INTO Strings VALUES (?)"), use(data), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -1307,7 +1308,7 @@ void SQLExecutor::doubles()
double data = 1.5; double data = 1.5;
double ret = 0.0; double ret = 0.0;
try { session() << "INSERT INTO Strings VALUES (?)", use(data), now; } try { session() << formatSQL("INSERT INTO Strings VALUES (?)"), use(data), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -1341,7 +1342,7 @@ void SQLExecutor::uuids()
Poco::UUID data("49cf6461-9b62-4163-9659-5472ef73153d"); Poco::UUID data("49cf6461-9b62-4163-9659-5472ef73153d");
Poco::UUID ret; Poco::UUID ret;
try { session() << "INSERT INTO Strings VALUES (?)", use(data), now; } try { session() << formatSQL("INSERT INTO Strings VALUES (?)"), use(data), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -1377,7 +1378,7 @@ void SQLExecutor::insertSingleBulkVec()
for (int x = 0; x < 100; ++x) for (int x = 0; x < 100; ++x)
data.push_back(x); data.push_back(x);
Statement stmt((session() << "INSERT INTO Strings VALUES (?)", use(data))); Statement stmt((session() << formatSQL("INSERT INTO Strings VALUES (?)"), use(data)));
stmt.execute(); stmt.execute();
int count = 0; int count = 0;
@ -1409,7 +1410,7 @@ void SQLExecutor::limits()
data.push_back(x); data.push_back(x);
} }
try { session() << "INSERT INTO Strings VALUES (?)", use(data), now; } try { session() << formatSQL("INSERT INTO Strings VALUES (?)"), use(data), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -1440,7 +1441,7 @@ void SQLExecutor::limitZero()
data.push_back(x); data.push_back(x);
} }
try { session() << "INSERT INTO Strings VALUES (?)", use(data), now; } try { session() << formatSQL("INSERT INTO Strings VALUES (?)"), use(data), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -1467,7 +1468,7 @@ void SQLExecutor::limitOnce()
data.push_back(x); data.push_back(x);
} }
try { session() << "INSERT INTO Strings VALUES (?)", use(data), now; } try { session() << formatSQL("INSERT INTO Strings VALUES (?)"), use(data), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -1502,7 +1503,7 @@ void SQLExecutor::limitPrepare()
try try
{ {
Statement stmt = (session() << "INSERT INTO Strings VALUES (?)", use(data)); Statement stmt = (session() << formatSQL("INSERT INTO Strings VALUES (?)"), use(data));
assertTrue (100 == stmt.execute()); assertTrue (100 == stmt.execute());
} }
catch(DataException& ce) catch(DataException& ce)
@ -1561,7 +1562,7 @@ void SQLExecutor::prepare()
} }
{ {
Statement stmt((session() << "INSERT INTO Strings VALUES (?)", use(data))); Statement stmt((session() << formatSQL("INSERT INTO Strings VALUES (?)"), use(data)));
} }
// stmt should not have been executed when destroyed // stmt should not have been executed when destroyed
@ -1588,7 +1589,7 @@ void SQLExecutor::doBulkPerformance(Poco::UInt32 size)
try try
{ {
sw.start(); sw.start();
session() << "INSERT INTO MiscTest (First, Third, Fourth, Fifth) VALUES (?,?,?,?)", session() << formatSQL("INSERT INTO MiscTest (First, Third, Fourth, Fifth) VALUES (?,?,?,?)"),
use(strings), use(strings),
use(ints), use(ints),
use(floats), use(floats),
@ -1611,7 +1612,7 @@ void SQLExecutor::doBulkPerformance(Poco::UInt32 size)
try try
{ {
sw.restart(); sw.restart();
session() << "INSERT INTO MiscTest (First, Third, Fourth, Fifth) VALUES (?,?,?,?)", session() << formatSQL("INSERT INTO MiscTest (First, Third, Fourth, Fifth) VALUES (?,?,?,?)"),
use(strings, bulk), use(strings, bulk),
use(ints, bulk), use(ints, bulk),
use(floats, bulk), use(floats, bulk),
@ -1724,7 +1725,7 @@ void SQLExecutor::setSimple()
int count = 0; int count = 0;
std::string result; std::string result;
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } catch(DataException& ce) try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastNames), use(firstNames), use(addresses), use(ages), now; } catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
fail (__func__, __LINE__, __FILE__); fail (__func__, __LINE__, __FILE__);
@ -1761,7 +1762,7 @@ void SQLExecutor::setComplex()
people.insert(Person("LN1", "FN1", "ADDR1", 1)); people.insert(Person("LN1", "FN1", "ADDR1", 1));
people.insert(Person("LN2", "FN2", "ADDR2", 2)); people.insert(Person("LN2", "FN2", "ADDR2", 2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } catch(DataException& ce) try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; } catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
fail (__func__, __LINE__, __FILE__); fail (__func__, __LINE__, __FILE__);
@ -1798,7 +1799,7 @@ void SQLExecutor::setComplexUnique()
Person p2("LN2", "FN2", "ADDR2", 2); Person p2("LN2", "FN2", "ADDR2", 2);
people.push_back(p2); people.push_back(p2);
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } catch(DataException& ce) try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; } catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
fail (__func__, __LINE__, __FILE__); fail (__func__, __LINE__, __FILE__);
@ -1845,7 +1846,7 @@ void SQLExecutor::multiSetSimple()
int count = 0; int count = 0;
std::string result; std::string result;
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastNames), use(firstNames), use(addresses), use(ages), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -1890,7 +1891,7 @@ void SQLExecutor::multiSetComplex()
Person p2("LN2", "FN2", "ADDR2", 2); Person p2("LN2", "FN2", "ADDR2", 2);
people.insert(p2); people.insert(p2);
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -1927,7 +1928,7 @@ void SQLExecutor::mapComplex()
people.insert(std::make_pair("LN1", p1)); people.insert(std::make_pair("LN1", p1));
people.insert(std::make_pair("LN2", p2)); people.insert(std::make_pair("LN2", p2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -1967,7 +1968,7 @@ void SQLExecutor::mapComplexUnique()
people.insert(std::make_pair("LN1", p1)); people.insert(std::make_pair("LN1", p1));
people.insert(std::make_pair("LN2", p2)); people.insert(std::make_pair("LN2", p2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -2007,7 +2008,7 @@ void SQLExecutor::multiMapComplex()
people.insert(std::make_pair("LN1", p1)); people.insert(std::make_pair("LN1", p1));
people.insert(std::make_pair("LN2", p2)); people.insert(std::make_pair("LN2", p2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -2044,7 +2045,7 @@ void SQLExecutor::selectIntoSingle()
people.insert(std::make_pair("LN1", p1)); people.insert(std::make_pair("LN1", p1));
people.insert(std::make_pair("LN2", p2)); people.insert(std::make_pair("LN2", p2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -2080,7 +2081,7 @@ void SQLExecutor::selectIntoSingleStep()
people.insert(std::make_pair("LN1", p1)); people.insert(std::make_pair("LN1", p1));
people.insert(std::make_pair("LN2", p2)); people.insert(std::make_pair("LN2", p2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -2116,7 +2117,7 @@ void SQLExecutor::selectIntoSingleFail()
people.insert(std::make_pair("LN1", p1)); people.insert(std::make_pair("LN1", p1));
people.insert(std::make_pair("LN2", p2)); people.insert(std::make_pair("LN2", p2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -2151,7 +2152,7 @@ void SQLExecutor::lowerLimitOk()
people.insert(std::make_pair("LN1", p1)); people.insert(std::make_pair("LN1", p1));
people.insert(std::make_pair("LN1", p2)); people.insert(std::make_pair("LN1", p2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -2186,7 +2187,7 @@ void SQLExecutor::singleSelect()
people.insert(std::make_pair("LN1", p1)); people.insert(std::make_pair("LN1", p1));
people.insert(std::make_pair("LN1", p2)); people.insert(std::make_pair("LN1", p2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -2221,7 +2222,7 @@ void SQLExecutor::lowerLimitFail()
people.insert(std::make_pair("LN1", p1)); people.insert(std::make_pair("LN1", p1));
people.insert(std::make_pair("LN1", p2)); people.insert(std::make_pair("LN1", p2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -2256,7 +2257,7 @@ void SQLExecutor::combinedLimits()
people.insert(std::make_pair("LN1", p1)); people.insert(std::make_pair("LN1", p1));
people.insert(std::make_pair("LN1", p2)); people.insert(std::make_pair("LN1", p2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -2295,7 +2296,7 @@ void SQLExecutor::ranges()
people.insert(std::make_pair("LN1", p1)); people.insert(std::make_pair("LN1", p1));
people.insert(std::make_pair("LN1", p2)); people.insert(std::make_pair("LN1", p2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -2333,7 +2334,7 @@ void SQLExecutor::combinedIllegalLimits()
people.insert(std::make_pair("LN1", p1)); people.insert(std::make_pair("LN1", p1));
people.insert(std::make_pair("LN1", p2)); people.insert(std::make_pair("LN1", p2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -2368,7 +2369,7 @@ void SQLExecutor::illegalRange()
people.insert(std::make_pair("LN1", p1)); people.insert(std::make_pair("LN1", p1));
people.insert(std::make_pair("LN1", p2)); people.insert(std::make_pair("LN1", p2));
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(people), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(people), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -2423,7 +2424,7 @@ void SQLExecutor::blob(int bigSize, const std::string& blobPlaceholder)
CLOB img("0123456789", 10); CLOB img("0123456789", 10);
int count = 0; int count = 0;
try { session() << format("INSERT INTO Person VALUES (?,?,?,%s)", blobPlaceholder), try { session() << formatSQL(format("INSERT INTO Person VALUES (?,?,?,%s)", blobPlaceholder)),
use(lastName), use(firstName), use(address), use(img), now; } use(lastName), use(firstName), use(address), use(img), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
@ -2466,7 +2467,7 @@ void SQLExecutor::blob(int bigSize, const std::string& blobPlaceholder)
try try
{ {
session() << format("INSERT INTO Person VALUES (?,?,?,%s)", blobPlaceholder), session() << formatSQL(format("INSERT INTO Person VALUES (?,?,?,%s)", blobPlaceholder)),
use(lastName), use(firstName), use(address), use(big), now; use(lastName), use(firstName), use(address), use(big), now;
} }
catch(DataException& ce) catch(DataException& ce)
@ -2490,7 +2491,7 @@ void SQLExecutor::blobStmt()
CLOB blob("0123456789", 10); CLOB blob("0123456789", 10);
int count = 0; int count = 0;
Statement ins = (session() << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(blob)); Statement ins = (session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastName), use(firstName), use(address), use(blob));
ins.execute(); ins.execute();
try { session() << "SELECT COUNT(*) FROM Person", into(count), now; } try { session() << "SELECT COUNT(*) FROM Person", into(count), now; }
catch(DataException& ce) catch(DataException& ce)
@ -2534,7 +2535,7 @@ void SQLExecutor::recordSet()
{ {
Statement stmt = (session() << Statement stmt = (session() <<
"INSERT INTO Person VALUES (?,?,?,?)", formatSQL("INSERT INTO Person VALUES (?,?,?,?)"),
use(lastName), use(firstName), use(address), use(born), now); use(lastName), use(firstName), use(address), use(born), now);
RecordSet rset(stmt); RecordSet rset(stmt);
assertTrue (rset.rowCount() == 0); assertTrue (rset.rowCount() == 0);
@ -2557,7 +2558,7 @@ void SQLExecutor::recordSet()
{ {
Statement stmt = (session() << Statement stmt = (session() <<
"DELETE FROM Person WHERE born = ?", use(born), now); formatSQL("DELETE FROM Person WHERE born = ?"), use(born), now);
RecordSet rset(stmt); RecordSet rset(stmt);
assertTrue (rset.rowCount() == 0); assertTrue (rset.rowCount() == 0);
assertTrue (rset.affectedRowCount() == 1); assertTrue (rset.affectedRowCount() == 1);
@ -2565,7 +2566,7 @@ void SQLExecutor::recordSet()
{ {
Statement stmt = (session() << Statement stmt = (session() <<
"INSERT INTO Person VALUES (?,?,?,?)", formatSQL("INSERT INTO Person VALUES (?,?,?,?)"),
use(lastName), use(firstName), use(address), use(born), now); use(lastName), use(firstName), use(address), use(born), now);
RecordSet rset(stmt); RecordSet rset(stmt);
assertTrue (rset.rowCount() == 0); assertTrue (rset.rowCount() == 0);
@ -2574,7 +2575,7 @@ void SQLExecutor::recordSet()
{ {
Statement stmt = (session() << Statement stmt = (session() <<
"INSERT INTO Person VALUES (?,?,?,?)", formatSQL("INSERT INTO Person VALUES (?,?,?,?)"),
use(lastName), use(firstName), use(address), use(born2), now); use(lastName), use(firstName), use(address), use(born2), now);
RecordSet rset(stmt); RecordSet rset(stmt);
assertTrue (rset.rowCount() == 0); assertTrue (rset.rowCount() == 0);
@ -2620,7 +2621,7 @@ void SQLExecutor::dateTime()
DateTime born(1965, 6, 18, 5, 35, 1); DateTime born(1965, 6, 18, 5, 35, 1);
int count = 0; int count = 0;
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(born), now; } catch (ConnectionFailedException& ce){ std::cout << ce.displayText() << std::endl; fail (__func__, __LINE__, __FILE__); } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastName), use(firstName), use(address), use(born), now; } catch (ConnectionFailedException& ce){ std::cout << ce.displayText() << std::endl; fail (__func__, __LINE__, __FILE__); }
try { session() << "SELECT COUNT(*) FROM Person", into(count), now; } catch (ConnectionFailedException& ce){ std::cout << ce.displayText() << std::endl; fail (__func__, __LINE__, __FILE__); } try { session() << "SELECT COUNT(*) FROM Person", into(count), now; } catch (ConnectionFailedException& ce){ std::cout << ce.displayText() << std::endl; fail (__func__, __LINE__, __FILE__); }
@ -2647,7 +2648,7 @@ void SQLExecutor::date()
Date bornDate(1965, 6, 18); Date bornDate(1965, 6, 18);
int count = 0; int count = 0;
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"),
use(lastName), use(lastName),
use(firstName), use(firstName),
use(address), use(address),
@ -2697,7 +2698,7 @@ void SQLExecutor::time()
Time bornTime (5, 35, 1); Time bornTime (5, 35, 1);
int count = 0; int count = 0;
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"),
use(lastName), use(lastName),
use(firstName), use(firstName),
use(address), use(address),
@ -2743,7 +2744,7 @@ void SQLExecutor::tuples()
typedef Tuple<int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int> TupleType; typedef Tuple<int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int> TupleType;
TupleType t(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19); TupleType t(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19);
try { session() << "INSERT INTO Tuples VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", use(t), now; } try { session() << formatSQL("INSERT INTO Tuples VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"), use(t), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -2775,7 +2776,7 @@ void SQLExecutor::tupleVector()
v.push_back(t10); v.push_back(t10);
v.push_back(t100); v.push_back(t100);
try { session() << "INSERT INTO Tuples VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", use(v), now; } try { session() << formatSQL("INSERT INTO Tuples VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"), use(v), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -2812,7 +2813,7 @@ void SQLExecutor::internalExtraction()
v.push_back(Tuple<int, double, std::string>(3, 3.5f, "5")); v.push_back(Tuple<int, double, std::string>(3, 3.5f, "5"));
v.push_back(Tuple<int, double, std::string>(4, 4.5f, "6")); v.push_back(Tuple<int, double, std::string>(4, 4.5f, "6"));
try { session() << "INSERT INTO Vectors VALUES (?,?,?)", use(v), now; } try { session() << formatSQL("INSERT INTO Vectors VALUES (?,?,?)"), use(v), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -2940,7 +2941,7 @@ void SQLExecutor::filter(const std::string& query, const std::string& intFldName
v.push_back(Tuple<int, double, std::string>(3, 3.5f, "5")); v.push_back(Tuple<int, double, std::string>(3, 3.5f, "5"));
v.push_back(Tuple<int, double, std::string>(4, 4.5f, "6")); v.push_back(Tuple<int, double, std::string>(4, 4.5f, "6"));
try { session() << "INSERT INTO Vectors VALUES (?,?,?)", use(v), now; } try { session() << formatSQL("INSERT INTO Vectors VALUES (?,?,?)"), use(v), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -3036,7 +3037,7 @@ void SQLExecutor::internalBulkExtraction()
try try
{ {
session() << "INSERT INTO Person VALUES (?,?,?,?)", session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"),
use(lastName, bulk), use(lastName, bulk),
use(firstName, bulk), use(firstName, bulk),
use(address, bulk), use(address, bulk),
@ -3106,7 +3107,7 @@ void SQLExecutor::internalBulkExtractionUTF16()
try try
{ {
session() << "INSERT INTO Person VALUES (?,?,?,?)", session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"),
use(lastName, bulk), use(lastName, bulk),
use(firstName, bulk), use(firstName, bulk),
use(address, bulk), use(address, bulk),
@ -3157,7 +3158,7 @@ void SQLExecutor::internalStorageType()
v.push_back(Tuple<int, double, std::string>(3, 3.5f, "5")); v.push_back(Tuple<int, double, std::string>(3, 3.5f, "5"));
v.push_back(Tuple<int, double, std::string>(4, 4.5f, "6")); v.push_back(Tuple<int, double, std::string>(4, 4.5f, "6"));
try { session() << "INSERT INTO Vectors VALUES (?,?,?)", use(v), now; } try { session() << formatSQL("INSERT INTO Vectors VALUES (?,?,?)"), use(v), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -3222,7 +3223,7 @@ void SQLExecutor::internalStorageType()
void SQLExecutor::nulls() void SQLExecutor::nulls()
{ {
try { session() << "INSERT INTO NullTest (i,r,v) VALUES (?,?,?)", use(null), use(null), use(null), now; } try { session() << formatSQL("INSERT INTO NullTest (i,r,v) VALUES (?,?,?)"), use(null), use(null), use(null), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -3254,7 +3255,7 @@ void SQLExecutor::nulls()
double f = 1.5; double f = 1.5;
std::string s = "123"; std::string s = "123";
try { session() << "INSERT INTO NullTest (i, r, v) VALUES (?,?,?)", use(i), use(f), use(s), now; } try { session() << formatSQL("INSERT INTO NullTest (i, r, v) VALUES (?,?,?)"), use(i), use(f), use(s), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -3284,7 +3285,7 @@ void SQLExecutor::nulls()
i = 2; i = 2;
f = 3.4; f = 3.4;
try { session() << "INSERT INTO NullTest (i, r, v) VALUES (?,?,?)", use(i), use(null), use(null), now; } try { session() << formatSQL("INSERT INTO NullTest (i, r, v) VALUES (?,?,?)"), use(i), use(null), use(null), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -3314,7 +3315,7 @@ void SQLExecutor::nulls()
fail (__func__, __LINE__, __FILE__); fail (__func__, __LINE__, __FILE__);
} }
try { session() << "INSERT INTO NullTest (v) VALUES (?)", bind(""), now; } try { session() << formatSQL("INSERT INTO NullTest (v) VALUES (?)"), bind(""), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -3376,7 +3377,7 @@ void SQLExecutor::rowIterator()
RecordSet rset0(session(), "SELECT * FROM Vectors"); RecordSet rset0(session(), "SELECT * FROM Vectors");
assertTrue (rset0.begin() == rset0.end()); assertTrue (rset0.begin() == rset0.end());
try { session() << "INSERT INTO Vectors VALUES (?,?,?)", use(v), now; } try { session() << formatSQL("INSERT INTO Vectors VALUES (?,?,?)"), use(v), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -3416,7 +3417,7 @@ void SQLExecutor::rowIterator()
void SQLExecutor::stdVectorBool() void SQLExecutor::stdVectorBool()
{ {
bool b = false; bool b = false;
try { session() << "INSERT INTO BoolTest VALUES (?)", use(b), now; } try { session() << formatSQL("INSERT INTO BoolTest VALUES (?)"), use(b), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -3429,7 +3430,7 @@ void SQLExecutor::stdVectorBool()
session() << "DELETE FROM BoolTest", now; session() << "DELETE FROM BoolTest", now;
b = true; b = true;
try { session() << "INSERT INTO BoolTest VALUES (?)", use(b), now; } try { session() << formatSQL("INSERT INTO BoolTest VALUES (?)"), use(b), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -3447,7 +3448,7 @@ void SQLExecutor::stdVectorBool()
v.push_back(false); v.push_back(false);
v.push_back(true); v.push_back(true);
try { session() << "INSERT INTO BoolTest VALUES (?)", use(v), now; } try { session() << formatSQL("INSERT INTO BoolTest VALUES (?)"), use(v), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -3465,9 +3466,9 @@ void SQLExecutor::stdVectorBool()
t += *it ? 1 : 0; t += *it ? 1 : 0;
assertTrue (2 == t); assertTrue (2 == t);
try { session() << "SELECT * FROM BoolTest WHERE b = ?", out(v), now; fail("must fail"); } catch (BindingException&) { } try { session() << formatSQL("SELECT * FROM BoolTest WHERE b = ?"), out(v), now; fail("must fail"); } catch (BindingException&) { }
try { session() << "SELECT * FROM BoolTest WHERE b = ?", io(v), now; fail("must fail"); } catch (BindingException&) { } try { session() << formatSQL("SELECT * FROM BoolTest WHERE b = ?"), io(v), now; fail("must fail"); } catch (BindingException&) { }
RecordSet rset(session(), "SELECT * FROM BoolTest"); RecordSet rset(session(), "SELECT * FROM BoolTest");
@ -3483,7 +3484,7 @@ void SQLExecutor::asynchronous(int rowCount)
Session tmp = session(); Session tmp = session();
std::vector<int> data(rowCount); std::vector<int> data(rowCount);
Statement stmt = (tmp << "INSERT INTO Strings VALUES(?)", use(data)); Statement stmt = (tmp << formatSQL("INSERT INTO Strings VALUES(?)"), use(data));
Statement::Result result = stmt.executeAsync(); Statement::Result result = stmt.executeAsync();
assertTrue (!stmt.isAsync()); assertTrue (!stmt.isAsync());
result.wait(); result.wait();
@ -3573,7 +3574,7 @@ void SQLExecutor::any()
Session tmp = session(); Session tmp = session();
tmp << "INSERT INTO Anys VALUES (?, ?, ?)", use(i), use(f), use(s), now; tmp << formatSQL("INSERT INTO Anys VALUES (?, ?, ?)"), use(i), use(f), use(s), now;
int count = 0; int count = 0;
tmp << "SELECT COUNT(*) FROM Anys", into(count), now; tmp << "SELECT COUNT(*) FROM Anys", into(count), now;
@ -3596,7 +3597,7 @@ void SQLExecutor::dynamicAny()
Var s = "42"; Var s = "42";
Session tmp = session(); Session tmp = session();
tmp << "INSERT INTO Anys VALUES (?, ?, ?)", use(i), use(f), use(s), now; tmp << formatSQL("INSERT INTO Anys VALUES (?, ?, ?)"), use(i), use(f), use(s), now;
int count = 0; int count = 0;
tmp << "SELECT COUNT(*) FROM Anys", into(count), now; tmp << "SELECT COUNT(*) FROM Anys", into(count), now;
@ -3621,7 +3622,7 @@ void SQLExecutor::multipleResults(const std::string& sql)
people.push_back(Person("Simpson", "Bart", "Springfield", 10)); people.push_back(Person("Simpson", "Bart", "Springfield", 10));
people.push_back(Person("Simpson", "Lisa", "Springfield", 8)); people.push_back(Person("Simpson", "Lisa", "Springfield", 8));
people.push_back(Person("Simpson", "Maggie", "Springfield", 3)); people.push_back(Person("Simpson", "Maggie", "Springfield", 3));
session() << "INSERT INTO Person VALUES (?, ?, ?, ?)", use(people), now; session() << formatSQL("INSERT INTO Person VALUES (?, ?, ?, ?)"), use(people), now;
Person pHomer; Person pHomer;
int aHomer = 42, aLisa = 8; int aHomer = 42, aLisa = 8;
@ -3856,7 +3857,7 @@ void SQLExecutor::sessionTransaction(const std::string& connector, const std::st
session().setFeature("autoCommit", false); session().setFeature("autoCommit", false);
assertTrue (!session().getFeature("autoCommit")); assertTrue (!session().getFeature("autoCommit"));
session() << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastNames), use(firstNames), use(addresses), use(ages), now;
assertTrue (session().isTransaction()); assertTrue (session().isTransaction());
Statement stmt = (local << "SELECT COUNT(*) FROM Person", into(locCount), async, now); Statement stmt = (local << "SELECT COUNT(*) FROM Person", into(locCount), async, now);
@ -3876,16 +3877,17 @@ void SQLExecutor::sessionTransaction(const std::string& connector, const std::st
assertTrue (!session().isTransaction()); assertTrue (!session().isTransaction());
session().begin(); session().begin();
session() << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastNames), use(firstNames), use(addresses), use(ages), now;
assertTrue (session().isTransaction()); assertTrue (session().isTransaction());
stmt = (local << "SELECT COUNT(*) FROM Person", into(locCount), async, now); stmt = (local << "SELECT COUNT(*) FROM Person", into(locCount), async, now);
assertTrue (0 == locCount);
session().commit(); session().commit();
assertTrue (!session().isTransaction()); assertTrue (!session().isTransaction());
stmt.wait(); stmt.wait();
assertTrue (2 == locCount); // in general, no guarantee if stmt was executed before or after the commit
assertTrue (0 == locCount || 2 == locCount);
session() << "SELECT count(*) FROM Person", into(count), now; session() << "SELECT count(*) FROM Person", into(count), now;
assertTrue (2 == count); assertTrue (2 == count);
@ -3900,7 +3902,7 @@ void SQLExecutor::sessionTransactionNoAutoCommit(const std::string& connector, c
{ {
bool autoCommit = session().getFeature("autoCommit"); bool autoCommit = session().getFeature("autoCommit");
Session local("odbc", connect); Session local(connector, connect);
local.setFeature("autoCommit", false); local.setFeature("autoCommit", false);
assertTrue (!local.getFeature("autoCommit")); assertTrue (!local.getFeature("autoCommit"));
@ -3921,7 +3923,7 @@ void SQLExecutor::sessionTransactionNoAutoCommit(const std::string& connector, c
// no autoCommit session becomes transaction without explicit begin() // no autoCommit session becomes transaction without explicit begin()
assertTrue (!local.isTransaction()); assertTrue (!local.isTransaction());
assertTrue (!session().isTransaction()); assertTrue (!session().isTransaction());
local << "INSERT INTO Person VALUES (?,?,?,?)", local << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"),
use(lastNames), use(firstNames), use(addresses), use(ages), now; use(lastNames), use(firstNames), use(addresses), use(ages), now;
Statement stmt = (session() << "SELECT COUNT(*) FROM Person", Statement stmt = (session() << "SELECT COUNT(*) FROM Person",
into(count), async, now); into(count), async, now);
@ -3949,17 +3951,21 @@ void SQLExecutor::sessionTransactionNoAutoCommit(const std::string& connector, c
assertTrue (!local.isTransaction()); assertTrue (!local.isTransaction());
stmt.wait(); stmt.wait();
assertTrue (2 == count); // in general, there is no guarantee if stmt was exeuted before or after the commit
assertTrue (2 == count || 0 == count);
count = 0; count = 0;
stmt.reset(session()); stmt.reset(session());
session() << "SELECT COUNT(*) FROM Person", into(count), now;
assertTrue (2 == count);
count = 0;
assertTrue (!local.isTransaction()); assertTrue (!local.isTransaction());
assertTrue (!session().isTransaction()); assertTrue (!session().isTransaction());
local << "INSERT INTO Person VALUES (?,?,?,?)", local << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"),
use(lastNames), use(firstNames), use(addresses), use(ages), now; use(lastNames), use(firstNames), use(addresses), use(ages), now;
stmt = (session() << "SELECT COUNT(*) FROM Person", into(count), async, now); stmt = (session() << "SELECT COUNT(*) FROM Person", into(count), async, now);
local << "SELECT COUNT(*) FROM Person", into(locCount), now; local << "SELECT COUNT(*) FROM Person", into(locCount), now;
assertTrue (0 == count); // no guarantee if stmt is executed or not:
assertTrue (0 == count || 2 == count);
assertTrue (4 == locCount); assertTrue (4 == locCount);
#ifndef POCO_DATA_NO_SQL_PARSER #ifndef POCO_DATA_NO_SQL_PARSER
assertTrue (local.isTransaction()); assertTrue (local.isTransaction());
@ -3994,7 +4000,7 @@ void SQLExecutor::transaction(const std::string& connector, const std::string& c
return; return;
} }
Session local("odbc", connect); Session local(connector, connect);
local.setFeature("autoCommit", true); local.setFeature("autoCommit", true);
bool autoCommit = session().getFeature("autoCommit"); bool autoCommit = session().getFeature("autoCommit");
@ -4025,7 +4031,7 @@ void SQLExecutor::transaction(const std::string& connector, const std::string& c
assertTrue (trans.isActive()); assertTrue (trans.isActive());
assertTrue (session().isTransaction()); assertTrue (session().isTransaction());
session() << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastNames), use(firstNames), use(addresses), use(ages), now;
assertTrue (session().isTransaction()); assertTrue (session().isTransaction());
assertTrue (trans.isActive()); assertTrue (trans.isActive());
@ -4039,12 +4045,12 @@ void SQLExecutor::transaction(const std::string& connector, const std::string& c
session() << "SELECT count(*) FROM Person", into(count), now; session() << "SELECT count(*) FROM Person", into(count), now;
assertTrue (0 == count); assertTrue (0 == count);
assertTrue (!session().isTransaction()); assertTrue (!(session().impl()->shouldParse() && session().isTransaction()));
session().commit(); session().commit();
{ {
Transaction trans(session()); Transaction trans(session());
session() << "INSERT INTO Person VALUES (?,?,?,?)", use(lastNames), use(firstNames), use(addresses), use(ages), now; session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastNames), use(firstNames), use(addresses), use(ages), now;
Statement stmt1 = (local << "SELECT COUNT(*) FROM Person", into(locCount), async, now); Statement stmt1 = (local << "SELECT COUNT(*) FROM Person", into(locCount), async, now);
@ -4270,7 +4276,7 @@ void SQLExecutor::reconnect()
int count = 0; int count = 0;
std::string result; std::string result;
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(age), now; } try { session() << formatSQL("INSERT INTO Person VALUES (?,?,?,?)"), use(lastName), use(firstName), use(address), use(age), now; }
catch(DataException& ce) catch(DataException& ce)
{ {
std::cout << ce.displayText() << std::endl; std::cout << ce.displayText() << std::endl;
@ -4319,7 +4325,7 @@ void SQLExecutor::unicode(const std::string& dbConnString)
UTF16String wtext; UTF16String wtext;
Poco::UnicodeConverter::convert(text, wtext); Poco::UnicodeConverter::convert(text, wtext);
session() << "INSERT INTO UnicodeTable VALUES (?)", use(wtext), now; session() << formatSQL("INSERT INTO UnicodeTable VALUES (?)"), use(wtext), now;
wtext.clear(); wtext.clear();
text.clear(); text.clear();
session() << "SELECT str FROM UnicodeTable", into(wtext), now; session() << "SELECT str FROM UnicodeTable", into(wtext), now;
@ -4337,7 +4343,7 @@ void SQLExecutor::encoding(const std::string& dbConnString)
std::string latinText((const char*)latinChars); std::string latinText((const char*)latinChars);
std::string utf8TextIn((const char*)utf8Chars); std::string utf8TextIn((const char*)utf8Chars);
session(true) << "INSERT INTO Latin1Table VALUES (?)", use(utf8TextIn), now; session(true) << formatSQL("INSERT INTO Latin1Table VALUES (?)"), use(utf8TextIn), now;
std::string latinTextOut; std::string latinTextOut;
session() << "SELECT str FROM Latin1Table", into(latinTextOut), now; session() << "SELECT str FROM Latin1Table", into(latinTextOut), now;
@ -4352,7 +4358,7 @@ void SQLExecutor::encoding(const std::string& dbConnString)
std::string latinText2 = (const char*)latinChars2; std::string latinText2 = (const char*)latinChars2;
std::string utf8TextIn2 = (const char*)utf8Chars2; std::string utf8TextIn2 = (const char*)utf8Chars2;
session(true) << "INSERT INTO Latin1Table VALUES (?)", use(utf8TextIn2), now; session(true) << formatSQL("INSERT INTO Latin1Table VALUES (?)"), use(utf8TextIn2), now;
std::vector<std::string> textOutVec; std::vector<std::string> textOutVec;
session() << "SELECT str FROM Latin1Table", into(textOutVec), now; session() << "SELECT str FROM Latin1Table", into(textOutVec), now;
@ -4378,4 +4384,27 @@ void SQLExecutor::encoding(const std::string& dbConnString)
} }
} }
std::string SQLExecutor::formatSQL(const std::string& s) const
{
if (!_numberedPlaceHolders)
return std::string(s);
std::string r;
r.reserve(s.size());
int idx = 0;
for (char c: s)
{
if (c == '?')
{
r += '$';
r += std::to_string(++idx);
}
else
r += c;
}
return r;
}
} } } // Poco::Data::Test } } } // Poco::Data::Test

View File

@ -39,6 +39,8 @@
#include <set> #include <set>
using namespace Poco;
using namespace Poco::Data;
using namespace Poco::Data::Keywords; using namespace Poco::Data::Keywords;
@ -181,6 +183,47 @@ void DataTest::testFeatures()
{ {
Session sess(SessionFactory::instance().create("test", "cs")); Session sess(SessionFactory::instance().create("test", "cs"));
// AbstractSession features
assertTrue (sess.hasFeature("bulk"));
assertTrue (!sess.getFeature("bulk"));
sess.setFeature("bulk", true);
assertTrue (sess.getFeature("bulk"));
sess.setFeature("bulk", false);
assertTrue (!sess.getFeature("bulk"));
assertTrue (sess.hasFeature("emptyStringIsNull"));
assertTrue (!sess.getFeature("emptyStringIsNull"));
sess.setFeature("emptyStringIsNull", true);
assertTrue (sess.getFeature("emptyStringIsNull"));
sess.setFeature("emptyStringIsNull", false);
assertTrue (!sess.getFeature("emptyStringIsNull"));
assertTrue (sess.hasFeature("forceEmptyString"));
assertTrue (!sess.getFeature("forceEmptyString"));
sess.setFeature("forceEmptyString", true);
assertTrue (sess.getFeature("forceEmptyString"));
sess.setFeature("forceEmptyString", false);
assertTrue (!sess.getFeature("forceEmptyString"));
assertTrue (sess.hasFeature("sqlParse"));
assertTrue (sess.getFeature("sqlParse"));
sess.setFeature("sqlParse", false);
assertTrue (!sess.getFeature("sqlParse"));
sess.setFeature("sqlParse", true);
assertTrue (sess.getFeature("sqlParse"));
assertTrue (sess.hasFeature("autoCommit"));
assertTrue (sess.getFeature("autoCommit"));
sess.setFeature("autoCommit", false);
assertTrue (!sess.getFeature("autoCommit"));
sess.setFeature("autoCommit", true);
assertTrue (sess.getFeature("autoCommit"));
// Session implementation features
sess.setFeature("f1", true);
assertTrue (sess.getFeature("f1"));
assertTrue (sess.getFeature("f2"));
sess.setFeature("f1", true); sess.setFeature("f1", true);
assertTrue (sess.getFeature("f1")); assertTrue (sess.getFeature("f1"));
assertTrue (sess.getFeature("f2")); assertTrue (sess.getFeature("f2"));
@ -218,6 +261,16 @@ void DataTest::testProperties()
{ {
Session sess(SessionFactory::instance().create("test", "cs")); Session sess(SessionFactory::instance().create("test", "cs"));
// AbstractSession properties
sess.setProperty("storage", "myStorage"s);
Poco::Any s1 = sess.getProperty("storage");
assertTrue (Poco::AnyCast<std::string>(s1) == "myStorage"s);
sess.setProperty("handle", 1);
Poco::Any h1 = sess.getProperty("handle");
assertTrue (Poco::AnyCast<int>(h1) == 1);
// Session implementation properties
sess.setProperty("p1", 1); sess.setProperty("p1", 1);
Poco::Any v1 = sess.getProperty("p1"); Poco::Any v1 = sess.getProperty("p1");
assertTrue (Poco::AnyCast<int>(v1) == 1); assertTrue (Poco::AnyCast<int>(v1) == 1);

View File

@ -21,15 +21,13 @@ namespace Test {
SessionImpl::SessionImpl(const std::string& init, std::size_t timeout): SessionImpl::SessionImpl(const std::string& init, std::size_t timeout):
Poco::Data::AbstractSessionImpl<SessionImpl>(init, timeout), Poco::Data::AbstractSessionImpl<SessionImpl>(init, timeout),
_f(false), _f(false),
_connected(true), _connected(true)
_autoCommit(true)
{ {
addFeature("f1", &SessionImpl::setF, &SessionImpl::getF); addFeature("f1", &SessionImpl::setF, &SessionImpl::getF);
addFeature("f2", 0, &SessionImpl::getF); addFeature("f2", 0, &SessionImpl::getF);
addFeature("f3", &SessionImpl::setF, 0); addFeature("f3", &SessionImpl::setF, 0);
addFeature("throwOnHasNext", &SessionImpl::setThrowOnHasNext, &SessionImpl::getThrowOnHasNext); addFeature("throwOnHasNext", &SessionImpl::setThrowOnHasNext, &SessionImpl::getThrowOnHasNext);
addFeature("connected", &SessionImpl::setConnected, &SessionImpl::getConnected); addFeature("connected", &SessionImpl::setConnected, &SessionImpl::getConnected);
addFeature("autoCommit", &SessionImpl::setAutoCommit, &SessionImpl::getAutoCommit);
addProperty("p1", &SessionImpl::setP, &SessionImpl::getP); addProperty("p1", &SessionImpl::setP, &SessionImpl::getP);
addProperty("p2", 0, &SessionImpl::getP); addProperty("p2", 0, &SessionImpl::getP);
addProperty("p3", &SessionImpl::setP, &SessionImpl::getP); addProperty("p3", &SessionImpl::setP, &SessionImpl::getP);
@ -148,18 +146,6 @@ void SessionImpl::setConnected(const std::string&, bool value)
} }
bool SessionImpl::getAutoCommit(const std::string& name) const
{
return _autoCommit;
}
void SessionImpl::setAutoCommit(const std::string&, bool value)
{
_autoCommit = value;
}
void SessionImpl::setF(const std::string&, bool value) void SessionImpl::setF(const std::string&, bool value)
{ {
_f = value; _f = value;

View File

@ -96,10 +96,6 @@ public:
/// This is normally done by implementation /// This is normally done by implementation
/// when a database connection loss is detected. /// when a database connection loss is detected.
void setAutoCommit(const std::string& name, bool value);
bool getAutoCommit(const std::string& name) const;
/// Sets/gets the autoCommit property.
void setF(const std::string& name, bool value); void setF(const std::string& name, bool value);
bool getF(const std::string& name) const; bool getF(const std::string& name) const;
void setThrowOnHasNext(const std::string& name, bool value); void setThrowOnHasNext(const std::string& name, bool value);
@ -112,7 +108,6 @@ private:
bool _throwOnHasNext = false; bool _throwOnHasNext = false;
Poco::Any _p; Poco::Any _p;
bool _connected; bool _connected;
bool _autoCommit = true;
std::string _connectionString; std::string _connectionString;
}; };

View File

@ -97,7 +97,7 @@ namespace Poco {
using namespace std::literals; using namespace std::literals;
} // Poco } // namespace Poco
// //

View File