Timeouts for running SQL statements. Author: Vlad Khorsun 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 16): - "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 [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 It allows to set statement execution timeout (in milliseconds) for the next statement. After statement execution it automatically reset to zero.