[SF 2580108] Improve transaction handling

This commit is contained in:
Aleksandar Fabijanic 2009-02-08 23:14:54 +00:00
parent ad543acb58
commit d11f007d23
41 changed files with 3484 additions and 2142 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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

View File

@ -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"

View File

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

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}
}}}

View File

@ -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);

View File

@ -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"

View File

@ -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;
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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;
};

View File

@ -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

View File

@ -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.");
return (SQL_TC_NONE != ret);
_canTransact = (SQL_TC_NONE != ret) ?
ODBC_TXN_CAPABILITY_TRUE :
ODBC_TXN_CAPABILITY_FALSE;
}
return ODBC_TXN_CAPABILITY_TRUE == _canTransact;
}
void SessionImpl::setTransactionIsolation(Poco::UInt32 ti)
{
Poco::UInt32 isolation = 0;
if (ti & Session::TRANSACTION_READ_UNCOMMITTED)
isolation |= SQL_TXN_READ_UNCOMMITTED;
if (ti & Session::TRANSACTION_READ_COMMITTED)
isolation |= SQL_TXN_READ_COMMITTED;
if (ti & Session::TRANSACTION_REPEATABLE_READ)
isolation |= SQL_TXN_REPEATABLE_READ;
if (ti & Session::TRANSACTION_SERIALIZABLE)
isolation |= SQL_TXN_SERIALIZABLE;
checkError(SQLSetConnectAttr(_db, SQL_ATTR_TXN_ISOLATION, (SQLPOINTER) isolation, 0));
}
Poco::UInt32 SessionImpl::getTransactionIsolation()
{
SQLUINTEGER isolation = 0;
checkError(SQLGetConnectAttr(_db, SQL_ATTR_TXN_ISOLATION,
&isolation,
0,
0));
return transactionIsolation(isolation);
}
bool SessionImpl::hasTransactionIsolation(Poco::UInt32 ti)
{
if (isTransaction()) throw InvalidAccessException();
bool retval = true;
Poco::UInt32 old = getTransactionIsolation();
try { setTransactionIsolation(ti); }
catch (Poco::Exception&) { retval = false; }
setTransactionIsolation(old);
return retval;
}
Poco::UInt32 SessionImpl::getDefaultTransactionIsolation()
{
SQLUINTEGER isolation = 0;
checkError(SQLGetInfo(_db, SQL_DEFAULT_TXN_ISOLATION,
&isolation,
0,
0));
return transactionIsolation(isolation);
}
Poco::UInt32 SessionImpl::transactionIsolation(SQLUINTEGER isolation)
{
if (0 == isolation)
throw InvalidArgumentException("transactionIsolation(SQLUINTEGER)");
Poco::UInt32 ret = 0;
if (isolation & SQL_TXN_READ_UNCOMMITTED)
ret |= Session::TRANSACTION_READ_UNCOMMITTED;
if (isolation & SQL_TXN_READ_COMMITTED)
ret |= Session::TRANSACTION_READ_COMMITTED;
if (isolation & SQL_TXN_REPEATABLE_READ)
ret |= Session::TRANSACTION_REPEATABLE_READ;
if (isolation & SQL_TXN_SERIALIZABLE)
ret |= Session::TRANSACTION_SERIALIZABLE;
if (0 == ret)
throw InvalidArgumentException("transactionIsolation(SQLUINTEGER)");
return ret;
}
@ -171,21 +266,57 @@ bool SessionImpl::isConnected()
0)))
return false;
return (0 == value);
return (SQL_CD_FALSE == value);
}
bool SessionImpl::isTransaction()
{
Poco::UInt32 value = 0;
if (!canTransact()) return false;
Poco::UInt32 value = 0;
checkError(Poco::Data::ODBC::SQLGetConnectAttr(_db,
SQL_ATTR_AUTOCOMMIT,
&value,
0,
0));
return (0 == value);
if (0 == value) return _inTransaction;
else return false;
}
void SessionImpl::begin()
{
if (isAutoCommit())
throw InvalidAccessException("Session in auto commit mode.");
{
Poco::FastMutex::ScopedLock l(_mutex);
if (_inTransaction)
throw InvalidAccessException("Transaction in progress.");
_inTransaction = true;
}
}
void SessionImpl::commit()
{
if (!isAutoCommit())
checkError(SQLEndTran(SQL_HANDLE_DBC, _db, SQL_COMMIT));
_inTransaction = false;
}
void SessionImpl::rollback()
{
if (!isAutoCommit())
checkError(SQLEndTran(SQL_HANDLE_DBC, _db, SQL_ROLLBACK));
_inTransaction = false;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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)
{

View File

@ -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.

View File

@ -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

View File

@ -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();

View File

@ -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();
}

View File

@ -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;

View File

@ -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.