diff --git a/doc/sql.extensions/README.builtin_functions.txt b/doc/sql.extensions/README.builtin_functions.txt index 7829f2bbcc..599d2e01b8 100644 --- a/doc/sql.extensions/README.builtin_functions.txt +++ b/doc/sql.extensions/README.builtin_functions.txt @@ -733,6 +733,37 @@ Example: select * from x order by rand(); +-------------------- +RDB$GET_TRANSACTION_CN +-------------------- + +(FB4 extension) +Function: + Returns commit number of given transaction. + Note, engine internally uses unsigned 8-bit integer for commit numbers, + while SQL language have no unsigned integers, thus one should be ready + to see negative numbers here (it is possible only if engine commits more + than 2^32 transactions since last database start, as global commit number + is reset at each restart). + + There are some "special" values used for non-committed transactions and + transactions, committed before database was started: + + 0 - transaction is active, + 1 - transaction committed before database started + -2 - transaction is dead (rolled back) + -1 - transaction is in limbo + + See also README.read_consistency.md + +Format: + RDB$SYSTEM_PRIVILEGE( ) + +Examples: + select rdb$get_transaction_cn(current_transaction) from rdb$database; + select rdb$get_transaction_cn(123) from rdb$database; + + -------------------- RDB$SYSTEM_PRIVILEGE -------------------- diff --git a/doc/sql.extensions/README.context_variables2 b/doc/sql.extensions/README.context_variables2 index 32a1e844e8..065da87346 100644 --- a/doc/sql.extensions/README.context_variables2 +++ b/doc/sql.extensions/README.context_variables2 @@ -102,6 +102,13 @@ Usage: | same as of CURRENT_ROLE pseudo-variable | ENGINE_VERSION | Engine version number, e.g. "2.1.0" (since V2.1) + | + GLOBAL_CN | Most current value of global Commit Number counter + | + SNAPSHOT_CN | Value of Commit Number of currently database snapshot: either + | transaction level (for SNAPSHOT or CONSISTENCY transaction), + | or request level (for READ COMMITTED READ CONSISTENCY + | transaction). NULL, if snapshot is not exist. Notes: To prevent DoS attacks against Firebird Server you are not allowed to have diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 7dd0bba02d..5a6eef38d5 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -630,6 +630,7 @@ using namespace Firebird; %token VARBINARY %token WINDOW %token CONSISTENCY +%token RDB_GET_TRANSACTION_CN // external connections pool management %token CONNECTIONS @@ -7787,6 +7788,7 @@ system_function_std_syntax | POWER | RAND | RDB_GET_CONTEXT + | RDB_GET_TRANSACTION_CN | RDB_ROLE_IN_USE | RDB_SET_CONTEXT | REPLACE diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index 60b7a5209e..1e67d4cfb2 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -46,6 +46,7 @@ #include "../jrd/mov_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/tra_proto.h" +#include "../jrd/tpc_proto.h" #include "../jrd/scl_proto.h" #include "../common/os/guid.h" #include "../jrd/license.h" @@ -169,6 +170,7 @@ void setParamsDblDec(DataTypeUtilBase* dataTypeUtil, const SysFunction* function void setParamsDecFloat(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); void setParamsFromList(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); void setParamsInteger(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); +void setParamsInt64(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); void setParamsSecondInteger(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); // specific setParams functions @@ -235,6 +237,7 @@ dsc* evlFloor(thread_db* tdbb, const SysFunction* function, const NestValueArray dsc* evlGenUuid(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlGetContext(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlSetContext(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); +dsc* evlGetTranCN(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlHash(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlLeft(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlLnLog10(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); @@ -276,6 +279,7 @@ const char // SYSTEM namespace: global and database wise items ENGINE_VERSION[] = "ENGINE_VERSION", DATABASE_NAME[] = "DB_NAME", + GLOBAL_CN_NAME[] = "GLOBAL_CN", EXT_CONN_POOL_SIZE[] = "EXT_CONN_POOL_SIZE", EXT_CONN_POOL_IDLE[] = "EXT_CONN_POOL_IDLE_COUNT", EXT_CONN_POOL_ACTIVE[] = "EXT_CONN_POOL_ACTIVE_COUNT", @@ -298,6 +302,7 @@ const char ISOLATION_LEVEL_NAME[] = "ISOLATION_LEVEL", LOCK_TIMEOUT_NAME[] = "LOCK_TIMEOUT", READ_ONLY_NAME[] = "READ_ONLY", + SNAPSHOT_CN_NAME[] = "SNAPSHOT_CN", // DDL_TRIGGER namespace DDL_EVENT_NAME[] = "DDL_EVENT", EVENT_TYPE_NAME[] = "EVENT_TYPE", @@ -473,6 +478,16 @@ void setParamsInteger(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc* } +void setParamsInt64(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args) +{ + for (int i = 0; i < argsCount; ++i) + { + if (args[i]->isUnknown()) + args[i]->makeInt64(0); + } +} + + void setParamsSecondInteger(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args) { if (argsCount >= 2) @@ -2685,6 +2700,24 @@ dsc* evlGetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar resultStr.printf("%" SLONGFORMAT, transaction->tra_lock_timeout); else if (nameStr == READ_ONLY_NAME) resultStr = (transaction->tra_flags & TRA_readonly) ? TRUE_VALUE : FALSE_VALUE; + else if (nameStr == GLOBAL_CN_NAME) + resultStr.printf("%" SQUADFORMAT, dbb->dbb_tip_cache->getGlobalCommitNumber()); + else if (nameStr == SNAPSHOT_CN_NAME) + { + if (!(transaction->tra_flags & TRA_read_committed)) + resultStr.printf("%" SQUADFORMAT, transaction->tra_snapshot_number); + else if ((transaction->tra_flags & TRA_read_committed) && + (transaction->tra_flags & TRA_read_consistency)) + { + jrd_req* snapshot_req = request->req_snapshot.m_owner; + if (snapshot_req) + resultStr.printf("%" SQUADFORMAT, snapshot_req->req_snapshot.m_number); + else + return NULL; + } + else + return NULL; + } else if (nameStr == EXT_CONN_POOL_SIZE) resultStr.printf("%d", EDS::Manager::getConnPool()->getMaxCount()); else if (nameStr == EXT_CONN_POOL_IDLE) @@ -2890,6 +2923,38 @@ dsc* evlSetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar } +dsc* evlGetTranCN(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, + impure_value* impure) +{ + fb_assert(args.getCount() == 1); + + Database* dbb = tdbb->getDatabase(); + jrd_req* request = tdbb->getRequest(); + + request->req_flags &= ~req_null; + const dsc* value = EVL_expr(tdbb, request, args[0]); + if (request->req_flags & req_null) + return NULL; + + TraNumber traNum = MOV_get_int64(tdbb, value, 0); + if (traNum > dbb->dbb_next_transaction) + { + request->req_flags |= req_null; + return NULL; + } + + CommitNumber cn = dbb->dbb_tip_cache->snapshotState(tdbb, traNum); + + dsc result; + result.makeInt64(0, (SINT64*)&cn); + + EVL_make_value(tdbb, &result, impure); + + request->req_flags &= ~req_null; + return &impure->vlu_desc; +} + + dsc* evlHash(thread_db* tdbb, const SysFunction*, const NestValueArray& args, impure_value* impure) { @@ -4565,6 +4630,7 @@ const SysFunction SysFunction::functions[] = {"QUANTIZE", 2, 2, setParamsDecFloat, makeDecFloatResult, evlQuantize, NULL}, {"RAND", 0, 0, NULL, makeDoubleResult, evlRand, NULL}, {RDB_GET_CONTEXT, 2, 2, setParamsGetSetContext, makeGetSetContext, evlGetContext, NULL}, + {"RDB$GET_TRANSACTION_CN", 1, 1, setParamsInt64, makeInt64Result, evlGetTranCN, NULL}, {"RDB$ROLE_IN_USE", 1, 1, setParamsAsciiVal, makeBooleanResult, evlRoleInUse, NULL}, {RDB_SET_CONTEXT, 3, 3, setParamsGetSetContext, makeGetSetContext, evlSetContext, NULL}, {"RDB$SYSTEM_PRIVILEGE", 1, 1, NULL, makeBooleanResult, evlSystemPrivilege, NULL}, diff --git a/src/jrd/tpc_proto.h b/src/jrd/tpc_proto.h index fd03a335f5..5881307571 100644 --- a/src/jrd/tpc_proto.h +++ b/src/jrd/tpc_proto.h @@ -140,6 +140,11 @@ public: //void assignLatestTransactionId(TraNumber number); void assignLatestAttachmentId(AttNumber number); + CommitNumber getGlobalCommitNumber() const + { + return m_tpcHeader->getHeader()->latest_commit_number.load(std::memory_order_acquire); + } + private: class GlobalTpcHeader : public Firebird::MemoryHeader { diff --git a/src/yvalve/keywords.cpp b/src/yvalve/keywords.cpp index a73e36fa36..b0f019631c 100644 --- a/src/yvalve/keywords.cpp +++ b/src/yvalve/keywords.cpp @@ -355,6 +355,7 @@ static const TOK tokens[] = {TOK_DB_KEY, "RDB$DB_KEY", false}, {TOK_RDB_ERROR, "RDB$ERROR", false}, {TOK_RDB_GET_CONTEXT, "RDB$GET_CONTEXT", false}, + {TOK_RDB_GET_TRANSACTION_CN, "RDB$GET_TRANSACTION_CN", false}, {TOK_RDB_RECORD_VERSION, "RDB$RECORD_VERSION", false}, {TOK_RDB_ROLE_IN_USE, "RDB$ROLE_IN_USE", false}, {TOK_RDB_SET_CONTEXT, "RDB$SET_CONTEXT", false},