[SF 2580108] Improve transaction handling

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

View File

@@ -144,43 +144,24 @@ void MySQLStatementImpl::compileImpl()
_metadata.init(_stmt);
if (_metadata.columnsReturned() > 0)
{
_stmt.bindResult(_metadata.row());
}
}
void MySQLStatementImpl::bindImpl()
{
//
// Bind all bindings
//
Poco::Data::AbstractBindingVec& binds = bindings();
size_t pos = 0;
Poco::Data::AbstractBindingVec::iterator it = binds.begin();
Poco::Data::AbstractBindingVec::iterator itEnd = binds.end();
for (; it != itEnd && (*it)->canBind(); ++it)
{
Poco::Data::AbstractBindingVec& binds = bindings();
size_t pos = 0;
Poco::Data::AbstractBindingVec::iterator it = binds.begin();
Poco::Data::AbstractBindingVec::iterator itEnd = binds.end();
for (; it != itEnd && (*it)->canBind(); ++it)
{
(*it)->bind(pos);
pos += (*it)->numOfColumnsHandled();
}
(*it)->bind(pos);
pos += (*it)->numOfColumnsHandled();
}
//
// And bind them to statement
//
_stmt.bindParams(_binder.getBindArray(), _binder.size());
//
// And execute
//
_stmt.execute();
_hasNext = NEXT_DONTKNOW;
}

View File

@@ -44,12 +44,8 @@ namespace MySQL {
SessionHandle::SessionHandle(MYSQL* mysql)
{
h = mysql_init(mysql);
if (!h)
{
if (!(_pHandle = mysql_init(mysql)))
throw ConnectionException("mysql_init error");
}
}
@@ -61,74 +57,54 @@ SessionHandle::~SessionHandle()
void SessionHandle::options(mysql_option opt)
{
int res = mysql_options(h, opt, 0);
if (res != 0)
{
throw ConnectionException("mysql_options error", h);
}
if (mysql_options(_pHandle, opt, 0) != 0)
throw ConnectionException("mysql_options error", _pHandle);
}
void SessionHandle::options(mysql_option opt, bool b)
{
my_bool tmp = b;
int res = mysql_options(h, opt, &tmp);
if (res != 0)
{
throw ConnectionException("mysql_options error", h);
}
if (mysql_options(_pHandle, opt, &tmp) != 0)
throw ConnectionException("mysql_options error", _pHandle);
}
void SessionHandle::connect(const char* host, const char* user, const char* password, const char* db, unsigned int port)
{
if (!mysql_real_connect(h, host, user, password, db, port, 0, 0))
{
throw ConnectionException("create session: mysql_real_connect error", h);
}
if (!mysql_real_connect(_pHandle, host, user, password, db, port, 0, 0))
throw ConnectionException("create session: mysql_real_connect error", _pHandle);
}
void SessionHandle::close()
{
if (h)
if (_pHandle)
{
mysql_close(h);
h = 0;
mysql_close(_pHandle);
_pHandle = 0;
}
}
void SessionHandle::startTransaction()
{
int res = mysql_autocommit(h, false);
if (res != 0)
{
throw TransactionException("Start transaction failed.", h);
}
if (mysql_autocommit(_pHandle, false) != 0)
throw TransactionException("Start transaction failed.", _pHandle);
}
void SessionHandle::commit()
{
int res = mysql_commit(h);
if (res != 0)
{
throw TransactionException("Commit failed.", h);
}
if (mysql_commit(_pHandle) != 0)
throw TransactionException("Commit failed.", _pHandle);
}
void SessionHandle::rollback()
{
int res = mysql_rollback(h);
if (res != 0)
{
throw TransactionException("Rollback failed.", h);
}
if (mysql_rollback(_pHandle) != 0)
throw TransactionException("Rollback failed.", _pHandle);
}

View File

@@ -1,233 +1,271 @@
//
// MySQLException.cpp
//
// $Id: //poco/1.4/Data/MySQL/src/SessionImpl.cpp#1 $
//
// Library: Data
// Package: MySQL
// Module: SessionImpl
//
// Copyright (c) 2008, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
#include "Poco/Data/MySQL/SessionImpl.h"
#include "Poco/Data/MySQL/MySQLStatementImpl.h"
#include "Poco/NumberParser.h"
#include "Poco/String.h"
namespace
{
std::string copyStripped(std::string::const_iterator from, std::string::const_iterator to)
{
// skip leading spaces
while ((from != to) && isspace(*from)) from++;
// skip trailing spaces
while ((from != to) && isspace(*(to - 1))) to--;
return std::string(from, to);
}
}
namespace Poco {
namespace Data {
namespace MySQL {
SessionImpl::SessionImpl(const std::string& connectionString) :
Poco::Data::AbstractSessionImpl<SessionImpl>(toLower(connectionString)),
_mysql(0),
_connected(false),
_inTransaction(0)
{
addProperty("insertId",
&SessionImpl::setInsertId,
&SessionImpl::getInsertId);
std::map<std::string, std::string> options;
// Default values
options["host"] = "localhost";
options["port"] = "3306";
options["user"] = "";
options["password"] = "";
options["db"] = "";
options["compress"] = "";
options["auto-reconnect"] = "";
//
// Parse string
//
for (std::string::const_iterator start = connectionString.begin();;)
{
// find next ';'
std::string::const_iterator finish = std::find(start, connectionString.end(), ';');
// find '='
std::string::const_iterator middle = std::find(start, finish, '=');
if (middle == finish)
{
throw MySQLException("create session: bad connection string format, can not find '='");
}
// Parse name and value, skip all spaces
options[copyStripped(start, middle)] = copyStripped(middle + 1, finish);
if ((finish == connectionString.end()) || (finish + 1 == connectionString.end()))
{
// end of parse
break;
}
// move start position after ';'
start = finish + 1;
}
//
// Checking
//
if (options["user"] == "")
{
throw MySQLException("create session: specify user name");
}
if (options["db"] == "")
{
throw MySQLException("create session: specify database");
}
unsigned int port = 0;
if (!NumberParser::tryParseUnsigned(options["port"], port) || 0 == port || port > 65535)
{
throw MySQLException("create session: specify correct port (numeric in decimal notation)");
}
//
// Options
//
if (options["compress"] == "true")
{
_mysql.options(MYSQL_OPT_COMPRESS);
}
else if (options["compress"] == "false")
{
// do nothing
}
else if (options["compress"] != "")
{
throw MySQLException("create session: specify correct compress option (true or false) or skip it");
}
if (options["auto-reconnect"] == "true")
{
_mysql.options(MYSQL_OPT_RECONNECT, true);
}
else if (options["auto-reconnect"] == "false")
{
_mysql.options(MYSQL_OPT_RECONNECT, false);
}
else if (options["auto-reconnect"] != "")
{
throw MySQLException("create session: specify correct auto-reconnect option (true or false) or skip it");
}
//
// Real connect
//
_mysql.connect(
options["host"].c_str(),
options["user"].c_str(),
options["password"].c_str(),
options["db"].c_str(),
port);
_connected = true;
}
SessionImpl::~SessionImpl()
{
close();
}
Poco::Data::StatementImpl* SessionImpl::createStatementImpl()
{
return new MySQLStatementImpl(*this);
}
void SessionImpl::begin()
{
_mysql.startTransaction();
_inTransaction++;
}
void SessionImpl::commit()
{
_mysql.commit();
_inTransaction--;
}
void SessionImpl::rollback()
{
_mysql.rollback();
_inTransaction--;
}
void SessionImpl::close()
{
if (_connected)
{
_mysql.close();
_connected = false;
}
}
bool SessionImpl::isConnected()
{
return _connected;
}
bool SessionImpl::isTransaction()
{
return (_inTransaction > 0);
}
}}}
//
// MySQLException.cpp
//
// $Id: //poco/1.4/Data/MySQL/src/SessionImpl.cpp#1 $
//
// Library: Data
// Package: MySQL
// Module: SessionImpl
//
// Copyright (c) 2008, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
#include "Poco/Data/MySQL/SessionImpl.h"
#include "Poco/Data/MySQL/MySQLStatementImpl.h"
#include "Poco/Data/MySQL/StatementExecutor.h"
#include "Poco/Data/Session.h"
#include "Poco/NumberParser.h"
#include "Poco/String.h"
namespace
{
std::string copyStripped(std::string::const_iterator from, std::string::const_iterator to)
{
// skip leading spaces
while ((from != to) && isspace(*from)) from++;
// skip trailing spaces
while ((from != to) && isspace(*(to - 1))) to--;
return std::string(from, to);
}
}
namespace Poco {
namespace Data {
namespace MySQL {
const std::string SessionImpl::MYSQL_READ_UNCOMMITTED = "READ UNCOMMITTED";
const std::string SessionImpl::MYSQL_READ_COMMITTED = "READ COMMITTED";
const std::string SessionImpl::MYSQL_REPEATABLE_READ = "REPEATABLE READ";
const std::string SessionImpl::MYSQL_SERIALIZABLE = "SERIALIZABLE";
SessionImpl::SessionImpl(const std::string& connectionString) :
Poco::Data::AbstractSessionImpl<SessionImpl>(toLower(connectionString)),
_handle(0),
_connected(false),
_inTransaction(false)
{
addProperty("insertId",
&SessionImpl::setInsertId,
&SessionImpl::getInsertId);
std::map<std::string, std::string> options;
// Default values
options["host"] = "localhost";
options["port"] = "3306";
options["user"] = "";
options["password"] = "";
options["db"] = "";
options["compress"] = "";
options["auto-reconnect"] = "";
for (std::string::const_iterator start = connectionString.begin();;)
{
std::string::const_iterator finish = std::find(start, connectionString.end(), ';');
std::string::const_iterator middle = std::find(start, finish, '=');
if (middle == finish)
throw MySQLException("create session: bad connection string format, can not find '='");
options[copyStripped(start, middle)] = copyStripped(middle + 1, finish);
if ((finish == connectionString.end()) || (finish + 1 == connectionString.end())) break;
start = finish + 1;
}
if (options["user"] == "")
throw MySQLException("create session: specify user name");
if (options["db"] == "")
throw MySQLException("create session: specify database");
unsigned int port = 0;
if (!NumberParser::tryParseUnsigned(options["port"], port) || 0 == port || port > 65535)
throw MySQLException("create session: specify correct port (numeric in decimal notation)");
if (options["compress"] == "true")
_handle.options(MYSQL_OPT_COMPRESS);
else if (options["compress"] == "false")
;
else if (options["compress"] != "")
throw MySQLException("create session: specify correct compress option (true or false) or skip it");
if (options["auto-reconnect"] == "true")
_handle.options(MYSQL_OPT_RECONNECT, true);
else if (options["auto-reconnect"] == "false")
_handle.options(MYSQL_OPT_RECONNECT, false);
else if (options["auto-reconnect"] != "")
throw MySQLException("create session: specify correct auto-reconnect option (true or false) or skip it");
// Real connect
_handle.connect(
options["host"].c_str(),
options["user"].c_str(),
options["password"].c_str(),
options["db"].c_str(),
port);
addFeature("autoCommit",
&SessionImpl::autoCommit,
&SessionImpl::isAutoCommit);
_connected = true;
}
SessionImpl::~SessionImpl()
{
close();
}
Poco::Data::StatementImpl* SessionImpl::createStatementImpl()
{
return new MySQLStatementImpl(*this);
}
void SessionImpl::begin()
{
Poco::FastMutex::ScopedLock l(_mutex);
if (_inTransaction)
throw Poco::InvalidAccessException("Already in transaction.");
_handle.startTransaction();
_inTransaction = true;
}
void SessionImpl::commit()
{
_handle.commit();
_inTransaction = false;
}
void SessionImpl::rollback()
{
_handle.rollback();
_inTransaction = false;
}
void SessionImpl::autoCommit(const std::string&, bool val)
{
StatementExecutor ex(_handle);
ex.prepare(Poco::format("SET autocommit=%d", val ? 1 : 0));
ex.execute();
}
bool SessionImpl::isAutoCommit(const std::string&)
{
int ac = 0;
return 1 == getSetting("autocommit", ac);
}
void SessionImpl::setTransactionIsolation(Poco::UInt32 ti)
{
std::string isolation;
switch (ti)
{
case Session::TRANSACTION_READ_UNCOMMITTED:
isolation = MYSQL_READ_UNCOMMITTED; break;
case Session::TRANSACTION_READ_COMMITTED:
isolation = MYSQL_READ_COMMITTED; break;
case Session::TRANSACTION_REPEATABLE_READ:
isolation = MYSQL_REPEATABLE_READ; break;
case Session::TRANSACTION_SERIALIZABLE:
isolation = MYSQL_SERIALIZABLE; break;
default:
throw Poco::InvalidArgumentException("setTransactionIsolation()");
}
StatementExecutor ex(_handle);
ex.prepare(Poco::format("SET SESSION TRANSACTION ISOLATION LEVEL %s", isolation));
ex.execute();
}
Poco::UInt32 SessionImpl::getTransactionIsolation()
{
std::string isolation;
getSetting("tx_isolation", isolation);
Poco::replaceInPlace(isolation, "-", " ");
if (MYSQL_READ_UNCOMMITTED == isolation)
return Session::TRANSACTION_READ_UNCOMMITTED;
else if (MYSQL_READ_COMMITTED == isolation)
return Session::TRANSACTION_READ_COMMITTED;
else if (MYSQL_REPEATABLE_READ == isolation)
return Session::TRANSACTION_REPEATABLE_READ;
else if (MYSQL_SERIALIZABLE == isolation)
return Session::TRANSACTION_SERIALIZABLE;
throw InvalidArgumentException("getTransactionIsolation()");
}
bool SessionImpl::hasTransactionIsolation(Poco::UInt32 ti)
{
return Session::TRANSACTION_READ_UNCOMMITTED == ti ||
Session::TRANSACTION_READ_COMMITTED == ti ||
Session::TRANSACTION_REPEATABLE_READ == ti ||
Session::TRANSACTION_SERIALIZABLE == ti;
}
void SessionImpl::close()
{
if (_connected)
{
_handle.close();
_connected = false;
}
}
bool SessionImpl::isConnected()
{
return _connected;
}
bool SessionImpl::isTransaction()
{
return _inTransaction;
}
}}}

View File

@@ -46,12 +46,8 @@ namespace MySQL {
StatementExecutor::StatementExecutor(MYSQL* mysql)
{
h = mysql_stmt_init(mysql);
if (!h)
{
if (!(_pHandle = mysql_stmt_init(mysql)))
throw StatementException("mysql_stmt_init error");
}
_state = STMT_INITED;
}
@@ -59,7 +55,7 @@ StatementExecutor::StatementExecutor(MYSQL* mysql)
StatementExecutor::~StatementExecutor()
{
mysql_stmt_close(h);
mysql_stmt_close(_pHandle);
}
@@ -75,16 +71,10 @@ void StatementExecutor::prepare(const std::string& query)
{
_state = STMT_COMPILED;
return;
//throw StatementException("Satement is already compiled");
}
// compile
int res = mysql_stmt_prepare(h, query.c_str(), static_cast<unsigned int>(query.length()));
if (res != 0)
{
throw StatementException("mysql_stmt_prepare error", h, query);
}
if (mysql_stmt_prepare(_pHandle, query.c_str(), static_cast<unsigned int>(query.length())) != 0)
throw StatementException("mysql_stmt_prepare error", _pHandle, query);
_query = query;
_state = STMT_COMPILED;
@@ -94,58 +84,35 @@ void StatementExecutor::prepare(const std::string& query)
void StatementExecutor::bindParams(MYSQL_BIND* params, size_t count)
{
if (_state < STMT_COMPILED)
{
throw StatementException("Satement is not compiled yet");
}
if (count != mysql_stmt_param_count(h))
{
if (count != mysql_stmt_param_count(_pHandle))
throw StatementException("wrong bind parameters count", 0, _query);
}
if (count == 0)
{
return;
}
if (count == 0) return;
int res = mysql_stmt_bind_param(h, params);
if (res != 0)
{
throw StatementException("mysql_stmt_bind_param() error ", h, _query);
}
if (mysql_stmt_bind_param(_pHandle, params) != 0)
throw StatementException("mysql_stmt_bind_param() error ", _pHandle, _query);
}
void StatementExecutor::bindResult(MYSQL_BIND* result)
{
if (_state < STMT_COMPILED)
{
throw StatementException("Satement is not compiled yet");
}
int res = mysql_stmt_bind_result(h, result);
if (res != 0)
{
throw StatementException("mysql_stmt_bind_result error ", h, _query);
}
if (mysql_stmt_bind_result(_pHandle, result) != 0)
throw StatementException("mysql_stmt_bind_result error ", _pHandle, _query);
}
void StatementExecutor::execute()
{
if (_state < STMT_COMPILED)
{
throw StatementException("Satement is not compiled yet");
}
int res = mysql_stmt_execute(h);
if (res != 0)
{
throw StatementException("mysql_stmt_execute error", h, _query);
}
if (mysql_stmt_execute(_pHandle) != 0)
throw StatementException("mysql_stmt_execute error", _pHandle, _query);
_state = STMT_EXECUTED;
}
@@ -154,16 +121,12 @@ void StatementExecutor::execute()
bool StatementExecutor::fetch()
{
if (_state < STMT_EXECUTED)
{
throw StatementException("Satement is not executed yet");
}
int res = mysql_stmt_fetch(h);
int res = mysql_stmt_fetch(_pHandle);
if ((res != 0) && (res != MYSQL_NO_DATA))
{
throw StatementException("mysql_stmt_fetch error", h, _query);
}
throw StatementException("mysql_stmt_fetch error", _pHandle, _query);
return (res == 0);
}
@@ -172,17 +135,15 @@ bool StatementExecutor::fetch()
bool StatementExecutor::fetchColumn(size_t n, MYSQL_BIND *bind)
{
if (_state < STMT_EXECUTED)
{
throw StatementException("Satement is not executed yet");
}
int res = mysql_stmt_fetch_column(h, bind, static_cast<unsigned int>(n), 0);
int res = mysql_stmt_fetch_column(_pHandle, bind, static_cast<unsigned int>(n), 0);
if ((res != 0) && (res != MYSQL_NO_DATA))
{
std::ostringstream msg;
msg << "mysql_stmt_fetch_column(" << n << ") error";
throw StatementException(msg.str(), h, _query);
throw StatementException(msg.str(), _pHandle, _query);
}
return (res == 0);