From 94d3a7cfe8fd50b45e82112b1c477330f388df2f Mon Sep 17 00:00:00 2001 From: hvlad Date: Fri, 15 Nov 2019 10:54:50 +0200 Subject: [PATCH] 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. --- doc/sql.extensions/README.execute_statement2 | 6 +- .../README.external_connections_pool | 4 +- src/jrd/extds/ExtDS.cpp | 3 +- src/jrd/extds/ExtDS.h | 17 ++++++ src/jrd/extds/InternalDS.cpp | 2 + src/jrd/extds/IscDS.cpp | 55 +++++++++++++++++-- src/jrd/extds/IscDS.h | 3 + 7 files changed, 83 insertions(+), 7 deletions(-) diff --git a/doc/sql.extensions/README.execute_statement2 b/doc/sql.extensions/README.execute_statement2 index 072012d52b..3476fd69da 100644 --- a/doc/sql.extensions/README.execute_statement2 +++ b/doc/sql.extensions/README.execute_statement2 @@ -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 is omitted, NULL or equal to CURRENT_USER value and diff --git a/doc/sql.extensions/README.external_connections_pool b/doc/sql.extensions/README.external_connections_pool index 3262bfcded..389352c97f 100644 --- a/doc/sql.extensions/README.external_connections_pool +++ b/doc/sql.extensions/README.external_connections_pool @@ -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: diff --git a/src/jrd/extds/ExtDS.cpp b/src/jrd/extds/ExtDS.cpp index b3bba18b79..aae9ef3589 100644 --- a/src/jrd/extds/ExtDS.cpp +++ b/src/jrd/extds/ExtDS.cpp @@ -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) { } diff --git a/src/jrd/extds/ExtDS.h b/src/jrd/extds/ExtDS.h index 247eff218d..d6838e35eb 100644 --- a/src/jrd/extds/ExtDS.h +++ b/src/jrd/extds/ExtDS.h @@ -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 { diff --git a/src/jrd/extds/InternalDS.cpp b/src/jrd/extds/InternalDS.cpp index 9057edf4e4..0b510ea8de 100644 --- a/src/jrd/extds/InternalDS.cpp +++ b/src/jrd/extds/InternalDS.cpp @@ -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) diff --git a/src/jrd/extds/IscDS.cpp b/src/jrd/extds/IscDS.cpp index 43ab8a5286..9d4847b938 100644 --- a/src/jrd/extds/IscDS.cpp +++ b/src/jrd/extds/IscDS.cpp @@ -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()); + { + 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"); } diff --git a/src/jrd/extds/IscDS.h b/src/jrd/extds/IscDS.h index 8f8301ea13..6f2a6e5307 100644 --- a/src/jrd/extds/IscDS.h +++ b/src/jrd/extds/IscDS.h @@ -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);