mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 17:23:03 +01:00
New feature CORE-5488 : Timeouts for running SQL statements and idle connections
This commit is contained in:
parent
4deeaac7c7
commit
2c49e6fcf2
@ -526,6 +526,30 @@
|
||||
#DeadlockTimeout = 10
|
||||
|
||||
|
||||
# ----------------------------
|
||||
#
|
||||
# Set number of seconds after which statement execution will be automatically
|
||||
# cancelled by the engine. Zero means no timeout is set.
|
||||
#
|
||||
# Per-database configurable.
|
||||
#
|
||||
# Type: integer
|
||||
#
|
||||
#StatementTimeout = 0
|
||||
|
||||
|
||||
# ----------------------------
|
||||
#
|
||||
# Set number of minutes after which idle attachment will be disconnected by the
|
||||
# engine. Zero means no timeout is set.
|
||||
#
|
||||
# Per-database configurable.
|
||||
#
|
||||
# Type: integer
|
||||
#
|
||||
#ConnectionIdleTimeout = 0
|
||||
|
||||
|
||||
# ----------------------------
|
||||
#
|
||||
# How often the pages are flushed on disk
|
||||
|
@ -222,6 +222,7 @@ EXPORTS
|
||||
isc_dsql_release @199
|
||||
isc_dsql_set_cursor_name @200
|
||||
isc_dsql_sql_info @201
|
||||
fb_dsql_set_timeout
|
||||
|
||||
; ESQL functions
|
||||
|
||||
|
113
doc/README.session_idle_timeouts
Normal file
113
doc/README.session_idle_timeouts
Normal file
@ -0,0 +1,113 @@
|
||||
Timeouts for idle database sessions.
|
||||
|
||||
Author:
|
||||
Vlad Khorsun <hvlad@users.sf.net>
|
||||
|
||||
|
||||
Description:
|
||||
|
||||
The feature allows to automatically close user connection after period of inactivity.
|
||||
It could be used by database administrators to forcibly close old inactive connections
|
||||
and free resources it occupies. Application and tools developers also could find it as
|
||||
easy replacement of self-made control for the connection life time.
|
||||
It is recommended (but not required) to set idle timeout to reasonable big value, such
|
||||
as few hours. By default it is not enabled.
|
||||
|
||||
The feature works as below
|
||||
- when user API call leaves engine, special idle timer assotiated with current connection
|
||||
is started
|
||||
- when user API call enters engine, idle timer is stopped
|
||||
- when idle time is fired engine immediately closes the connection in the same way as
|
||||
with asyncronous connection cancellation:
|
||||
- all active statements and cursors are closed
|
||||
- all active transactions are rolled back
|
||||
- network connection is not closed at this moment. It allows client application to get
|
||||
exact error code on next API call. Network connection will be closed by the server
|
||||
side after error is reported or due to network timeout if client side disconnects.
|
||||
- idle session timeout could be set:
|
||||
- at database level, by setting value in firebird.conf (or databases.conf) by database
|
||||
administrator
|
||||
scope - all user connections, except of system connections (garbage collector, cache
|
||||
writer, etc)
|
||||
units - minutes
|
||||
- at connection level, using API and\or new SQL statement (see below)
|
||||
scope - given connection
|
||||
units - up to seconds
|
||||
- effective value of idle timeout is evaluated every time user API call leaves the engine
|
||||
as:
|
||||
- if not set at connection level, look at database level
|
||||
- in any case can't be greater than value set at database level
|
||||
i.e. value of idle timeout could be overriden by application developer at given
|
||||
connection but it can't relax limit set by DBA (in config)
|
||||
- zero timeout means no timeout, i.e. idle timer will not start
|
||||
- while idle timeout is set in seconds at API level, we can't promise absolute precision.
|
||||
With high load it could be less precise. The only guarantee is that timeout will not
|
||||
fire before specified moment.
|
||||
- if connection was cancelled, next user API call returns error isc_att_shutdown with
|
||||
secondary error code specifying exact reason:
|
||||
isc_att_shut_killed: Killed by database administrator
|
||||
isc_att_shut_idle: Idle timeout expired
|
||||
isc_att_shut_db_down: Database is shutdown
|
||||
isc_att_shut_engine: Engine is shutdown
|
||||
|
||||
|
||||
Support at configuration level (firebird.conf and\or databases.conf)
|
||||
|
||||
New setting "ConnectionIdleTimeout": set number of minutes after which idle connection
|
||||
will be disconnected by the engine. Zero means no timeout is set.
|
||||
Per-database configurable. Type: integer. Default value is 0.
|
||||
|
||||
|
||||
Support at API level
|
||||
|
||||
- get\set idle connection timeout, seconds
|
||||
interface Attachment
|
||||
uint getIdleTimeout(Status status);
|
||||
void setIdleTimeout(Status status, uint timeOut);
|
||||
|
||||
- get idle connection timeout at config and\or connection level is possible
|
||||
using isc_database_info() API with new info tags:
|
||||
- fb_info_ses_idle_timeout_db value set at config level
|
||||
- fb_info_ses_idle_timeout_att value set at given connection level
|
||||
- fb_info_ses_idle_timeout_run actual timeout value for given connection
|
||||
evaluated considering values set at config and
|
||||
connection levels, see "effective value of idle
|
||||
timeout" above
|
||||
|
||||
Remote client implementation notes:
|
||||
- Attachment::setIdleTimeout() issued "SET SESSION IDLE TIMEOUT" SQL statement
|
||||
- Attachment::getIdleTimeout() calls isc_database_info() with
|
||||
fb_info_ses_idle_timeout_att tag
|
||||
|
||||
If remote server doesn't support idle connection timeouts (protocol version less than 15):
|
||||
- Attachment::setIdleTimeout() will return isc_wish_list error
|
||||
- Attachment::getIdleTimeout() will return zero and set isc_wish_list error
|
||||
- isc_database_info() will return isc_info_error tag in info buffer (as usual).
|
||||
|
||||
|
||||
Support in SQL
|
||||
|
||||
- New SQL statement allows to set idle connection timeout at connection level:
|
||||
|
||||
SET SESSION IDLE TIMEOUT <value> [HOUR | MINUTE | SECOND]
|
||||
|
||||
if timepart is not set, default is MINUTE.
|
||||
This statement could run outside of transaction control and immediately effective.
|
||||
|
||||
- Context variables
|
||||
|
||||
Context 'SYSTEM' have new variable: 'SESSION_IDLE_TIMEOUT'. It contains current value
|
||||
of idle connection timeout that was set at connection level, or zero, if timeout was not
|
||||
set.
|
||||
|
||||
- Monitoring tables
|
||||
|
||||
MON$ATTACHMENTS
|
||||
MON$IDLE_TIMEOUT Connection level idle timeout
|
||||
MON$IDLE_TIMER Idle timer expiration time
|
||||
|
||||
MON$IDLE_TIMEOUT contains timeout value set at connection level, in seconds. Zero, if
|
||||
timeout is not set.
|
||||
|
||||
MON$IDLE_TIMER contains NULL value if idle timeout was not set or if timer is not
|
||||
running.
|
159
doc/README.statement_timeouts
Normal file
159
doc/README.statement_timeouts
Normal file
@ -0,0 +1,159 @@
|
||||
Timeouts for running SQL statements.
|
||||
|
||||
Author:
|
||||
Vlad Khorsun <hvlad@users.sf.net>
|
||||
|
||||
|
||||
Description:
|
||||
|
||||
The feature allows to set timeout for SQL statement, i.e. it allows to automatically
|
||||
stop execution of SQL statement when it running longer than given timeout value.
|
||||
|
||||
The feature could be useful for:
|
||||
- database administrators get instrument to limit heavy queries from consuming too
|
||||
much resources
|
||||
- application developers could use statement timeout when creating\debugging complex
|
||||
queries with unknown in advance execution time
|
||||
- testers could use statement timeout to detect long running queries and ensure finite
|
||||
run time of the test suites
|
||||
- and so on
|
||||
|
||||
From the end user point of view feature have following details:
|
||||
- when statement starts execution (or cursor is opened), engine starts special timer
|
||||
- fetch doesn't reset timer
|
||||
- timer is stopped when statement execution finished (or last record is fetched)
|
||||
- when timer is fired
|
||||
- if statement execution is active, it stops at closest possible moment
|
||||
- if statement is not active currently (between fetches, for example), it is marked
|
||||
as cancelled and next fetch will actually break execution and returns with error
|
||||
- timeout value could be set:
|
||||
- at database level, by setting value in firebird.conf (or databases.conf) by database
|
||||
administrator
|
||||
scope - all statements in all connections
|
||||
units - seconds
|
||||
- at connection level, using API and\or new SQL statement (see below)
|
||||
scope - all statements at given connection
|
||||
units - up to milliseconds
|
||||
- at statement level, using API
|
||||
scope - given statement
|
||||
units - milliseconds
|
||||
- effective value of timeout is evaluated every time statement starts execution
|
||||
(or cursor is opened) as:
|
||||
- if not set at statement level, look at connection level
|
||||
- if not set at connection level, look at database level
|
||||
- in any case can't be greater than value set at database level
|
||||
i.e. value of statement timeout could be overriden by application developer at lower
|
||||
scope but it can't relax limit set by DBA (in config)
|
||||
- zero timeout means no timeout, i.e. timer will not start
|
||||
- while statement timeout is set in milliseconds at API level, we can't promise
|
||||
absolute precision. With big load it could be less precise. The only guarantee
|
||||
is that timeout will not fire before specified moment.
|
||||
- if statement execution is cancelled due to timeout, then API call returns error
|
||||
isc_cancelled with secondary error code specifying exact reason:
|
||||
- isc_cfg_stmt_timeout: Config level timeout expired
|
||||
- isc_att_stmt_timeout: Attachment level timeout expired
|
||||
- isc_req_stmt_timeout: Statement level timeout expired
|
||||
- statement timeout is ignored for all internal queries issued by engine itself
|
||||
- statement timeout is ignored for DDL statements
|
||||
- client application could wait more time than set by timeout value if engine
|
||||
need to undo many actions due to statement cancellation
|
||||
- when engine run EXECUTE STATEMENT statement, it pass rest of currently active timeout
|
||||
to the new statement. If external (remote) engine doesn't support statement timeouts,
|
||||
local engine silently ignores corresponding error
|
||||
- when engine acquires some lock of lock manager, it could lower value of lock timeout
|
||||
using rest of the currently active statement timeout, if possible. Due to lock manager
|
||||
internals rest of statement timeout will be rounded up to the whole seconds.
|
||||
|
||||
|
||||
Support at configuration level (firebird.conf and\or databases.conf)
|
||||
|
||||
New setting "StatementTimeout": set number of seconds after which statement execution
|
||||
will be automatically cancelled by the engine. Zero means no timeout is set.
|
||||
Per-database configurable. Type: integer. Default value is 0.
|
||||
|
||||
|
||||
Support at API level
|
||||
|
||||
- get\set statement execution timeout at connection level, milliseconds:
|
||||
interface Attachment
|
||||
uint getStatementTimeout(Status status);
|
||||
void setStatementTimeout(Status status, uint timeOut);
|
||||
|
||||
- get\set statement execution timeout at statement level, milliseconds:
|
||||
interface Statement
|
||||
uint getTimeout(Status status);
|
||||
void setTimeout(Status status, uint timeOut);
|
||||
|
||||
- set statement execution timeout at statement level using ISC API, milliseconds:
|
||||
|
||||
ISC_STATUS ISC_EXPORT fb_dsql_set_timeout(ISC_STATUS*, isc_stmt_handle*, ISC_ULONG);
|
||||
|
||||
- get statement execution timeout at config and\or connection level is possible
|
||||
using isc_database_info() API with new info tags:
|
||||
- fb_info_statement_timeout_db
|
||||
- fb_info_statement_timeout_att
|
||||
|
||||
- get statement execution timeout at statement level is possible using isc_dsql_info()
|
||||
API with new info tags:
|
||||
- isc_info_sql_stmt_timeout_user timeout value of given statement
|
||||
- isc_info_sql_stmt_timeout_run actual timeout value of given statement
|
||||
evaluated considering values set at config, connection and statement levels, see
|
||||
"effective value of timeout" above. Valid only when timeout timer is running, i.e.
|
||||
for currently executed statements.
|
||||
|
||||
Remote client implementation notes:
|
||||
- Attachment::setStatementTimeout() issued "SET STATEMENT TIMEOUT" SQL statement
|
||||
- Attachment::getStatementTimeout() calls isc_database_info() with
|
||||
fb_info_statement_timeout_att tag
|
||||
- Statement::setTimeout() save timeout value given and pass it with op_execute
|
||||
and op_execute2 packets
|
||||
- Statement::getTimeout() returns saved timeout value
|
||||
- fb_dsql_set_timeout() is a wrapper over Statement::setTimeout()
|
||||
|
||||
If remote server doesn't support statement timeouts (protocol version less than 15):
|
||||
- "set" functions will return isc_wish_list error
|
||||
- "get" functions will return zero and set isc_wish_list error
|
||||
- "info" functions will return isc_info_error tag in info buffer (as usual).
|
||||
|
||||
|
||||
Support in SQL
|
||||
|
||||
- New SQL statement allows to set set statement execution timeout at connection level:
|
||||
|
||||
SET STATEMENT TIMEOUT <value> [HOUR | MINUTE | SECOND | MILLISECOND]
|
||||
|
||||
if timepart is not set, default is SECOND.
|
||||
This statement could run outside of transaction control and immediately effective.
|
||||
|
||||
- Context variables
|
||||
|
||||
Context 'SYSTEM' have new variable: 'STATEMENT_TIMEOUT'. It contains current value of
|
||||
statement execution timeout that was set at connection level, or zero, if timeout was
|
||||
not set.
|
||||
|
||||
- Monitoring tables
|
||||
|
||||
MON$ATTACHMENTS
|
||||
MON$STATEMENT_TIMEOUT Connection level statement timeout
|
||||
|
||||
MON$STATEMENTS
|
||||
MON$STATEMENT_TIMEOUT Statement level statement timeout
|
||||
MON$STATEMENT_TIMER Timeout timer expiration time
|
||||
|
||||
|
||||
MON$STATEMENT_TIMEOUT contains timeout values set at connection\statement level,
|
||||
in milliseconds. Zero, if timeout is not set.
|
||||
|
||||
MON$STATEMENT_TIMER contains NULL value if timeout was not set or if timer is not
|
||||
running.
|
||||
|
||||
|
||||
Support in ISQL tool
|
||||
|
||||
New ISQL command is introduced:
|
||||
|
||||
SET LOCAL_TIMEOUT <int>
|
||||
|
||||
It allows to set statement execution timeout (in milliseconds) for the next statement.
|
||||
After statement execution it automatically reset to zero.
|
||||
|
@ -1664,6 +1664,20 @@ C --
|
||||
PARAMETER (GDS__dsql_window_duplicate = 335545125)
|
||||
INTEGER*4 GDS__sql_too_long
|
||||
PARAMETER (GDS__sql_too_long = 335545126)
|
||||
INTEGER*4 GDS__cfg_stmt_timeout
|
||||
PARAMETER (GDS__cfg_stmt_timeout = 335545127)
|
||||
INTEGER*4 GDS__att_stmt_timeout
|
||||
PARAMETER (GDS__att_stmt_timeout = 335545128)
|
||||
INTEGER*4 GDS__req_stmt_timeout
|
||||
PARAMETER (GDS__req_stmt_timeout = 335545129)
|
||||
INTEGER*4 GDS__att_shut_killed
|
||||
PARAMETER (GDS__att_shut_killed = 335545130)
|
||||
INTEGER*4 GDS__att_shut_idle
|
||||
PARAMETER (GDS__att_shut_idle = 335545131)
|
||||
INTEGER*4 GDS__att_shut_db_down
|
||||
PARAMETER (GDS__att_shut_db_down = 335545132)
|
||||
INTEGER*4 GDS__att_shut_engine
|
||||
PARAMETER (GDS__att_shut_engine = 335545133)
|
||||
INTEGER*4 GDS__gfix_db_name
|
||||
PARAMETER (GDS__gfix_db_name = 335740929)
|
||||
INTEGER*4 GDS__gfix_invalid_sw
|
||||
|
@ -1659,6 +1659,20 @@ const
|
||||
gds_dsql_window_duplicate = 335545125;
|
||||
isc_sql_too_long = 335545126;
|
||||
gds_sql_too_long = 335545126;
|
||||
isc_cfg_stmt_timeout = 335545127;
|
||||
gds_cfg_stmt_timeout = 335545127;
|
||||
isc_att_stmt_timeout = 335545128;
|
||||
gds_att_stmt_timeout = 335545128;
|
||||
isc_req_stmt_timeout = 335545129;
|
||||
gds_req_stmt_timeout = 335545129;
|
||||
isc_att_shut_killed = 335545130;
|
||||
gds_att_shut_killed = 335545130;
|
||||
isc_att_shut_idle = 335545131;
|
||||
gds_att_shut_idle = 335545131;
|
||||
isc_att_shut_db_down = 335545132;
|
||||
gds_att_shut_db_down = 335545132;
|
||||
isc_att_shut_engine = 335545133;
|
||||
gds_att_shut_engine = 335545133;
|
||||
isc_gfix_db_name = 335740929;
|
||||
gds_gfix_db_name = 335740929;
|
||||
isc_gfix_invalid_sw = 335740930;
|
||||
|
@ -197,7 +197,9 @@ const Config::ConfigEntry Config::entries[MAX_CONFIG_KEY] =
|
||||
{TYPE_BOOLEAN, "WireCompression", (ConfigValue) false},
|
||||
{TYPE_INTEGER, "MaxIdentifierByteLength", (ConfigValue) -1},
|
||||
{TYPE_INTEGER, "MaxIdentifierCharLength", (ConfigValue) -1},
|
||||
{TYPE_BOOLEAN, "CryptSecurityDatabase", (ConfigValue) false}
|
||||
{TYPE_BOOLEAN, "CryptSecurityDatabase", (ConfigValue) false},
|
||||
{TYPE_INTEGER, "StatementTimeout", (ConfigValue) 0},
|
||||
{TYPE_INTEGER, "ConnectionIdleTimeout", (ConfigValue) 0}
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
@ -818,3 +820,13 @@ bool Config::getCryptSecurityDatabase() const
|
||||
{
|
||||
return get<bool>(KEY_ENCRYPT_SECURITY_DATABASE);
|
||||
}
|
||||
|
||||
unsigned int Config::getStatementTimeout() const
|
||||
{
|
||||
return get<unsigned int>(KEY_STMT_TIMEOUT);
|
||||
}
|
||||
|
||||
unsigned int Config::getConnIdleTimeout() const
|
||||
{
|
||||
return get<unsigned int>(KEY_CONN_IDLE_TIMEOUT);
|
||||
}
|
||||
|
@ -143,6 +143,8 @@ public:
|
||||
KEY_MAX_IDENTIFIER_BYTE_LENGTH,
|
||||
KEY_MAX_IDENTIFIER_CHAR_LENGTH,
|
||||
KEY_ENCRYPT_SECURITY_DATABASE,
|
||||
KEY_STMT_TIMEOUT,
|
||||
KEY_CONN_IDLE_TIMEOUT,
|
||||
MAX_CONFIG_KEY // keep it last
|
||||
};
|
||||
|
||||
@ -352,6 +354,11 @@ public:
|
||||
int getMaxIdentifierCharLength() const;
|
||||
|
||||
bool getCryptSecurityDatabase() const;
|
||||
|
||||
// set in seconds
|
||||
unsigned int getStatementTimeout() const;
|
||||
// set in minutes
|
||||
unsigned int getConnIdleTimeout() const;
|
||||
};
|
||||
|
||||
// Implementation of interface to access master configuration file
|
||||
|
@ -281,6 +281,24 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class SetSessionNode : public Node
|
||||
{
|
||||
public:
|
||||
enum Type {TYPE_IDLE_TIMEOUT, TYPE_STMT_TIMEOUT};
|
||||
|
||||
SetSessionNode(MemoryPool& pool, Type aType, ULONG aVal, UCHAR blr_timepart);
|
||||
|
||||
public:
|
||||
virtual Firebird::string internalPrint(NodePrinter& printer) const;
|
||||
virtual SetSessionNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);
|
||||
virtual void execute(thread_db* tdbb, dsql_req* request) const;
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
ULONG m_value;
|
||||
};
|
||||
|
||||
|
||||
class DmlNode : public Node
|
||||
{
|
||||
public:
|
||||
|
@ -3448,6 +3448,10 @@ const StmtNode* ExecStatementNode::execute(thread_db* tdbb, jrd_req* request, Ex
|
||||
const MetaName* const* inpNames = inputNames ? inputNames->begin() : NULL;
|
||||
stmt->prepare(tdbb, tran, sSql, inputNames != NULL);
|
||||
|
||||
const TimeoutTimer* timer = tdbb->getTimeoutTimer();
|
||||
if (timer)
|
||||
stmt->setTimeout(tdbb, timer->timeToExpire());
|
||||
|
||||
if (stmt->isSelectable())
|
||||
stmt->open(tdbb, tran, inpNames, inputs, !innerStmt);
|
||||
else
|
||||
@ -7973,6 +7977,78 @@ void SetRoleNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** transact
|
||||
}
|
||||
|
||||
|
||||
//--------------------
|
||||
SetSessionNode::SetSessionNode(MemoryPool& pool, Type aType, ULONG aVal, UCHAR blr_timepart)
|
||||
: Node(pool),
|
||||
m_type(aType),
|
||||
m_value(0)
|
||||
{
|
||||
// TYPE_IDLE_TIMEOUT should be set in seconds
|
||||
// TYPE_STMT_TIMEOUT should be set in milliseconds
|
||||
|
||||
ULONG mult = 1;
|
||||
|
||||
switch (blr_timepart)
|
||||
{
|
||||
case blr_extract_hour:
|
||||
mult = (aType == TYPE_IDLE_TIMEOUT) ? 3660 : 3660000;
|
||||
break;
|
||||
|
||||
case blr_extract_minute:
|
||||
mult = (aType == TYPE_IDLE_TIMEOUT) ? 60 : 60000;
|
||||
break;
|
||||
|
||||
case blr_extract_second:
|
||||
mult = (aType == TYPE_IDLE_TIMEOUT) ? 1 : 1000;
|
||||
break;
|
||||
|
||||
case blr_extract_millisecond:
|
||||
if (aType == TYPE_IDLE_TIMEOUT)
|
||||
Arg::Gds(isc_invalid_extractpart_time).raise();
|
||||
mult = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
Arg::Gds(isc_invalid_extractpart_time).raise();
|
||||
break;
|
||||
}
|
||||
m_value = aVal * mult;
|
||||
}
|
||||
|
||||
string SetSessionNode::internalPrint(NodePrinter& printer) const
|
||||
{
|
||||
Node::internalPrint(printer);
|
||||
|
||||
NODE_PRINT(printer, m_type);
|
||||
NODE_PRINT(printer, m_value);
|
||||
|
||||
return "SetSessionNode";
|
||||
}
|
||||
|
||||
|
||||
SetSessionNode* SetSessionNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SET_SESSION);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
void SetSessionNode::execute(thread_db* tdbb, dsql_req* request) const
|
||||
{
|
||||
Attachment* att = tdbb->getAttachment();
|
||||
switch (m_type)
|
||||
{
|
||||
case TYPE_IDLE_TIMEOUT:
|
||||
att->setIdleTimeout(m_value);
|
||||
break;
|
||||
|
||||
case TYPE_STMT_TIMEOUT:
|
||||
att->setStatementTimeout(m_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------
|
||||
|
||||
|
||||
|
@ -147,9 +147,10 @@ void DSQL_execute(thread_db* tdbb,
|
||||
Arg::Gds(isc_bad_req_handle));
|
||||
}
|
||||
|
||||
// Only allow NULL trans_handle if we're starting a transaction
|
||||
// Only allow NULL trans_handle if we're starting a transaction or set session properties
|
||||
|
||||
if (!*tra_handle && statement->getType() != DsqlCompiledStatement::TYPE_START_TRANS)
|
||||
if (!*tra_handle && statement->getType() != DsqlCompiledStatement::TYPE_START_TRANS &&
|
||||
statement->getType() != DsqlCompiledStatement::TYPE_SET_SESSION)
|
||||
{
|
||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
|
||||
Arg::Gds(isc_bad_trans_handle));
|
||||
@ -274,6 +275,10 @@ bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer)
|
||||
Jrd::Attachment* att = req_dbb->dbb_attachment;
|
||||
TraceDSQLFetch trace(att, this);
|
||||
|
||||
thread_db::TimerGuard timerGuard(tdbb, req_timer, false);
|
||||
if (req_timer && req_timer->expired())
|
||||
tdbb->checkCancelState(true);
|
||||
|
||||
UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number];
|
||||
JRD_receive(tdbb, req_request, message->msg_number, message->msg_length, dsqlMsgBuffer);
|
||||
|
||||
@ -283,6 +288,9 @@ bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer)
|
||||
|
||||
if (eofReached)
|
||||
{
|
||||
if (req_timer)
|
||||
req_timer->stop();
|
||||
|
||||
delayedFormat = NULL;
|
||||
trace.fetch(true, ITracePlugin::RESULT_SUCCESS);
|
||||
return false;
|
||||
@ -535,9 +543,10 @@ void DSQL_execute_immediate(thread_db* tdbb, Jrd::Attachment* attachment, jrd_tr
|
||||
|
||||
const DsqlCompiledStatement* statement = request->getStatement();
|
||||
|
||||
// Only allow NULL trans_handle if we're starting a transaction
|
||||
// Only allow NULL trans_handle if we're starting a transaction or set session properties
|
||||
|
||||
if (!*tra_handle && statement->getType() != DsqlCompiledStatement::TYPE_START_TRANS)
|
||||
if (!*tra_handle && statement->getType() != DsqlCompiledStatement::TYPE_START_TRANS &&
|
||||
statement->getType() != DsqlCompiledStatement::TYPE_SET_SESSION)
|
||||
{
|
||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
|
||||
Arg::Gds(isc_bad_trans_handle));
|
||||
@ -676,6 +685,12 @@ void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle,
|
||||
// manager know statement parameters values
|
||||
TraceDSQLExecute trace(req_dbb->dbb_attachment, this);
|
||||
|
||||
// Setup and start timeout timer
|
||||
const bool have_cursor = reqTypeWithCursor(statement->getType()) && !singleton;
|
||||
|
||||
setupTimer(tdbb);
|
||||
thread_db::TimerGuard timerGuard(tdbb, req_timer, !have_cursor);
|
||||
|
||||
if (!message)
|
||||
JRD_start(tdbb, req_request, req_transaction);
|
||||
else
|
||||
@ -798,7 +813,6 @@ void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle,
|
||||
break;
|
||||
}
|
||||
|
||||
const bool have_cursor = reqTypeWithCursor(statement->getType()) && !singleton;
|
||||
trace.finish(have_cursor, ITracePlugin::RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
@ -896,16 +910,36 @@ void DsqlTransactionRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scra
|
||||
req_traced = false;
|
||||
}
|
||||
|
||||
|
||||
// Execute a dynamic SQL statement.
|
||||
void DsqlTransactionRequest::execute(thread_db* tdbb, jrd_tra** traHandle,
|
||||
Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg,
|
||||
Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg,
|
||||
bool singleton)
|
||||
IMessageMetadata* /*inMetadata*/, const UCHAR* /*inMsg*/,
|
||||
IMessageMetadata* /*outMetadata*/, UCHAR* /*outMsg*/,
|
||||
bool /*singleton*/)
|
||||
{
|
||||
node->execute(tdbb, this, traHandle);
|
||||
}
|
||||
|
||||
|
||||
void SetSessionRequest::execute(thread_db* tdbb, jrd_tra** /*traHandle*/,
|
||||
IMessageMetadata* /*inMetadata*/, const UCHAR* /*inMsg*/,
|
||||
IMessageMetadata* /*outMetadata*/, UCHAR* /*outMsg*/,
|
||||
bool /*singleton*/)
|
||||
{
|
||||
node->execute(tdbb, this);
|
||||
}
|
||||
|
||||
|
||||
void SetSessionRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch,
|
||||
ntrace_result_t* /*traceResult*/)
|
||||
{
|
||||
node = Node::doDsqlPass(scratch, node);
|
||||
|
||||
// Don't trace pseudo-statements (without requests associated).
|
||||
req_traced = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
||||
get_request_info
|
||||
@ -1530,7 +1564,12 @@ dsql_req::dsql_req(MemoryPool& pool)
|
||||
req_cursor_name(req_pool),
|
||||
req_cursor(NULL),
|
||||
req_user_descs(req_pool),
|
||||
req_traced(false)
|
||||
req_traced(false),
|
||||
req_timeout(0)
|
||||
{
|
||||
}
|
||||
|
||||
dsql_req::~dsql_req()
|
||||
{
|
||||
}
|
||||
|
||||
@ -1560,11 +1599,93 @@ bool dsql_req::fetch(thread_db* /*tdbb*/, UCHAR* /*msgBuffer*/)
|
||||
return false; // avoid warning
|
||||
}
|
||||
|
||||
|
||||
unsigned int dsql_req::getTimeout()
|
||||
{
|
||||
return req_timeout;
|
||||
}
|
||||
|
||||
unsigned int dsql_req::getActualTimeout()
|
||||
{
|
||||
if (req_timer)
|
||||
return req_timer->getValue();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void dsql_req::setTimeout(unsigned int timeOut)
|
||||
{
|
||||
req_timeout = timeOut;
|
||||
}
|
||||
|
||||
void dsql_req::setupTimer(thread_db* tdbb)
|
||||
{
|
||||
if (statement->getFlags() & JrdStatement::FLAG_INTERNAL)
|
||||
return;
|
||||
|
||||
if (req_request)
|
||||
{
|
||||
req_request->req_timeout = this->req_timeout;
|
||||
|
||||
fb_assert(!req_request->req_caller);
|
||||
if (req_request->req_caller)
|
||||
{
|
||||
if (req_timer)
|
||||
req_timer->setup(0, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
Attachment* att = tdbb->getAttachment();
|
||||
|
||||
ISC_STATUS toutErr = isc_cfg_stmt_timeout;
|
||||
unsigned int timeOut = dbb->dbb_config->getStatementTimeout() * 1000;
|
||||
|
||||
if (req_timeout)
|
||||
{
|
||||
if (!timeOut || req_timeout < timeOut)
|
||||
{
|
||||
timeOut = req_timeout;
|
||||
toutErr = isc_req_stmt_timeout;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const unsigned int attTout = att->getStatementTimeout();
|
||||
|
||||
if (!timeOut || attTout && attTout < timeOut)
|
||||
{
|
||||
timeOut = attTout;
|
||||
toutErr = isc_att_stmt_timeout;
|
||||
}
|
||||
}
|
||||
|
||||
if (!req_timer && timeOut)
|
||||
{
|
||||
req_timer = FB_NEW TimeoutTimer();
|
||||
req_request->req_timer = this->req_timer;
|
||||
}
|
||||
|
||||
if (req_timer)
|
||||
{
|
||||
req_timer->setup(timeOut, toutErr);
|
||||
req_timer->start();
|
||||
}
|
||||
}
|
||||
|
||||
// Release a dynamic request.
|
||||
void dsql_req::destroy(thread_db* tdbb, dsql_req* request, bool drop)
|
||||
{
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
if (request->req_timer)
|
||||
{
|
||||
request->req_timer->stop();
|
||||
request->req_timer = NULL;
|
||||
}
|
||||
|
||||
// If request is parent, orphan the children and release a portion of their requests
|
||||
|
||||
for (FB_SIZE_T i = 0; i < request->cursors.getCount(); ++i)
|
||||
@ -1757,6 +1878,7 @@ static void sql_info(thread_db* tdbb,
|
||||
case DsqlCompiledStatement::TYPE_CREATE_DB:
|
||||
case DsqlCompiledStatement::TYPE_DDL:
|
||||
case DsqlCompiledStatement::TYPE_SET_ROLE:
|
||||
case DsqlCompiledStatement::TYPE_SET_SESSION:
|
||||
number = isc_info_sql_stmt_ddl;
|
||||
break;
|
||||
case DsqlCompiledStatement::TYPE_COMMIT:
|
||||
@ -1828,6 +1950,16 @@ static void sql_info(thread_db* tdbb,
|
||||
return;
|
||||
break;
|
||||
|
||||
case isc_info_sql_stmt_timeout_user:
|
||||
case isc_info_sql_stmt_timeout_run:
|
||||
value = (item == isc_info_sql_stmt_timeout_user) ?
|
||||
request->getTimeout() : request->getActualTimeout();
|
||||
|
||||
length = put_vax_long(buffer, value);
|
||||
if (!(info = put_item(item, length, buffer, info, end_info)))
|
||||
return;
|
||||
break;
|
||||
|
||||
case isc_info_sql_get_plan:
|
||||
case isc_info_sql_explain_plan:
|
||||
{
|
||||
|
@ -80,6 +80,7 @@ namespace Jrd
|
||||
class RseNode;
|
||||
class StmtNode;
|
||||
class TransactionNode;
|
||||
class SetSessionNode;
|
||||
class ValueExprNode;
|
||||
class ValueListNode;
|
||||
class WindowClause;
|
||||
@ -93,6 +94,7 @@ namespace Jrd
|
||||
class dsql_par;
|
||||
class dsql_map;
|
||||
class dsql_intlsym;
|
||||
class TimeoutTimer;
|
||||
|
||||
typedef Firebird::Stack<dsql_ctx*> DsqlContextStack;
|
||||
|
||||
@ -431,7 +433,7 @@ public:
|
||||
TYPE_SELECT, TYPE_SELECT_UPD, TYPE_INSERT, TYPE_DELETE, TYPE_UPDATE, TYPE_UPDATE_CURSOR,
|
||||
TYPE_DELETE_CURSOR, TYPE_COMMIT, TYPE_ROLLBACK, TYPE_CREATE_DB, TYPE_DDL, TYPE_START_TRANS,
|
||||
TYPE_EXEC_PROCEDURE, TYPE_COMMIT_RETAIN, TYPE_ROLLBACK_RETAIN, TYPE_SET_GENERATOR,
|
||||
TYPE_SAVEPOINT, TYPE_EXEC_BLOCK, TYPE_SELECT_BLOCK, TYPE_SET_ROLE
|
||||
TYPE_SAVEPOINT, TYPE_EXEC_BLOCK, TYPE_SELECT_BLOCK, TYPE_SET_ROLE, TYPE_SET_SESSION
|
||||
};
|
||||
|
||||
// Statement flags.
|
||||
@ -556,6 +558,18 @@ public:
|
||||
|
||||
virtual void setDelayedFormat(thread_db* tdbb, Firebird::IMessageMetadata* metadata);
|
||||
|
||||
// Get session-level timeout, milliseconds
|
||||
unsigned int getTimeout();
|
||||
// Set session-level timeout, milliseconds
|
||||
void setTimeout(unsigned int timeOut);
|
||||
|
||||
// Get actual timeout, milliseconds
|
||||
unsigned int getActualTimeout();
|
||||
|
||||
// Evaluate actual timeout value, consider config- and session-level timeout values,
|
||||
// setup and start timer
|
||||
void setupTimer(thread_db* tdbb);
|
||||
|
||||
static void destroy(thread_db* tdbb, dsql_req* request, bool drop);
|
||||
|
||||
private:
|
||||
@ -580,11 +594,12 @@ public:
|
||||
bool req_traced; // request is traced via TraceAPI
|
||||
|
||||
protected:
|
||||
unsigned int req_timeout; // query timeout in milliseconds, set by the user
|
||||
Firebird::RefPtr<TimeoutTimer> req_timer; // timeout timer
|
||||
|
||||
// Request should never be destroyed using delete.
|
||||
// It dies together with it's pool in release_request().
|
||||
~dsql_req()
|
||||
{
|
||||
}
|
||||
~dsql_req();
|
||||
|
||||
// To avoid posix warning about missing public destructor declare
|
||||
// MemoryPool as friend class. In fact IT releases request memory!
|
||||
@ -670,6 +685,28 @@ private:
|
||||
NestConst<TransactionNode> node;
|
||||
};
|
||||
|
||||
class SetSessionRequest : public dsql_req
|
||||
{
|
||||
public:
|
||||
explicit SetSessionRequest(MemoryPool& pool, SetSessionNode* aNode)
|
||||
: dsql_req(pool),
|
||||
node(aNode)
|
||||
{
|
||||
req_traced = false;
|
||||
}
|
||||
|
||||
virtual void dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch,
|
||||
ntrace_result_t* traceResult);
|
||||
|
||||
virtual void execute(thread_db* tdbb, jrd_tra** traHandle,
|
||||
Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg,
|
||||
Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg,
|
||||
bool singleton);
|
||||
|
||||
private:
|
||||
NestConst<SetSessionNode> node;
|
||||
};
|
||||
|
||||
//! Implicit (NATURAL and USING) joins
|
||||
class ImplicitJoin : public pool_alloc<dsql_type_imp_join>
|
||||
{
|
||||
|
@ -614,6 +614,8 @@ using namespace Firebird;
|
||||
%token <metaNamePtr> UNBOUNDED
|
||||
%token <metaNamePtr> VARBINARY
|
||||
%token <metaNamePtr> WINDOW
|
||||
%token <metaNamePtr> IDLE
|
||||
%token <metaNamePtr> SESSION
|
||||
|
||||
// precedence declarations for expression evaluation
|
||||
|
||||
@ -754,6 +756,7 @@ using namespace Firebird;
|
||||
Jrd::MappingNode* mappingNode;
|
||||
Jrd::MappingNode::OP mappingOp;
|
||||
Jrd::SetRoleNode* setRoleNode;
|
||||
Jrd::SetSessionNode* setSessionNode;
|
||||
Jrd::CreateAlterRoleNode* createAlterRoleNode;
|
||||
}
|
||||
|
||||
@ -773,6 +776,7 @@ statement
|
||||
: dml_statement { $$ = newNode<DsqlDmlRequest>($1); }
|
||||
| ddl_statement { $$ = newNode<DsqlDdlRequest>($1); }
|
||||
| tra_statement { $$ = newNode<DsqlTransactionRequest>($1); }
|
||||
| session_statement { $$ = newNode<SetSessionRequest>($1); }
|
||||
;
|
||||
|
||||
%type <stmtNode> dml_statement
|
||||
@ -4985,6 +4989,31 @@ set_role
|
||||
{ $$ = newNode<SetRoleNode>(); }
|
||||
;
|
||||
|
||||
%type <setSessionNode> session_statement
|
||||
session_statement
|
||||
: SET SESSION IDLE TIMEOUT long_integer timepart_sesion_idle_tout
|
||||
{ $$ = newNode<SetSessionNode>(SetSessionNode::TYPE_IDLE_TIMEOUT, $5, $6); }
|
||||
| SET STATEMENT TIMEOUT long_integer timepart_ses_stmt_tout
|
||||
{ $$ = newNode<SetSessionNode>(SetSessionNode::TYPE_STMT_TIMEOUT, $4, $5); }
|
||||
;
|
||||
|
||||
%type <blrOp> timepart_sesion_idle_tout
|
||||
timepart_sesion_idle_tout
|
||||
: { $$ = blr_extract_minute; }
|
||||
| HOUR { $$ = blr_extract_hour; }
|
||||
| MINUTE { $$ = blr_extract_minute; }
|
||||
| SECOND { $$ = blr_extract_second; }
|
||||
;
|
||||
|
||||
%type <blrOp> timepart_ses_stmt_tout
|
||||
timepart_ses_stmt_tout
|
||||
: { $$ = blr_extract_second; }
|
||||
| HOUR { $$ = blr_extract_hour; }
|
||||
| MINUTE { $$ = blr_extract_minute; }
|
||||
| SECOND { $$ = blr_extract_second; }
|
||||
| MILLISECOND { $$ = blr_extract_millisecond; }
|
||||
;
|
||||
|
||||
%type tran_option_list_opt(<setTransactionNode>)
|
||||
tran_option_list_opt($setTransactionNode)
|
||||
: // nothing
|
||||
@ -8227,6 +8256,8 @@ non_reserved_word
|
||||
| SQL
|
||||
| SYSTEM
|
||||
| TIES
|
||||
| SESSION
|
||||
| IDLE
|
||||
;
|
||||
|
||||
%%
|
||||
|
@ -442,6 +442,11 @@ typedef ISC_STATUS API_ROUTINE prototype_fb_cancel_operation(ISC_STATUS *,
|
||||
typedef ISC_STATUS API_ROUTINE prototype_fb_database_crypt_callback(ISC_STATUS *,
|
||||
void *);
|
||||
|
||||
typedef ISC_STATUS API_ROUTINE prototype_fb_dsql_set_timeout(ISC_STATUS*,
|
||||
isc_stmt_handle*,
|
||||
ULONG);
|
||||
|
||||
|
||||
struct FirebirdApiPointers
|
||||
{
|
||||
prototype_isc_attach_database *isc_attach_database;
|
||||
@ -523,6 +528,7 @@ struct FirebirdApiPointers
|
||||
prototype_isc_service_start *isc_service_start;
|
||||
prototype_fb_cancel_operation *fb_cancel_operation;
|
||||
prototype_fb_database_crypt_callback *fb_database_crypt_callback;
|
||||
prototype_fb_dsql_set_timeout* fb_dsql_set_timeout;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -439,6 +439,11 @@ interface Statement : ReferenceCounted
|
||||
void setCursorName(Status status, const string name);
|
||||
void free(Status status);
|
||||
uint getFlags(Status status);
|
||||
|
||||
version: // 3.0 => 4.0
|
||||
// Statement execution timeout, milliseconds
|
||||
uint getTimeout(Status status);
|
||||
void setTimeout(Status status, uint timeOut);
|
||||
}
|
||||
|
||||
interface Request : ReferenceCounted
|
||||
@ -516,6 +521,15 @@ interface Attachment : ReferenceCounted
|
||||
void ping(Status status);
|
||||
void detach(Status status);
|
||||
void dropDatabase(Status status);
|
||||
|
||||
version: // 3.0 => 4.0
|
||||
// Idle attachment timeout, seconds
|
||||
uint getIdleTimeout(Status status);
|
||||
void setIdleTimeout(Status status, uint timeOut);
|
||||
|
||||
// Statement execution timeout, milliseconds
|
||||
uint getStatementTimeout(Status status);
|
||||
void setStatementTimeout(Status status, uint timeOut);
|
||||
}
|
||||
|
||||
interface Service : ReferenceCounted
|
||||
|
@ -1556,6 +1556,8 @@ namespace Firebird
|
||||
void (CLOOP_CARG *setCursorName)(IStatement* self, IStatus* status, const char* name) throw();
|
||||
void (CLOOP_CARG *free)(IStatement* self, IStatus* status) throw();
|
||||
unsigned (CLOOP_CARG *getFlags)(IStatement* self, IStatus* status) throw();
|
||||
unsigned (CLOOP_CARG *getTimeout)(IStatement* self, IStatus* status) throw();
|
||||
void (CLOOP_CARG *setTimeout)(IStatement* self, IStatus* status, unsigned timeOut) throw();
|
||||
};
|
||||
|
||||
protected:
|
||||
@ -1569,7 +1571,7 @@ namespace Firebird
|
||||
}
|
||||
|
||||
public:
|
||||
static const unsigned VERSION = 3;
|
||||
static const unsigned VERSION = 4;
|
||||
|
||||
static const unsigned PREPARE_PREFETCH_NONE = 0;
|
||||
static const unsigned PREPARE_PREFETCH_TYPE = 1;
|
||||
@ -1669,6 +1671,33 @@ namespace Firebird
|
||||
StatusType::checkException(status);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename StatusType> unsigned getTimeout(StatusType* status)
|
||||
{
|
||||
if (cloopVTable->version < 4)
|
||||
{
|
||||
StatusType::setVersionError(status, "IStatement", cloopVTable->version, 4);
|
||||
StatusType::checkException(status);
|
||||
return 0;
|
||||
}
|
||||
StatusType::clearException(status);
|
||||
unsigned ret = static_cast<VTable*>(this->cloopVTable)->getTimeout(this, status);
|
||||
StatusType::checkException(status);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename StatusType> void setTimeout(StatusType* status, unsigned timeOut)
|
||||
{
|
||||
if (cloopVTable->version < 4)
|
||||
{
|
||||
StatusType::setVersionError(status, "IStatement", cloopVTable->version, 4);
|
||||
StatusType::checkException(status);
|
||||
return;
|
||||
}
|
||||
StatusType::clearException(status);
|
||||
static_cast<VTable*>(this->cloopVTable)->setTimeout(this, status, timeOut);
|
||||
StatusType::checkException(status);
|
||||
}
|
||||
};
|
||||
|
||||
class IRequest : public IReferenceCounted
|
||||
@ -1862,6 +1891,10 @@ namespace Firebird
|
||||
void (CLOOP_CARG *ping)(IAttachment* self, IStatus* status) throw();
|
||||
void (CLOOP_CARG *detach)(IAttachment* self, IStatus* status) throw();
|
||||
void (CLOOP_CARG *dropDatabase)(IAttachment* self, IStatus* status) throw();
|
||||
unsigned (CLOOP_CARG *getIdleTimeout)(IAttachment* self, IStatus* status) throw();
|
||||
void (CLOOP_CARG *setIdleTimeout)(IAttachment* self, IStatus* status, unsigned timeOut) throw();
|
||||
unsigned (CLOOP_CARG *getStatementTimeout)(IAttachment* self, IStatus* status) throw();
|
||||
void (CLOOP_CARG *setStatementTimeout)(IAttachment* self, IStatus* status, unsigned timeOut) throw();
|
||||
};
|
||||
|
||||
protected:
|
||||
@ -1875,7 +1908,7 @@ namespace Firebird
|
||||
}
|
||||
|
||||
public:
|
||||
static const unsigned VERSION = 3;
|
||||
static const unsigned VERSION = 4;
|
||||
|
||||
template <typename StatusType> void getInfo(StatusType* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer)
|
||||
{
|
||||
@ -2012,6 +2045,60 @@ namespace Firebird
|
||||
static_cast<VTable*>(this->cloopVTable)->dropDatabase(this, status);
|
||||
StatusType::checkException(status);
|
||||
}
|
||||
|
||||
template <typename StatusType> unsigned getIdleTimeout(StatusType* status)
|
||||
{
|
||||
if (cloopVTable->version < 4)
|
||||
{
|
||||
StatusType::setVersionError(status, "IAttachment", cloopVTable->version, 4);
|
||||
StatusType::checkException(status);
|
||||
return 0;
|
||||
}
|
||||
StatusType::clearException(status);
|
||||
unsigned ret = static_cast<VTable*>(this->cloopVTable)->getIdleTimeout(this, status);
|
||||
StatusType::checkException(status);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename StatusType> void setIdleTimeout(StatusType* status, unsigned timeOut)
|
||||
{
|
||||
if (cloopVTable->version < 4)
|
||||
{
|
||||
StatusType::setVersionError(status, "IAttachment", cloopVTable->version, 4);
|
||||
StatusType::checkException(status);
|
||||
return;
|
||||
}
|
||||
StatusType::clearException(status);
|
||||
static_cast<VTable*>(this->cloopVTable)->setIdleTimeout(this, status, timeOut);
|
||||
StatusType::checkException(status);
|
||||
}
|
||||
|
||||
template <typename StatusType> unsigned getStatementTimeout(StatusType* status)
|
||||
{
|
||||
if (cloopVTable->version < 4)
|
||||
{
|
||||
StatusType::setVersionError(status, "IAttachment", cloopVTable->version, 4);
|
||||
StatusType::checkException(status);
|
||||
return 0;
|
||||
}
|
||||
StatusType::clearException(status);
|
||||
unsigned ret = static_cast<VTable*>(this->cloopVTable)->getStatementTimeout(this, status);
|
||||
StatusType::checkException(status);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename StatusType> void setStatementTimeout(StatusType* status, unsigned timeOut)
|
||||
{
|
||||
if (cloopVTable->version < 4)
|
||||
{
|
||||
StatusType::setVersionError(status, "IAttachment", cloopVTable->version, 4);
|
||||
StatusType::checkException(status);
|
||||
return;
|
||||
}
|
||||
StatusType::clearException(status);
|
||||
static_cast<VTable*>(this->cloopVTable)->setStatementTimeout(this, status, timeOut);
|
||||
StatusType::checkException(status);
|
||||
}
|
||||
};
|
||||
|
||||
class IService : public IReferenceCounted
|
||||
@ -8183,6 +8270,8 @@ namespace Firebird
|
||||
this->setCursorName = &Name::cloopsetCursorNameDispatcher;
|
||||
this->free = &Name::cloopfreeDispatcher;
|
||||
this->getFlags = &Name::cloopgetFlagsDispatcher;
|
||||
this->getTimeout = &Name::cloopgetTimeoutDispatcher;
|
||||
this->setTimeout = &Name::cloopsetTimeoutDispatcher;
|
||||
}
|
||||
} vTable;
|
||||
|
||||
@ -8351,6 +8440,35 @@ namespace Firebird
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned CLOOP_CARG cloopgetTimeoutDispatcher(IStatement* self, IStatus* status) throw()
|
||||
{
|
||||
StatusType status2(status);
|
||||
|
||||
try
|
||||
{
|
||||
return static_cast<Name*>(self)->Name::getTimeout(&status2);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
StatusType::catchException(&status2);
|
||||
return static_cast<unsigned>(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void CLOOP_CARG cloopsetTimeoutDispatcher(IStatement* self, IStatus* status, unsigned timeOut) throw()
|
||||
{
|
||||
StatusType status2(status);
|
||||
|
||||
try
|
||||
{
|
||||
static_cast<Name*>(self)->Name::setTimeout(&status2, timeOut);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
StatusType::catchException(&status2);
|
||||
}
|
||||
}
|
||||
|
||||
static void CLOOP_CARG cloopaddRefDispatcher(IReferenceCounted* self) throw()
|
||||
{
|
||||
try
|
||||
@ -8401,6 +8519,8 @@ namespace Firebird
|
||||
virtual void setCursorName(StatusType* status, const char* name) = 0;
|
||||
virtual void free(StatusType* status) = 0;
|
||||
virtual unsigned getFlags(StatusType* status) = 0;
|
||||
virtual unsigned getTimeout(StatusType* status) = 0;
|
||||
virtual void setTimeout(StatusType* status, unsigned timeOut) = 0;
|
||||
};
|
||||
|
||||
template <typename Name, typename StatusType, typename Base>
|
||||
@ -8825,6 +8945,10 @@ namespace Firebird
|
||||
this->ping = &Name::clooppingDispatcher;
|
||||
this->detach = &Name::cloopdetachDispatcher;
|
||||
this->dropDatabase = &Name::cloopdropDatabaseDispatcher;
|
||||
this->getIdleTimeout = &Name::cloopgetIdleTimeoutDispatcher;
|
||||
this->setIdleTimeout = &Name::cloopsetIdleTimeoutDispatcher;
|
||||
this->getStatementTimeout = &Name::cloopgetStatementTimeoutDispatcher;
|
||||
this->setStatementTimeout = &Name::cloopsetStatementTimeoutDispatcher;
|
||||
}
|
||||
} vTable;
|
||||
|
||||
@ -9093,6 +9217,64 @@ namespace Firebird
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned CLOOP_CARG cloopgetIdleTimeoutDispatcher(IAttachment* self, IStatus* status) throw()
|
||||
{
|
||||
StatusType status2(status);
|
||||
|
||||
try
|
||||
{
|
||||
return static_cast<Name*>(self)->Name::getIdleTimeout(&status2);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
StatusType::catchException(&status2);
|
||||
return static_cast<unsigned>(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void CLOOP_CARG cloopsetIdleTimeoutDispatcher(IAttachment* self, IStatus* status, unsigned timeOut) throw()
|
||||
{
|
||||
StatusType status2(status);
|
||||
|
||||
try
|
||||
{
|
||||
static_cast<Name*>(self)->Name::setIdleTimeout(&status2, timeOut);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
StatusType::catchException(&status2);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned CLOOP_CARG cloopgetStatementTimeoutDispatcher(IAttachment* self, IStatus* status) throw()
|
||||
{
|
||||
StatusType status2(status);
|
||||
|
||||
try
|
||||
{
|
||||
return static_cast<Name*>(self)->Name::getStatementTimeout(&status2);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
StatusType::catchException(&status2);
|
||||
return static_cast<unsigned>(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void CLOOP_CARG cloopsetStatementTimeoutDispatcher(IAttachment* self, IStatus* status, unsigned timeOut) throw()
|
||||
{
|
||||
StatusType status2(status);
|
||||
|
||||
try
|
||||
{
|
||||
static_cast<Name*>(self)->Name::setStatementTimeout(&status2, timeOut);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
StatusType::catchException(&status2);
|
||||
}
|
||||
}
|
||||
|
||||
static void CLOOP_CARG cloopaddRefDispatcher(IReferenceCounted* self) throw()
|
||||
{
|
||||
try
|
||||
@ -9150,6 +9332,10 @@ namespace Firebird
|
||||
virtual void ping(StatusType* status) = 0;
|
||||
virtual void detach(StatusType* status) = 0;
|
||||
virtual void dropDatabase(StatusType* status) = 0;
|
||||
virtual unsigned getIdleTimeout(StatusType* status) = 0;
|
||||
virtual void setIdleTimeout(StatusType* status, unsigned timeOut) = 0;
|
||||
virtual unsigned getStatementTimeout(StatusType* status) = 0;
|
||||
virtual void setStatementTimeout(StatusType* status, unsigned timeOut) = 0;
|
||||
};
|
||||
|
||||
template <typename Name, typename StatusType, typename Base>
|
||||
|
@ -828,6 +828,13 @@ static const struct {
|
||||
{"dsql_window_cant_overr_frame", 335545124},
|
||||
{"dsql_window_duplicate", 335545125},
|
||||
{"sql_too_long", 335545126},
|
||||
{"cfg_stmt_timeout", 335545127},
|
||||
{"att_stmt_timeout", 335545128},
|
||||
{"req_stmt_timeout", 335545129},
|
||||
{"att_shut_killed", 335545130},
|
||||
{"att_shut_idle", 335545131},
|
||||
{"att_shut_db_down", 335545132},
|
||||
{"att_shut_engine", 335545133},
|
||||
{"gfix_db_name", 335740929},
|
||||
{"gfix_invalid_sw", 335740930},
|
||||
{"gfix_incmp_sw", 335740932},
|
||||
|
@ -862,6 +862,13 @@ const ISC_STATUS isc_dsql_window_cant_overr_order = 335545123L;
|
||||
const ISC_STATUS isc_dsql_window_cant_overr_frame = 335545124L;
|
||||
const ISC_STATUS isc_dsql_window_duplicate = 335545125L;
|
||||
const ISC_STATUS isc_sql_too_long = 335545126L;
|
||||
const ISC_STATUS isc_cfg_stmt_timeout = 335545127L;
|
||||
const ISC_STATUS isc_att_stmt_timeout = 335545128L;
|
||||
const ISC_STATUS isc_req_stmt_timeout = 335545129L;
|
||||
const ISC_STATUS isc_att_shut_killed = 335545130L;
|
||||
const ISC_STATUS isc_att_shut_idle = 335545131L;
|
||||
const ISC_STATUS isc_att_shut_db_down = 335545132L;
|
||||
const ISC_STATUS isc_att_shut_engine = 335545133L;
|
||||
const ISC_STATUS isc_gfix_db_name = 335740929L;
|
||||
const ISC_STATUS isc_gfix_invalid_sw = 335740930L;
|
||||
const ISC_STATUS isc_gfix_incmp_sw = 335740932L;
|
||||
@ -1336,7 +1343,7 @@ const ISC_STATUS isc_trace_switch_user_only = 337182757L;
|
||||
const ISC_STATUS isc_trace_switch_param_miss = 337182758L;
|
||||
const ISC_STATUS isc_trace_param_act_notcompat = 337182759L;
|
||||
const ISC_STATUS isc_trace_mandatory_switch_miss = 337182760L;
|
||||
const ISC_STATUS isc_err_max = 1280;
|
||||
const ISC_STATUS isc_err_max = 1287;
|
||||
|
||||
#else /* c definitions */
|
||||
|
||||
@ -2168,6 +2175,13 @@ const ISC_STATUS isc_err_max = 1280;
|
||||
#define isc_dsql_window_cant_overr_frame 335545124L
|
||||
#define isc_dsql_window_duplicate 335545125L
|
||||
#define isc_sql_too_long 335545126L
|
||||
#define isc_cfg_stmt_timeout 335545127L
|
||||
#define isc_att_stmt_timeout 335545128L
|
||||
#define isc_req_stmt_timeout 335545129L
|
||||
#define isc_att_shut_killed 335545130L
|
||||
#define isc_att_shut_idle 335545131L
|
||||
#define isc_att_shut_db_down 335545132L
|
||||
#define isc_att_shut_engine 335545133L
|
||||
#define isc_gfix_db_name 335740929L
|
||||
#define isc_gfix_invalid_sw 335740930L
|
||||
#define isc_gfix_incmp_sw 335740932L
|
||||
@ -2642,7 +2656,7 @@ const ISC_STATUS isc_err_max = 1280;
|
||||
#define isc_trace_switch_param_miss 337182758L
|
||||
#define isc_trace_param_act_notcompat 337182759L
|
||||
#define isc_trace_mandatory_switch_miss 337182760L
|
||||
#define isc_err_max 1280
|
||||
#define isc_err_max 1287
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -518,6 +518,9 @@
|
||||
const USHORT f_mon_att_remote_os_user = 17;
|
||||
const USHORT f_mon_att_auth_method = 18;
|
||||
const USHORT f_mon_att_sys_flag = 19;
|
||||
const USHORT f_mon_att_idle_timeout = 20;
|
||||
const USHORT f_mon_att_idle_timer = 21;
|
||||
const USHORT f_mon_att_stmt_timeout = 22;
|
||||
|
||||
|
||||
// Relation 35 (MON$TRANSACTIONS)
|
||||
@ -547,6 +550,8 @@
|
||||
const USHORT f_mon_stmt_sql_text = 5;
|
||||
const USHORT f_mon_stmt_stat_id = 6;
|
||||
const USHORT f_mon_stmt_expl_plan = 7;
|
||||
const USHORT f_mon_stmt_timeout = 8;
|
||||
const USHORT f_mon_stmt_timer = 9;
|
||||
|
||||
|
||||
// Relation 37 (MON$CALL_STACK)
|
||||
|
@ -831,6 +831,13 @@ Data source : @4"}, /* eds_statement */
|
||||
{335545124, "Cannot override the window @1 because it has a frame clause. Tip: it can be used without parenthesis in OVER"}, /* dsql_window_cant_overr_frame */
|
||||
{335545125, "Duplicate window definition for @1"}, /* dsql_window_duplicate */
|
||||
{335545126, "SQL statement is too long. Maximum size is @1 bytes."}, /* sql_too_long */
|
||||
{335545127, "Config level timeout expired."}, /* cfg_stmt_timeout */
|
||||
{335545128, "Attachment level timeout expired."}, /* att_stmt_timeout */
|
||||
{335545129, "Statement level timeout expired."}, /* req_stmt_timeout */
|
||||
{335545130, "Killed by database administrator."}, /* att_shut_killed */
|
||||
{335545131, "Idle timeout expired."}, /* att_shut_idle */
|
||||
{335545132, "Database is shutdown."}, /* att_shut_db_down */
|
||||
{335545133, "Engine is shutdown."}, /* att_shut_engine */
|
||||
{335740929, "data base file name (@1) already given"}, /* gfix_db_name */
|
||||
{335740930, "invalid switch @1"}, /* gfix_invalid_sw */
|
||||
{335740932, "incompatible switch combination"}, /* gfix_incmp_sw */
|
||||
|
@ -827,6 +827,13 @@ static const struct {
|
||||
{335545124, -833}, /* 804 dsql_window_cant_overr_frame */
|
||||
{335545125, -833}, /* 805 dsql_window_duplicate */
|
||||
{335545126, -902}, /* 806 sql_too_long */
|
||||
{335545127, -901}, /* 807 cfg_stmt_timeout */
|
||||
{335545128, -901}, /* 808 att_stmt_timeout */
|
||||
{335545129, -901}, /* 809 req_stmt_timeout */
|
||||
{335545130, -902}, /* 810 att_shut_killed */
|
||||
{335545131, -902}, /* 811 att_shut_idle */
|
||||
{335545132, -902}, /* 812 att_shut_db_down */
|
||||
{335545133, -902}, /* 813 att_shut_engine */
|
||||
{335740929, -901}, /* 1 gfix_db_name */
|
||||
{335740930, -901}, /* 2 gfix_invalid_sw */
|
||||
{335740932, -901}, /* 4 gfix_incmp_sw */
|
||||
|
@ -827,6 +827,13 @@ static const struct {
|
||||
{335545124, "42000"}, // 804 dsql_window_cant_overr_frame
|
||||
{335545125, "42000"}, // 805 dsql_window_duplicate
|
||||
{335545126, "54001"}, // 806 sql_too_long
|
||||
{335545127, "HY008"}, // 807 cfg_stmt_timeout
|
||||
{335545128, "HY008"}, // 808 att_stmt_timeout
|
||||
{335545129, "HY008"}, // 809 req_stmt_timeout
|
||||
{335545130, "08003"}, // 810 att_shut_killed
|
||||
{335545131, "08003"}, // 811 att_shut_idle
|
||||
{335545132, "08003"}, // 812 att_shut_db_down
|
||||
{335545133, "08003"}, // 813 att_shut_engine
|
||||
{335740929, "00000"}, // 1 gfix_db_name
|
||||
{335740930, "00000"}, // 2 gfix_invalid_sw
|
||||
{335740932, "00000"}, // 4 gfix_incmp_sw
|
||||
|
@ -453,6 +453,7 @@ public:
|
||||
ExplainPlan = false;
|
||||
Heading = true;
|
||||
BailOnError = false;
|
||||
StmtTimeout = 0;
|
||||
ISQL_charset[0] = 0;
|
||||
}
|
||||
|
||||
@ -473,6 +474,7 @@ public:
|
||||
bool ExplainPlan;
|
||||
bool Heading;
|
||||
bool BailOnError;
|
||||
unsigned int StmtTimeout;
|
||||
SCHAR ISQL_charset[MAXCHARSET_SIZE];
|
||||
};
|
||||
|
||||
@ -4765,7 +4767,8 @@ static processing_state frontend_set(const char* cmd, const char* const* parms,
|
||||
sqlda_display,
|
||||
//#endif
|
||||
sql, warning, sqlCont, heading, bail,
|
||||
bulk_insert, maxrows, wrong
|
||||
bulk_insert, maxrows, stmtTimeout,
|
||||
wrong
|
||||
};
|
||||
SetOptions(const optionsMap* inmap, size_t insize, int wrongval)
|
||||
: OptionsBase(inmap, insize, wrongval)
|
||||
@ -4803,6 +4806,7 @@ static processing_state frontend_set(const char* cmd, const char* const* parms,
|
||||
{SetOptions::maxrows, "MAXROWS", 0},
|
||||
{SetOptions::sqlCont, "ROLE", 0},
|
||||
{SetOptions::sqlCont, "TRUSTED", 0}, // TRUSTED ROLE, will get DSQL error other case
|
||||
{SetOptions::stmtTimeout, "LOCAL_TIMEOUT", 0},
|
||||
};
|
||||
|
||||
// Display current set options
|
||||
@ -4964,14 +4968,28 @@ static processing_state frontend_set(const char* cmd, const char* const* parms,
|
||||
ret = newMaxRows((*lparms[2]) ? lparms[2] : "0");
|
||||
break;
|
||||
|
||||
default:
|
||||
case SetOptions::stmtTimeout:
|
||||
{
|
||||
TEXT msg_string[MSG_LENGTH];
|
||||
IUTILS_msg_get(VALID_OPTIONS, msg_string);
|
||||
isqlGlob.printf("%s\n", msg_string);
|
||||
int val = strtol(parms[2], NULL, 10);
|
||||
if (val < 0)
|
||||
ret = ps_ERR;
|
||||
else
|
||||
{
|
||||
setValues.StmtTimeout = val;
|
||||
ret = SKIP;
|
||||
}
|
||||
}
|
||||
setoptions.showCommands(isqlGlob.Out);
|
||||
ret = ps_ERR;
|
||||
break;
|
||||
|
||||
default:
|
||||
//{
|
||||
// TEXT msg_string[MSG_LENGTH];
|
||||
// IUTILS_msg_get(VALID_OPTIONS, msg_string);
|
||||
// isqlGlob.printf("%s\n", msg_string);
|
||||
//}
|
||||
//setoptions.showCommands(isqlGlob.Out);
|
||||
//ret = ps_ERR;
|
||||
ret = CONT; // pass unknown SET command to server as is
|
||||
break;
|
||||
}
|
||||
|
||||
@ -5793,6 +5811,7 @@ static processing_state print_sets()
|
||||
print_set("Time:", setValues.Time_display);
|
||||
print_set("Warnings:", setValues.Warnings);
|
||||
print_set("Bail on error:", setValues.BailOnError);
|
||||
isqlGlob.printf("%-25s%lu%s", "Local statement timeout:", setValues.StmtTimeout, NEWLINE);
|
||||
return SKIP;
|
||||
}
|
||||
|
||||
@ -8130,6 +8149,11 @@ static processing_state process_statement(const TEXT* str2)
|
||||
// check for warnings
|
||||
ISQL_warning(fbStatus);
|
||||
|
||||
global_Stmt->setTimeout(fbStatus, setValues.StmtTimeout);
|
||||
if (ISQL_errmsg(fbStatus))
|
||||
setValues.StmtTimeout = 0;
|
||||
|
||||
|
||||
// Find out what kind of statement this is
|
||||
const int statement_type = process_request_type();
|
||||
if (!statement_type)
|
||||
@ -8193,6 +8217,7 @@ static processing_state process_statement(const TEXT* str2)
|
||||
statement_type == isc_info_sql_stmt_set_generator))
|
||||
{
|
||||
DB->execute(fbStatus, D__trans, 0, str2, isqlGlob.SQL_dialect, NULL, NULL, NULL, NULL);
|
||||
setValues.StmtTimeout = 0;
|
||||
if (ISQL_errmsg(fbStatus))
|
||||
{
|
||||
ret = ps_ERR;
|
||||
@ -8235,6 +8260,7 @@ static processing_state process_statement(const TEXT* str2)
|
||||
// This is a non-select DML statement or trans
|
||||
|
||||
M__trans = global_Stmt->execute(fbStatus, M__trans, NULL, NULL, NULL, NULL);
|
||||
setValues.StmtTimeout = 0;
|
||||
if (ISQL_errmsg(fbStatus))
|
||||
{
|
||||
// CVC: Make this conditional if it causes problems. For example
|
||||
@ -8337,6 +8363,7 @@ static processing_state process_statement(const TEXT* str2)
|
||||
if (statement_type == isc_info_sql_stmt_exec_procedure)
|
||||
{
|
||||
global_Stmt->execute(fbStatus, M__trans, NULL, NULL, message, buffer);
|
||||
setValues.StmtTimeout = 0;
|
||||
if (ISQL_errmsg(fbStatus))
|
||||
{
|
||||
ret = ps_ERR;
|
||||
@ -8366,6 +8393,7 @@ static processing_state process_statement(const TEXT* str2)
|
||||
|
||||
Firebird::IResultSet* curs = global_Stmt->openCursor(fbStatus, M__trans,
|
||||
NULL, NULL, message, 0);
|
||||
setValues.StmtTimeout = 0;
|
||||
if (ISQL_errmsg(fbStatus))
|
||||
{
|
||||
return ps_ERR;
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "../common/classes/MetaName.h"
|
||||
#include "../common/StatusArg.h"
|
||||
#include "../common/isc_proto.h"
|
||||
#include "../common/classes/RefMutex.h"
|
||||
|
||||
|
||||
using namespace Jrd;
|
||||
@ -204,7 +205,9 @@ Jrd::Attachment::Attachment(MemoryPool* pool, Database* dbb)
|
||||
att_dyn_req(*pool),
|
||||
att_charsets(*pool),
|
||||
att_charset_ids(*pool),
|
||||
att_pools(*pool)
|
||||
att_pools(*pool),
|
||||
att_idle_timeout(0),
|
||||
att_stmt_timeout(0)
|
||||
{
|
||||
att_internal.grow(irq_MAX);
|
||||
att_dyn_req.grow(drq_MAX);
|
||||
@ -371,9 +374,11 @@ void Jrd::Attachment::signalCancel()
|
||||
}
|
||||
|
||||
|
||||
void Jrd::Attachment::signalShutdown()
|
||||
void Jrd::Attachment::signalShutdown(ISC_STATUS code)
|
||||
{
|
||||
att_flags |= ATT_shutdown;
|
||||
if (getStable())
|
||||
getStable()->setShutError(code);
|
||||
|
||||
if (att_ext_connection && att_ext_connection->isConnected())
|
||||
att_ext_connection->cancelExecution();
|
||||
@ -639,7 +644,7 @@ int Jrd::Attachment::blockingAstShutdown(void* ast_object)
|
||||
|
||||
AsyncContextHolder tdbb(dbb, FB_FUNCTION, attachment->att_id_lock);
|
||||
|
||||
attachment->signalShutdown();
|
||||
attachment->signalShutdown(isc_att_shut_killed);
|
||||
|
||||
JRD_shutdown_attachment(attachment);
|
||||
}
|
||||
@ -765,3 +770,122 @@ JAttachment* Attachment::getInterface() throw()
|
||||
return att_stable->getInterface();
|
||||
}
|
||||
|
||||
unsigned int Attachment::getActualIdleTimeout() const
|
||||
{
|
||||
unsigned int timeout = att_database->dbb_config->getConnIdleTimeout() * 60;
|
||||
if (att_idle_timeout && (att_idle_timeout < timeout || !timeout))
|
||||
timeout = att_idle_timeout;
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
void Attachment::setupIdleTimer(bool clear)
|
||||
{
|
||||
unsigned int timeout = clear ? 0 : getActualIdleTimeout();
|
||||
if (!timeout)
|
||||
{
|
||||
if (att_idle_timer)
|
||||
att_idle_timer->reset(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!att_idle_timer)
|
||||
att_idle_timer = FB_NEW IdleTimer(getInterface());
|
||||
|
||||
att_idle_timer->reset(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
bool Attachment::getIdleTimerTimestamp(TimeStamp& ts) const
|
||||
{
|
||||
if (!att_idle_timer)
|
||||
return false;
|
||||
|
||||
time_t value = att_idle_timer->getExpiryTime();
|
||||
if (!value)
|
||||
return false;
|
||||
|
||||
struct tm* times = localtime(&value);
|
||||
if (!times)
|
||||
return false;
|
||||
|
||||
ts = TimeStamp::encode_timestamp(times);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Attachment::IdleTimer
|
||||
|
||||
void Attachment::IdleTimer::handler()
|
||||
{
|
||||
m_fireTime = 0;
|
||||
if (!m_expTime) // Timer was reset to zero, do nothing
|
||||
return;
|
||||
|
||||
// Ensure attachment is still alive and idle
|
||||
|
||||
StableAttachmentPart* stable = m_attachment->getStable();
|
||||
if (!stable)
|
||||
return;
|
||||
|
||||
MutexEnsureUnlock guard(*stable->getMutex(), FB_FUNCTION);
|
||||
if (!guard.tryEnter())
|
||||
return;
|
||||
|
||||
if (!m_expTime)
|
||||
return;
|
||||
|
||||
// If timer was reset to fire later, restart ITimer
|
||||
time_t curTime = time(NULL);
|
||||
if (curTime < m_expTime)
|
||||
{
|
||||
reset(m_expTime - curTime);
|
||||
return;
|
||||
}
|
||||
|
||||
Attachment* att = stable->getHandle();
|
||||
att->signalShutdown(isc_att_shut_idle);
|
||||
JRD_shutdown_attachment(att);
|
||||
}
|
||||
|
||||
int Attachment::IdleTimer::release()
|
||||
{
|
||||
if (--refCounter == 0)
|
||||
{
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Attachment::IdleTimer::reset(unsigned int timeout)
|
||||
{
|
||||
// Start timer if necessary. If timer was already started, don't restart
|
||||
// (or stop) it - handler() will take care about it.
|
||||
|
||||
if (!timeout)
|
||||
{
|
||||
m_expTime = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const time_t curTime = time(NULL);
|
||||
m_expTime = curTime + timeout;
|
||||
|
||||
FbLocalStatus s;
|
||||
ITimerControl* timerCtrl = Firebird::TimerInterfacePtr();
|
||||
|
||||
if (m_fireTime)
|
||||
{
|
||||
if (m_fireTime <= m_expTime)
|
||||
return;
|
||||
|
||||
timerCtrl->stop(&s, this);
|
||||
check(&s);
|
||||
m_fireTime = 0;
|
||||
}
|
||||
|
||||
timerCtrl->start(&s, this, (m_expTime - curTime) * 1000 * 1000);
|
||||
check(&s);
|
||||
m_fireTime = m_expTime;
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ class StableAttachmentPart : public Firebird::RefCounted, public Firebird::Globa
|
||||
{
|
||||
public:
|
||||
explicit StableAttachmentPart(Attachment* handle)
|
||||
: att(handle), jAtt(NULL)
|
||||
: att(handle), jAtt(NULL), shutError(0)
|
||||
{ }
|
||||
|
||||
Attachment* getHandle() throw()
|
||||
@ -171,6 +171,7 @@ public:
|
||||
jAtt->detachEngine();
|
||||
|
||||
jAtt = ja;
|
||||
shutError = 0;
|
||||
}
|
||||
|
||||
Firebird::Mutex* getMutex(bool useAsync = false, bool forceAsync = false)
|
||||
@ -208,9 +209,21 @@ public:
|
||||
void manualUnlock(ULONG& flags);
|
||||
void manualAsyncUnlock(ULONG& flags);
|
||||
|
||||
void setShutError(ISC_STATUS code)
|
||||
{
|
||||
if (!shutError)
|
||||
shutError = code;
|
||||
}
|
||||
|
||||
ISC_STATUS getShutError() const
|
||||
{
|
||||
return shutError;
|
||||
}
|
||||
|
||||
private:
|
||||
Attachment* att;
|
||||
JAttachment* jAtt;
|
||||
ISC_STATUS shutError;
|
||||
|
||||
// These mutexes guarantee attachment existence. After releasing both of them with possibly
|
||||
// zero att_use_count one should check does attachment still exists calling getHandle().
|
||||
@ -391,7 +404,7 @@ public:
|
||||
const Firebird::ByteChunk& chunk);
|
||||
|
||||
void signalCancel();
|
||||
void signalShutdown();
|
||||
void signalShutdown(ISC_STATUS code);
|
||||
|
||||
void mergeStats();
|
||||
|
||||
@ -412,9 +425,70 @@ public:
|
||||
|
||||
JAttachment* getInterface() throw();
|
||||
|
||||
unsigned int getIdleTimeout() const
|
||||
{
|
||||
return att_idle_timeout;
|
||||
}
|
||||
|
||||
void setIdleTimeout(unsigned int timeOut)
|
||||
{
|
||||
att_idle_timeout = timeOut;
|
||||
}
|
||||
|
||||
unsigned int getActualIdleTimeout() const;
|
||||
|
||||
unsigned int getStatementTimeout() const
|
||||
{
|
||||
return att_stmt_timeout;
|
||||
}
|
||||
|
||||
void setStatementTimeout(unsigned int timeOut)
|
||||
{
|
||||
att_stmt_timeout = timeOut;
|
||||
}
|
||||
|
||||
// evaluate new value or clear idle timer
|
||||
void setupIdleTimer(bool clear);
|
||||
|
||||
// returns time when idle timer will be expired, if set
|
||||
bool getIdleTimerTimestamp(Firebird::TimeStamp& ts) const;
|
||||
|
||||
private:
|
||||
Attachment(MemoryPool* pool, Database* dbb);
|
||||
~Attachment();
|
||||
|
||||
class IdleTimer FB_FINAL :
|
||||
public Firebird::RefCntIface<Firebird::ITimerImpl<IdleTimer, Firebird::CheckStatusWrapper> >
|
||||
{
|
||||
public:
|
||||
explicit IdleTimer(JAttachment* jAtt) :
|
||||
m_attachment(jAtt),
|
||||
m_fireTime(0),
|
||||
m_expTime(0)
|
||||
{ }
|
||||
|
||||
// ITimer implementation
|
||||
void handler();
|
||||
int release();
|
||||
|
||||
// Set timeout, seconds
|
||||
void reset(unsigned int timeout);
|
||||
|
||||
time_t getExpiryTime() const
|
||||
{
|
||||
return m_expTime;
|
||||
}
|
||||
|
||||
private:
|
||||
Firebird::RefPtr<JAttachment> m_attachment;
|
||||
time_t m_fireTime; // when ITimer will fire, could be less than m_expTime
|
||||
time_t m_expTime; // when actual idle timeout will expire
|
||||
};
|
||||
|
||||
|
||||
unsigned int att_idle_timeout; // seconds
|
||||
unsigned int att_stmt_timeout; // milliseconds
|
||||
Firebird::RefPtr<IdleTimer> att_idle_timer;
|
||||
};
|
||||
|
||||
|
||||
|
@ -1362,14 +1362,14 @@ namespace Jrd {
|
||||
}
|
||||
|
||||
if (!found)
|
||||
att->signalShutdown();
|
||||
att->signalShutdown(0 /* no special shutdown code */);
|
||||
}
|
||||
|
||||
// Loop through internal attachments list closing one missing valid holders
|
||||
for (unsigned i = 0; i < knownHolders.getCount(); ++i)
|
||||
{
|
||||
if (!validateHoldersGroup(knownHolders[i], keyName))
|
||||
knownHolders[i].first->signalShutdown();
|
||||
knownHolders[i].first->signalShutdown(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,6 +205,9 @@ public:
|
||||
void setCursorName(Firebird::CheckStatusWrapper* status, const char* name);
|
||||
unsigned getFlags(Firebird::CheckStatusWrapper* status);
|
||||
|
||||
unsigned int getTimeout(Firebird::CheckStatusWrapper* status);
|
||||
void setTimeout(Firebird::CheckStatusWrapper* status, unsigned int timeOut);
|
||||
|
||||
public:
|
||||
JStatement(dsql_req* handle, StableAttachmentPart* sa, Firebird::Array<UCHAR>& meta);
|
||||
|
||||
@ -345,6 +348,11 @@ public:
|
||||
void detach(Firebird::CheckStatusWrapper* status);
|
||||
void dropDatabase(Firebird::CheckStatusWrapper* status);
|
||||
|
||||
unsigned int getIdleTimeout(Firebird::CheckStatusWrapper* status);
|
||||
void setIdleTimeout(Firebird::CheckStatusWrapper* status, unsigned int timeOut);
|
||||
unsigned int getStatementTimeout(Firebird::CheckStatusWrapper* status);
|
||||
void setStatementTimeout(Firebird::CheckStatusWrapper* status, unsigned int timeOut);
|
||||
|
||||
public:
|
||||
explicit JAttachment(StableAttachmentPart* js);
|
||||
|
||||
|
@ -922,6 +922,15 @@ void Monitoring::putAttachment(SnapshotData::DumpRecord& record, const Jrd::Atta
|
||||
temp = (attachment->att_flags & ATT_system) ? 1 : 0;
|
||||
record.storeInteger(f_mon_att_sys_flag, temp);
|
||||
|
||||
// session idle timeout, seconds
|
||||
record.storeInteger(f_mon_att_idle_timeout, attachment->getIdleTimeout());
|
||||
// when idle timer expires, NULL if not running
|
||||
TimeStamp idleTimer;
|
||||
if (attachment->getIdleTimerTimestamp(idleTimer))
|
||||
record.storeTimestamp(f_mon_att_idle_timer, idleTimer);
|
||||
// statement timeout, milliseconds
|
||||
record.storeInteger(f_mon_att_stmt_timeout, attachment->getStatementTimeout());
|
||||
|
||||
record.write();
|
||||
|
||||
if (attachment->att_database->dbb_flags & DBB_shared)
|
||||
@ -1020,6 +1029,13 @@ void Monitoring::putRequest(SnapshotData::DumpRecord& record, const jrd_req* req
|
||||
if (request->req_transaction)
|
||||
record.storeInteger(f_mon_stmt_tra_id, request->req_transaction->tra_number);
|
||||
record.storeTimestamp(f_mon_stmt_timestamp, request->req_timestamp);
|
||||
|
||||
ISC_TIMESTAMP ts;
|
||||
if (request->req_timer &&
|
||||
request->req_timer->getExpireTimestamp(request->req_timestamp.value(), ts))
|
||||
{
|
||||
record.storeTimestamp(f_mon_stmt_timer, ts);
|
||||
}
|
||||
}
|
||||
else
|
||||
record.storeInteger(f_mon_stmt_state, mon_state_idle);
|
||||
@ -1038,6 +1054,8 @@ void Monitoring::putRequest(SnapshotData::DumpRecord& record, const jrd_req* req
|
||||
const int stat_id = fb_utils::genUniqueId();
|
||||
record.storeGlobalId(f_mon_stmt_stat_id, getGlobalId(stat_id));
|
||||
|
||||
// statement timeout, milliseconds
|
||||
record.storeInteger(f_mon_stmt_timeout, request->req_timeout);
|
||||
record.write();
|
||||
|
||||
putStatistics(record, request->req_stats, stat_id, stat_statement);
|
||||
|
@ -137,6 +137,11 @@ public:
|
||||
storeField(field_id, VALUE_TIMESTAMP, sizeof(ISC_TIMESTAMP), &value.value());
|
||||
}
|
||||
|
||||
void storeTimestamp(int field_id, const ISC_TIMESTAMP& value)
|
||||
{
|
||||
storeField(field_id, VALUE_TIMESTAMP, sizeof(ISC_TIMESTAMP), &value);
|
||||
}
|
||||
|
||||
void storeString(int field_id, const Firebird::string& value)
|
||||
{
|
||||
if (value.length())
|
||||
|
@ -218,6 +218,8 @@ const char
|
||||
CLIENT_PROCESS_NAME[] = "CLIENT_PROCESS",
|
||||
CURRENT_USER_NAME[] = "CURRENT_USER",
|
||||
CURRENT_ROLE_NAME[] = "CURRENT_ROLE",
|
||||
SESSION_IDLE_TIMEOUT[] = "SESSION_IDLE_TIMEOUT",
|
||||
STATEMENT_TIMEOUT[] = "STATEMENT_TIMEOUT",
|
||||
// SYSTEM namespace: transaction wise items
|
||||
TRANSACTION_ID_NAME[] = "TRANSACTION_ID",
|
||||
ISOLATION_LEVEL_NAME[] = "ISOLATION_LEVEL",
|
||||
@ -2254,6 +2256,10 @@ dsc* evlGetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar
|
||||
return NULL;
|
||||
resultStr = role.c_str();
|
||||
}
|
||||
else if (nameStr == SESSION_IDLE_TIMEOUT)
|
||||
resultStr.printf("%" ULONGFORMAT, attachment->getIdleTimeout());
|
||||
else if (nameStr == STATEMENT_TIMEOUT)
|
||||
resultStr.printf("%" ULONGFORMAT, attachment->getStatementTimeout());
|
||||
else if (nameStr == TRANSACTION_ID_NAME)
|
||||
resultStr.printf("%" SQUADFORMAT, transaction->tra_number);
|
||||
else if (nameStr == ISOLATION_LEVEL_NAME)
|
||||
|
@ -849,6 +849,11 @@ void Statement::prepare(thread_db* tdbb, Transaction* tran, const string& sql, b
|
||||
m_preparedByReq = m_callerPrivileges ? tdbb->getRequest() : NULL;
|
||||
}
|
||||
|
||||
void Statement::setTimeout(thread_db* tdbb, unsigned int timeout)
|
||||
{
|
||||
doSetTimeout(tdbb, timeout);
|
||||
}
|
||||
|
||||
void Statement::execute(thread_db* tdbb, Transaction* tran,
|
||||
const MetaName* const* in_names, const ValueListNode* in_params,
|
||||
const ValueListNode* out_params)
|
||||
|
@ -318,6 +318,7 @@ public:
|
||||
Transaction* getTransaction() { return m_transaction; }
|
||||
|
||||
void prepare(Jrd::thread_db* tdbb, Transaction* tran, const Firebird::string& sql, bool named);
|
||||
void setTimeout(Jrd::thread_db* tdbb, unsigned int timeout);
|
||||
void execute(Jrd::thread_db* tdbb, Transaction* tran,
|
||||
const Firebird::MetaName* const* in_names, const Jrd::ValueListNode* in_params,
|
||||
const Jrd::ValueListNode* out_params);
|
||||
@ -352,6 +353,7 @@ public:
|
||||
|
||||
protected:
|
||||
virtual void doPrepare(Jrd::thread_db* tdbb, const Firebird::string& sql) = 0;
|
||||
virtual void doSetTimeout(Jrd::thread_db* tdbb, unsigned int timeout) = 0;
|
||||
virtual void doExecute(Jrd::thread_db* tdbb) = 0;
|
||||
virtual void doOpen(Jrd::thread_db* tdbb) = 0;
|
||||
virtual bool doFetch(Jrd::thread_db* tdbb) = 0;
|
||||
|
@ -507,6 +507,21 @@ void InternalStatement::doPrepare(thread_db* tdbb, const string& sql)
|
||||
}
|
||||
|
||||
|
||||
void InternalStatement::doSetTimeout(thread_db* tdbb, unsigned int timeout)
|
||||
{
|
||||
FbLocalStatus status;
|
||||
|
||||
{
|
||||
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
|
||||
|
||||
m_request->setTimeout(&status, timeout);
|
||||
}
|
||||
|
||||
if (status->getState() & IStatus::STATE_ERRORS)
|
||||
raise(&status, tdbb, "JStatement::setTimeout");
|
||||
}
|
||||
|
||||
|
||||
void InternalStatement::doExecute(thread_db* tdbb)
|
||||
{
|
||||
JTransaction* transaction = getIntTransaction()->getJrdTran();
|
||||
|
@ -130,6 +130,7 @@ protected:
|
||||
|
||||
protected:
|
||||
virtual void doPrepare(Jrd::thread_db* tdbb, const Firebird::string& sql);
|
||||
virtual void doSetTimeout(Jrd::thread_db* tdbb, unsigned int timeout);
|
||||
virtual void doExecute(Jrd::thread_db* tdbb);
|
||||
virtual void doOpen(Jrd::thread_db* tdbb);
|
||||
virtual bool doFetch(Jrd::thread_db* tdbb);
|
||||
|
@ -493,6 +493,24 @@ void IscStatement::doPrepare(thread_db* tdbb, const string& sql)
|
||||
}
|
||||
}
|
||||
|
||||
void IscStatement::doSetTimeout(thread_db* tdbb, unsigned int timeout)
|
||||
{
|
||||
FbLocalStatus status;
|
||||
{
|
||||
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
|
||||
m_iscProvider.fb_dsql_set_timeout(&status, &m_handle, timeout);
|
||||
}
|
||||
if (status->getState() & IStatus::STATE_ERRORS)
|
||||
{
|
||||
// silently ignore error if timeouts is not supported by remote server
|
||||
// or loaded client library
|
||||
if (status[0] == isc_arg_gds && (status[1] == isc_wish_list || status[1] == isc_unavailable))
|
||||
return;
|
||||
|
||||
raise(&status, tdbb, "fb_dsql_set_timeout");
|
||||
}
|
||||
}
|
||||
|
||||
void IscStatement::doExecute(thread_db* tdbb)
|
||||
{
|
||||
FB_API_HANDLE& h_tran = getIscTransaction()->getAPIHandle();
|
||||
@ -1503,6 +1521,16 @@ ISC_STATUS ISC_EXPORT IscProvider::fb_database_crypt_callback(FbStatusVector* us
|
||||
return notImplemented(user_status);
|
||||
}
|
||||
|
||||
ISC_STATUS ISC_EXPORT IscProvider::fb_dsql_set_timeout(Jrd::FbStatusVector* user_status,
|
||||
isc_stmt_handle* stmt_handle,
|
||||
ULONG timeout)
|
||||
{
|
||||
if (m_api.fb_dsql_set_timeout)
|
||||
return m_api.fb_dsql_set_timeout(IscStatus(user_status), stmt_handle, timeout);
|
||||
|
||||
return notImplemented(user_status);
|
||||
}
|
||||
|
||||
void IscProvider::loadAPI()
|
||||
{
|
||||
FbLocalStatus status;
|
||||
@ -1595,7 +1623,8 @@ static FirebirdApiPointers isc_callbacks =
|
||||
PROTO(isc_service_query),
|
||||
PROTO(isc_service_start),
|
||||
PROTO(fb_cancel_operation),
|
||||
PROTO(fb_database_crypt_callback)
|
||||
PROTO(fb_database_crypt_callback),
|
||||
PROTO(fb_dsql_set_timeout)
|
||||
};
|
||||
|
||||
|
||||
|
@ -485,6 +485,10 @@ public:
|
||||
|
||||
virtual ISC_STATUS ISC_EXPORT fb_database_crypt_callback(Jrd::FbStatusVector*,
|
||||
void*);
|
||||
|
||||
virtual ISC_STATUS API_ROUTINE fb_dsql_set_timeout(Jrd::FbStatusVector*,
|
||||
isc_stmt_handle*,
|
||||
ULONG);
|
||||
};
|
||||
|
||||
|
||||
@ -576,6 +580,7 @@ protected:
|
||||
|
||||
protected:
|
||||
virtual void doPrepare(Jrd::thread_db* tdbb, const Firebird::string& sql);
|
||||
virtual void doSetTimeout(Jrd::thread_db* tdbb, unsigned int timeout);
|
||||
virtual void doExecute(Jrd::thread_db* tdbb);
|
||||
virtual void doOpen(Jrd::thread_db* tdbb);
|
||||
virtual bool doFetch(Jrd::thread_db* tdbb);
|
||||
|
@ -195,3 +195,8 @@
|
||||
|
||||
FIELD(fld_system_privileges, nam_system_privileges, dtype_text, 8 , dsc_text_type_fixed , dflt_no_privs, true)
|
||||
FIELD(fld_b_sql_security, nam_sql_security , dtype_boolean , 1 , 0 , NULL , true)
|
||||
|
||||
FIELD(fld_idle_timeout , nam_idle_timeout , dtype_long , sizeof(SLONG) , 0 , NULL , false)
|
||||
FIELD(fld_idle_timer , nam_idle_timer , dtype_timestamp, TIMESTAMP_SIZE , 0 , NULL , true)
|
||||
FIELD(fld_stmt_timeout , nam_stmt_timeout , dtype_long , sizeof(SLONG) , 0 , NULL , false)
|
||||
FIELD(fld_stmt_timer , nam_stmt_timer , dtype_timestamp, TIMESTAMP_SIZE , 0 , NULL , true)
|
||||
|
@ -475,6 +475,10 @@ ISC_STATUS ISC_EXPORT isc_dsql_sql_info(ISC_STATUS*,
|
||||
short,
|
||||
ISC_SCHAR*);
|
||||
|
||||
ISC_STATUS ISC_EXPORT fb_dsql_set_timeout(ISC_STATUS*,
|
||||
isc_stmt_handle*,
|
||||
ISC_ULONG);
|
||||
|
||||
void ISC_EXPORT isc_encode_date(const void*,
|
||||
ISC_QUAD*);
|
||||
|
||||
|
@ -238,7 +238,7 @@ void INF_database_info(thread_db* tdbb,
|
||||
const UCHAR* const end_items = items + item_length;
|
||||
const UCHAR* const end = info + output_length;
|
||||
|
||||
const Jrd::Attachment* const err_att = tdbb->getAttachment();
|
||||
const Jrd::Attachment* const att = tdbb->getAttachment();
|
||||
|
||||
while (items < end_items && *items != isc_info_end)
|
||||
{
|
||||
@ -633,7 +633,7 @@ void INF_database_info(thread_db* tdbb,
|
||||
case fb_info_tpage_warns:
|
||||
case fb_info_pip_errors:
|
||||
case fb_info_pip_warns:
|
||||
err_val = (err_att->att_validation) ? err_att->att_validation->getInfo(item) : 0;
|
||||
err_val = (att->att_validation) ? att->att_validation->getInfo(item) : 0;
|
||||
|
||||
length = INF_convert(err_val, buffer);
|
||||
break;
|
||||
@ -769,6 +769,26 @@ void INF_database_info(thread_db* tdbb,
|
||||
dbb->dbb_crypto_manager->getCurrentState() : 0, buffer);
|
||||
break;
|
||||
|
||||
case fb_info_statement_timeout_db:
|
||||
length = INF_convert(dbb->dbb_config->getStatementTimeout(), buffer);
|
||||
break;
|
||||
|
||||
case fb_info_statement_timeout_att:
|
||||
length = INF_convert(att->getStatementTimeout(), buffer);
|
||||
break;
|
||||
|
||||
case fb_info_ses_idle_timeout_db:
|
||||
length = INF_convert(dbb->dbb_config->getConnIdleTimeout() * 60, buffer);
|
||||
break;
|
||||
|
||||
case fb_info_ses_idle_timeout_att:
|
||||
length = INF_convert(att->getIdleTimeout(), buffer);
|
||||
break;
|
||||
|
||||
case fb_info_ses_idle_timeout_run:
|
||||
length = INF_convert(att->getActualIdleTimeout(), buffer);
|
||||
break;
|
||||
|
||||
default:
|
||||
buffer[0] = item;
|
||||
item = isc_info_error;
|
||||
|
@ -142,6 +142,13 @@ enum db_info_types
|
||||
|
||||
fb_info_crypt_state = 126,
|
||||
|
||||
fb_info_statement_timeout_db,
|
||||
fb_info_statement_timeout_att,
|
||||
|
||||
fb_info_ses_idle_timeout_db,
|
||||
fb_info_ses_idle_timeout_att,
|
||||
fb_info_ses_idle_timeout_run,
|
||||
|
||||
isc_info_db_last_value /* Leave this LAST! */
|
||||
};
|
||||
|
||||
@ -419,6 +426,8 @@ enum info_db_provider
|
||||
#define isc_info_sql_relation_alias 25
|
||||
#define isc_info_sql_explain_plan 26
|
||||
#define isc_info_sql_stmt_flags 27
|
||||
#define isc_info_sql_stmt_timeout_user 28
|
||||
#define isc_info_sql_stmt_timeout_run 29
|
||||
|
||||
/*********************************/
|
||||
/* SQL information return values */
|
||||
|
333
src/jrd/jrd.cpp
333
src/jrd/jrd.cpp
@ -680,14 +680,21 @@ namespace
|
||||
// with the flag set cause shutdownMutex mutex is not locked here.
|
||||
// That's not a danger cause check of att_use_count
|
||||
// in shutdown code makes it anyway safe.
|
||||
status_exception::raise(Arg::Gds(isc_att_shutdown));
|
||||
Arg::Gds err(isc_att_shutdown);
|
||||
if (sAtt->getShutError())
|
||||
err << Arg::Gds(sAtt->getShutError());
|
||||
|
||||
err.raise();
|
||||
}
|
||||
|
||||
tdbb->setAttachment(attachment);
|
||||
tdbb->setDatabase(attachment->att_database);
|
||||
|
||||
if (!async)
|
||||
{
|
||||
attachment->att_use_count++;
|
||||
attachment->setupIdleTimer(true);
|
||||
}
|
||||
}
|
||||
catch (const Firebird::Exception&)
|
||||
{
|
||||
@ -709,7 +716,11 @@ namespace
|
||||
Jrd::Attachment* attachment = sAtt->getHandle();
|
||||
|
||||
if (attachment && !async)
|
||||
{
|
||||
attachment->att_use_count--;
|
||||
if (!attachment->att_use_count)
|
||||
attachment->setupIdleTimer(false);
|
||||
}
|
||||
|
||||
if (!nolock)
|
||||
sAtt->getMutex(async)->leave();
|
||||
@ -1681,8 +1692,12 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch
|
||||
|
||||
if (attachment->att_flags & ATT_shutdown)
|
||||
{
|
||||
const ISC_STATUS err = jAtt->getStable()->getShutError();
|
||||
|
||||
if (dbb->dbb_ast_flags & DBB_shutdown)
|
||||
ERR_post(Arg::Gds(isc_shutdown) << Arg::Str(org_filename));
|
||||
else if (err)
|
||||
ERR_post(Arg::Gds(isc_att_shutdown) << Arg::Gds(err));
|
||||
else
|
||||
ERR_post(Arg::Gds(isc_att_shutdown));
|
||||
}
|
||||
@ -2915,7 +2930,15 @@ void JAttachment::freeEngineData(CheckStatusWrapper* user_status, bool forceFree
|
||||
if (forceFree)
|
||||
flags |= PURGE_NOCHECK;
|
||||
|
||||
attachment->signalShutdown();
|
||||
ISC_STATUS reason = 0;
|
||||
if (!forceFree)
|
||||
reason = 0;
|
||||
else if (engineShutdown)
|
||||
reason = isc_att_shut_engine;
|
||||
else if (dbb->dbb_ast_flags & DBB_shutdown)
|
||||
reason = isc_att_shut_db_down;
|
||||
|
||||
attachment->signalShutdown(reason);
|
||||
purge_attachment(tdbb, getStable(), flags);
|
||||
|
||||
att->release();
|
||||
@ -2985,15 +3008,15 @@ void JAttachment::dropDatabase(CheckStatusWrapper* user_status)
|
||||
|
||||
if (attachment->att_flags & ATT_shutdown)
|
||||
{
|
||||
const ISC_STATUS err = getStable()->getShutError();
|
||||
|
||||
if (dbb->dbb_ast_flags & DBB_shutdown)
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_shutdown) << Arg::Str(file_name));
|
||||
}
|
||||
else if (err)
|
||||
ERR_post(Arg::Gds(isc_att_shutdown) << Arg::Gds(err));
|
||||
else
|
||||
{
|
||||
ERR_post(Arg::Gds(isc_att_shutdown));
|
||||
}
|
||||
}
|
||||
|
||||
if (!CCH_exclusive(tdbb, LCK_PW, WAIT_PERIOD, NULL))
|
||||
{
|
||||
@ -4331,6 +4354,82 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction*
|
||||
successful_completion(user_status);
|
||||
}
|
||||
|
||||
unsigned int JAttachment::getIdleTimeout(Firebird::CheckStatusWrapper* user_status)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
try
|
||||
{
|
||||
EngineContextHolder tdbb(user_status, this, FB_FUNCTION);
|
||||
check_database(tdbb);
|
||||
|
||||
result = getHandle()->getIdleTimeout();
|
||||
}
|
||||
catch (const Exception& ex)
|
||||
{
|
||||
ex.stuffException(user_status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
successful_completion(user_status);
|
||||
return result;
|
||||
}
|
||||
|
||||
void JAttachment::setIdleTimeout(Firebird::CheckStatusWrapper* user_status, unsigned int timeOut)
|
||||
{
|
||||
try
|
||||
{
|
||||
EngineContextHolder tdbb(user_status, this, FB_FUNCTION);
|
||||
check_database(tdbb);
|
||||
|
||||
getHandle()->setIdleTimeout(timeOut);
|
||||
}
|
||||
catch (const Exception& ex)
|
||||
{
|
||||
ex.stuffException(user_status);
|
||||
return;
|
||||
}
|
||||
|
||||
successful_completion(user_status);
|
||||
}
|
||||
|
||||
unsigned int JAttachment::getStatementTimeout(Firebird::CheckStatusWrapper* user_status)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
try
|
||||
{
|
||||
EngineContextHolder tdbb(user_status, this, FB_FUNCTION);
|
||||
check_database(tdbb);
|
||||
|
||||
result = getHandle()->getStatementTimeout();
|
||||
}
|
||||
catch (const Exception& ex)
|
||||
{
|
||||
ex.stuffException(user_status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
successful_completion(user_status);
|
||||
return result;
|
||||
}
|
||||
|
||||
void JAttachment::setStatementTimeout(Firebird::CheckStatusWrapper* user_status, unsigned int timeOut)
|
||||
{
|
||||
try
|
||||
{
|
||||
EngineContextHolder tdbb(user_status, this, FB_FUNCTION);
|
||||
check_database(tdbb);
|
||||
|
||||
getHandle()->setStatementTimeout(timeOut);
|
||||
}
|
||||
catch (const Exception& ex)
|
||||
{
|
||||
ex.stuffException(user_status);
|
||||
return;
|
||||
}
|
||||
|
||||
successful_completion(user_status);
|
||||
}
|
||||
|
||||
|
||||
void JTransaction::getInfo(CheckStatusWrapper* user_status,
|
||||
unsigned int itemsLength, const unsigned char* items,
|
||||
@ -5287,6 +5386,66 @@ void JStatement::getInfo(CheckStatusWrapper* user_status,
|
||||
successful_completion(user_status);
|
||||
}
|
||||
|
||||
|
||||
unsigned int JStatement::getTimeout(CheckStatusWrapper* user_status)
|
||||
{
|
||||
try
|
||||
{
|
||||
EngineContextHolder tdbb(user_status, this, FB_FUNCTION);
|
||||
check_database(tdbb);
|
||||
|
||||
try
|
||||
{
|
||||
Jrd::dsql_req* req = getHandle();
|
||||
return req->getTimeout();
|
||||
}
|
||||
catch (const Exception& ex)
|
||||
{
|
||||
transliterateException(tdbb, ex, user_status, FB_FUNCTION);
|
||||
return 0;
|
||||
}
|
||||
trace_warning(tdbb, user_status, FB_FUNCTION);
|
||||
}
|
||||
catch (const Exception& ex)
|
||||
{
|
||||
ex.stuffException(user_status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
successful_completion(user_status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void JStatement::setTimeout(CheckStatusWrapper* user_status, unsigned int timeOut)
|
||||
{
|
||||
try
|
||||
{
|
||||
EngineContextHolder tdbb(user_status, this, FB_FUNCTION);
|
||||
check_database(tdbb);
|
||||
|
||||
try
|
||||
{
|
||||
Jrd::dsql_req* req = getHandle();
|
||||
req->setTimeout(timeOut);
|
||||
}
|
||||
catch (const Exception& ex)
|
||||
{
|
||||
transliterateException(tdbb, ex, user_status, FB_FUNCTION);
|
||||
return;
|
||||
}
|
||||
trace_warning(tdbb, user_status, FB_FUNCTION);
|
||||
}
|
||||
catch (const Exception& ex)
|
||||
{
|
||||
ex.stuffException(user_status);
|
||||
return;
|
||||
}
|
||||
|
||||
successful_completion(user_status);
|
||||
}
|
||||
|
||||
|
||||
void JAttachment::ping(CheckStatusWrapper* user_status)
|
||||
{
|
||||
/**************************************
|
||||
@ -5442,7 +5601,11 @@ static void check_database(thread_db* tdbb, bool async)
|
||||
}
|
||||
else
|
||||
{
|
||||
status_exception::raise(Arg::Gds(isc_att_shutdown));
|
||||
Arg::Gds err(isc_att_shutdown);
|
||||
if (attachment->getStable() && attachment->getStable()->getShutError())
|
||||
err << Arg::Gds(attachment->getStable()->getShutError());
|
||||
|
||||
err.raise();
|
||||
}
|
||||
}
|
||||
|
||||
@ -7338,7 +7501,7 @@ namespace
|
||||
Attachment* attachment = sAtt->getHandle();
|
||||
|
||||
if (attachment)
|
||||
attachment->signalShutdown();
|
||||
attachment->signalShutdown(isc_att_shut_engine);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7478,6 +7641,139 @@ static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM arg)
|
||||
}
|
||||
|
||||
|
||||
/// TimeoutTimer
|
||||
#ifdef USE_ITIMER
|
||||
void TimeoutTimer::handler()
|
||||
{
|
||||
m_expired = true;
|
||||
m_started = 0;
|
||||
}
|
||||
|
||||
int TimeoutTimer::release()
|
||||
{
|
||||
if (--refCounter == 0)
|
||||
{
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned int TimeoutTimer::timeToExpire() const
|
||||
{
|
||||
if (!m_started || m_expired)
|
||||
return 0;
|
||||
|
||||
const SINT64 t = fb_utils::query_performance_counter() * 1000 / fb_utils::query_performance_frequency();
|
||||
const SINT64 r = m_started + m_value - t;
|
||||
return r > 0 ? r : 0;
|
||||
}
|
||||
|
||||
bool TimeoutTimer::getExpireTimestamp(const ISC_TIMESTAMP start, ISC_TIMESTAMP& exp) const
|
||||
{
|
||||
if (!m_started || m_expired)
|
||||
return false;
|
||||
|
||||
static const SINT64 ISC_TICKS_PER_DAY = 24 * 60 * 60 * ISC_TIME_SECONDS_PRECISION;
|
||||
|
||||
SINT64 ticks = start.timestamp_date * ISC_TICKS_PER_DAY + start.timestamp_time;
|
||||
ticks += m_value * ISC_TIME_SECONDS_PRECISION / 1000;
|
||||
|
||||
exp.timestamp_date = ticks / ISC_TICKS_PER_DAY;
|
||||
exp.timestamp_time = ticks % ISC_TICKS_PER_DAY;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TimeoutTimer::start()
|
||||
{
|
||||
FbLocalStatus s;
|
||||
ITimerControl* timerCtrl = Firebird::TimerInterfacePtr();
|
||||
|
||||
m_expired = false;
|
||||
|
||||
// todo: timerCtrl->restart to avoid 2 times acquire timerCtrl mutex
|
||||
|
||||
if (m_started)
|
||||
{
|
||||
timerCtrl->stop(&s, this);
|
||||
m_started = 0;
|
||||
}
|
||||
|
||||
if (m_value != 0)
|
||||
{
|
||||
timerCtrl->start(&s, this, m_value * 1000);
|
||||
check(&s); // ?? todo
|
||||
m_started = fb_utils::query_performance_counter() * 1000 / fb_utils::query_performance_frequency();
|
||||
}
|
||||
|
||||
fb_assert(m_value && m_started || !m_value && !m_started);
|
||||
}
|
||||
|
||||
void TimeoutTimer::stop()
|
||||
{
|
||||
if (m_started)
|
||||
{
|
||||
m_started = 0;
|
||||
|
||||
FbLocalStatus s;
|
||||
ITimerControl* timerCtrl = Firebird::TimerInterfacePtr();
|
||||
timerCtrl->stop(&s, this);
|
||||
}
|
||||
}
|
||||
#else
|
||||
bool TimeoutTimer::expired() const
|
||||
{
|
||||
if (!m_start)
|
||||
return false;
|
||||
|
||||
const SINT64 t = currTime();
|
||||
return t > m_start + m_value;
|
||||
}
|
||||
|
||||
unsigned int TimeoutTimer::timeToExpire() const
|
||||
{
|
||||
if (!m_start)
|
||||
return 0;
|
||||
|
||||
const SINT64 t = currTime();
|
||||
const SINT64 r = m_start + m_value - t;
|
||||
return r > 0 ? r : 0;
|
||||
}
|
||||
|
||||
bool TimeoutTimer::getExpireTimestamp(const ISC_TIMESTAMP start, ISC_TIMESTAMP& exp) const
|
||||
{
|
||||
if (!m_start)
|
||||
return false;
|
||||
|
||||
static const SINT64 ISC_TICKS_PER_DAY = 24 * 60 * 60 * ISC_TIME_SECONDS_PRECISION;
|
||||
|
||||
SINT64 ticks = start.timestamp_date * ISC_TICKS_PER_DAY + start.timestamp_time;
|
||||
ticks += m_value * ISC_TIME_SECONDS_PRECISION / 1000;
|
||||
|
||||
exp.timestamp_date = ticks / ISC_TICKS_PER_DAY;
|
||||
exp.timestamp_time = ticks % ISC_TICKS_PER_DAY;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TimeoutTimer::start()
|
||||
{
|
||||
m_start = 0;
|
||||
|
||||
if (m_value != 0)
|
||||
m_start = currTime();
|
||||
}
|
||||
|
||||
void TimeoutTimer::stop()
|
||||
{
|
||||
m_start = 0;
|
||||
}
|
||||
|
||||
|
||||
#endif // USE_ITIMER
|
||||
|
||||
// begin thread_db methods
|
||||
|
||||
void thread_db::setDatabase(Database* val)
|
||||
@ -7515,7 +7811,7 @@ SSHORT thread_db::getCharSet() const
|
||||
return attachment->att_charset;
|
||||
}
|
||||
|
||||
ISC_STATUS thread_db::checkCancelState()
|
||||
ISC_STATUS thread_db::checkCancelState(ISC_STATUS* secondary)
|
||||
{
|
||||
// Test for asynchronous shutdown/cancellation requests.
|
||||
// But do that only if we're neither in the verb cleanup state
|
||||
@ -7535,8 +7831,13 @@ ISC_STATUS thread_db::checkCancelState()
|
||||
if (database->dbb_ast_flags & DBB_shutdown)
|
||||
return isc_shutdown;
|
||||
else if (!(tdbb_flags & TDBB_shutdown_manager))
|
||||
{
|
||||
if (secondary)
|
||||
*secondary = attachment->getStable() ? attachment->getStable()->getShutError() : 0;
|
||||
|
||||
return isc_att_shutdown;
|
||||
}
|
||||
}
|
||||
|
||||
// If a cancel has been raised, defer its acknowledgement
|
||||
// when executing in the context of an internal request or
|
||||
@ -7556,6 +7857,14 @@ ISC_STATUS thread_db::checkCancelState()
|
||||
}
|
||||
}
|
||||
|
||||
if (tdbb_reqTimer && tdbb_reqTimer->expired())
|
||||
{
|
||||
if (secondary)
|
||||
*secondary = tdbb_reqTimer->getErrCode();
|
||||
|
||||
return isc_cancelled;
|
||||
}
|
||||
|
||||
// Check the thread state for already posted system errors. If any still persists,
|
||||
// then someone tries to ignore our attempts to interrupt him. Let's insist.
|
||||
|
||||
@ -7567,7 +7876,8 @@ ISC_STATUS thread_db::checkCancelState()
|
||||
|
||||
bool thread_db::checkCancelState(bool punt)
|
||||
{
|
||||
const ISC_STATUS error = checkCancelState();
|
||||
ISC_STATUS secondary = 0;
|
||||
const ISC_STATUS error = checkCancelState(&secondary);
|
||||
|
||||
if (!error)
|
||||
return false;
|
||||
@ -7577,6 +7887,9 @@ bool thread_db::checkCancelState(bool punt)
|
||||
if (error == isc_shutdown)
|
||||
status << Arg::Str(attachment->att_filename);
|
||||
|
||||
if (secondary)
|
||||
status << Arg::Gds(secondary);
|
||||
|
||||
if (attachment)
|
||||
attachment->att_flags &= ~ATT_cancel_raise;
|
||||
|
||||
|
141
src/jrd/jrd.h
141
src/jrd/jrd.h
@ -349,6 +349,110 @@ const USHORT WIN_garbage_collector = 4; // garbage collector's window
|
||||
const USHORT WIN_garbage_collect = 8; // scan left a page for garbage collector
|
||||
|
||||
|
||||
#ifdef USE_ITIMER
|
||||
class TimeoutTimer FB_FINAL :
|
||||
public Firebird::RefCntIface<Firebird::ITimerImpl<TimeoutTimer, Firebird::CheckStatusWrapper> >
|
||||
{
|
||||
public:
|
||||
explicit TimeoutTimer() :
|
||||
m_started(0),
|
||||
m_expired(false),
|
||||
m_value(0),
|
||||
m_error(0)
|
||||
{ }
|
||||
|
||||
// ITimer implementation
|
||||
void handler();
|
||||
int release();
|
||||
|
||||
bool expired() const
|
||||
{
|
||||
return m_expired;
|
||||
}
|
||||
|
||||
unsigned int getValue() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
unsigned int getErrCode() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
// milliseconds left before timer expiration
|
||||
unsigned int timeToExpire() const;
|
||||
|
||||
// evaluate expire timestamp using start timestamp
|
||||
bool getExpireTimestamp(const ISC_TIMESTAMP start, ISC_TIMESTAMP& exp) const;
|
||||
|
||||
// set timeout value in milliseconds and secondary error code
|
||||
void setup(unsigned int value, ISC_STATUS error)
|
||||
{
|
||||
m_value = value;
|
||||
m_error = error;
|
||||
}
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
SINT64 m_started;
|
||||
bool m_expired;
|
||||
unsigned int m_value; // milliseconds
|
||||
ISC_STATUS m_error;
|
||||
};
|
||||
#else
|
||||
class TimeoutTimer : public Firebird::RefCounted
|
||||
{
|
||||
public:
|
||||
explicit TimeoutTimer() :
|
||||
m_start(0),
|
||||
m_value(0),
|
||||
m_error(0)
|
||||
{ }
|
||||
|
||||
bool expired() const;
|
||||
|
||||
unsigned int getValue() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
unsigned int getErrCode() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
// milliseconds left before timer expiration
|
||||
unsigned int timeToExpire() const;
|
||||
|
||||
// evaluate expire timestamp using start timestamp
|
||||
bool getExpireTimestamp(const ISC_TIMESTAMP start, ISC_TIMESTAMP& exp) const;
|
||||
|
||||
// set timeout value in milliseconds and secondary error code
|
||||
void setup(unsigned int value, ISC_STATUS error)
|
||||
{
|
||||
m_start = 0;
|
||||
m_value = value;
|
||||
m_error = error;
|
||||
}
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
SINT64 currTime() const
|
||||
{
|
||||
return fb_utils::query_performance_counter() * 1000 / fb_utils::query_performance_frequency();
|
||||
}
|
||||
|
||||
SINT64 m_start;
|
||||
unsigned int m_value; // milliseconds
|
||||
ISC_STATUS m_error;
|
||||
};
|
||||
#endif // USE_ITIMER
|
||||
|
||||
// Thread specific database block
|
||||
|
||||
// tdbb_flags
|
||||
@ -516,9 +620,13 @@ public:
|
||||
attStat->bumpRelValue(index, relation_id, delta);
|
||||
}
|
||||
|
||||
ISC_STATUS checkCancelState();
|
||||
ISC_STATUS checkCancelState(ISC_STATUS* secondary = NULL);
|
||||
bool checkCancelState(bool punt);
|
||||
bool reschedule(SLONG quantum, bool punt);
|
||||
const TimeoutTimer* getTimeoutTimer() const
|
||||
{
|
||||
return tdbb_reqTimer;
|
||||
}
|
||||
|
||||
void registerBdb(BufferDesc* bdb)
|
||||
{
|
||||
@ -587,6 +695,37 @@ public:
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
class TimerGuard
|
||||
{
|
||||
public:
|
||||
TimerGuard(thread_db* tdbb, TimeoutTimer* timer, bool autoStop) :
|
||||
m_tdbb(tdbb),
|
||||
m_autoStop(autoStop && timer)
|
||||
{
|
||||
fb_assert(m_tdbb->tdbb_reqTimer == NULL);
|
||||
|
||||
m_tdbb->tdbb_reqTimer = timer;
|
||||
if (timer && timer->expired())
|
||||
m_tdbb->tdbb_quantum = 0;
|
||||
}
|
||||
|
||||
~TimerGuard()
|
||||
{
|
||||
if (m_autoStop)
|
||||
m_tdbb->tdbb_reqTimer->stop();
|
||||
|
||||
m_tdbb->tdbb_reqTimer = NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
thread_db* m_tdbb;
|
||||
bool m_autoStop;
|
||||
};
|
||||
|
||||
private:
|
||||
Firebird::RefPtr<TimeoutTimer> tdbb_reqTimer;
|
||||
|
||||
};
|
||||
|
||||
class ThreadContextHolder
|
||||
|
@ -56,6 +56,7 @@
|
||||
using namespace Jrd;
|
||||
using namespace Firebird;
|
||||
|
||||
static SSHORT adjust_wait(thread_db* tdbb, SSHORT wait);
|
||||
static void bug_lck(const TEXT*);
|
||||
static bool compatible(const Lock*, const Lock*, USHORT);
|
||||
static void enqueue(thread_db*, CheckStatusWrapper*, Lock*, USHORT, SSHORT);
|
||||
@ -340,6 +341,7 @@ bool LCK_convert(thread_db* tdbb, Lock* lock, USHORT level, SSHORT wait)
|
||||
WaitCancelGuard guard(tdbb, lock, wait);
|
||||
FbLocalStatus statusVector;
|
||||
|
||||
wait = adjust_wait(tdbb, wait);
|
||||
const bool result = CONVERT(tdbb, &statusVector, lock, level, wait);
|
||||
|
||||
if (!result)
|
||||
@ -657,6 +659,7 @@ bool LCK_lock(thread_db* tdbb, Lock* lock, USHORT level, SSHORT wait)
|
||||
WaitCancelGuard guard(tdbb, lock, wait);
|
||||
FbLocalStatus statusVector;
|
||||
|
||||
wait = adjust_wait(tdbb, wait);
|
||||
ENQUEUE(tdbb, &statusVector, lock, level, wait);
|
||||
fb_assert(LCK_CHECK_LOCK(lock));
|
||||
|
||||
@ -856,6 +859,40 @@ void LCK_write_data(thread_db* tdbb, Lock* lock, SINT64 data)
|
||||
}
|
||||
|
||||
|
||||
static SSHORT adjust_wait(thread_db* tdbb, SSHORT wait)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* a d j u s t _ w a i t
|
||||
*
|
||||
**************************************
|
||||
*
|
||||
* Functional description
|
||||
* If wait is cancellable and if statement timer was started - calc new wait
|
||||
* time to ensure it will not take longer than rest of timeout.
|
||||
*
|
||||
**************************************/
|
||||
if ((wait == LCK_NO_WAIT) || (tdbb->tdbb_flags & TDBB_wait_cancel_disable) || !tdbb->getTimeoutTimer())
|
||||
return wait;
|
||||
|
||||
unsigned int tout = tdbb->getTimeoutTimer()->timeToExpire();
|
||||
if (tout > 0)
|
||||
{
|
||||
SSHORT t;
|
||||
if (tout < 1000)
|
||||
t = 1;
|
||||
else if (tout < MAX_SSHORT * 1000)
|
||||
t = (tout + 999) / 1000;
|
||||
else
|
||||
t = MAX_SSHORT;
|
||||
|
||||
if ((wait == LCK_WAIT) || (-wait > t))
|
||||
return -t;
|
||||
}
|
||||
return wait;
|
||||
}
|
||||
|
||||
|
||||
static void bug_lck(const TEXT* string)
|
||||
{
|
||||
/**************************************
|
||||
|
@ -408,3 +408,8 @@ NAME("SEC$USER_TYPE", nam_sec_user_type)
|
||||
|
||||
NAME("RDB$SYSTEM_PRIVILEGES", nam_system_privileges)
|
||||
NAME("RDB$SQL_SECURITY", nam_sql_security)
|
||||
|
||||
NAME("MON$IDLE_TIMEOUT", nam_idle_timeout)
|
||||
NAME("MON$IDLE_TIMER", nam_idle_timer)
|
||||
NAME("MON$STATEMENT_TIMEOUT", nam_stmt_timeout)
|
||||
NAME("MON$STATEMENT_TIMER", nam_stmt_timer)
|
||||
|
@ -517,6 +517,9 @@ RELATION(nam_mon_attachments, rel_mon_attachments, ODS_11_1, rel_virtual)
|
||||
FIELD(f_mon_att_remote_os_user, nam_mon_remote_os_user, fld_os_user, 0, ODS_12_0)
|
||||
FIELD(f_mon_att_auth_method, nam_mon_auth_method, fld_auth_method, 0, ODS_12_0)
|
||||
FIELD(f_mon_att_sys_flag, nam_mon_sys_flag, fld_flag, 0, ODS_12_0)
|
||||
FIELD(f_mon_att_idle_timeout, nam_idle_timeout, fld_idle_timeout, 0, ODS_13_0)
|
||||
FIELD(f_mon_att_idle_timer, nam_idle_timer, fld_idle_timer, 0, ODS_13_0)
|
||||
FIELD(f_mon_att_stmt_timeout, nam_stmt_timeout, fld_stmt_timeout, 0, ODS_13_0)
|
||||
END_RELATION
|
||||
|
||||
// Relation 35 (MON$TRANSACTIONS)
|
||||
@ -546,6 +549,8 @@ RELATION(nam_mon_statements, rel_mon_statements, ODS_11_1, rel_virtual)
|
||||
FIELD(f_mon_stmt_sql_text, nam_mon_sql_text, fld_source, 0, ODS_11_1)
|
||||
FIELD(f_mon_stmt_stat_id, nam_mon_stat_id, fld_stat_id, 0, ODS_11_1)
|
||||
FIELD(f_mon_stmt_expl_plan, nam_mon_expl_plan, fld_source, 0, ODS_11_1)
|
||||
FIELD(f_mon_stmt_timeout, nam_stmt_timeout, fld_stmt_timeout, 0, ODS_13_0)
|
||||
FIELD(f_mon_stmt_timer, nam_stmt_timer, fld_stmt_timer, 0, ODS_13_0)
|
||||
END_RELATION
|
||||
|
||||
// Relation 37 (MON$CALL_STACK)
|
||||
|
@ -175,6 +175,7 @@ public:
|
||||
req_ext_stmt(NULL),
|
||||
req_cursors(*req_pool),
|
||||
req_ext_resultset(NULL),
|
||||
req_timeout(0),
|
||||
req_domain_validation(NULL),
|
||||
req_auto_trans(*req_pool),
|
||||
req_sorts(*req_pool),
|
||||
@ -252,6 +253,8 @@ public:
|
||||
Savepoint* req_savepoints; // Looper savepoint list
|
||||
Savepoint* req_proc_sav_point; // procedure savepoint list
|
||||
Firebird::TimeStamp req_timestamp; // Start time of request
|
||||
unsigned int req_timeout; // query timeout in milliseconds, set by the dsql_req::setupTimer
|
||||
Firebird::RefPtr<TimeoutTimer> req_timer; // timeout timer, shared with dsql_req
|
||||
|
||||
Firebird::AutoPtr<Jrd::RuntimeStatistics> req_fetch_baseline; // State of request performance counters when we reported it last time
|
||||
SINT64 req_fetch_elapsed; // Number of clock ticks spent while fetching rows for this request since we reported it last time
|
||||
|
@ -532,7 +532,7 @@ static bool shutdown(thread_db* tdbb, SSHORT flag, bool force)
|
||||
{
|
||||
if (!(attachment->att_flags & ATT_shutdown))
|
||||
{
|
||||
attachment->signalShutdown();
|
||||
attachment->signalShutdown(isc_att_shut_db_down);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* MAX_NUMBER is the next number to be used, always one more than the highest message number. */
|
||||
set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?);
|
||||
--
|
||||
('2016-11-08 11:46:00', 'JRD', 0, 807)
|
||||
('2016-12-20 12:57:00', 'JRD', 0, 814)
|
||||
('2015-03-17 18:33:00', 'QLI', 1, 533)
|
||||
('2015-01-07 18:01:51', 'GFIX', 3, 134)
|
||||
('1996-11-07 13:39:40', 'GPRE', 4, 1)
|
||||
|
@ -914,6 +914,13 @@ Data source : @4', NULL, NULL)
|
||||
('dsql_window_cant_overr_frame', NULL, 'ExprNodes.cpp', NULL, 0, 804, NULL, 'Cannot override the window @1 because it has a frame clause. Tip: it can be used without parenthesis in OVER', NULL, NULL);
|
||||
('dsql_window_duplicate', NULL, 'ExprNodes.cpp', NULL, 0, 805, NULL, 'Duplicate window definition for @1', NULL, NULL);
|
||||
('sql_too_long', 'prepareStatement', 'dsql.cpp', NULL, 0, 806, NULL, 'SQL statement is too long. Maximum size is @1 bytes.', NULL, NULL);
|
||||
('cfg_stmt_timeout', 'thread_db::checkCancelState', 'jrd.cpp', NULL, 0, 807, NULL, 'Config level timeout expired.', NULL, NULL);
|
||||
('att_stmt_timeout', 'thread_db::checkCancelState', 'jrd.cpp', NULL, 0, 808, NULL, 'Attachment level timeout expired.', NULL, NULL);
|
||||
('req_stmt_timeout', 'thread_db::checkCancelState', 'jrd.cpp', NULL, 0, 809, NULL, 'Statement level timeout expired.', NULL, NULL);
|
||||
('att_shut_killed', NULL, 'jrd.cpp', NULL, 0, 810, NULL, 'Killed by database administrator.', NULL, NULL);
|
||||
('att_shut_idle', NULL, 'jrd.cpp', NULL, 0, 811, NULL, 'Idle timeout expired.', NULL, NULL);
|
||||
('att_shut_db_down', NULL, 'jrd.cpp', NULL, 0, 812, NULL, 'Database is shutdown.', NULL, NULL);
|
||||
('att_shut_engine', NULL, 'jrd.cpp', NULL, 0, 813, NULL, 'Engine is shutdown.', NULL, NULL);
|
||||
-- QLI
|
||||
(NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL);
|
||||
(NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL);
|
||||
|
@ -813,6 +813,13 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA
|
||||
(-833, '42', '000', 0, 804, 'dsql_window_cant_overr_frame', NULL, NULL)
|
||||
(-833, '42', '000', 0, 805, 'dsql_window_duplicate', NULL, NULL)
|
||||
(-902, '54', '001', 0, 806, 'sql_too_long', NULL, NULL)
|
||||
(-901, 'HY', '008', 0, 807, 'cfg_stmt_timeout', NULL, NULL)
|
||||
(-901, 'HY', '008', 0, 808, 'att_stmt_timeout', NULL, NULL)
|
||||
(-901, 'HY', '008', 0, 809, 'req_stmt_timeout', NULL, NULL)
|
||||
(-902, '08', '003', 0, 810, 'att_shut_killed', NULL, NULL)
|
||||
(-902, '08', '003', 0, 811, 'att_shut_idle', NULL, NULL)
|
||||
(-902, '08', '003', 0, 812, 'att_shut_db_down', NULL, NULL)
|
||||
(-902, '08', '003', 0, 813, 'att_shut_engine', NULL, NULL)
|
||||
-- GFIX
|
||||
(-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL)
|
||||
(-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)
|
||||
|
@ -322,6 +322,28 @@ public:
|
||||
void free(CheckStatusWrapper* status);
|
||||
unsigned getFlags(CheckStatusWrapper* status);
|
||||
|
||||
unsigned int getTimeout(CheckStatusWrapper* status)
|
||||
{
|
||||
if (statement->rsr_rdb->rdb_port->port_protocol < PROTOCOL_VERSION15)
|
||||
{
|
||||
status->setErrors(Arg::Gds(isc_wish_list).value());
|
||||
return 0;
|
||||
}
|
||||
|
||||
return statement->rsr_timeout;
|
||||
}
|
||||
|
||||
void setTimeout(CheckStatusWrapper* status, unsigned int timeOut)
|
||||
{
|
||||
if (timeOut && statement->rsr_rdb->rdb_port->port_protocol < PROTOCOL_VERSION15)
|
||||
{
|
||||
status->setErrors(Arg::Gds(isc_wish_list).value());
|
||||
return;
|
||||
}
|
||||
|
||||
statement->rsr_timeout = timeOut;
|
||||
}
|
||||
|
||||
public:
|
||||
Statement(Rsr* handle, Attachment* a, unsigned aDialect)
|
||||
: metadata(getPool(), this, NULL),
|
||||
@ -509,6 +531,11 @@ public:
|
||||
void detach(CheckStatusWrapper* status);
|
||||
void dropDatabase(CheckStatusWrapper* status);
|
||||
|
||||
unsigned int getIdleTimeout(CheckStatusWrapper* status);
|
||||
void setIdleTimeout(CheckStatusWrapper* status, unsigned int timeOut);
|
||||
unsigned int getStatementTimeout(CheckStatusWrapper* status);
|
||||
void setStatementTimeout(CheckStatusWrapper* status, unsigned int timeOut);
|
||||
|
||||
public:
|
||||
Attachment(Rdb* handle, const PathName& path)
|
||||
: rdb(handle), dbPath(getPool(), path)
|
||||
@ -529,7 +556,9 @@ public:
|
||||
Statement* createStatement(CheckStatusWrapper* status, unsigned dialect);
|
||||
|
||||
private:
|
||||
void execWithCheck(CheckStatusWrapper* status, const string& stmt);
|
||||
void freeClientData(CheckStatusWrapper* status, bool force = false);
|
||||
SLONG getSingleInfo(CheckStatusWrapper* status, UCHAR infoItem);
|
||||
|
||||
Rdb* rdb;
|
||||
const PathName dbPath;
|
||||
@ -1730,6 +1759,102 @@ void Attachment::dropDatabase(CheckStatusWrapper* status)
|
||||
}
|
||||
|
||||
|
||||
SLONG Attachment::getSingleInfo(CheckStatusWrapper* status, UCHAR infoItem)
|
||||
{
|
||||
UCHAR buff[16];
|
||||
|
||||
getInfo(status, 1, &infoItem, sizeof(buff), buff);
|
||||
if (status->getState() & IStatus::STATE_ERRORS)
|
||||
return 0;
|
||||
|
||||
const UCHAR* p = buff;
|
||||
const UCHAR* const end = buff + sizeof(buff);
|
||||
UCHAR item;
|
||||
while ((item = *p++) != isc_info_end && p < end - 1)
|
||||
{
|
||||
const SLONG length = gds__vax_integer(p, 2);
|
||||
p += 2;
|
||||
|
||||
if (item == infoItem)
|
||||
return gds__vax_integer(p, (SSHORT)length);
|
||||
|
||||
fb_assert(false);
|
||||
|
||||
p += length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void Attachment::execWithCheck(CheckStatusWrapper* status, const string& stmt)
|
||||
{
|
||||
/**************************************
|
||||
*
|
||||
* Used to execute "SET xxx TIMEOUT" statements. Checks for protocol version
|
||||
* and convert expected SQL error into isc_wish_list error. The only possible
|
||||
* case is when modern network server works with legacy engine.
|
||||
*
|
||||
**************************************/
|
||||
if (rdb->rdb_port->port_protocol >= PROTOCOL_VERSION15)
|
||||
{
|
||||
execute(status, NULL, stmt.length(), stmt.c_str(), SQL_DIALECT_CURRENT, NULL, NULL, NULL, NULL);
|
||||
|
||||
if (!(status->getState() & IStatus::STATE_ERRORS))
|
||||
return;
|
||||
|
||||
// handle isc_dsql_token_unk_err
|
||||
const ISC_STATUS* errs = status->getErrors();
|
||||
|
||||
if (!fb_utils::containsErrorCode(errs, isc_sqlerr) ||
|
||||
!fb_utils::containsErrorCode(errs, isc_dsql_token_unk_err))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
status->init();
|
||||
}
|
||||
status->setErrors(Arg::Gds(isc_wish_list).value());
|
||||
}
|
||||
|
||||
|
||||
unsigned int Attachment::getIdleTimeout(CheckStatusWrapper* status)
|
||||
{
|
||||
if (rdb->rdb_port->port_protocol >= PROTOCOL_VERSION15)
|
||||
return getSingleInfo(status, fb_info_ses_idle_timeout_att);
|
||||
|
||||
status->setErrors(Arg::Gds(isc_wish_list).value());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void Attachment::setIdleTimeout(CheckStatusWrapper* status, unsigned int timeOut)
|
||||
{
|
||||
string stmt;
|
||||
stmt.printf("SET SESSION IDLE TIMEOUT %lu", timeOut);
|
||||
|
||||
execWithCheck(status, stmt);
|
||||
}
|
||||
|
||||
|
||||
unsigned int Attachment::getStatementTimeout(CheckStatusWrapper* status)
|
||||
{
|
||||
if (rdb->rdb_port->port_protocol >= PROTOCOL_VERSION15)
|
||||
return getSingleInfo(status, fb_info_statement_timeout_att);
|
||||
|
||||
status->setErrors(Arg::Gds(isc_wish_list).value());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void Attachment::setStatementTimeout(CheckStatusWrapper* status, unsigned int timeOut)
|
||||
{
|
||||
string stmt;
|
||||
stmt.printf("SET STATEMENT TIMEOUT %lu", timeOut);
|
||||
|
||||
execWithCheck(status, stmt);
|
||||
}
|
||||
|
||||
|
||||
Firebird::ITransaction* Statement::execute(CheckStatusWrapper* status, Firebird::ITransaction* apiTra,
|
||||
IMessageMetadata* inMetadata, void* inBuffer, IMessageMetadata* outMetadata, void* outBuffer)
|
||||
{
|
||||
@ -1857,6 +1982,7 @@ Firebird::ITransaction* Statement::execute(CheckStatusWrapper* status, Firebird:
|
||||
sqldata->p_sqldata_out_blr.cstr_length = out_blr_length;
|
||||
sqldata->p_sqldata_out_blr.cstr_address = const_cast<UCHAR*>(out_blr);
|
||||
sqldata->p_sqldata_out_message_number = 0; // out_msg_type
|
||||
sqldata->p_sqldata_timeout = statement->rsr_timeout;
|
||||
|
||||
send_packet(port, packet);
|
||||
|
||||
@ -2021,6 +2147,7 @@ ResultSet* Statement::openCursor(CheckStatusWrapper* status, Firebird::ITransact
|
||||
sqldata->p_sqldata_out_blr.cstr_length = out_blr_length;
|
||||
sqldata->p_sqldata_out_blr.cstr_address = const_cast<UCHAR*>(out_blr);
|
||||
sqldata->p_sqldata_out_message_number = 0; // out_msg_type
|
||||
sqldata->p_sqldata_timeout = statement->rsr_timeout;
|
||||
|
||||
send_partial_packet(port, packet);
|
||||
defer_packet(port, packet, true);
|
||||
|
@ -305,7 +305,8 @@ rem_port* XNET_analyze(ClntAuthBlock* cBlock,
|
||||
REMOTE_PROTOCOL(PROTOCOL_VERSION11, ptype_batch_send, 2),
|
||||
REMOTE_PROTOCOL(PROTOCOL_VERSION12, ptype_batch_send, 3),
|
||||
REMOTE_PROTOCOL(PROTOCOL_VERSION13, ptype_batch_send, 4),
|
||||
REMOTE_PROTOCOL(PROTOCOL_VERSION14, ptype_batch_send, 5)
|
||||
REMOTE_PROTOCOL(PROTOCOL_VERSION14, ptype_batch_send, 5),
|
||||
REMOTE_PROTOCOL(PROTOCOL_VERSION15, ptype_batch_send, 6)
|
||||
};
|
||||
fb_assert(FB_NELEM(protocols_to_try) <= FB_NELEM(cnct->p_cnct_versions));
|
||||
cnct->p_cnct_count = FB_NELEM(protocols_to_try);
|
||||
|
@ -646,6 +646,11 @@ bool_t xdr_protocol(XDR* xdrs, PACKET* p)
|
||||
}
|
||||
MAP(xdr_short, reinterpret_cast<SSHORT&>(sqldata->p_sqldata_out_message_number));
|
||||
}
|
||||
{ // scope
|
||||
rem_port* port = (rem_port*)xdrs->x_public;
|
||||
if (port->port_protocol >= PROTOCOL_VERSION15)
|
||||
MAP(xdr_u_long, sqldata->p_sqldata_timeout);
|
||||
}
|
||||
DEBUG_PRINTSIZE(xdrs, p->p_operation);
|
||||
return P_TRUE(xdrs, p);
|
||||
|
||||
|
@ -84,6 +84,7 @@ const USHORT PROTOCOL_VERSION14 = (FB_PROTOCOL_FLAG | 14);
|
||||
|
||||
// Protocol 15:
|
||||
// - supports crypt key callback at connect phaze
|
||||
// - supports statement timeouts
|
||||
|
||||
const USHORT PROTOCOL_VERSION15 = (FB_PROTOCOL_FLAG | 15);
|
||||
|
||||
@ -577,6 +578,7 @@ typedef struct p_sqldata
|
||||
CSTRING p_sqldata_out_blr; // blr describing output message
|
||||
USHORT p_sqldata_out_message_number;
|
||||
ULONG p_sqldata_status; // final eof status
|
||||
ULONG p_sqldata_timeout; // statement timeout
|
||||
} P_SQLDATA;
|
||||
|
||||
typedef struct p_sqlfree
|
||||
|
@ -476,6 +476,7 @@ struct Rsr : public Firebird::GlobalStorage, public TypedHandle<rem_type_rsr>
|
||||
|
||||
Firebird::string rsr_cursor_name; // Name for cursor to be set on open
|
||||
bool rsr_delayed_format; // Out format was delayed on execute, set it on fetch
|
||||
unsigned int rsr_timeout; // Statement timeout to be set on open\execute
|
||||
Rsr** rsr_self;
|
||||
|
||||
public:
|
||||
@ -498,7 +499,7 @@ public:
|
||||
rsr_format(0), rsr_message(0), rsr_buffer(0), rsr_status(0),
|
||||
rsr_id(0), rsr_fmt_length(0),
|
||||
rsr_rows_pending(0), rsr_msgs_waiting(0), rsr_reorder_level(0), rsr_batch_count(0),
|
||||
rsr_cursor_name(getPool()), rsr_delayed_format(false), rsr_self(NULL)
|
||||
rsr_cursor_name(getPool()), rsr_delayed_format(false), rsr_timeout(0), rsr_self(NULL)
|
||||
{ }
|
||||
|
||||
~Rsr()
|
||||
|
@ -3383,6 +3383,9 @@ ISC_STATUS rem_port::execute_statement(P_OP op, P_SQLDATA* sqldata, PACKET* send
|
||||
unsigned flags = statement->rsr_iface->getFlags(&status_vector);
|
||||
check(&status_vector);
|
||||
|
||||
statement->rsr_iface->setTimeout(&status_vector, sqldata->p_sqldata_timeout);
|
||||
check(&status_vector);
|
||||
|
||||
if ((flags & IStatement::FLAG_HAS_CURSOR) && (out_msg_length == 0))
|
||||
{
|
||||
statement->rsr_cursor =
|
||||
|
@ -379,6 +379,9 @@ public:
|
||||
void free(Firebird::CheckStatusWrapper* status);
|
||||
unsigned getFlags(Firebird::CheckStatusWrapper* status);
|
||||
|
||||
unsigned int getTimeout(Firebird::CheckStatusWrapper* status);
|
||||
void setTimeout(Firebird::CheckStatusWrapper* status, unsigned int timeOut);
|
||||
|
||||
public:
|
||||
Firebird::Mutex statementMutex;
|
||||
YAttachment* attachment;
|
||||
@ -470,6 +473,11 @@ public:
|
||||
Firebird::IMessageMetadata* inMetadata, void* inBuffer,
|
||||
Firebird::IMessageMetadata* outMetadata, void* outBuffer);
|
||||
|
||||
unsigned int getIdleTimeout(Firebird::CheckStatusWrapper* status);
|
||||
void setIdleTimeout(Firebird::CheckStatusWrapper* status, unsigned int timeOut);
|
||||
unsigned int getStatementTimeout(Firebird::CheckStatusWrapper* status);
|
||||
void setStatementTimeout(Firebird::CheckStatusWrapper* status, unsigned int timeOut);
|
||||
|
||||
public:
|
||||
Firebird::IProvider* provider;
|
||||
Firebird::PathName dbPath;
|
||||
|
@ -221,6 +221,7 @@ static const TOK tokens[] =
|
||||
{TOK_HAVING, "HAVING", false},
|
||||
{TOK_HOUR, "HOUR", false},
|
||||
{TOK_IDENTITY, "IDENTITY", true},
|
||||
{TOK_IDLE, "IDLE", true},
|
||||
{TOK_IF, "IF", true},
|
||||
{TOK_IGNORE, "IGNORE", true},
|
||||
{TOK_IIF, "IIF", true},
|
||||
@ -394,6 +395,7 @@ static const TOK tokens[] =
|
||||
{TOK_SENSITIVE, "SENSITIVE", false},
|
||||
{TOK_SEQUENCE, "SEQUENCE", true},
|
||||
{TOK_SERVERWIDE, "SERVERWIDE", true},
|
||||
{TOK_SESSION, "SESSION", true},
|
||||
{TOK_SET, "SET", false},
|
||||
{TOK_SHADOW, "SHADOW", true},
|
||||
{TOK_SHARED, "SHARED", true},
|
||||
|
@ -2661,6 +2661,29 @@ ISC_STATUS API_ROUTINE isc_dsql_set_cursor_name(ISC_STATUS* userStatus, FB_API_H
|
||||
}
|
||||
|
||||
|
||||
// Set statement timeout.
|
||||
ISC_STATUS API_ROUTINE fb_dsql_set_timeout(ISC_STATUS* userStatus, FB_API_HANDLE* stmtHandle,
|
||||
ULONG timeout)
|
||||
{
|
||||
StatusVector status(userStatus);
|
||||
CheckStatusWrapper statusWrapper(&status);
|
||||
|
||||
try
|
||||
{
|
||||
RefPtr<IscStatement> statement(translateHandle(statements, stmtHandle));
|
||||
|
||||
if (statement->statement)
|
||||
statement->statement->setTimeout(&statusWrapper, timeout);
|
||||
}
|
||||
catch (const Exception& e)
|
||||
{
|
||||
e.stuffException(&statusWrapper);
|
||||
}
|
||||
|
||||
return status[1];
|
||||
}
|
||||
|
||||
|
||||
// Provide information on sql statement.
|
||||
ISC_STATUS API_ROUTINE isc_dsql_sql_info(ISC_STATUS* userStatus, FB_API_HANDLE* stmtHandle,
|
||||
SSHORT itemLength, const SCHAR* items, SSHORT bufferLength, SCHAR* buffer)
|
||||
@ -4464,6 +4487,36 @@ FB_BOOLEAN IscStatement::fetch(CheckStatusWrapper* status, IMessageMetadata* out
|
||||
return statement->cursor->fetchNext(status, outBuffer) == IStatus::RESULT_OK;
|
||||
}
|
||||
|
||||
unsigned int YStatement::getTimeout(CheckStatusWrapper* status)
|
||||
{
|
||||
try
|
||||
{
|
||||
YEntry<YStatement> entry(status, this);
|
||||
return entry.next()->getTimeout(status);
|
||||
}
|
||||
catch (const Exception& e)
|
||||
{
|
||||
e.stuffException(status);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void YStatement::setTimeout(CheckStatusWrapper* status, unsigned int timeOut)
|
||||
{
|
||||
try
|
||||
{
|
||||
YEntry<YStatement> entry(status, this);
|
||||
entry.next()->setTimeout(status, timeOut);
|
||||
}
|
||||
catch (const Exception& e)
|
||||
{
|
||||
e.stuffException(status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
|
||||
@ -5539,6 +5592,66 @@ void YAttachment::getNextTransaction(CheckStatusWrapper* status, ITransaction* t
|
||||
}
|
||||
|
||||
|
||||
unsigned int YAttachment::getIdleTimeout(CheckStatusWrapper* status)
|
||||
{
|
||||
try
|
||||
{
|
||||
YEntry<YAttachment> entry(status, this);
|
||||
return entry.next()->getIdleTimeout(status);
|
||||
}
|
||||
catch (const Exception& e)
|
||||
{
|
||||
e.stuffException(status);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void YAttachment::setIdleTimeout(CheckStatusWrapper* status, unsigned int timeOut)
|
||||
{
|
||||
try
|
||||
{
|
||||
YEntry<YAttachment> entry(status, this);
|
||||
entry.next()->setIdleTimeout(status, timeOut);
|
||||
}
|
||||
catch (const Exception& e)
|
||||
{
|
||||
e.stuffException(status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned int YAttachment::getStatementTimeout(CheckStatusWrapper* status)
|
||||
{
|
||||
try
|
||||
{
|
||||
YEntry<YAttachment> entry(status, this);
|
||||
return entry.next()->getStatementTimeout(status);
|
||||
}
|
||||
catch (const Exception& e)
|
||||
{
|
||||
e.stuffException(status);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void YAttachment::setStatementTimeout(CheckStatusWrapper* status, unsigned int timeOut)
|
||||
{
|
||||
try
|
||||
{
|
||||
YEntry<YAttachment> entry(status, this);
|
||||
entry.next()->setStatementTimeout(status, timeOut);
|
||||
}
|
||||
catch (const Exception& e)
|
||||
{
|
||||
e.stuffException(status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
|
||||
|
@ -89,6 +89,7 @@ ISC_STATUS API_ROUTINE isc_dsql_prepare_m(ISC_STATUS*, FB_API_HANDLE*,
|
||||
SCHAR*);
|
||||
ISC_STATUS API_ROUTINE isc_dsql_set_cursor_name(ISC_STATUS*, FB_API_HANDLE*,
|
||||
const SCHAR*, USHORT);
|
||||
ISC_STATUS API_ROUTINE fb_dsql_set_timeout(ISC_STATUS*, FB_API_HANDLE*, ULONG timeout);
|
||||
ISC_STATUS API_ROUTINE isc_dsql_sql_info(ISC_STATUS*, FB_API_HANDLE*, SSHORT,
|
||||
const SCHAR*, SSHORT, SCHAR*);
|
||||
//ISC_STATUS API_ROUTINE isc_prepare_transaction2(ISC_STATUS*, FB_API_HANDLE*, USHORT,
|
||||
|
Loading…
Reference in New Issue
Block a user