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

Receive features list from a real provider (#266)

* Receive features list from a real provider

* Prevent statement caching if the provider doesn't support it

* Changes requested by Dmitry Yemanov
This commit is contained in:
Dimitry Sibiryakov 2020-06-14 19:01:53 +02:00 committed by GitHub
parent e7ac0c0a9b
commit 2401b66e9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 114 additions and 58 deletions

View File

@ -45,6 +45,17 @@ New items for isc_database_info
isc_dpb_addr_flag_conn_encrypted - connection is encrypted;
fb_info_wire_crypt - name of connection encryption plugin.
6. fb_info_provider_features:
return list of features supported by current connection's provider.
Each byte in returned array shall be one of following info_provider_features:
fb_feature_multi_statements - multiple prepared statements in single attachment
fb_feature_multi_transactions - multiple concurrent transactions in single attachment
fb_feature_named_parameters - query parameters can be named
fb_feature_session_reset - ALTER SESSION RESET is supported
fb_feature_read_consistency - read consistency TIL is supported
fb_feature_statement_timeout - statement timeout is supported
New items for isc_transaction_info:

View File

@ -162,6 +162,9 @@ enum db_info_types
fb_info_wire_crypt = 140,
// Return list of features supported by provider of current connection
fb_info_provider_features = 141,
isc_info_db_last_value /* Leave this LAST! */
};
@ -171,6 +174,19 @@ enum db_info_crypt /* flags set in fb_info_crypt_state */
fb_info_crypt_process = 0x02
};
enum info_provider_features // response to fb_info_provider_features
{
fb_feature_multi_statements = 1, // Multiple prepared statements in single attachment
fb_feature_multi_transactions = 2, // Multiple concurrent transaction in single attachment
fb_feature_named_parameters = 3, // Query parameters can be named
fb_feature_session_reset = 4, // ALTER SESSION RESET is supported
fb_feature_read_consistency = 5, // Read consistency TIL is supported
fb_feature_statement_timeout = 6, // Statement timeout is supported
fb_feature_statement_long_life = 7, // Prepared statement can survive transaction end
info_provider_features_max // Not really a feature. Keep this last.
};
#define isc_info_version isc_info_isc_version

View File

@ -466,6 +466,14 @@ const int OPT_STATIC_ITEMS = 64;
#define CURRENT_ENGINE "Engine13"
#define EMBEDDED_PROVIDERS "Providers=" CURRENT_ENGINE
// Feature mask for current version of engine provider
#define ENGINE_FEATURES {fb_feature_multi_statements, \
fb_feature_multi_transactions, \
fb_feature_session_reset, \
fb_feature_read_consistency, \
fb_feature_statement_timeout, \
fb_feature_statement_long_life}
const int WITH_GRANT_OPTION = 1;
const int WITH_ADMIN_OPTION = 2;

View File

@ -524,7 +524,7 @@ Connection::Connection(Provider& prov) :
m_sqlDialect(0),
m_wrapErrors(true),
m_broken(false),
m_features(0)
m_features{}
{
}
@ -639,7 +639,7 @@ void Connection::releaseStatement(Jrd::thread_db* tdbb, Statement* stmt)
{
fb_assert(stmt && !stmt->isActive());
if (stmt->isAllocated() && m_free_stmts < MAX_CACHED_STMTS)
if (stmt->isAllocated() && testFeature(fb_feature_statement_long_life) && m_free_stmts < MAX_CACHED_STMTS)
{
stmt->m_nextFree = m_freeStatements;
m_freeStatements = stmt;
@ -1753,7 +1753,7 @@ void Statement::prepare(thread_db* tdbb, Transaction* tran, const string& sql, b
string sql2(getPool());
const string* readySql = &sql;
if (named && !(m_provider.getFlags() & prvNamedParams))
if (named && !m_connection.testFeature(fb_feature_named_parameters))
{
preprocess(sql, sql2);
readySql = &sql2;

View File

@ -197,10 +197,7 @@ protected:
};
// Provider flags
const int prvMultyStmts = 0x0001; // supports many active statements per connection
const int prvMultyTrans = 0x0002; // supports many active transactions per connection
const int prvNamedParams = 0x0004; // supports named parameters
const int prvTrustedAuth = 0x0008; // supports trusted authentication
const int prvTrustedAuth = 0x0001; // supports trusted authentication
class ConnectionsPool
@ -494,12 +491,12 @@ 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; }
// Test specified feature flag
bool testFeature(info_provider_features value) const { return m_features[value]; }
// Set specified flag
void setFeature(info_provider_features value) { m_features[value] = true; }
// Clear specified flag
void clearFeature(info_provider_features value) { m_features[value] = false; }
protected:
virtual Transaction* doCreateTransaction() = 0;
@ -531,17 +528,9 @@ protected:
int m_sqlDialect; // must be filled in attach call
bool m_wrapErrors;
bool m_broken;
ULONG m_features; // bitmask
bool m_features[info_provider_features_max];
};
// 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
{
protected:

View File

@ -188,7 +188,10 @@ 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;
memset(m_features, false, sizeof(m_features));
static const info_provider_features features[] = ENGINE_FEATURES;
for (int i = 0; i < sizeof(features); i++)
setFeature(features[i]);
}
void InternalConnection::doDetach(thread_db* tdbb)

View File

@ -34,7 +34,6 @@ public:
explicit InternalProvider(const char* prvName) :
Provider(prvName)
{
m_flags = (prvMultyStmts | prvMultyTrans);
}
~InternalProvider()

View File

@ -144,17 +144,20 @@ void IscConnection::attach(thread_db* tdbb)
}
}
char buff[16];
char buff[BUFFER_TINY];
{
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
const char info[] = {isc_info_db_sql_dialect, isc_info_end};
const unsigned char info[] = {isc_info_db_sql_dialect, fb_info_provider_features, isc_info_end};
m_iscProvider.isc_database_info(&status, &m_handle, sizeof(info), info, sizeof(buff), buff);
}
if (status->getState() & IStatus::STATE_ERRORS) {
raise(&status, tdbb, "isc_database_info");
}
memset(m_features, false, sizeof(m_features));
m_sqlDialect = 1;
const char* p = buff, *end = buff + sizeof(buff);
while (p < end)
{
@ -168,22 +171,42 @@ void IscConnection::attach(thread_db* tdbb)
m_sqlDialect = m_iscProvider.isc_vax_integer(p, len);
break;
case fb_info_provider_features:
for (int i = 0; i < len; i++)
{
if (p[i] == 0)
{
ERR_post(Arg::Gds(isc_random) << Arg::Str("Bad provider feature value"));
}
if (p[i] < info_provider_features_max)
{
setFeature(static_cast<info_provider_features>(p[i]));
}
// else this provider supports unknown feature, ignore it.
}
break;
case isc_info_error:
if (*p == isc_info_db_sql_dialect)
{
const ULONG err = m_iscProvider.isc_vax_integer(p + 1, len - 1);
if (err == isc_infunk)
{
// Remote server don't understand isc_info_db_sql_dialect.
// Consider it as pre-IB6 server and use SQL dialect 1 to work with it.
m_sqlDialect = 1;
break;
}
}
// fall thru
{
const ULONG err = m_iscProvider.isc_vax_integer(p + 1, len - 1);
if (err == isc_infunk)
{
if (*p == fb_info_provider_features)
{
// Used provider follow Firebird error reporting conventions but is not aware of
// this info item. Assume Firebird 3 or earlier.
m_features[fb_feature_multi_statements] = true;
m_features[fb_feature_multi_transactions] = true;
m_features[fb_feature_statement_long_life] = true;
}
break;
}
ERR_post(Arg::Gds(isc_random) << Arg::Str("Unexpected error in isc_database_info"));
}
case isc_info_truncated:
ERR_post(Arg::Gds(isc_random) << Arg::Str("Unexpected error in isc_database_info"));
ERR_post(Arg::Gds(isc_random) << Arg::Str("Result truncation in isc_database_info"));
case isc_info_end:
p = end;
@ -192,7 +215,6 @@ 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)
@ -240,7 +262,7 @@ bool IscConnection::resetSession()
if (!m_handle)
return false;
if (!testFeature(conFtrSessionReset))
if (!testFeature(fb_feature_session_reset))
return true;
FbLocalStatus status;
@ -252,7 +274,7 @@ bool IscConnection::resetSession()
if (status->getErrors()[1] == isc_dsql_error)
{
clearFeature(conFtrSessionReset);
clearFeature(fb_feature_session_reset);
return true;
}
@ -267,11 +289,10 @@ bool IscConnection::resetSession()
// transactions
bool IscConnection::isAvailable(thread_db* tdbb, TraScope traScope) const
{
const int flags = m_provider.getFlags();
if (m_used_stmts && !(flags & prvMultyStmts))
if (m_used_stmts && !testFeature(fb_feature_multi_statements))
return false;
if (m_transactions.getCount() && !(flags & prvMultyTrans) && !findTransaction(tdbb, traScope))
if (m_transactions.getCount() && !testFeature(fb_feature_multi_transactions) && !findTransaction(tdbb, traScope))
{
return false;
}
@ -288,7 +309,7 @@ bool IscConnection::validate(Jrd::thread_db* tdbb)
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
char info[] = {isc_info_attachment_id, isc_info_end};
const unsigned char info[] = {isc_info_attachment_id, isc_info_end};
char buff[32];
return m_iscProvider.isc_database_info(&status, &m_handle,
@ -315,7 +336,7 @@ Statement* IscConnection::doCreateStatement()
void IscTransaction::generateTPB(thread_db* tdbb, ClumpletWriter& tpb,
TraModes traMode, bool readOnly, bool wait, int lockTimeout) const
{
if (traMode == traReadCommitedReadConsistency && !m_connection.testFeature(conFtrReadConsistency))
if (traMode == traReadCommitedReadConsistency && !m_connection.testFeature(fb_feature_read_consistency))
traMode = traConcurrency;
Transaction::generateTPB(tdbb, tpb, traMode, readOnly, wait, lockTimeout);
@ -335,7 +356,7 @@ void IscTransaction::doStart(FbStatusVector* status, thread_db* tdbb, Firebird::
if ((status->getState() & IStatus::STATE_ERRORS) &&
(status->getErrors()[1] == isc_bad_tpb_form) &&
tpb.find(isc_tpb_read_consistency) &&
m_connection.testFeature(conFtrReadConsistency))
m_connection.testFeature(fb_feature_read_consistency))
{
tpb.deleteWithTag(isc_tpb_read_committed);
tpb.deleteWithTag(isc_tpb_read_consistency);
@ -348,7 +369,7 @@ void IscTransaction::doStart(FbStatusVector* status, thread_db* tdbb, Firebird::
}
if (!(status->getState() & IStatus::STATE_ERRORS))
m_connection.clearFeature(conFtrReadConsistency);
m_connection.clearFeature(fb_feature_read_consistency);
}
}
@ -562,7 +583,7 @@ void IscStatement::doPrepare(thread_db* tdbb, const string& sql)
void IscStatement::doSetTimeout(thread_db* tdbb, unsigned int timeout)
{
if (!m_connection.testFeature(conFtrStatementTimeout))
if (!m_connection.testFeature(fb_feature_statement_timeout))
return;
FbLocalStatus status;
@ -578,7 +599,7 @@ void IscStatement::doSetTimeout(thread_db* tdbb, unsigned int timeout)
// or loaded client library
if (status[0] == isc_arg_gds && (status[1] == isc_wish_list || status[1] == isc_unavailable))
{
m_connection.clearFeature(conFtrStatementTimeout);
m_connection.clearFeature(fb_feature_statement_timeout);
return;
}
@ -1047,7 +1068,7 @@ ISC_STATUS ISC_EXPORT IscProvider::isc_create_database(FbStatusVector* user_stat
ISC_STATUS ISC_EXPORT IscProvider::isc_database_info(FbStatusVector* user_status,
isc_db_handle* db_handle,
short info_len,
const char* info,
const unsigned char* info,
short res_len,
char* res)
{
@ -1055,7 +1076,7 @@ ISC_STATUS ISC_EXPORT IscProvider::isc_database_info(FbStatusVector* user_status
return notImplemented(user_status);
return (*m_api.database_info) (IscStatus(user_status), db_handle,
info_len, info, res_len, res);
info_len, reinterpret_cast<const ISC_SCHAR*>(info), res_len, res);
}
void ISC_EXPORT IscProvider::isc_decode_date(const ISC_QUAD*,

View File

@ -186,7 +186,7 @@ public:
virtual ISC_STATUS ISC_EXPORT isc_database_info(Jrd::FbStatusVector*,
isc_db_handle*,
short,
const char*,
const unsigned char*,
short,
char*);
@ -497,7 +497,8 @@ public:
explicit FBProvider(const char* prvName) :
IscProvider(prvName)
{
m_flags = (prvMultyStmts | prvMultyTrans | prvTrustedAuth);
// Assume that winsspi auth plugin is enabled in configuration
m_flags = prvTrustedAuth;
}
protected:

View File

@ -855,6 +855,14 @@ void INF_database_info(thread_db* tdbb,
length = INF_convert(att->getActualIdleTimeout(), buffer);
break;
case fb_info_provider_features:
{
static const unsigned char features[] = ENGINE_FEATURES;
length = sizeof(features);
memcpy(buffer, features, length);
break;
}
default:
buffer[0] = item;
item = isc_info_error;