mirror of
https://github.com/pocoproject/poco.git
synced 2025-03-31 07:58:24 +02:00
582 lines
12 KiB
C++
582 lines
12 KiB
C++
//
|
|
// SessionHandle.cpp
|
|
//
|
|
// Library: Data/PostgreSQL
|
|
// Package: PostgreSQL
|
|
// Module: SessionHandle
|
|
//
|
|
// Copyright (c) 2015, Applied Informatics Software Engineering GmbH.
|
|
// and Contributors.
|
|
//
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
//
|
|
|
|
|
|
#include "Poco/Data/PostgreSQL/SessionHandle.h"
|
|
#include "Poco/Data/PostgreSQL/PostgreSQLException.h"
|
|
#include "Poco/Data/PostgreSQL/PostgreSQLTypes.h"
|
|
#include "Poco/Data/Session.h"
|
|
#include "Poco/NumberFormatter.h"
|
|
|
|
|
|
#define POCO_POSTGRESQL_VERSION_NUMBER ((NDB_VERSION_MAJOR<<16) | (NDB_VERSION_MINOR<<8) | (NDB_VERSION_BUILD&0xFF))
|
|
|
|
|
|
namespace Poco {
|
|
namespace Data {
|
|
namespace PostgreSQL {
|
|
|
|
|
|
//const std::string SessionHandle::POSTGRESQL_READ_UNCOMMITTED = "READ UNCOMMITTED";
|
|
const std::string SessionHandle::POSTGRESQL_READ_COMMITTED = "READ COMMITTED";
|
|
const std::string SessionHandle::POSTGRESQL_REPEATABLE_READ = "REPEATABLE READ";
|
|
const std::string SessionHandle::POSTGRESQL_SERIALIZABLE = "SERIALIZABLE";
|
|
|
|
|
|
SessionHandle::SessionHandle():
|
|
_pConnection(0),
|
|
_inTransaction(false),
|
|
_isAutoCommit(true),
|
|
_isAsynchronousCommit(false),
|
|
_tranactionIsolationLevel(Session::TRANSACTION_READ_COMMITTED)
|
|
{
|
|
}
|
|
|
|
|
|
SessionHandle::~SessionHandle()
|
|
{
|
|
try
|
|
{
|
|
disconnect();
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
bool SessionHandle::isConnected() const
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
return isConnectedNoLock();
|
|
}
|
|
|
|
|
|
bool SessionHandle::isConnectedNoLock() const
|
|
{
|
|
// DO NOT ACQUIRE THE MUTEX IN PRIVATE METHODS
|
|
|
|
if (_pConnection && PQstatus(_pConnection) == CONNECTION_OK)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void SessionHandle::connect(const std::string& aConnectionString)
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (isConnectedNoLock())
|
|
{
|
|
throw ConnectionFailedException("Already Connected");
|
|
}
|
|
|
|
_pConnection = PQconnectdb(aConnectionString.c_str());
|
|
|
|
if (!isConnectedNoLock())
|
|
{
|
|
throw ConnectionFailedException(std::string("Connection Error: ") + lastErrorNoLock());
|
|
}
|
|
|
|
_connectionString = aConnectionString;
|
|
}
|
|
|
|
|
|
void SessionHandle::connect(const char* aConnectionString)
|
|
{
|
|
connect(std::string(aConnectionString));
|
|
}
|
|
|
|
|
|
void SessionHandle::connect(const char* aHost, const char* aUser, const char* aPassword,
|
|
const char* aDatabase, unsigned short aPort, unsigned int aConnectionTimeout)
|
|
{
|
|
std::string connectionString;
|
|
|
|
connectionString.append("host=");
|
|
connectionString.append(aHost);
|
|
connectionString.append(" ");
|
|
|
|
connectionString.append("user=");
|
|
connectionString.append(aUser);
|
|
connectionString.append(" ");
|
|
|
|
connectionString.append("password=");
|
|
connectionString.append(aPassword);
|
|
connectionString.append(" ");
|
|
|
|
connectionString.append("dbname=");
|
|
connectionString.append(aDatabase);
|
|
connectionString.append(" ");
|
|
|
|
connectionString.append("port=");
|
|
Poco::NumberFormatter::append(connectionString, aPort);
|
|
connectionString.append(" ");
|
|
|
|
connectionString.append("connect_timeout=");
|
|
Poco::NumberFormatter::append(connectionString, aConnectionTimeout);
|
|
|
|
connect(connectionString);
|
|
}
|
|
|
|
|
|
void SessionHandle::disconnect()
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (isConnectedNoLock())
|
|
{
|
|
PQfinish(_pConnection);
|
|
|
|
_pConnection = 0;
|
|
|
|
_connectionString = std::string();
|
|
_inTransaction= false;
|
|
_isAutoCommit = true;
|
|
_isAsynchronousCommit = false;
|
|
_tranactionIsolationLevel = Session::TRANSACTION_READ_COMMITTED;
|
|
}
|
|
}
|
|
|
|
|
|
// TODO: Figure out what happens if a connection is reset with a pending transaction
|
|
bool SessionHandle::reset()
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (_pConnection)
|
|
{
|
|
PQreset(_pConnection);
|
|
}
|
|
|
|
if (isConnectedNoLock())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
std::string SessionHandle::lastError() const
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (!isConnectedNoLock())
|
|
{
|
|
return std::string();
|
|
}
|
|
|
|
return lastErrorNoLock();
|
|
}
|
|
|
|
|
|
std::string SessionHandle::lastErrorNoLock() const
|
|
{
|
|
// DO NOT ACQUIRE THE MUTEX IN PRIVATE METHODS
|
|
std::string lastErrorString (0 != _pConnection ? PQerrorMessage(_pConnection) : "not connected");
|
|
|
|
return lastErrorString;
|
|
}
|
|
|
|
|
|
void SessionHandle::startTransaction()
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (!isConnectedNoLock())
|
|
{
|
|
throw NotConnectedException();
|
|
}
|
|
|
|
if (_inTransaction)
|
|
{
|
|
return; // NO-OP
|
|
}
|
|
|
|
PGresult* pPQResult = PQexec(_pConnection, "BEGIN");
|
|
|
|
PQResultClear resultClearer(pPQResult);
|
|
|
|
if (PQresultStatus(pPQResult) != PGRES_COMMAND_OK)
|
|
{
|
|
throw StatementException(std::string("BEGIN statement failed:: ") + lastErrorNoLock());
|
|
}
|
|
|
|
_inTransaction = true;
|
|
}
|
|
|
|
|
|
void SessionHandle::commit()
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (!isConnectedNoLock())
|
|
{
|
|
throw NotConnectedException();
|
|
}
|
|
|
|
PGresult* pPQResult = PQexec(_pConnection, "COMMIT");
|
|
|
|
PQResultClear resultClearer(pPQResult);
|
|
|
|
if (PQresultStatus(pPQResult) != PGRES_COMMAND_OK)
|
|
{
|
|
throw StatementException(std::string("COMMIT statement failed:: ") + lastErrorNoLock());
|
|
}
|
|
|
|
_inTransaction = false;
|
|
|
|
deallocateStoredPreparedStatements();
|
|
}
|
|
|
|
|
|
void SessionHandle::rollback()
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (!isConnectedNoLock())
|
|
{
|
|
throw NotConnectedException();
|
|
}
|
|
|
|
PGresult* pPQResult = PQexec(_pConnection, "ROLLBACK");
|
|
|
|
PQResultClear resultClearer(pPQResult);
|
|
|
|
if (PQresultStatus(pPQResult) != PGRES_COMMAND_OK)
|
|
{
|
|
throw StatementException(std::string("ROLLBACK statement failed:: ") + lastErrorNoLock());
|
|
}
|
|
|
|
_inTransaction = false;
|
|
|
|
deallocateStoredPreparedStatements();
|
|
}
|
|
|
|
|
|
void SessionHandle::setAutoCommit(bool aShouldAutoCommit)
|
|
{
|
|
if (aShouldAutoCommit == _isAutoCommit)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (aShouldAutoCommit)
|
|
{
|
|
commit(); // end any in process transaction
|
|
}
|
|
else
|
|
{
|
|
startTransaction(); // start a new transaction
|
|
}
|
|
|
|
_isAutoCommit = aShouldAutoCommit;
|
|
}
|
|
|
|
|
|
void SessionHandle::setAsynchronousCommit(bool aShouldAsynchronousCommit)
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (!isConnectedNoLock())
|
|
{
|
|
throw NotConnectedException();
|
|
}
|
|
|
|
if (aShouldAsynchronousCommit == _isAsynchronousCommit)
|
|
{
|
|
return;
|
|
}
|
|
|
|
PGresult* pPQResult = PQexec(_pConnection, aShouldAsynchronousCommit ? "SET SYNCHRONOUS COMMIT TO OFF" : "SET SYNCHRONOUS COMMIT TO ON");
|
|
|
|
PQResultClear resultClearer(pPQResult);
|
|
|
|
if (PQresultStatus(pPQResult) != PGRES_COMMAND_OK)
|
|
{
|
|
throw StatementException(std::string("SET SYNCHRONUS COMMIT statement failed:: ") + lastErrorNoLock());
|
|
}
|
|
|
|
_isAsynchronousCommit = aShouldAsynchronousCommit;
|
|
}
|
|
|
|
|
|
void SessionHandle::cancel()
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (!isConnectedNoLock())
|
|
{
|
|
throw NotConnectedException();
|
|
}
|
|
|
|
PGcancel* ptrPGCancel = PQgetCancel(_pConnection);
|
|
|
|
PGCancelFree cancelFreer(ptrPGCancel);
|
|
|
|
PQcancel(ptrPGCancel, 0, 0); // no error buffer
|
|
}
|
|
|
|
|
|
void SessionHandle::setTransactionIsolation(Poco::UInt32 aTI)
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (!isConnectedNoLock())
|
|
{
|
|
throw NotConnectedException();
|
|
}
|
|
|
|
if (aTI == _tranactionIsolationLevel)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!hasTransactionIsolation(aTI))
|
|
{
|
|
throw Poco::InvalidArgumentException("setTransactionIsolation()");
|
|
}
|
|
|
|
std::string isolationLevel;
|
|
|
|
switch (aTI)
|
|
{
|
|
case Session::TRANSACTION_READ_COMMITTED:
|
|
isolationLevel = POSTGRESQL_READ_COMMITTED; break;
|
|
case Session::TRANSACTION_REPEATABLE_READ:
|
|
isolationLevel = POSTGRESQL_REPEATABLE_READ; break;
|
|
case Session::TRANSACTION_SERIALIZABLE:
|
|
isolationLevel = POSTGRESQL_SERIALIZABLE; break;
|
|
}
|
|
|
|
PGresult* pPQResult = PQexec(_pConnection, Poco::format("SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL %s", isolationLevel).c_str());
|
|
|
|
PQResultClear resultClearer(pPQResult);
|
|
|
|
if (PQresultStatus(pPQResult) != PGRES_COMMAND_OK)
|
|
{
|
|
throw StatementException(std::string("set transaction isolation statement failed: ") + lastErrorNoLock());
|
|
}
|
|
|
|
_tranactionIsolationLevel = aTI;
|
|
}
|
|
|
|
|
|
Poco::UInt32 SessionHandle::transactionIsolation()
|
|
{
|
|
return _tranactionIsolationLevel;
|
|
}
|
|
|
|
|
|
bool SessionHandle::hasTransactionIsolation(Poco::UInt32 aTI)
|
|
{
|
|
return Session::TRANSACTION_READ_COMMITTED == aTI
|
|
|| Session::TRANSACTION_REPEATABLE_READ == aTI
|
|
|| Session::TRANSACTION_SERIALIZABLE == aTI;
|
|
}
|
|
|
|
|
|
void SessionHandle::deallocatePreparedStatement(const std::string& aPreparedStatementToDeAllocate)
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (!isConnectedNoLock())
|
|
{
|
|
throw NotConnectedException();
|
|
}
|
|
|
|
if (!_inTransaction)
|
|
{
|
|
deallocatePreparedStatementNoLock(aPreparedStatementToDeAllocate);
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
_preparedStatementsToBeDeallocated.push_back(aPreparedStatementToDeAllocate);
|
|
}
|
|
catch (std::bad_alloc&)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SessionHandle::deallocatePreparedStatementNoLock(const std::string& aPreparedStatementToDeAllocate)
|
|
{
|
|
PGresult* pPQResult = PQexec(_pConnection, (std::string("DEALLOCATE ") + aPreparedStatementToDeAllocate).c_str());
|
|
|
|
PQResultClear resultClearer(pPQResult);
|
|
|
|
if (PQresultStatus(pPQResult) != PGRES_COMMAND_OK)
|
|
{
|
|
throw StatementException(std::string("DEALLOCATE statement failed: ") + lastErrorNoLock());
|
|
}
|
|
}
|
|
|
|
|
|
void SessionHandle::deallocateStoredPreparedStatements()
|
|
{
|
|
// DO NOT ACQUIRE THE MUTEX IN PRIVATE METHODS
|
|
while (!_preparedStatementsToBeDeallocated.empty())
|
|
{
|
|
deallocatePreparedStatementNoLock(_preparedStatementsToBeDeallocated.back());
|
|
|
|
_preparedStatementsToBeDeallocated.pop_back();
|
|
}
|
|
}
|
|
|
|
|
|
int SessionHandle::serverVersion() const
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (!isConnectedNoLock())
|
|
{
|
|
throw NotConnectedException();
|
|
}
|
|
|
|
return PQserverVersion(_pConnection);
|
|
}
|
|
|
|
|
|
int SessionHandle::serverProcessID() const
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (!isConnectedNoLock())
|
|
{
|
|
throw NotConnectedException();
|
|
}
|
|
|
|
return PQbackendPID(_pConnection);
|
|
}
|
|
|
|
|
|
int SessionHandle::protocoVersion() const
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (!isConnectedNoLock())
|
|
{
|
|
throw NotConnectedException();
|
|
}
|
|
|
|
return PQprotocolVersion(_pConnection);
|
|
}
|
|
|
|
|
|
std::string SessionHandle::clientEncoding() const
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (!isConnectedNoLock())
|
|
{
|
|
throw NotConnectedException();
|
|
}
|
|
|
|
return pg_encoding_to_char(PQclientEncoding(_pConnection));
|
|
}
|
|
|
|
|
|
std::string SessionHandle::parameterStatus(const std::string& param) const
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
if (!isConnectedNoLock())
|
|
{
|
|
throw NotConnectedException();
|
|
}
|
|
|
|
const char* pValue = PQparameterStatus(_pConnection, param.c_str());
|
|
if (pValue)
|
|
return std::string(pValue);
|
|
else
|
|
return std::string();
|
|
}
|
|
|
|
|
|
int SessionHandle::libpqVersion() const
|
|
{
|
|
return PQlibVersion();
|
|
}
|
|
|
|
|
|
SessionParametersMap SessionHandle::setConnectionInfoParameters(PQconninfoOption* pConnInfOpt)
|
|
{
|
|
SessionParametersMap sessionParametersMap;
|
|
|
|
while (pConnInfOpt->keyword)
|
|
{
|
|
try
|
|
{
|
|
std::string keyword = pConnInfOpt->keyword ? pConnInfOpt->keyword : std::string();
|
|
std::string environmentVariableVersion = pConnInfOpt->envvar ? pConnInfOpt->envvar : std::string();
|
|
std::string compiledVersion = pConnInfOpt->compiled ? pConnInfOpt->compiled : std::string();
|
|
std::string currentValue = pConnInfOpt->val ? pConnInfOpt->val : std::string();
|
|
std::string dialogLabel = pConnInfOpt->label? pConnInfOpt->label: std::string();
|
|
std::string dialogDisplayCharacter = pConnInfOpt->dispchar ? pConnInfOpt->dispchar : std::string();
|
|
int dialogDisplaysize = pConnInfOpt->dispsize;
|
|
|
|
SessionParameters connParams(keyword, environmentVariableVersion, compiledVersion,
|
|
currentValue, dialogLabel, dialogDisplayCharacter, dialogDisplaysize);
|
|
|
|
sessionParametersMap.insert(SessionParametersMap::value_type(connParams.keyword(), connParams));
|
|
}
|
|
catch (std::bad_alloc&)
|
|
{
|
|
}
|
|
|
|
++pConnInfOpt;
|
|
}
|
|
|
|
return sessionParametersMap;
|
|
}
|
|
|
|
|
|
SessionParametersMap SessionHandle::connectionDefaultParameters()
|
|
{
|
|
PQconninfoOption* ptrConnInfoOptions = PQconndefaults();
|
|
|
|
PQConnectionInfoOptionsFree connectionOptionsFreeer(ptrConnInfoOptions);
|
|
|
|
return setConnectionInfoParameters(ptrConnInfoOptions);
|
|
}
|
|
|
|
|
|
SessionParametersMap SessionHandle::connectionParameters() const
|
|
{
|
|
if (!isConnected())
|
|
{
|
|
throw NotConnectedException();
|
|
}
|
|
|
|
PQconninfoOption* ptrConnInfoOptions = 0;
|
|
{
|
|
Poco::FastMutex::ScopedLock mutexLocker(_sessionMutex);
|
|
|
|
ptrConnInfoOptions = PQconninfo(_pConnection);
|
|
}
|
|
|
|
PQConnectionInfoOptionsFree connectionOptionsFreeer(ptrConnInfoOptions);
|
|
|
|
return setConnectionInfoParameters(ptrConnInfoOptions);
|
|
}
|
|
|
|
|
|
} } } // Poco::Data::PostgreSQL
|