mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2025-02-07 21:37:14 +01:00
Switched to POCO library for unified platform/library interface. Deprecated the external module API. It was creating more problems than solving. Removed most built-in libraries in favor of system libraries for easier maintenance. Cleaned and secured code with help from static analyzers.
354 lines
8.3 KiB
C++
354 lines
8.3 KiB
C++
//
|
|
// SessionImpl.cpp
|
|
//
|
|
// Library: Data/MySQL
|
|
// Package: MySQL
|
|
// Module: SessionImpl
|
|
//
|
|
// Copyright (c) 2008, Applied Informatics Software Engineering GmbH.
|
|
// and Contributors.
|
|
//
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
//
|
|
|
|
|
|
#include "Poco/Data/MySQL/SessionImpl.h"
|
|
#include "Poco/Data/MySQL/MySQLStatementImpl.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, std::size_t loginTimeout) :
|
|
Poco::Data::AbstractSessionImpl<SessionImpl>(connectionString, loginTimeout),
|
|
_connector("MySQL"),
|
|
_handle(0),
|
|
_reset(false),
|
|
_connected(false),
|
|
_inTransaction(false),
|
|
_failIfInnoReadOnly(false),
|
|
_lastError(0)
|
|
{
|
|
addProperty("insertId", &SessionImpl::setInsertId, &SessionImpl::getInsertId);
|
|
setProperty("handle", static_cast<MYSQL*>(_handle));
|
|
addFeature("failIfInnoReadOnly", &SessionImpl::setFailIfInnoReadOnly, &SessionImpl::getFailIfInnoReadOnly);
|
|
open();
|
|
}
|
|
|
|
|
|
void SessionImpl::open(const std::string& connect)
|
|
{
|
|
if (connect != connectionString())
|
|
{
|
|
if (isConnected())
|
|
throw InvalidAccessException("Session already connected");
|
|
|
|
if (!connect.empty())
|
|
setConnectionString(connect);
|
|
}
|
|
|
|
poco_assert_dbg (!connectionString().empty());
|
|
|
|
_handle.init();
|
|
|
|
unsigned int timeout = static_cast<unsigned int>(getLoginTimeout());
|
|
_handle.options(MYSQL_OPT_CONNECT_TIMEOUT, timeout);
|
|
|
|
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"] = "";
|
|
options["secure-auth"] = "";
|
|
options["character-set"] = "utf8";
|
|
options["reset"] = "";
|
|
options["fail-readonly"] = "";
|
|
|
|
const std::string& connString = connectionString();
|
|
for (std::string::const_iterator start = connString.begin();;)
|
|
{
|
|
std::string::const_iterator finish = std::find(start, connString.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 == connString.end()) || (finish + 1 == connString.end())) break;
|
|
|
|
start = finish + 1;
|
|
}
|
|
|
|
if (options["user"].empty())
|
|
throw MySQLException("create session: specify user name");
|
|
|
|
const char * db = NULL;
|
|
if (!options["db"].empty())
|
|
db = options["db"].c_str();
|
|
|
|
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"].empty())
|
|
throw MySQLException("create session: specify correct compress option (true or false)");
|
|
|
|
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"].empty())
|
|
throw MySQLException("create session: specify correct auto-reconnect option (true or false)");
|
|
|
|
#ifdef MYSQL_SECURE_AUTH
|
|
if (options["secure-auth"] == "true")
|
|
_handle.options(MYSQL_SECURE_AUTH, true);
|
|
else if (options["secure-auth"] == "false")
|
|
_handle.options(MYSQL_SECURE_AUTH, false);
|
|
else if (!options["secure-auth"].empty())
|
|
throw MySQLException("create session: specify correct secure-auth option (true or false)");
|
|
#endif
|
|
|
|
if (!options["character-set"].empty())
|
|
_handle.options(MYSQL_SET_CHARSET_NAME, options["character-set"].c_str());
|
|
|
|
if (options["reset"] == "true")
|
|
_reset = true;
|
|
else if (options["reset"] == "false")
|
|
_reset = false;
|
|
else if (!options["reset"].empty())
|
|
throw MySQLException("create session: specify correct reset option (true or false)");
|
|
|
|
if (options["fail-readonly"] == "true")
|
|
_failIfInnoReadOnly = true;
|
|
else if (options["fail-readonly"] == "false")
|
|
_failIfInnoReadOnly = false;
|
|
else if (!options["fail-readonly"].empty())
|
|
throw MySQLException("create session: specify correct fail-readonly option (true or false)");
|
|
|
|
// Real connect
|
|
_handle.connect(options["host"].c_str(),
|
|
options["user"].c_str(),
|
|
options["password"].c_str(),
|
|
db,
|
|
port);
|
|
|
|
addFeature("autoCommit",
|
|
&SessionImpl::autoCommit,
|
|
&SessionImpl::isAutoCommit);
|
|
|
|
_connected = true;
|
|
}
|
|
|
|
|
|
SessionImpl::~SessionImpl()
|
|
{
|
|
close();
|
|
}
|
|
|
|
|
|
Poco::Data::StatementImpl::Ptr 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&) const
|
|
{
|
|
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() const
|
|
{
|
|
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) const
|
|
{
|
|
return Session::TRANSACTION_READ_UNCOMMITTED == ti ||
|
|
Session::TRANSACTION_READ_COMMITTED == ti ||
|
|
Session::TRANSACTION_REPEATABLE_READ == ti ||
|
|
Session::TRANSACTION_SERIALIZABLE == ti;
|
|
}
|
|
|
|
|
|
void SessionImpl::reset()
|
|
{
|
|
if (_connected && _reset)
|
|
{
|
|
_handle.reset();
|
|
}
|
|
}
|
|
|
|
|
|
inline bool SessionImpl::isConnected() const
|
|
{
|
|
return _connected;
|
|
}
|
|
|
|
|
|
bool SessionImpl::isGood() const
|
|
{
|
|
if (_connected)
|
|
{
|
|
if (_lastError)
|
|
{
|
|
if (_failIfInnoReadOnly)
|
|
{
|
|
try
|
|
{
|
|
int ro = 0;
|
|
if (0 == getSetting("innodb_read_only", ro))
|
|
{
|
|
_lastError = 0;
|
|
return true;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (_handle.ping())
|
|
{
|
|
_lastError = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
else return true;
|
|
}
|
|
else return false;
|
|
}
|
|
|
|
|
|
void SessionImpl::close()
|
|
{
|
|
if (_connected)
|
|
{
|
|
_handle.close();
|
|
_connected = false;
|
|
}
|
|
}
|
|
|
|
|
|
void SessionImpl::setConnectionTimeout(std::size_t timeout)
|
|
{
|
|
_handle.options(MYSQL_OPT_READ_TIMEOUT, static_cast<unsigned int>(timeout));
|
|
_handle.options(MYSQL_OPT_WRITE_TIMEOUT, static_cast<unsigned int>(timeout));
|
|
_timeout = timeout;
|
|
}
|
|
|
|
|
|
} } } // namespace Poco::Data::MySQL
|