Browse Source

[SF 2580108] Improve transaction handling

Aleksandar Fabijanic 17 years ago
parent
commit
d11f007d23
41 changed files with 3484 additions and 2142 deletions
  1. 9 0
      Data/Data_VS71.vcproj
  2. 8 4
      Data/Data_VS80.vcproj
  3. 8 4
      Data/Data_VS90.vcproj
  4. 1 1
      Data/Makefile
  5. 3 0
      Data/MySQL/MySQL_VS90.sln
  6. 1 1
      Data/MySQL/MySQL_VS90.vcproj
  7. 2 2
      Data/MySQL/include/Poco/Data/MySQL/SessionHandle.h
  8. 213 138
      Data/MySQL/include/Poco/Data/MySQL/SessionImpl.h
  9. 2 2
      Data/MySQL/include/Poco/Data/MySQL/StatementExecutor.h
  10. 7 26
      Data/MySQL/src/MySQLStatementImpl.cpp
  11. 18 42
      Data/MySQL/src/SessionHandle.cpp
  12. 271 233
      Data/MySQL/src/SessionImpl.cpp
  13. 16 55
      Data/MySQL/src/StatementExecutor.cpp
  14. 1 1
      Data/MySQL/testsuite/TestSuite_VS90.vcproj
  15. 20 21
      Data/MySQL/testsuite/src/MySQLTest.cpp
  16. 3 0
      Data/MySQL/testsuite/src/MySQLTest.h
  17. 1805 1520
      Data/MySQL/testsuite/src/SQLExecutor.cpp
  18. 5 0
      Data/MySQL/testsuite/src/SQLExecutor.h
  19. 36 15
      Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h
  20. 145 14
      Data/ODBC/src/SessionImpl.cpp
  21. 3 0
      Data/ODBC/testsuite/src/ODBCDB2Test.cpp
  22. 3 0
      Data/ODBC/testsuite/src/ODBCMySQLTest.cpp
  23. 3 0
      Data/ODBC/testsuite/src/ODBCOracleTest.cpp
  24. 3 0
      Data/ODBC/testsuite/src/ODBCPostgreSQLTest.cpp
  25. 3 0
      Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp
  26. 3 0
      Data/ODBC/testsuite/src/ODBCSQLiteTest.cpp
  27. 46 0
      Data/ODBC/testsuite/src/ODBCTest.cpp
  28. 4 1
      Data/ODBC/testsuite/src/ODBCTest.h
  29. 328 0
      Data/ODBC/testsuite/src/SQLExecutor.cpp
  30. 6 1
      Data/ODBC/testsuite/src/SQLExecutor.h
  31. 24 1
      Data/SQLite/include/Poco/Data/SQLite/SessionImpl.h
  32. 29 0
      Data/SQLite/src/SessionImpl.cpp
  33. 3 38
      Data/include/Poco/Data/AutoTransaction.h
  34. 5 0
      Data/include/Poco/Data/PooledSessionImpl.h
  35. 52 0
      Data/include/Poco/Data/Session.h
  36. 17 0
      Data/include/Poco/Data/SessionImpl.h
  37. 242 0
      Data/include/Poco/Data/Transaction.h
  38. 30 0
      Data/src/PooledSessionImpl.cpp
  39. 60 22
      Data/src/Transaction.cpp
  40. 29 0
      Data/testsuite/src/SessionImpl.cpp
  41. 17 0
      Data/testsuite/src/SessionImpl.h

+ 9 - 0
Data/Data_VS71.vcproj

@@ -270,6 +270,9 @@
 				<File
 					RelativePath=".\include\Poco\Data\AbstractSessionImpl.h">
 				</File>
+				<File
+					RelativePath=".\include\Poco\Data\AutoTransaction.h">
+				</File>
 				<File
 					RelativePath=".\include\Poco\Data\Binder.h">
 				</File>
@@ -366,6 +369,9 @@
 				<File
 					RelativePath=".\include\Poco\Data\Time.h">
 				</File>
+				<File
+					RelativePath=".\include\Poco\Data\Transaction.h">
+				</File>
 				<File
 					RelativePath=".\include\Poco\Data\TypeHandler.h">
 				</File>
@@ -454,6 +460,9 @@
 				<File
 					RelativePath=".\src\Time.cpp">
 				</File>
+				<File
+					RelativePath=".\src\Transaction.cpp">
+				</File>
 			</Filter>
 		</Filter>
 		<Filter

+ 8 - 4
Data/Data_VS80.vcproj

@@ -505,6 +505,10 @@
 					RelativePath=".\include\Poco\Data\Time.h"
 					>
 				</File>
+				<File
+					RelativePath=".\include\Poco\Data\Transaction.h"
+					>
+				</File>
 				<File
 					RelativePath=".\include\Poco\Data\TypeHandler.h"
 					>
@@ -537,10 +541,6 @@
 					RelativePath=".\src\AbstractPreparator.cpp"
 					>
 				</File>
-				<File
-					RelativePath=".\src\AutoTransaction.cpp"
-					>
-				</File>
 				<File
 					RelativePath=".\src\Bulk.cpp"
 					>
@@ -625,6 +625,10 @@
 					RelativePath=".\src\Time.cpp"
 					>
 				</File>
+				<File
+					RelativePath=".\src\Transaction.cpp"
+					>
+				</File>
 			</Filter>
 		</Filter>
 		<Filter

+ 8 - 4
Data/Data_VS90.vcproj

@@ -510,6 +510,10 @@
 					RelativePath=".\include\Poco\Data\Time.h"
 					>
 				</File>
+				<File
+					RelativePath=".\include\Poco\Data\Transaction.h"
+					>
+				</File>
 				<File
 					RelativePath=".\include\Poco\Data\TypeHandler.h"
 					>
@@ -542,10 +546,6 @@
 					RelativePath=".\src\AbstractPreparator.cpp"
 					>
 				</File>
-				<File
-					RelativePath=".\src\AutoTransaction.cpp"
-					>
-				</File>
 				<File
 					RelativePath=".\src\Bulk.cpp"
 					>
@@ -630,6 +630,10 @@
 					RelativePath=".\src\Time.cpp"
 					>
 				</File>
+				<File
+					RelativePath=".\src\Transaction.cpp"
+					>
+				</File>
 			</Filter>
 		</Filter>
 		<Filter

+ 1 - 1
Data/Makefile

@@ -9,7 +9,7 @@
 include $(POCO_BASE)/build/rules/global
 
 objects = AbstractBinder AbstractBinding AbstractExtraction AbstractExtractor \
-	AbstractPreparation AbstractPreparator ArchiveStrategy AutoTransaction \
+	AbstractPreparation AbstractPreparator ArchiveStrategy Transaction \
 	Bulk Connector DataException Date Limit MetaColumn \
 	PooledSessionHolder PooledSessionImpl Position \
 	Range RecordSet Row RowFilter RowFormatter RowIterator \

+ 3 - 0
Data/MySQL/MySQL_VS90.sln

@@ -4,6 +4,9 @@ Microsoft Visual Studio Solution File, Format Version 10.00
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MySQL", "MySQL_VS90.vcproj", "{D9C692A6-D089-4269-B444-C445ED192F0D}"
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestSuite", "testsuite\TestSuite_VS90.vcproj", "{1B30A91B-375F-11DB-837B-00123FC423B5}"
+	ProjectSection(ProjectDependencies) = postProject
+		{D9C692A6-D089-4269-B444-C445ED192F0D} = {D9C692A6-D089-4269-B444-C445ED192F0D}
+	EndProjectSection
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution

+ 1 - 1
Data/MySQL/MySQL_VS90.vcproj

@@ -41,7 +41,7 @@
 			<Tool
 				Name="VCCLCompilerTool"
 				Optimization="0"
-				AdditionalIncludeDirectories=".\include;.\src;..\..\Foundation\include;..\..\Data\include"
+				AdditionalIncludeDirectories=".\include;.\src;..\..\Foundation\include;..\..\Data\include;.\include\Poco\Data\MySQL\mysql"
 				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MYSQL_EXPORTS;POCO_DLL;NO_TCL;THREADSAFE;__LCC__"
 				MinimalRebuild="true"
 				BasicRuntimeChecks="3"

+ 2 - 2
Data/MySQL/include/Poco/Data/MySQL/SessionHandle.h

@@ -89,7 +89,7 @@ private:
 
 private:
 	
-	MYSQL* h;
+	MYSQL* _pHandle;
 };
 
 
@@ -99,7 +99,7 @@ private:
 
 inline SessionHandle::operator MYSQL* ()
 {
-	return h;
+	return _pHandle;
 }
 
 

+ 213 - 138
Data/MySQL/include/Poco/Data/MySQL/SessionImpl.h

@@ -1,146 +1,221 @@
-//
-// 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"
-
-
-namespace Poco {
-namespace Data {
-namespace MySQL {
-
-
-class MySQL_API SessionImpl: public Poco::Data::AbstractSessionImpl<SessionImpl>
-	/// Implements SessionImpl interface
-{
-public:
-
-	SessionImpl(const std::string& connectionString);
-		/// Creates the SessionImpl. Opens a connection to the database
-		///
-		/// Connection string format:
-		///     <str> == <assignment> | <assignment> ';' <str>
-		///     <assignment> == <name> '=' <value>
-		///     <name> == 'host' | 'port' | 'user' | 'password' | 'db' } 'compress' | 'auto-reconnect'
-		///     <value> == [~;]*
-		///
-		/// 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<SessionImpl>
+	/// 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:
+		///     <str> == <assignment> | <assignment> ';' <str>
+		///     <assignment> == <name> '=' <value>
+		///     <name> == 'host' | 'port' | 'user' | 'password' | 'db' } 'compress' | 'auto-reconnect'
+		///     <value> == [~;]*
+		///
+		/// 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 <typename T>
+	inline T& getValue(MYSQL_BIND* pResult, T& val)
+	{
+		return val = *((T*) pResult->buffer);
+	}
+
+	template <typename T>
+	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<T>(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

+ 2 - 2
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;
 }
 
 

+ 7 - 26
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;
 }
 

+ 18 - 42
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);
 } 
 
 

+ 271 - 233
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<SessionImpl>(toLower(connectionString)),
-	_mysql(0),
-	_connected(false),
-	_inTransaction(0)
-{
-	addProperty("insertId", 
-		&SessionImpl::setInsertId, 
-		&SessionImpl::getInsertId);
-
-	std::map<std::string, std::string> 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<SessionImpl>(toLower(connectionString)),
+	_handle(0),
+	_connected(false),
+	_inTransaction(false)
+{
+	addProperty("insertId", 
+		&SessionImpl::setInsertId, 
+		&SessionImpl::getInsertId);
+
+	std::map<std::string, std::string> 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;
+}
+
+
+}}}

+ 16 - 55
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<unsigned int>(query.length()));
-
-    if (res != 0)
-    {
-        throw StatementException("mysql_stmt_prepare error", h, query);
-    }
+    if (mysql_stmt_prepare(_pHandle, query.c_str(), static_cast<unsigned int>(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<unsigned int>(n), 0);
+	int res = mysql_stmt_fetch_column(_pHandle, bind, static_cast<unsigned int>(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);

+ 1 - 1
Data/MySQL/testsuite/TestSuite_VS90.vcproj

@@ -41,7 +41,7 @@
 			<Tool
 				Name="VCCLCompilerTool"
 				Optimization="0"
-				AdditionalIncludeDirectories="..\include;..\..\..\Foundation\include;..\..\..\CppUnit\include;..\..\..\CppUnit\WinTestRunner\include;..\..\..\Data\include"
+				AdditionalIncludeDirectories="..\include;..\..\..\Foundation\include;..\..\..\CppUnit\include;..\..\..\CppUnit\WinTestRunner\include;..\..\..\Data\include;..\..\MySQL\include\Poco\Data\MySQL\mysql"
 				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;POCO_DLL;WINVER=0x0500"
 				MinimalRebuild="true"
 				BasicRuntimeChecks="3"

+ 20 - 21
Data/MySQL/testsuite/src/MySQLTest.cpp

@@ -79,27 +79,6 @@ MySQLTest::MySQLTest(const std::string& name):
 	CppUnit::TestCase(name)
 {
     MySQL::Connector::registerConnector();
-
-	/*static bool beenHere = false;
-	
-
-	if (!beenHere)
-	{
-		try
-		{
-			_pSession = new Session(SessionFactory::instance().create(MySQL::Connector::KEY, _dbConnString));
-		}catch (ConnectionException& ex)
-		{
-			std::cout << "!!! WARNING: Connection failed. MySQL tests will fail !!!" << std::endl;
-			std::cout << ex.toString() << std::endl;
-		}
-
-		if (_pSession && _pSession->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;
 }

+ 3 - 0
Data/MySQL/testsuite/src/MySQLTest.h

@@ -113,6 +113,9 @@ public:
 	void testNullableString();
 	void testTupleWithNullable();
 	
+	void testSessionTransaction();
+	void testTransaction();
+
 	void setUp();
 	void tearDown();
 

+ 1805 - 1520
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 <Winsock2.h>
-#endif 
-
-#include <mysql.h>
-#include <iostream>
-
-
-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<Person>
-{
-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<unsigned long>(sql.length()));
-	
-	sql = tableCreateString;
-	rc = mysql_stmt_prepare(hstmt, sql.c_str(), static_cast<unsigned long>(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<unsigned long>(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<char*>(str[0].c_str());
-	bind_param[0].buffer_length = static_cast<unsigned long>(str[0].length());
-	bind_param[0].buffer_type   = MYSQL_TYPE_STRING;
-
-	bind_param[1].buffer        = const_cast<char*>(str[1].c_str());
-	bind_param[1].buffer_length = static_cast<unsigned long>(str[1].length());
-	bind_param[1].buffer_type   = MYSQL_TYPE_STRING;
-
-	bind_param[2].buffer        = const_cast<char*>(str[2].c_str());
-	bind_param[2].buffer_length = static_cast<unsigned long>(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<unsigned long>(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<unsigned long>(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<std::string> lastNames;
-	std::vector<std::string> firstNames;
-	std::vector<std::string> addresses;
-	std::vector<int> 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<std::string> lastNamesR;
-	std::vector<std::string> firstNamesR;
-	std::vector<std::string> addressesR;
-	std::vector<int> 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<Person> 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<Person> 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<std::string> 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<std::string> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<std::string> lastNames;
-	std::set<std::string> firstNames;
-	std::set<std::string> addresses;
-	std::set<int> 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<std::string> lastNamesR;
-	std::set<std::string> firstNamesR;
-	std::set<std::string> addressesR;
-	std::set<int> 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<Person> 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<Person> 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<Person> 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<Person> 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<std::string> lastNames;
-	std::multiset<std::string> firstNames;
-	std::multiset<std::string> addresses;
-	std::multiset<int> 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<std::string> lastNamesR;
-	std::multiset<std::string> firstNamesR;
-	std::multiset<std::string> addressesR;
-	std::multiset<int> 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<Person> 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<Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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 <Person> 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<std::string, Person> 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 <Person> 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<std::string, Person> 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<std::string, Person> 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<char> 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<int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int> 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<int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int> 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<int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int> 
-		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<TupleType> 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<Tuple<int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int> > 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<Tuple<int, double, std::string> > v;
-	v.push_back(Tuple<int, double, std::string>(1, 1.5f, "3"));
-	v.push_back(Tuple<int, double, std::string>(2, 2.5f, "4"));
-	v.push_back(Tuple<int, double, std::string>(3, 3.5f, "5"));
-	v.push_back(Tuple<int, double, std::string>(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<int>(0,0);
-		assert (1 == i);
-
-		std::string s = rset.value(0,0);
-		assert ("1" == s);
-
-		int a = rset.value<int>(0,2);
-		assert (3 == a);
-
-		try
-		{
-			double d = rset.value<double>(1,1);
-			assert (2.5 == d);
-		}
-		catch (BadCastException&)
-		{
-			float f = rset.value<float>(1,1);
-			assert (2.5 == f);
-		}
-
-		s = rset.value<std::string>(2,2);
-		assert ("5" == s);
-		i = rset.value("str0", 2);
-		assert (5 == i);
-		
-		const Column<int>& col = rset.column<int>(0);
-		Column<int>::Iterator it = col.begin();
-		Column<int>::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<int>(0,0);
-			assert (4 == i);
-		}
-		catch(BadCastException&)
-		{
-			try
-			{
-				//this is for Oracle
-				double i = rset.value<double>(0,0);
-				assert (4 == int(i));
-			}
-			catch(BadCastException&)
-			{
-				//this is for PostgreSQL
-				Poco::Int64 big = rset.value<Poco::Int64>(0,0);
-				assert (4 == big);
-			}
-		}
-
-		s = rset.value("cnt", 0).convert<std::string>();
-		assert ("4" == s);
-
-		try { const Column<int>& col1 = rset.column<int>(100); fail ("must fail"); }
-		catch (RangeException&) { }
-
-		try	{ rset.value<std::string>(0,0); fail ("must fail"); }
-		catch (BadCastException&) {	}
-		
-		stmt = (*_pSession << "DELETE FROM Vectors", now);
-		rset = stmt;
-
-		try { const Column<int>& col1 = rset.column<int>(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 <Winsock2.h>
+#endif 
+
+#include <mysql.h>
+#include <iostream>
+
+
+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<Person>
+{
+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<unsigned long>(sql.length()));
+	
+	sql = tableCreateString;
+	rc = mysql_stmt_prepare(hstmt, sql.c_str(), static_cast<unsigned long>(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<unsigned long>(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<char*>(str[0].c_str());
+	bind_param[0].buffer_length = static_cast<unsigned long>(str[0].length());
+	bind_param[0].buffer_type   = MYSQL_TYPE_STRING;
+
+	bind_param[1].buffer        = const_cast<char*>(str[1].c_str());
+	bind_param[1].buffer_length = static_cast<unsigned long>(str[1].length());
+	bind_param[1].buffer_type   = MYSQL_TYPE_STRING;
+
+	bind_param[2].buffer        = const_cast<char*>(str[2].c_str());
+	bind_param[2].buffer_length = static_cast<unsigned long>(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<unsigned long>(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<unsigned long>(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<std::string> lastNames;
+	std::vector<std::string> firstNames;
+	std::vector<std::string> addresses;
+	std::vector<int> 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<std::string> lastNamesR;
+	std::vector<std::string> firstNamesR;
+	std::vector<std::string> addressesR;
+	std::vector<int> 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<Person> 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<Person> 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<std::string> 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<std::string> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<std::string> lastNames;
+	std::set<std::string> firstNames;
+	std::set<std::string> addresses;
+	std::set<int> 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<std::string> lastNamesR;
+	std::set<std::string> firstNamesR;
+	std::set<std::string> addressesR;
+	std::set<int> 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<Person> 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<Person> 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<Person> 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<Person> 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<std::string> lastNames;
+	std::multiset<std::string> firstNames;
+	std::multiset<std::string> addresses;
+	std::multiset<int> 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<std::string> lastNamesR;
+	std::multiset<std::string> firstNamesR;
+	std::multiset<std::string> addressesR;
+	std::multiset<int> 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<Person> 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<Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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<std::string, Person> 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 <Person> 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<std::string, Person> 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 <Person> 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<std::string, Person> 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<std::string, Person> 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<char> 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<int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int> 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<int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int> 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<int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int> 
+		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<TupleType> 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<Tuple<int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int> > 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<Tuple<int, double, std::string> > v;
+	v.push_back(Tuple<int, double, std::string>(1, 1.5f, "3"));
+	v.push_back(Tuple<int, double, std::string>(2, 2.5f, "4"));
+	v.push_back(Tuple<int, double, std::string>(3, 3.5f, "5"));
+	v.push_back(Tuple<int, double, std::string>(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<int>(0,0);
+		assert (1 == i);
+
+		std::string s = rset.value(0,0);
+		assert ("1" == s);
+
+		int a = rset.value<int>(0,2);
+		assert (3 == a);
+
+		try
+		{
+			double d = rset.value<double>(1,1);
+			assert (2.5 == d);
+		}
+		catch (BadCastException&)
+		{
+			float f = rset.value<float>(1,1);
+			assert (2.5 == f);
+		}
+
+		s = rset.value<std::string>(2,2);
+		assert ("5" == s);
+		i = rset.value("str0", 2);
+		assert (5 == i);
+		
+		const Column<int>& col = rset.column<int>(0);
+		Column<int>::Iterator it = col.begin();
+		Column<int>::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<int>(0,0);
+			assert (4 == i);
+		}
+		catch(BadCastException&)
+		{
+			try
+			{
+				//this is for Oracle
+				double i = rset.value<double>(0,0);
+				assert (4 == int(i));
+			}
+			catch(BadCastException&)
+			{
+				//this is for PostgreSQL
+				Poco::Int64 big = rset.value<Poco::Int64>(0,0);
+				assert (4 == big);
+			}
+		}
+
+		s = rset.value("cnt", 0).convert<std::string>();
+		assert ("4" == s);
+
+		try { const Column<int>& col1 = rset.column<int>(100); fail ("must fail"); }
+		catch (RangeException&) { }
+
+		try	{ rset.value<std::string>(0,0); fail ("must fail"); }
+		catch (BadCastException&) {	}
+		
+		stmt = (*_pSession << "DELETE FROM Vectors", now);
+		rset = stmt;
+
+		try { const Column<int>& col1 = rset.column<int>(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<std::string> lastNames;
+	std::vector<std::string> firstNames;
+	std::vector<std::string> addresses;
+	std::vector<int> 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<std::string> lastNames;
+	std::vector<std::string> firstNames;
+	std::vector<std::string> addresses;
+	std::vector<int> 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<std::string> 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);
+}

+ 5 - 0
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;
 };
 

+ 36 - 15
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 <windows.h>
 #endif
@@ -63,6 +64,13 @@ class ODBC_API SessionImpl: public Poco::Data::AbstractSessionImpl<SessionImpl>
 	/// 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
 
 

+ 145 - 14
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 <sqlext.h>
 
@@ -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.");
+
+		_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 (SQL_TC_NONE != ret);
+	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;
 }
 
 

+ 3 - 0
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;
 	}

+ 3 - 0
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;
 	}

+ 3 - 0
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;
 	}

+ 3 - 0
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;
 	}

+ 3 - 0
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;
 	}

+ 3 - 0
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;
 	}

+ 46 - 0
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,

+ 4 - 1
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;
 

+ 328 - 0
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<std::string> lastNames;
+	std::vector<std::string> firstNames;
+	std::vector<std::string> addresses;
+	std::vector<int> 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<std::string> lastNames;
+	std::vector<std::string> firstNames;
+	std::vector<std::string> addresses;
+	std::vector<int> 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<std::string> 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);
+}

+ 6 - 1
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;
 };
 

+ 24 - 1
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;

+ 29 - 0
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 <cstdlib>
 
@@ -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);

+ 3 - 38
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

+ 5 - 0
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);

+ 52 - 0
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<SessionImpl> 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)
 {

+ 17 - 0
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.
 

+ 242 - 0
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 <typename T>
+	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 <typename T>
+	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<std::string>& 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 <typename T>
+	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<T> 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

+ 30 - 0
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();

+ 60 - 22
Data/src/AutoTransaction.cpp → 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<std::string>& sql)
+{
+	try
+	{
+		std::vector<std::string>::const_iterator it = sql.begin();
+		std::vector<std::string>::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();
 }
 
 

+ 29 - 0
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;

+ 17 - 0
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.