8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 18:03:03 +01:00

Let external transaction run with CONCURRENCY isolation mode if local transaction runs with READ COMMITED READ CONSISTENCY isolation and such isolation mode is not supported by external data source.

Allow to reuse external connection if external data source doesn't support ALTER SESSION RESET statement.
Update documentation.
This commit is contained in:
hvlad 2019-11-15 10:54:50 +02:00
parent 6260af2a78
commit 94d3a7cfe8
7 changed files with 83 additions and 7 deletions

View File

@ -76,7 +76,11 @@ Syntax and notes :
This transaction lifetime is bound to the lifetime of current (local) transaction
and commits\rolled back the same way as current transaction.
- by default COMMON TRANSACTION is used
- by default COMMON TRANSACTION is used.
- if local transaction is READ COMMITTED READ CONSISTENCY and if external data source not
supported this isolation mode, then external transaction will run with CONCURRENCY
isolation mode.
- if PASSWORD clause is omitted then
a) if <user_name> is omitted, NULL or equal to CURRENT_USER value and

View File

@ -15,7 +15,9 @@ How pool works:
active transactions), it is reset and placed into idle list (on successful
reset) or closed (if reset failed).
Connection is reset using ALTER SESSION RESET statement. It is considered
successful if no error occurs.
successful if no error occurs. Note, if external data source not supported
ALTER SESSION RESET statement - it is not considered as error and such
connection will be placed into pool.
- if the pool has reached max. size, the oldest idle connection is closed
- when engine ask to create a new external connection, the pool first looks
for candidate at the idle list. The search is based on 4 parameters:

View File

@ -525,7 +525,8 @@ Connection::Connection(Provider& prov) :
m_deleting(false),
m_sqlDialect(0),
m_wrapErrors(true),
m_broken(false)
m_broken(false),
m_features(0)
{
}

View File

@ -437,6 +437,8 @@ public:
virtual void detach(Jrd::thread_db* tdbb);
virtual bool cancelExecution(bool forced) = 0;
// Try to reset connection, return true if it can be pooled
virtual bool resetSession() = 0;
int getSqlDialect() const { return m_sqlDialect; }
@ -492,6 +494,13 @@ public:
virtual Blob* createBlob() = 0;
// Test specified flags, return true if all bits present
bool testFeature(ULONG value) const { return m_features & value; }
// Set specified flags, return new value
ULONG setFeature(ULONG value) { return m_features |= value; }
// Clear specified flags, return new value
ULONG clearFeature(ULONG value) { return m_features &= ~value; }
protected:
virtual Transaction* doCreateTransaction() = 0;
virtual Statement* doCreateStatement() = 0;
@ -522,8 +531,16 @@ protected:
int m_sqlDialect; // must be filled in attach call
bool m_wrapErrors;
bool m_broken;
ULONG m_features; // bitmask
};
// Connection features flags
const ULONG conFtrSessionReset = 0x01; // supports ALTER SESSION RESET
const ULONG conFtrReadConsistency = 0x02; // supports READ COMMITTED READ CONSISTENCY
const ULONG conFtrStatementTimeout = 0x04; // supports statements timeout
// Features of Firebird 4
const ULONG conFtrFB4 = conFtrSessionReset | conFtrReadConsistency | conFtrStatementTimeout;
class Transaction : public Firebird::PermanentStorage
{

View File

@ -187,6 +187,8 @@ void InternalConnection::attach(thread_db* tdbb)
m_sqlDialect = (attachment->att_database->dbb_flags & DBB_DB_SQL_dialect_3) ?
SQL_DIALECT_V6 : SQL_DIALECT_V5;
m_features = conFtrFB4;
}
void InternalConnection::doDetach(thread_db* tdbb)

View File

@ -191,6 +191,8 @@ void IscConnection::attach(thread_db* tdbb)
}
p += len;
}
m_features = conFtrFB4; // Exact feature set will be detected at first usage
}
void IscConnection::doDetach(thread_db* tdbb)
@ -238,6 +240,9 @@ bool IscConnection::resetSession()
if (!m_handle)
return false;
if (!testFeature(conFtrSessionReset))
return true;
FbLocalStatus status;
m_iscProvider.isc_dsql_execute_immediate(&status, &m_handle,
NULL, 0, "ALTER SESSION RESET", m_sqlDialect, NULL);
@ -245,7 +250,13 @@ bool IscConnection::resetSession()
if (!(status->getState() & IStatus::STATE_ERRORS))
return true;
return false; // (status->getErrors()[1] == isc_dsql_error);
if (status->getErrors()[1] == isc_dsql_error)
{
clearFeature(conFtrSessionReset);
return true;
}
return false;
}
// this ISC connection instance is available for the current execution context if it
@ -301,14 +312,44 @@ Statement* IscConnection::doCreateStatement()
// IscTransaction
void IscTransaction::generateTPB(thread_db* tdbb, ClumpletWriter& tpb,
TraModes traMode, bool readOnly, bool wait, int lockTimeout) const
{
if (traMode == traReadCommitedReadConsistency && !m_connection.testFeature(conFtrReadConsistency))
traMode = traConcurrency;
Transaction::generateTPB(tdbb, tpb, traMode, readOnly, wait, lockTimeout);
}
void IscTransaction::doStart(FbStatusVector* status, thread_db* tdbb, Firebird::ClumpletWriter& tpb)
{
fb_assert(!m_handle);
FB_API_HANDLE& db_handle = m_iscConnection.getAPIHandle();
{
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
m_iscProvider.isc_start_transaction(status, &m_handle, 1, &db_handle,
tpb.getBufferLength(), tpb.getBuffer());
}
if ((status->getState() & IStatus::STATE_ERRORS) &&
(status->getErrors()[1] == isc_bad_tpb_form) &&
tpb.find(isc_tpb_read_consistency) &&
m_connection.testFeature(conFtrReadConsistency))
{
tpb.deleteWithTag(isc_tpb_read_committed);
tpb.deleteWithTag(isc_tpb_read_consistency);
tpb.insertTag(isc_tpb_concurrency);
{
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
m_iscProvider.isc_start_transaction(status, &m_handle, 1, &db_handle,
tpb.getBufferLength(), tpb.getBuffer());
}
if (!(status->getState() & IStatus::STATE_ERRORS))
m_connection.clearFeature(conFtrReadConsistency);
}
}
void IscTransaction::doPrepare(FbStatusVector* /*status*/, thread_db* /*tdbb*/, int /*info_len*/, const char* /*info*/)
@ -521,6 +562,9 @@ void IscStatement::doPrepare(thread_db* tdbb, const string& sql)
void IscStatement::doSetTimeout(thread_db* tdbb, unsigned int timeout)
{
if (!m_connection.testFeature(conFtrStatementTimeout))
return;
FbLocalStatus status;
{
@ -533,7 +577,10 @@ void IscStatement::doSetTimeout(thread_db* tdbb, unsigned int timeout)
// silently ignore error if timeouts is not supported by remote server
// or loaded client library
if (status[0] == isc_arg_gds && (status[1] == isc_wish_list || status[1] == isc_unavailable))
{
m_connection.clearFeature(conFtrStatementTimeout);
return;
}
raise(&status, tdbb, "fb_dsql_set_timeout");
}

View File

@ -556,6 +556,9 @@ protected:
virtual ~IscTransaction() {}
virtual void generateTPB(Jrd::thread_db* tdbb, Firebird::ClumpletWriter& tpb,
TraModes traMode, bool readOnly, bool wait, int lockTimeout) const;
virtual void doStart(Jrd::FbStatusVector* status, Jrd::thread_db* tdbb, Firebird::ClumpletWriter& tpb);
virtual void doPrepare(Jrd::FbStatusVector* status, Jrd::thread_db* tdbb, int info_len, const char* info);
virtual void doCommit(Jrd::FbStatusVector* status, Jrd::thread_db* tdbb, bool retain);