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);