mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 20:43:02 +01:00
Merge branch 'ExternalConnectionsPool'
This commit is contained in:
commit
e688ced75a
@ -986,3 +986,22 @@
|
||||
# Type: string
|
||||
#
|
||||
#ServerMode = Super
|
||||
|
||||
# ============================
|
||||
# Settings of External Connections Pool
|
||||
# ============================
|
||||
|
||||
# Set the maximum number of inactive (idle) external connections to retain at
|
||||
# the pool. Valid values are between 0 and 1000. If set to zero, pool is disabed,
|
||||
# i.e. external connection is destroyed immediately after the use.
|
||||
#
|
||||
# Type: integer
|
||||
#
|
||||
#ExtConnPoolSize = 0
|
||||
|
||||
# Set the time before destroyng inactive external connection, seconds.
|
||||
# Valid values are between 1 and 86400.
|
||||
#
|
||||
# Type: integer
|
||||
#
|
||||
#ExtConnPoolLifeTime = 7200
|
||||
|
@ -555,6 +555,7 @@ USE_GRANTED_BY_CLAUSE Use GRANTED BY in GRANT and REVOKE operators
|
||||
GRANT_REVOKE_ON_ANY_OBJECT GRANT and REVOKE rights on any object in database
|
||||
GRANT_REVOKE_ANY_DDL_RIGHT GRANT and REVOKE any DDL rights
|
||||
CREATE_PRIVILEGED_ROLES Use SET SYSTEM PRIVILEGES in roles
|
||||
MODIFY_EXT_CONN_POOL Manage properties of pool of external connections
|
||||
|
||||
|
||||
22) New grantee type in GRANT and REVOKE operators - SYSTEM PRIVILEGE.
|
||||
|
100
doc/sql.extensions/README.external_connections_pool
Normal file
100
doc/sql.extensions/README.external_connections_pool
Normal file
@ -0,0 +1,100 @@
|
||||
The pool of external connections.
|
||||
|
||||
To avoid delays when external connections established frequently, external
|
||||
data source (EDS) subsystem is supplemented by the pool of external connections.
|
||||
The pool keeps unused external connections for some time and allows to avoid the
|
||||
cost of connecting / disconnecting for frequently used connection strings.
|
||||
|
||||
Author:
|
||||
Vlad Khorsun <hvlad@users.sourceforge.net>
|
||||
|
||||
How pool works:
|
||||
- every external connection is associated with a pool when created
|
||||
- pool maintains two lists: idle connections and active connections
|
||||
- when some connection become unused (i.e. it have no active requests and no
|
||||
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.
|
||||
- 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:
|
||||
- connection string,
|
||||
- username,
|
||||
- password,
|
||||
- role.
|
||||
The search is case sensitive.
|
||||
- if suitable connection is found, then it tested if it is still alive
|
||||
- if it did not pass the check, it is deleted and the search is repeated
|
||||
(the error is not reported to the user)
|
||||
- found (and alive) connection is moved from idle list to active list and
|
||||
returned to the caller
|
||||
- if there are several suitable connections, the most recently used is chosen
|
||||
- if there is no suitable connection, the new one is created (and put into
|
||||
active list)
|
||||
- when idle connection gets expired (exceeded the lifetime), it is deleted from
|
||||
the pool and closed.
|
||||
|
||||
Key characteristics:
|
||||
- absence of "eternal" external connections
|
||||
- limited number of inactive (idle) external connections at the pool
|
||||
- support a quick search among the connections (using 4 parameters above)
|
||||
- the pool is common for all external databases
|
||||
- the pool is common for all local connections handled by the given Firebird
|
||||
process
|
||||
|
||||
Pool parameters:
|
||||
- connection life time: the time interval from the moment of the last usage of
|
||||
connection after which it will be forcibly closed
|
||||
- pool size: the maximum allowed number of idle connections in the pool
|
||||
|
||||
Pool management:
|
||||
New SQL statement is introduced to manage the pool:
|
||||
|
||||
ALTER EXTERNAL CONNECTIONS POOL.
|
||||
|
||||
When prepared it is described as DDL statement but have immediate effect: i.e.
|
||||
it is executed immediately and completely, not waiting for transaction commit.
|
||||
Changes applied to the in-memory instance of the pool in current Firebird
|
||||
process. Therefore change in one Classic process doesn't affect other Classic
|
||||
processes. Changes is not persistent and after restart Firebird will use pool
|
||||
settings at firebird.conf (see below).
|
||||
|
||||
New system privilege "MODIFY_EXT_CONN_POOL" is required to run the statement.
|
||||
|
||||
The full syntax is:
|
||||
|
||||
- ALTER EXTERNAL CONNECTIONS POOL SET SIZE <int>
|
||||
set maximum number of idle connections.
|
||||
|
||||
Valid values are from 0 to 1000.
|
||||
Value of zero means that pool is disabled.
|
||||
Default value is set in firebird.conf (see below).
|
||||
|
||||
- ALTER EXTERNAL CONNECTIONS POOL SET LIFETIME <int> <time_part>,
|
||||
where <time_part> is SECOND | MINUTE | HOUR
|
||||
|
||||
Set idle connection lifetime, in seconds.
|
||||
Valid values are from 1 SECOND to 24 HOUR.
|
||||
Default value is set in firebird.conf (see below).
|
||||
|
||||
- ALTER EXTERNAL CONNECTIONS POOL CLEAR ALL
|
||||
Closes all idle connections.
|
||||
Disassociates all active connections off the pool (such connections will be
|
||||
closed immediately when gets unused).
|
||||
|
||||
- ALTER EXTERNAL CONNECTIONS POOL CLEAR OLDEST
|
||||
Closes expired idle connections.
|
||||
|
||||
The state of external connections pool could be queried using new context
|
||||
variables in 'SYSTEM' namespace:
|
||||
- EXT_CONN_POOL_SIZE pool size
|
||||
- EXT_CONN_POOL_LIFETIME idle connection lifetime, in seconds
|
||||
- EXT_CONN_POOL_IDLE_COUNT count of currently inactive connections
|
||||
- EXT_CONN_POOL_ACTIVE_COUNT count of active connections, associated with pool
|
||||
|
||||
|
||||
Firebird configuration (firebird.conf) got two new settings related with pool:
|
||||
|
||||
- ExtConnPoolSize = 0, pool size, and
|
||||
- ExtConnPoolLifeTime = 7200, idle connection lifetime, seconds
|
@ -144,6 +144,9 @@ public:
|
||||
const FB_SIZE_T len = getBufferLength();
|
||||
return (len == other.getBufferLength()) && (memcmp(getBuffer(), other.getBuffer(), len) == 0);
|
||||
}
|
||||
// Methods are virtual so writer can override 'em
|
||||
virtual const UCHAR* getBuffer() const;
|
||||
virtual const UCHAR* getBufferEnd() const;
|
||||
|
||||
protected:
|
||||
enum ClumpletType {TraditionalDpb, SingleTpb, StringSpb, IntSpb, BigIntSpb, ByteSpb, Wide};
|
||||
@ -155,10 +158,6 @@ protected:
|
||||
Kind kind;
|
||||
UCHAR spbState; // Reflects state of spb parser/writer
|
||||
|
||||
// Methods are virtual so writer can override 'em
|
||||
virtual const UCHAR* getBuffer() const;
|
||||
virtual const UCHAR* getBufferEnd() const;
|
||||
|
||||
// These functions are called when error condition is detected by this class.
|
||||
// They may throw exceptions. If they don't reader tries to do something
|
||||
// sensible, certainly not overwrite memory or read past the end of buffer
|
||||
|
@ -209,9 +209,11 @@ const Config::ConfigEntry Config::entries[MAX_CONFIG_KEY] =
|
||||
#ifdef WIN_NT
|
||||
{TYPE_STRING, "OutputRedirectionFile", (ConfigValue) "nul"},
|
||||
#else
|
||||
{TYPE_STRING, "OutputRedirectionFile", (ConfigValue) "/dev/null"}
|
||||
{TYPE_STRING, "OutputRedirectionFile", (ConfigValue) "/dev/null"},
|
||||
#endif
|
||||
#endif
|
||||
{TYPE_INTEGER, "ExtConnPoolSize", (ConfigValue) 0},
|
||||
{TYPE_INTEGER, "ExtConnPoolLifeTime", (ConfigValue) 7200}
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
@ -853,3 +855,13 @@ const char* Config::getOutputRedirectionFile()
|
||||
const char* file = (const char*) (getDefaultConfig()->values[KEY_OUTPUT_REDIRECTION_FILE]);
|
||||
return file;
|
||||
}
|
||||
|
||||
int Config::getExtConnPoolSize()
|
||||
{
|
||||
return getDefaultConfig()->get<int>(KEY_EXT_CONN_POOL_SIZE);
|
||||
}
|
||||
|
||||
int Config::getExtConnPoolLifeTime()
|
||||
{
|
||||
return getDefaultConfig()->get<int>(KEY_EXT_CONN_POOL_LIFETIME);
|
||||
}
|
||||
|
@ -147,6 +147,8 @@ public:
|
||||
KEY_CONN_IDLE_TIMEOUT,
|
||||
KEY_CLIENT_BATCH_BUFFER,
|
||||
KEY_OUTPUT_REDIRECTION_FILE,
|
||||
KEY_EXT_CONN_POOL_SIZE,
|
||||
KEY_EXT_CONN_POOL_LIFETIME,
|
||||
MAX_CONFIG_KEY // keep it last
|
||||
};
|
||||
|
||||
@ -365,6 +367,10 @@ public:
|
||||
unsigned int getClientBatchBuffer() const;
|
||||
|
||||
static const char* getOutputRedirectionFile();
|
||||
|
||||
static int getExtConnPoolSize();
|
||||
|
||||
static int getExtConnPoolLifeTime();
|
||||
};
|
||||
|
||||
// Implementation of interface to access master configuration file
|
||||
|
@ -63,6 +63,7 @@
|
||||
#include "../common/StatusArg.h"
|
||||
#include "../auth/SecureRemotePassword/Message.h"
|
||||
#include "../jrd/Mapping.h"
|
||||
#include "../jrd/extds/ExtDS.h"
|
||||
|
||||
namespace Jrd {
|
||||
|
||||
@ -1161,6 +1162,86 @@ void AlterCharSetNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch
|
||||
}
|
||||
|
||||
|
||||
bool AlterEDSPoolSetNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
|
||||
{
|
||||
if (!tdbb->getAttachment()->locksmith(tdbb, MODIFY_EXT_CONN_POOL))
|
||||
status_exception::raise(Arg::Gds(isc_miss_prvlg) << "MODIFY_EXT_CONN_POOL");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string AlterEDSPoolSetNode::internalPrint(NodePrinter& printer) const
|
||||
{
|
||||
DdlNode::internalPrint(printer);
|
||||
|
||||
NODE_PRINT(printer, m_param);
|
||||
NODE_PRINT(printer, m_value);
|
||||
|
||||
return "AlterEDSPoolSetNode";
|
||||
}
|
||||
|
||||
void AlterEDSPoolSetNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScratch*/, jrd_tra* /*transaction*/)
|
||||
{
|
||||
switch (m_param)
|
||||
{
|
||||
case POOL_SIZE:
|
||||
EDS::Manager::getConnPool()->setMaxCount(m_value);
|
||||
break;
|
||||
|
||||
case POOL_LIFETIME:
|
||||
EDS::Manager::getConnPool()->setLifeTime(m_value);
|
||||
break;
|
||||
|
||||
default:
|
||||
status_exception::raise(
|
||||
Arg::Gds(isc_random) << Arg::Str("Unknown param for ALTER EXTERNAL CONNECTIONS POOL statement"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool AlterEDSPoolClearNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
|
||||
{
|
||||
if (!tdbb->getAttachment()->locksmith(tdbb, MODIFY_EXT_CONN_POOL))
|
||||
status_exception::raise(Arg::Gds(isc_miss_prvlg) << "MODIFY_EXT_CONN_POOL");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string AlterEDSPoolClearNode::internalPrint(NodePrinter& printer) const
|
||||
{
|
||||
DdlNode::internalPrint(printer);
|
||||
|
||||
NODE_PRINT(printer, m_param);
|
||||
NODE_PRINT(printer, m_value);
|
||||
|
||||
return "AlterEDSPoolClearNode";
|
||||
}
|
||||
|
||||
void AlterEDSPoolClearNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScratch*/, jrd_tra* /*transaction*/)
|
||||
{
|
||||
switch (m_param)
|
||||
{
|
||||
case POOL_ALL:
|
||||
{
|
||||
EDS::Manager::getConnPool()->clearIdle(tdbb, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case POOL_OLDEST:
|
||||
{
|
||||
EDS::Manager::getConnPool()->clearIdle(tdbb, false);
|
||||
break;
|
||||
}
|
||||
|
||||
case POOL_DB:
|
||||
//break;
|
||||
|
||||
default:
|
||||
status_exception::raise(
|
||||
Arg::Gds(isc_random) << Arg::Str("Unknown param for ALTER EXTERNAL CONNECTIONS POOL statement"));
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------
|
||||
|
||||
|
||||
|
@ -295,6 +295,65 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class AlterEDSPoolSetNode : public DdlNode
|
||||
{
|
||||
public:
|
||||
enum PARAM {POOL_SIZE, POOL_LIFETIME};
|
||||
|
||||
AlterEDSPoolSetNode(MemoryPool& pool, PARAM prm, int val) :
|
||||
DdlNode(pool),
|
||||
m_param(prm),
|
||||
m_value(val)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction);
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
|
||||
|
||||
protected:
|
||||
virtual void putErrorPrefix(Firebird::Arg::StatusVector& statusVector)
|
||||
{
|
||||
// TODO: statusVector << Firebird::Arg::Gds(??);
|
||||
}
|
||||
|
||||
private:
|
||||
PARAM m_param;
|
||||
int m_value;
|
||||
};
|
||||
|
||||
|
||||
class AlterEDSPoolClearNode : public DdlNode
|
||||
{
|
||||
public:
|
||||
enum PARAM {POOL_ALL, POOL_OLDEST, POOL_DB};
|
||||
|
||||
AlterEDSPoolClearNode(MemoryPool& pool, PARAM prm, const Firebird::string& val = "") :
|
||||
DdlNode(pool),
|
||||
m_param(prm),
|
||||
m_value(pool)
|
||||
{
|
||||
m_value = val;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual bool checkPermission(thread_db* tdbb, jrd_tra* transaction);
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
|
||||
|
||||
protected:
|
||||
virtual void putErrorPrefix(Firebird::Arg::StatusVector& statusVector)
|
||||
{
|
||||
// TODO: statusVector << Firebird::Arg::Gds(??);
|
||||
}
|
||||
|
||||
private:
|
||||
PARAM m_param;
|
||||
Firebird::string m_value;
|
||||
};
|
||||
|
||||
|
||||
class CommentOnNode : public DdlNode
|
||||
{
|
||||
public:
|
||||
|
@ -630,6 +630,13 @@ using namespace Firebird;
|
||||
%token <metaNamePtr> VARBINARY
|
||||
%token <metaNamePtr> WINDOW
|
||||
|
||||
// external connections pool management
|
||||
%token <metaNamePtr> CONNECTIONS
|
||||
%token <metaNamePtr> POOL
|
||||
%token <metaNamePtr> LIFETIME
|
||||
%token <metaNamePtr> CLEAR
|
||||
%token <metaNamePtr> OLDEST
|
||||
|
||||
// precedence declarations for expression evaluation
|
||||
|
||||
%left OR
|
||||
@ -1972,6 +1979,29 @@ alter_charset_clause
|
||||
{ $$ = newNode<AlterCharSetNode>(*$1, *$5); }
|
||||
;
|
||||
|
||||
//
|
||||
%type <ddlNode> alter_eds_conn_pool_clause
|
||||
alter_eds_conn_pool_clause
|
||||
: SET SIZE unsigned_short_integer
|
||||
{ $$ = newNode<AlterEDSPoolSetNode>(AlterEDSPoolSetNode::POOL_SIZE, $3); }
|
||||
| SET LIFETIME unsigned_short_integer eds_pool_lifetime_mult
|
||||
{ $$ = newNode<AlterEDSPoolSetNode>(AlterEDSPoolSetNode::POOL_LIFETIME, $3 * $4); }
|
||||
| CLEAR sql_string
|
||||
{ $$ = newNode<AlterEDSPoolClearNode>(AlterEDSPoolClearNode::POOL_DB, $2->getString()); }
|
||||
| CLEAR ALL
|
||||
{ $$ = newNode<AlterEDSPoolClearNode>(AlterEDSPoolClearNode::POOL_ALL); }
|
||||
| CLEAR OLDEST
|
||||
{ $$ = newNode<AlterEDSPoolClearNode>(AlterEDSPoolClearNode::POOL_OLDEST); }
|
||||
;
|
||||
|
||||
%type <intVal> eds_pool_lifetime_mult
|
||||
eds_pool_lifetime_mult :
|
||||
HOUR { $$ = 3600; }
|
||||
| MINUTE { $$ = 60; }
|
||||
| SECOND { $$ = 1; }
|
||||
;
|
||||
|
||||
|
||||
// CREATE DATABASE
|
||||
// ASF: CREATE DATABASE command is divided in three pieces: name, initial options and
|
||||
// remote options.
|
||||
@ -3873,6 +3903,7 @@ alter_clause
|
||||
| SEQUENCE alter_sequence_clause { $$ = $2; }
|
||||
| MAPPING alter_map_clause(false) { $$ = $2; }
|
||||
| GLOBAL MAPPING alter_map_clause(true) { $$ = $3; }
|
||||
| EXTERNAL CONNECTIONS POOL alter_eds_conn_pool_clause { $$ = $4; }
|
||||
;
|
||||
|
||||
%type <alterDomainNode> alter_domain
|
||||
@ -8526,6 +8557,11 @@ non_reserved_word
|
||||
| TIES
|
||||
| TOTALORDER
|
||||
| TRAPS
|
||||
| CONNECTIONS // external connections pool management
|
||||
| POOL
|
||||
| LIFETIME
|
||||
| CLEAR
|
||||
| OLDEST
|
||||
;
|
||||
|
||||
%%
|
||||
|
@ -197,6 +197,7 @@ Jrd::Attachment::Attachment(MemoryPool* pool, Database* dbb)
|
||||
att_dsql_cache(*pool),
|
||||
att_udf_pointers(*pool),
|
||||
att_ext_connection(NULL),
|
||||
att_ext_parent(NULL),
|
||||
att_ext_call_depth(0),
|
||||
att_trace_manager(FB_NEW_POOL(*att_pool) TraceManager(this)),
|
||||
att_utility(UTIL_NONE),
|
||||
|
@ -332,6 +332,7 @@ public:
|
||||
ThreadId att_purge_tid; // ID of thread running purge_attachment()
|
||||
|
||||
EDS::Connection* att_ext_connection; // external connection executed by this attachment
|
||||
EDS::Connection* att_ext_parent; // external connection, parent of this attachment
|
||||
ULONG att_ext_call_depth; // external connection call depth, 0 for user attachment
|
||||
TraceManager* att_trace_manager; // Trace API manager
|
||||
|
||||
|
@ -53,6 +53,7 @@
|
||||
#include "../jrd/trace/TraceObjects.h"
|
||||
#include "../jrd/Collation.h"
|
||||
#include "../common/classes/FpeControl.h"
|
||||
#include "../jrd/extds/ExtDS.h"
|
||||
#include <math.h>
|
||||
|
||||
using namespace Firebird;
|
||||
@ -275,6 +276,10 @@ const char
|
||||
// SYSTEM namespace: global and database wise items
|
||||
ENGINE_VERSION[] = "ENGINE_VERSION",
|
||||
DATABASE_NAME[] = "DB_NAME",
|
||||
EXT_CONN_POOL_SIZE[] = "EXT_CONN_POOL_SIZE",
|
||||
EXT_CONN_POOL_IDLE[] = "EXT_CONN_POOL_IDLE_COUNT",
|
||||
EXT_CONN_POOL_ACTIVE[] = "EXT_CONN_POOL_ACTIVE_COUNT",
|
||||
EXT_CONN_POOL_LIFETIME[] = "EXT_CONN_POOL_LIFETIME",
|
||||
// SYSTEM namespace: connection wise items
|
||||
SESSION_ID_NAME[] = "SESSION_ID",
|
||||
NETWORK_PROTOCOL_NAME[] = "NETWORK_PROTOCOL",
|
||||
@ -2664,6 +2669,17 @@ dsc* evlGetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar
|
||||
resultStr.printf("%" SLONGFORMAT, transaction->tra_lock_timeout);
|
||||
else if (nameStr == READ_ONLY_NAME)
|
||||
resultStr = (transaction->tra_flags & TRA_readonly) ? TRUE_VALUE : FALSE_VALUE;
|
||||
else if (nameStr == EXT_CONN_POOL_SIZE)
|
||||
resultStr.printf("%d", EDS::Manager::getConnPool()->getMaxCount());
|
||||
else if (nameStr == EXT_CONN_POOL_IDLE)
|
||||
resultStr.printf("%d", EDS::Manager::getConnPool()->getIdleCount());
|
||||
else if (nameStr == EXT_CONN_POOL_ACTIVE)
|
||||
{
|
||||
EDS::ConnectionsPool* connPool = EDS::Manager::getConnPool();
|
||||
resultStr.printf("%d", connPool->getAllCount() - connPool->getIdleCount());
|
||||
}
|
||||
else if (nameStr == EXT_CONN_POOL_LIFETIME)
|
||||
resultStr.printf("%d", EDS::Manager::getConnPool()->getLifeTime());
|
||||
else
|
||||
{
|
||||
// "Context variable %s is not found in namespace %s"
|
||||
|
@ -63,6 +63,7 @@ SYSTEM_PRIVILEGE(GRANT_REVOKE_ON_ANY_OBJECT)
|
||||
SYSTEM_PRIVILEGE(GRANT_REVOKE_ANY_DDL_RIGHT)
|
||||
SYSTEM_PRIVILEGE(CREATE_PRIVILEGED_ROLES)
|
||||
SYSTEM_PRIVILEGE(GET_DBCRYPT_INFO)
|
||||
SYSTEM_PRIVILEGE(MODIFY_EXT_CONN_POOL)
|
||||
|
||||
#ifdef FB_JRD_SYSTEM_PRIVILEGES_TMP
|
||||
maxSystemPrivilege
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -42,6 +42,7 @@ namespace EDS {
|
||||
class Manager;
|
||||
class Provider;
|
||||
class Connection;
|
||||
class ConnectionsPool;
|
||||
class Transaction;
|
||||
class Statement;
|
||||
class Blob;
|
||||
@ -67,19 +68,22 @@ public:
|
||||
const Firebird::string& dataSource, const Firebird::string& user,
|
||||
const Firebird::string& pwd, const Firebird::string& role, TraScope tra_scope);
|
||||
|
||||
// Notify providers when some jrd attachment is about to be released
|
||||
static void jrdAttachmentEnd(Jrd::thread_db* tdbb, Jrd::Attachment* att);
|
||||
static int shutdown();
|
||||
static ConnectionsPool* getConnPool() { return m_connPool; }
|
||||
|
||||
// Release bound external connections when some jrd attachment is about to be released
|
||||
static void jrdAttachmentEnd(Jrd::thread_db* tdbb, Jrd::Attachment* att, bool forced);
|
||||
|
||||
static int shutdown();
|
||||
private:
|
||||
static Firebird::GlobalPtr<Manager> manager;
|
||||
static Firebird::Mutex m_mutex;
|
||||
static Provider* m_providers;
|
||||
static volatile bool m_initialized;
|
||||
static ConnectionsPool* m_connPool;
|
||||
};
|
||||
|
||||
|
||||
// manages connections\connection pool
|
||||
// manages connections
|
||||
|
||||
class Provider : public Firebird::GlobalStorage
|
||||
{
|
||||
@ -89,16 +93,24 @@ class Provider : public Firebird::GlobalStorage
|
||||
public:
|
||||
explicit Provider(const char* prvName);
|
||||
|
||||
// return existing or create new Connection
|
||||
virtual Connection* getConnection(Jrd::thread_db* tdbb, const Firebird::PathName& dbName,
|
||||
const Firebird::string& user, const Firebird::string& pwd, const Firebird::string& role,
|
||||
// create new Connection
|
||||
virtual Connection* createConnection(Jrd::thread_db* tdbb,
|
||||
const Firebird::PathName& dbName, Firebird::ClumpletReader& dpb,
|
||||
TraScope tra_scope);
|
||||
|
||||
// Connection gets unused, release it into pool or delete it completely
|
||||
// bind connection to the current attachment
|
||||
void bindConnection(Jrd::thread_db* tdbb, Connection* conn);
|
||||
|
||||
// get available connection already bound to the current attachment
|
||||
Connection* getBoundConnection(Jrd::thread_db* tdbb,
|
||||
const Firebird::PathName& dbName, Firebird::ClumpletReader& dpb,
|
||||
TraScope tra_scope);
|
||||
|
||||
// Connection gets unused, release it into pool or delete it immediately
|
||||
virtual void releaseConnection(Jrd::thread_db* tdbb, Connection& conn, bool inPool = true);
|
||||
|
||||
// Notify provider when some jrd attachment is about to be released
|
||||
virtual void jrdAttachmentEnd(Jrd::thread_db* tdbb, Jrd::Attachment* att) = 0;
|
||||
// release connections bound to the attachment
|
||||
virtual void jrdAttachmentEnd(Jrd::thread_db* tdbb, Jrd::Attachment* att, bool forced);
|
||||
|
||||
// cancel execution of every connection
|
||||
void cancelConnections();
|
||||
@ -123,6 +135,10 @@ protected:
|
||||
void clearConnections(Jrd::thread_db* tdbb);
|
||||
virtual Connection* doCreateConnection() = 0;
|
||||
|
||||
void generateDPB(Jrd::thread_db* tdbb, Firebird::ClumpletWriter& dpb,
|
||||
const Firebird::string& user, const Firebird::string& pwd,
|
||||
const Firebird::string& role) const;
|
||||
|
||||
// Protection against simultaneous attach database calls. Not sure we still
|
||||
// need it, but i believe it will not harm
|
||||
Firebird::Mutex m_mutex;
|
||||
@ -130,7 +146,39 @@ protected:
|
||||
Firebird::string m_name;
|
||||
Provider* m_next;
|
||||
|
||||
Firebird::Array<Connection*> m_connections;
|
||||
class AttToConn
|
||||
{
|
||||
public:
|
||||
Jrd::Attachment* m_att;
|
||||
Connection* m_conn;
|
||||
|
||||
AttToConn() :
|
||||
m_att(NULL),
|
||||
m_conn(NULL)
|
||||
{}
|
||||
|
||||
AttToConn(Jrd::Attachment* att, Connection* conn) :
|
||||
m_att(att),
|
||||
m_conn(conn)
|
||||
{}
|
||||
|
||||
static const AttToConn& generate(const void*, const AttToConn& item)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
static bool greaterThan(const AttToConn& i1, const AttToConn& i2)
|
||||
{
|
||||
return (i1.m_att > i2.m_att) ||
|
||||
(i1.m_att == i2.m_att && i1.m_conn > i2.m_conn);
|
||||
}
|
||||
};
|
||||
|
||||
typedef Firebird::BePlusTree<AttToConn, AttToConn, Firebird::MemoryPool,
|
||||
AttToConn, AttToConn>
|
||||
AttToConnMap;
|
||||
|
||||
AttToConnMap m_connections;
|
||||
int m_flags;
|
||||
};
|
||||
|
||||
@ -141,26 +189,238 @@ const int prvNamedParams = 0x0004; // supports named parameters
|
||||
const int prvTrustedAuth = 0x0008; // supports trusted authentication
|
||||
|
||||
|
||||
class ConnectionsPool
|
||||
{
|
||||
public:
|
||||
ConnectionsPool(Firebird::MemoryPool& pool);
|
||||
~ConnectionsPool();
|
||||
|
||||
// find and return cached connection or NULL
|
||||
Connection* getConnection(Jrd::thread_db* tdbb, Provider* prv, ULONG hash, const Firebird::PathName& dbName,
|
||||
Firebird::ClumpletReader& dpb);
|
||||
|
||||
// put unused connection into pool or destroy it
|
||||
void putConnection(Jrd::thread_db* tdbb, Connection* conn);
|
||||
|
||||
// assotiate new active connection with pool
|
||||
void addConnection(Jrd::thread_db* tdbb, Connection* conn, ULONG hash);
|
||||
|
||||
// clear connection relation with pool
|
||||
void delConnection(Jrd::thread_db* tdbb, Connection* conn, bool destroy);
|
||||
|
||||
ULONG getIdleCount() const { return m_idleArray.getCount(); }
|
||||
ULONG getAllCount() const { return m_allCount; } ;
|
||||
|
||||
ULONG getMaxCount() const { return m_maxCount; }
|
||||
void setMaxCount(ULONG val);
|
||||
|
||||
ULONG getLifeTime() const { return m_lifeTime; }
|
||||
void setLifeTime(ULONG val);
|
||||
|
||||
// delete idle connections: all or older than lifetime
|
||||
void clearIdle(Jrd::thread_db* tdbb, bool all);
|
||||
|
||||
// delete all idle connections, remove from pool all active connections
|
||||
void clear(Jrd::thread_db* tdbb);
|
||||
|
||||
// return time when oldest idle connection should be released, or zero
|
||||
time_t getIdleExpireTime();
|
||||
|
||||
// verify bound connection internals
|
||||
static bool checkBoundConnection(Jrd::thread_db* tdbb, Connection* conn);
|
||||
public:
|
||||
// this class is embedded into Connection but managed by ConnectionsPool
|
||||
class Data
|
||||
{
|
||||
public:
|
||||
|
||||
// constructor for embedded into Connection instance
|
||||
explicit Data(Connection* conn)
|
||||
{
|
||||
clear();
|
||||
m_conn = conn;
|
||||
}
|
||||
|
||||
ConnectionsPool* getConnPool() const { return m_connPool; }
|
||||
|
||||
static const Data& generate(const Data* item)
|
||||
{
|
||||
return *item;
|
||||
}
|
||||
|
||||
static bool greaterThan(const Data& i1, const Data& i2)
|
||||
{
|
||||
if (i1.m_hash == i2.m_hash)
|
||||
{
|
||||
if (i1.m_lastUsed == i2.m_lastUsed)
|
||||
return &i1 > &i2;
|
||||
|
||||
return (i1.m_lastUsed < i2.m_lastUsed);
|
||||
}
|
||||
|
||||
return (i1.m_hash > i2.m_hash);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ConnectionsPool;
|
||||
|
||||
ConnectionsPool* m_connPool;
|
||||
Connection* m_conn;
|
||||
ULONG m_hash;
|
||||
time_t m_lastUsed;
|
||||
|
||||
// placement in connections list
|
||||
Data* m_next;
|
||||
Data* m_prev;
|
||||
|
||||
Data(const Data&);
|
||||
Data& operator=(const Data&);
|
||||
|
||||
// create instance used to search for recently used connection by hash
|
||||
explicit Data(ULONG hash)
|
||||
{
|
||||
clear();
|
||||
m_conn = NULL;
|
||||
m_hash = hash;
|
||||
m_lastUsed = MAX_SINT64;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_connPool = NULL;
|
||||
// m_conn = NULL;
|
||||
m_hash = 0;
|
||||
m_lastUsed = 0;
|
||||
m_next = m_prev = NULL;
|
||||
}
|
||||
|
||||
void setConnPool(ConnectionsPool *connPool)
|
||||
{
|
||||
fb_assert(!connPool || !m_connPool);
|
||||
m_connPool = connPool;
|
||||
}
|
||||
Firebird::string print();
|
||||
int verify(ConnectionsPool *connPool, bool active);
|
||||
};
|
||||
|
||||
private:
|
||||
class IdleTimer FB_FINAL :
|
||||
public Firebird::RefCntIface<Firebird::ITimerImpl<IdleTimer, Firebird::CheckStatusWrapper> >
|
||||
{
|
||||
public:
|
||||
explicit IdleTimer(ConnectionsPool& connPool) :
|
||||
m_connPool(connPool),
|
||||
m_time(0)
|
||||
{}
|
||||
|
||||
// ITimer implementation
|
||||
void handler();
|
||||
int release();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
private:
|
||||
ConnectionsPool& m_connPool;
|
||||
Firebird::Mutex m_mutex;
|
||||
time_t m_time; // time when timer should fire, or zero
|
||||
};
|
||||
|
||||
void addToList(Data** head, Data* item)
|
||||
{
|
||||
fb_assert(item->m_next == NULL);
|
||||
fb_assert(item->m_prev == NULL);
|
||||
fb_assert(head == (item->m_lastUsed ? &m_idleList : &m_activeList));
|
||||
|
||||
if (*head)
|
||||
{
|
||||
item->m_next = (*head);
|
||||
item->m_prev = (*head)->m_prev;
|
||||
|
||||
item->m_next->m_prev = item;
|
||||
item->m_prev->m_next = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->m_next = item;
|
||||
item->m_prev = item;
|
||||
}
|
||||
|
||||
*head = item;
|
||||
}
|
||||
void removeFromList(Data** head, Data* item)
|
||||
{
|
||||
if (!item->m_next)
|
||||
return;
|
||||
|
||||
fb_assert(head == (item->m_lastUsed ? &m_idleList : &m_activeList));
|
||||
|
||||
if (item->m_next != item)
|
||||
{
|
||||
item->m_next->m_prev = item->m_prev;
|
||||
item->m_prev->m_next = item->m_next;
|
||||
if (*head == item)
|
||||
*head = item->m_next;
|
||||
}
|
||||
else
|
||||
{
|
||||
fb_assert((*head) == item);
|
||||
*head = NULL;
|
||||
}
|
||||
|
||||
item->m_next = item->m_prev = NULL;
|
||||
}
|
||||
|
||||
void removeFromPool(Data* item, FB_SIZE_T pos);
|
||||
Data* removeOldest();
|
||||
|
||||
void printPool(Firebird::string& s);
|
||||
bool verifyPool();
|
||||
|
||||
// Array of Data*, sorted by [hash, lastUsed desc]
|
||||
typedef Firebird::SortedArray<Data*, Firebird::EmptyStorage<Data*>, Data, Data, Data>
|
||||
IdleArray;
|
||||
|
||||
Firebird::MemoryPool& m_pool;
|
||||
Firebird::Mutex m_mutex;
|
||||
IdleArray m_idleArray;
|
||||
Data* m_idleList;
|
||||
Data* m_activeList;
|
||||
ULONG m_allCount;
|
||||
ULONG m_maxCount;
|
||||
ULONG m_lifeTime; // How long idle connection should wait before destroyng, seconds
|
||||
Firebird::RefPtr<IdleTimer> m_timer;
|
||||
};
|
||||
|
||||
|
||||
class Connection : public Firebird::PermanentStorage
|
||||
{
|
||||
protected:
|
||||
friend class EngineCallbackGuard;
|
||||
friend class Provider;
|
||||
|
||||
// only Provider could create, setup and delete Connections
|
||||
|
||||
explicit Connection(Provider& prov);
|
||||
virtual ~Connection();
|
||||
|
||||
public:
|
||||
static void deleteConnection(Jrd::thread_db* tdbb, Connection* conn);
|
||||
void setup(const Firebird::PathName& dbName, const Firebird::ClumpletReader& dpb);
|
||||
|
||||
void setBoundAtt(Jrd::Attachment* att) { m_boundAtt = att; }
|
||||
|
||||
public:
|
||||
Provider* getProvider() { return &m_provider; }
|
||||
|
||||
virtual void attach(Jrd::thread_db* tdbb, const Firebird::PathName& dbName,
|
||||
const Firebird::MetaName& user, const Firebird::string& pwd,
|
||||
const Firebird::MetaName& role) = 0;
|
||||
Jrd::Attachment* getBoundAtt() const { return m_boundAtt; }
|
||||
|
||||
ConnectionsPool* getConnPool() { return m_poolData.getConnPool(); }
|
||||
ConnectionsPool::Data* getPoolData() { return &m_poolData; }
|
||||
|
||||
virtual void attach(Jrd::thread_db* tdbb) = 0;
|
||||
virtual void detach(Jrd::thread_db* tdbb);
|
||||
|
||||
virtual bool cancelExecution(bool forced) = 0;
|
||||
virtual bool resetSession() = 0;
|
||||
|
||||
int getSqlDialect() const { return m_sqlDialect; }
|
||||
|
||||
@ -171,10 +431,13 @@ public:
|
||||
virtual bool isAvailable(Jrd::thread_db* tdbb, TraScope traScope) const = 0;
|
||||
|
||||
virtual bool isConnected() const = 0;
|
||||
virtual bool validate(Jrd::thread_db* tdbb) = 0;
|
||||
|
||||
virtual bool isSameDatabase(Jrd::thread_db* tdbb, const Firebird::PathName& dbName,
|
||||
const Firebird::MetaName& user, const Firebird::string& pwd,
|
||||
const Firebird::MetaName& role) const;
|
||||
virtual bool isSameDatabase(const Firebird::PathName& dbName,
|
||||
Firebird::ClumpletReader& dpb) const;
|
||||
|
||||
// only Internal provider is able to create "current" connections
|
||||
virtual bool isCurrent() const { return false; }
|
||||
|
||||
bool isBroken() const
|
||||
{
|
||||
@ -213,10 +476,6 @@ public:
|
||||
virtual Blob* createBlob() = 0;
|
||||
|
||||
protected:
|
||||
void generateDPB(Jrd::thread_db* tdbb, Firebird::ClumpletWriter& dpb,
|
||||
const Firebird::MetaName& user, const Firebird::string& pwd,
|
||||
const Firebird::MetaName& role) const;
|
||||
|
||||
virtual Transaction* doCreateTransaction() = 0;
|
||||
virtual Statement* doCreateStatement() = 0;
|
||||
|
||||
@ -230,13 +489,14 @@ protected:
|
||||
|
||||
Provider& m_provider;
|
||||
Firebird::PathName m_dbName;
|
||||
Firebird::ClumpletWriter m_dpb;
|
||||
Firebird::UCharBuffer m_dpb;
|
||||
Jrd::Attachment* m_boundAtt;
|
||||
|
||||
Firebird::Array<Transaction*> m_transactions;
|
||||
Firebird::Array<Statement*> m_statements;
|
||||
Statement* m_freeStatements;
|
||||
|
||||
const Jrd::Attachment* m_boundAtt;
|
||||
ConnectionsPool::Data m_poolData;
|
||||
|
||||
static const int MAX_CACHED_STMTS = 16;
|
||||
int m_used_stmts;
|
||||
|
@ -64,26 +64,35 @@ static GlobalPtr<RegisterInternalProvider> reg;
|
||||
|
||||
// InternalProvider
|
||||
|
||||
void InternalProvider::jrdAttachmentEnd(thread_db* tdbb, Jrd::Attachment* att)
|
||||
void InternalProvider::jrdAttachmentEnd(thread_db* tdbb, Attachment* att, bool forced)
|
||||
{
|
||||
/***
|
||||
hvlad: this inactive code could be useful in the future, for example when EDS
|
||||
connection pool will be implemented - it allows to remove closed connections
|
||||
from the pool.
|
||||
Provider::jrdAttachmentEnd(tdbb, att, forced);
|
||||
|
||||
if (m_connections.getCount() == 0)
|
||||
Connection* conn = att->att_ext_parent;
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
Connection** ptr = m_connections.end();
|
||||
Connection** begin = m_connections.begin();
|
||||
|
||||
for (ptr--; ptr >= begin; ptr--)
|
||||
{
|
||||
InternalConnection* conn = (InternalConnection*) *ptr;
|
||||
if (conn->getJrdAtt() == att->getInterface())
|
||||
releaseConnection(tdbb, *conn, false);
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
MutexLockGuard guard(m_mutex, FB_FUNCTION);
|
||||
|
||||
AttToConnMap::Accessor acc(&m_connections);
|
||||
if (acc.locate(AttToConn(conn->getBoundAtt(), conn)))
|
||||
{
|
||||
InternalConnection* intConn = (InternalConnection*) acc.current().m_conn;
|
||||
if (!intConn->getJrdAtt() || intConn->getJrdAtt()->getHandle() != att)
|
||||
{
|
||||
fb_assert(intConn->getJrdAtt() == NULL);
|
||||
return;
|
||||
}
|
||||
fb_assert(intConn == conn);
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
***/
|
||||
|
||||
if (conn)
|
||||
releaseConnection(tdbb, *conn, false);
|
||||
}
|
||||
|
||||
void InternalProvider::getRemoteError(const FbStatusVector* status, string& err) const
|
||||
@ -135,22 +144,17 @@ private:
|
||||
FbStatusVector *v;
|
||||
};
|
||||
|
||||
void InternalConnection::attach(thread_db* tdbb, const PathName& dbName,
|
||||
const MetaName& user, const string& pwd,
|
||||
const MetaName& role)
|
||||
void InternalConnection::attach(thread_db* tdbb)
|
||||
{
|
||||
fb_assert(!m_attachment);
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
fb_assert(dbName.isEmpty() || dbName == dbb->dbb_database_name.c_str());
|
||||
Attachment* attachment = tdbb->getAttachment();
|
||||
fb_assert(m_dbName.isEmpty() || m_dbName == dbb->dbb_database_name.c_str());
|
||||
|
||||
// Don't wrap raised errors. This is needed for backward compatibility.
|
||||
setWrapErrors(false);
|
||||
|
||||
Jrd::Attachment* attachment = tdbb->getAttachment();
|
||||
if (attachment->att_user &&
|
||||
(user.isEmpty() || user == attachment->att_user->getUserName()) &&
|
||||
pwd.isEmpty() &&
|
||||
(role.isEmpty() || role == attachment->att_user->getSqlRole()))
|
||||
if (m_dpb.isEmpty())
|
||||
{
|
||||
m_isCurrent = true;
|
||||
m_attachment = attachment->getInterface();
|
||||
@ -159,11 +163,11 @@ void InternalConnection::attach(thread_db* tdbb, const PathName& dbName,
|
||||
{
|
||||
m_isCurrent = false;
|
||||
m_dbName = dbb->dbb_database_name.c_str();
|
||||
generateDPB(tdbb, m_dpb, user, pwd, role);
|
||||
|
||||
// Avoid change of m_dpb by validatePassword() below
|
||||
ClumpletWriter newDpb(m_dpb);
|
||||
ClumpletWriter newDpb(ClumpletReader::Tagged, MAX_DPB_SIZE, m_dpb.begin(), m_dpb.getCount(), 0);
|
||||
validatePassword(tdbb, m_dbName, newDpb);
|
||||
newDpb.insertInt(isc_dpb_ext_call_depth, attachment->att_ext_call_depth + 1);
|
||||
|
||||
FbLocalStatus status;
|
||||
{
|
||||
@ -176,9 +180,11 @@ void InternalConnection::attach(thread_db* tdbb, const PathName& dbName,
|
||||
|
||||
if (status->getState() & IStatus::STATE_ERRORS)
|
||||
raise(&status, tdbb, "JProvider::attach");
|
||||
|
||||
attachment->att_ext_parent = this;
|
||||
}
|
||||
|
||||
m_sqlDialect = (m_attachment->getHandle()->att_database->dbb_flags & DBB_DB_SQL_dialect_3) ?
|
||||
m_sqlDialect = (attachment->att_database->dbb_flags & DBB_DB_SQL_dialect_3) ?
|
||||
SQL_DIALECT_V6 : SQL_DIALECT_V5;
|
||||
}
|
||||
|
||||
@ -204,7 +210,7 @@ void InternalConnection::doDetach(thread_db* tdbb)
|
||||
att->detach(&status);
|
||||
}
|
||||
|
||||
if (status->getErrors()[1] == isc_att_shutdown)
|
||||
if (status->getErrors()[1] == isc_att_shutdown || status->getErrors()[1] == isc_shutdown)
|
||||
{
|
||||
status->init();
|
||||
}
|
||||
@ -233,6 +239,20 @@ bool InternalConnection::cancelExecution(bool /*forced*/)
|
||||
return !(status->getState() & IStatus::STATE_ERRORS);
|
||||
}
|
||||
|
||||
bool InternalConnection::resetSession()
|
||||
{
|
||||
fb_assert(!m_isCurrent);
|
||||
|
||||
if (m_isCurrent)
|
||||
return true;
|
||||
|
||||
FbLocalStatus status;
|
||||
m_attachment->execute(&status, NULL, 0, "ALTER SESSION RESET",
|
||||
m_sqlDialect, NULL, NULL, NULL, NULL);
|
||||
|
||||
return !(status->getState() & IStatus::STATE_ERRORS);
|
||||
}
|
||||
|
||||
// this internal connection instance is available for the current execution context if it
|
||||
// a) is current connection and current thread's attachment is equal to
|
||||
// this attachment, or
|
||||
@ -243,20 +263,46 @@ bool InternalConnection::isAvailable(thread_db* tdbb, TraScope /*traScope*/) con
|
||||
(m_isCurrent && (tdbb->getAttachment() == m_attachment->getHandle()));
|
||||
}
|
||||
|
||||
bool InternalConnection::isSameDatabase(thread_db* tdbb, const PathName& dbName,
|
||||
const MetaName& user, const string& pwd,
|
||||
const MetaName& role) const
|
||||
bool InternalConnection::validate(thread_db* tdbb)
|
||||
{
|
||||
if (m_isCurrent)
|
||||
return true;
|
||||
|
||||
if (!m_attachment)
|
||||
return false;
|
||||
|
||||
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
|
||||
FbLocalStatus status;
|
||||
m_attachment->ping(&status);
|
||||
return status.isSuccess();
|
||||
}
|
||||
|
||||
bool InternalConnection::isSameDatabase(const PathName& dbName, ClumpletReader& dpb) const
|
||||
{
|
||||
if (m_isCurrent)
|
||||
{
|
||||
const UserId* attUser = m_attachment->getHandle()->att_user;
|
||||
return (attUser &&
|
||||
(user.isEmpty() || user == attUser->getUserName()) &&
|
||||
pwd.isEmpty() &&
|
||||
(role.isEmpty() || role == attUser->getSqlRole()));
|
||||
const Attachment* att = m_attachment->getHandle();
|
||||
const MetaName& attUser = att->att_user->getUserName();
|
||||
const MetaName& attRole = att->att_user->getSqlRole();
|
||||
|
||||
MetaName str;
|
||||
|
||||
if (dpb.find(isc_dpb_user_name))
|
||||
{
|
||||
dpb.getString(str);
|
||||
if (str != attUser)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dpb.find(isc_dpb_sql_role_name))
|
||||
{
|
||||
dpb.getString(str);
|
||||
if (str != attRole)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return Connection::isSameDatabase(tdbb, dbName, user, pwd, role);
|
||||
return Connection::isSameDatabase(dbName, dpb);
|
||||
}
|
||||
|
||||
Transaction* InternalConnection::doCreateTransaction()
|
||||
@ -358,7 +404,7 @@ void InternalTransaction::doRollback(FbStatusVector* status, thread_db* tdbb, bo
|
||||
m_transaction->rollback(&s);
|
||||
}
|
||||
|
||||
if (status->getErrors()[1] == isc_att_shutdown && !retain)
|
||||
if ((status->getErrors()[1] == isc_att_shutdown || status->getErrors()[1] == isc_shutdown) && !retain)
|
||||
{
|
||||
m_transaction = NULL;
|
||||
status->init();
|
||||
|
@ -40,8 +40,9 @@ public:
|
||||
~InternalProvider()
|
||||
{}
|
||||
|
||||
virtual void jrdAttachmentEnd(Jrd::thread_db* tdbb, Jrd::Attachment* att, bool forced);
|
||||
|
||||
virtual void initialize() {}
|
||||
virtual void jrdAttachmentEnd(Jrd::thread_db* tdbb, Jrd::Attachment* att);
|
||||
virtual void getRemoteError(const Jrd::FbStatusVector* status, Firebird::string& err) const;
|
||||
|
||||
protected:
|
||||
@ -63,21 +64,20 @@ protected:
|
||||
virtual ~InternalConnection();
|
||||
|
||||
public:
|
||||
virtual void attach(Jrd::thread_db* tdbb, const Firebird::PathName& dbName,
|
||||
const Firebird::MetaName& user, const Firebird::string& pwd,
|
||||
const Firebird::MetaName& role);
|
||||
virtual void attach(Jrd::thread_db* tdbb);
|
||||
|
||||
virtual bool cancelExecution(bool forced);
|
||||
virtual bool resetSession();
|
||||
|
||||
virtual bool isAvailable(Jrd::thread_db* tdbb, TraScope traScope) const;
|
||||
|
||||
virtual bool isConnected() const { return (m_attachment != 0); }
|
||||
virtual bool validate(Jrd::thread_db* tdbb);
|
||||
|
||||
virtual bool isSameDatabase(Jrd::thread_db* tdbb, const Firebird::PathName& dbName,
|
||||
const Firebird::MetaName& user, const Firebird::string& pwd,
|
||||
const Firebird::MetaName& role) const;
|
||||
virtual bool isSameDatabase(const Firebird::PathName& dbName,
|
||||
Firebird::ClumpletReader& dpb) const;
|
||||
|
||||
bool isCurrent() const { return m_isCurrent; }
|
||||
virtual bool isCurrent() const { return m_isCurrent; }
|
||||
|
||||
Jrd::JAttachment* getJrdAtt() { return m_attachment; }
|
||||
|
||||
|
@ -110,15 +110,14 @@ IscConnection::~IscConnection()
|
||||
{
|
||||
}
|
||||
|
||||
void IscConnection::attach(thread_db* tdbb, const PathName& dbName, const MetaName& user,
|
||||
const string& pwd, const MetaName& role)
|
||||
void IscConnection::attach(thread_db* tdbb)
|
||||
{
|
||||
m_dbName = dbName;
|
||||
generateDPB(tdbb, m_dpb, user, pwd, role);
|
||||
Attachment* attachment = tdbb->getAttachment();
|
||||
|
||||
// Avoid change of m_dpb by validatePassword() below
|
||||
ClumpletWriter newDpb(m_dpb);
|
||||
ClumpletWriter newDpb(ClumpletReader::Tagged, MAX_DPB_SIZE, m_dpb.begin(), m_dpb.getCount(), 0);
|
||||
validatePassword(tdbb, m_dbName, newDpb);
|
||||
newDpb.insertInt(isc_dpb_ext_call_depth, attachment->att_ext_call_depth + 1);
|
||||
|
||||
FbLocalStatus status;
|
||||
{
|
||||
@ -240,6 +239,21 @@ bool IscConnection::cancelExecution(bool forced)
|
||||
return !(status->getState() & IStatus::STATE_ERRORS);
|
||||
}
|
||||
|
||||
bool IscConnection::resetSession()
|
||||
{
|
||||
if (!m_handle)
|
||||
return false;
|
||||
|
||||
FbLocalStatus status;
|
||||
m_iscProvider.isc_dsql_execute_immediate(&status, &m_handle,
|
||||
NULL, 0, "ALTER SESSION RESET", m_sqlDialect, NULL);
|
||||
|
||||
if (!(status->getState() & IStatus::STATE_ERRORS))
|
||||
return true;
|
||||
|
||||
return false; // (status->getErrors()[1] == isc_dsql_error);
|
||||
}
|
||||
|
||||
// this ISC connection instance is available for the current execution context if it
|
||||
// a) has no active statements or supports many active statements
|
||||
// and
|
||||
@ -260,6 +274,22 @@ bool IscConnection::isAvailable(thread_db* tdbb, TraScope traScope) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IscConnection::validate(Jrd::thread_db* tdbb)
|
||||
{
|
||||
if (!m_handle)
|
||||
return false;
|
||||
|
||||
FbLocalStatus status;
|
||||
|
||||
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
|
||||
|
||||
char info[] = {isc_info_attachment_id, isc_info_end};
|
||||
char buff[32];
|
||||
|
||||
return m_iscProvider.isc_database_info(&status, &m_handle,
|
||||
sizeof(info), info, sizeof(buff), buff) == 0;
|
||||
}
|
||||
|
||||
Blob* IscConnection::createBlob()
|
||||
{
|
||||
return FB_NEW IscBlob(*this);
|
||||
@ -1096,14 +1126,14 @@ ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_execute2(FbStatusVector* user_status
|
||||
}
|
||||
|
||||
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_execute_immediate(FbStatusVector* user_status,
|
||||
isc_db_handle *,
|
||||
isc_tr_handle *,
|
||||
unsigned short,
|
||||
const char*,
|
||||
unsigned short,
|
||||
const XSQLDA *)
|
||||
isc_db_handle* db_handle, isc_tr_handle* tra_handle, unsigned short length,
|
||||
const char* str, unsigned short dialect, const XSQLDA* sqlda)
|
||||
{
|
||||
return notImplemented(user_status);
|
||||
if (!m_api.isc_dsql_execute_immediate)
|
||||
return notImplemented(user_status);
|
||||
|
||||
return (*m_api.isc_dsql_execute_immediate) (IscStatus(user_status),
|
||||
db_handle, tra_handle, length, str, dialect, sqlda);
|
||||
}
|
||||
|
||||
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_fetch(FbStatusVector* user_status,
|
||||
@ -1641,8 +1671,17 @@ void FBProvider::loadAPI()
|
||||
|
||||
static bool isConnectionBrokenError(FbStatusVector* status)
|
||||
{
|
||||
ISC_STATUS code = status->getErrors()[1];
|
||||
return (fb_utils::isNetworkError(code) || code == isc_att_shutdown);
|
||||
const ISC_STATUS code = status->getErrors()[1];
|
||||
switch (code)
|
||||
{
|
||||
case isc_shutdown:
|
||||
case isc_att_shutdown:
|
||||
case isc_bad_db_handle:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return fb_utils::isNetworkError(code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -49,7 +49,6 @@ public:
|
||||
loadAPI();
|
||||
}
|
||||
|
||||
virtual void jrdAttachmentEnd(Jrd::thread_db* /*tdbb*/, Jrd::Attachment* /*att*/) {}
|
||||
virtual void getRemoteError(const Jrd::FbStatusVector* status, Firebird::string& err) const;
|
||||
|
||||
protected:
|
||||
@ -517,15 +516,14 @@ protected:
|
||||
public:
|
||||
FB_API_HANDLE& getAPIHandle() { return m_handle; }
|
||||
|
||||
virtual void attach(Jrd::thread_db* tdbb, const Firebird::PathName& dbName,
|
||||
const Firebird::MetaName& user, const Firebird::string& pwd,
|
||||
const Firebird::MetaName& role);
|
||||
virtual void attach(Jrd::thread_db* tdbb);
|
||||
|
||||
virtual bool cancelExecution(bool forced);
|
||||
virtual bool resetSession();
|
||||
|
||||
virtual bool isAvailable(Jrd::thread_db* tdbb, TraScope traScope) const;
|
||||
|
||||
virtual bool isConnected() const { return (m_handle != 0); }
|
||||
virtual bool validate(Jrd::thread_db* tdbb);
|
||||
|
||||
virtual Blob* createBlob();
|
||||
|
||||
|
@ -4183,6 +4183,8 @@ void JProvider::shutdown(CheckStatusWrapper* status, unsigned int timeout, const
|
||||
|
||||
ThreadContextHolder tdbb;
|
||||
|
||||
EDS::Manager::shutdown();
|
||||
|
||||
ULONG attach_count, database_count, svc_count;
|
||||
JRD_enum_attachments(NULL, attach_count, database_count, svc_count);
|
||||
|
||||
@ -7593,7 +7595,7 @@ static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsign
|
||||
try
|
||||
{
|
||||
// allow to free resources used by dynamic statements
|
||||
EDS::Manager::jrdAttachmentEnd(tdbb, attachment);
|
||||
EDS::Manager::jrdAttachmentEnd(tdbb, attachment, forcedPurge);
|
||||
|
||||
if (!(dbb->dbb_flags & DBB_bugcheck))
|
||||
{
|
||||
@ -8014,9 +8016,6 @@ static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM arg)
|
||||
|
||||
try
|
||||
{
|
||||
// Shutdown external datasets manager
|
||||
EDS::Manager::shutdown();
|
||||
|
||||
{ // scope
|
||||
MutexLockGuard guard(databases_mutex, FB_FUNCTION);
|
||||
|
||||
|
@ -116,6 +116,7 @@ static const TOK tokens[] =
|
||||
{TOK_CHARACTER, "CHARACTER", false},
|
||||
{TOK_CHARACTER_LENGTH, "CHARACTER_LENGTH", false},
|
||||
{TOK_CHECK, "CHECK", false},
|
||||
{TOK_CLEAR, "CLEAR", true},
|
||||
{TOK_CLOSE, "CLOSE", false},
|
||||
{TOK_COALESCE, "COALESCE", true},
|
||||
{TOK_COLLATE, "COLLATE", false},
|
||||
@ -129,6 +130,7 @@ static const TOK tokens[] =
|
||||
{TOK_COMPUTED, "COMPUTED", true},
|
||||
{TOK_CONDITIONAL, "CONDITIONAL", true},
|
||||
{TOK_CONNECT, "CONNECT", false},
|
||||
{TOK_CONNECTIONS, "CONNECTIONS", true},
|
||||
{TOK_CONSTRAINT, "CONSTRAINT", false},
|
||||
{TOK_CONTAINING, "CONTAINING", true},
|
||||
{TOK_CONTINUE, "CONTINUE", true},
|
||||
@ -257,6 +259,7 @@ static const TOK tokens[] =
|
||||
{TOK_LEFT, "LEFT", false},
|
||||
{TOK_LENGTH, "LENGTH", true},
|
||||
{TOK_LEVEL, "LEVEL", true},
|
||||
{TOK_LIFETIME, "LIFETIME", true},
|
||||
{TOK_LIKE, "LIKE", false},
|
||||
{TOK_LIMBO, "LIMBO", true},
|
||||
{TOK_LINGER, "LINGER", true},
|
||||
@ -303,6 +306,7 @@ static const TOK tokens[] =
|
||||
{TOK_OCTET_LENGTH, "OCTET_LENGTH", false},
|
||||
{TOK_OF, "OF", false},
|
||||
{TOK_OFFSET, "OFFSET", false},
|
||||
{TOK_OLDEST, "OLDEST", true},
|
||||
{TOK_ON, "ON", false},
|
||||
{TOK_ONLY, "ONLY", false},
|
||||
{TOK_OPEN, "OPEN", false},
|
||||
@ -330,6 +334,7 @@ static const TOK tokens[] =
|
||||
{TOK_PLACING, "PLACING", true},
|
||||
{TOK_PLAN, "PLAN", false},
|
||||
{TOK_PLUGIN, "PLUGIN", true},
|
||||
{TOK_POOL, "POOL", true},
|
||||
{TOK_POSITION, "POSITION", false},
|
||||
{TOK_POST_EVENT, "POST_EVENT", false},
|
||||
{TOK_POWER, "POWER", true},
|
||||
|
Loading…
Reference in New Issue
Block a user