mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-02-02 08:40:39 +01:00
Initial implementation of external connections pool
This commit is contained in:
parent
aae8f2fd29
commit
f162f2336e
@ -975,3 +975,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
|
||||
|
91
doc/sql.extensions/README.external_connections_pool
Normal file
91
doc/sql.extensions/README.external_connections_pool
Normal file
@ -0,0 +1,91 @@
|
||||
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 assotiated with a pool when created
|
||||
- pool maintains two lists : idle connections and active connections
|
||||
- when some connection gets unused (i.e. it have no active requests and
|
||||
active transactions), it puts into idle list
|
||||
- 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 choosed
|
||||
- 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
|
||||
- finite 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.
|
||||
|
||||
This statement is prepared as DDL statement but have immediate effect on
|
||||
execution (i.e. not at transaction commit).
|
||||
|
||||
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 valus 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.
|
||||
Disassotiates all active connections off the pool (such connections will be
|
||||
closed immediately when gets unused).
|
||||
|
||||
- ALTER EXTERNAL CONNECTIONS POOL CLEAR OLD
|
||||
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, assotiated 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
|
||||
|
@ -202,7 +202,9 @@ const Config::ConfigEntry Config::entries[MAX_CONFIG_KEY] =
|
||||
{TYPE_BOOLEAN, "AllowEncryptedSecurityDatabase", (ConfigValue) false},
|
||||
{TYPE_INTEGER, "StatementTimeout", (ConfigValue) 0},
|
||||
{TYPE_INTEGER, "ConnectionIdleTimeout", (ConfigValue) 0},
|
||||
{TYPE_INTEGER, "ClientBatchBuffer", (ConfigValue) (128 * 1024)}
|
||||
{TYPE_INTEGER, "ClientBatchBuffer", (ConfigValue) (128 * 1024)},
|
||||
{TYPE_INTEGER, "ExtConnPoolSize", (ConfigValue) 0},
|
||||
{TYPE_INTEGER, "ExtConnPoolLifeTime", (ConfigValue) 7200}
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
@ -839,3 +841,12 @@ unsigned int Config::getClientBatchBuffer() const
|
||||
return get<unsigned int>(KEY_CLIENT_BATCH_BUFFER);
|
||||
}
|
||||
|
||||
int Config::getExtConnPoolSize()
|
||||
{
|
||||
return getDefaultConfig()->get<int>(KEY_EXT_CONN_POOL_SIZE);
|
||||
}
|
||||
|
||||
int Config::getExtConnPoolLifeTime()
|
||||
{
|
||||
return getDefaultConfig()->get<int>(KEY_EXT_CONN_POOL_LIFETIME);
|
||||
}
|
||||
|
@ -146,6 +146,8 @@ public:
|
||||
KEY_STMT_TIMEOUT,
|
||||
KEY_CONN_IDLE_TIMEOUT,
|
||||
KEY_CLIENT_BATCH_BUFFER,
|
||||
KEY_EXT_CONN_POOL_SIZE,
|
||||
KEY_EXT_CONN_POOL_LIFETIME,
|
||||
MAX_CONFIG_KEY // keep it last
|
||||
};
|
||||
|
||||
@ -362,6 +364,10 @@ public:
|
||||
unsigned int getConnIdleTimeout() const;
|
||||
|
||||
unsigned int getClientBatchBuffer() const;
|
||||
|
||||
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 {
|
||||
|
||||
@ -1152,6 +1153,82 @@ void AlterCharSetNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch
|
||||
}
|
||||
|
||||
|
||||
bool AlterEDSPoolSetNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
|
||||
{
|
||||
SCL_check_database(tdbb, SCL_alter); // TODO
|
||||
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)
|
||||
{
|
||||
SCL_check_database(tdbb, SCL_alter); // TODO
|
||||
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:
|
||||
|
@ -629,6 +629,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
|
||||
@ -1969,6 +1976,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.
|
||||
@ -3870,6 +3900,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
|
||||
@ -8508,6 +8539,11 @@ non_reserved_word
|
||||
| TIES
|
||||
| TOTALORDER
|
||||
| TRAPS
|
||||
| CONNECTIONS // external connections pool management
|
||||
| POOL
|
||||
| LIFETIME
|
||||
| CLEAR
|
||||
| OLDEST
|
||||
;
|
||||
|
||||
%%
|
||||
|
@ -196,6 +196,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"
|
||||
|
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
|
||||
{
|
||||
@ -90,16 +94,24 @@ public:
|
||||
explicit Provider(const char* prvName);
|
||||
virtual ~Provider();
|
||||
|
||||
// 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,23 +189,234 @@ 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;
|
||||
@ -171,10 +430,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 +475,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 +488,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();
|
||||
}
|
||||
@ -243,20 +249,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()
|
||||
@ -351,7 +383,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,19 @@ 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 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;
|
||||
{
|
||||
@ -260,6 +259,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);
|
||||
@ -1641,8 +1656,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,13 @@ 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 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();
|
||||
|
||||
|
@ -4176,6 +4176,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);
|
||||
|
||||
@ -7595,7 +7597,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))
|
||||
{
|
||||
@ -8015,9 +8017,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