mirror of
https://github.com/pocoproject/poco.git
synced 2024-12-12 18:20:26 +01:00
[SF 2580108] Improve transaction handling
This commit is contained in:
parent
ad543acb58
commit
d11f007d23
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -89,7 +89,7 @@ private:
|
||||
|
||||
private:
|
||||
|
||||
MYSQL* h;
|
||||
MYSQL* _pHandle;
|
||||
};
|
||||
|
||||
|
||||
@ -99,7 +99,7 @@ private:
|
||||
|
||||
inline SessionHandle::operator MYSQL* ()
|
||||
{
|
||||
return h;
|
||||
return _pHandle;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}}}
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
Data/include/Poco/Data/Transaction.h
Normal file
242
Data/include/Poco/Data/Transaction.h
Normal 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
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user