mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 12:43:02 +01:00
Make it possible to start multiple transactions using the same initial transaction snapshot - CORE-6018 (#193)
With this feature it's possible to create parallel (via different attachments) processes reading consistent data from a database. For example, a backup process may create multiple threads paralleling read data from the database. Also a web service may dispatch distributed sub services paralleling doing some processing. That is accomplished creating a transaction with SET TRANSACTION SNAPSHOT [ AT NUMBER <snapshot number> ] or isc_tpb_at_snapshot_number. The <snapshot number> from the first transaction may be obtained with RDB$GET_CONTEXT('SYSTEM', 'SNAPSHOT_NUMBER') or transaction info call with fb_info_tra_snapshot_number. Also added CORE-6017 - Add transaction info fb_info_tra_snapshot_number.
This commit is contained in:
parent
05b5d16c44
commit
cfbcbeda3c
17
doc/README.transaction_at_snapshot.md
Normal file
17
doc/README.transaction_at_snapshot.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Transaction at defined snapshot number
|
||||
|
||||
With this feature it's possible to create parallel (via different attachments) processes reading consistent data from a database.
|
||||
|
||||
For example, a backup process may create multiple threads paralleling reading data from the database.
|
||||
|
||||
Or a web service may dispatch distributed sub services paralleling doing some processing.
|
||||
|
||||
That is accomplished creating a transaction with `SET TRANSACTION SNAPSHOT [ AT NUMBER <snapshot number> ]` or `isc_tpb_at_snapshot_number <snapshot number length> <snapshot number>`.
|
||||
|
||||
The `snapshot number` from the first transaction may be obtained with `RDB$GET_CONTEXT('SYSTEM', 'SNAPSHOT_NUMBER')` or transaction info call with `fb_info_tra_snapshot_number`.
|
||||
|
||||
Note that the `snapshot number` passed to the new transaction must be a snapshot of an active transaction.
|
||||
|
||||
|
||||
Author:
|
||||
Adriano dos Santos Fernandes <adrianosf at gmail.com>
|
@ -330,6 +330,7 @@ Firebird 4.0
|
||||
NATIVE *
|
||||
NORMALIZE_DECFLOAT *
|
||||
NTILE (1)
|
||||
NUMBER
|
||||
OTHERS
|
||||
OVERRIDING
|
||||
PERCENT_RANK (1)
|
||||
|
@ -1914,6 +1914,8 @@ C --
|
||||
PARAMETER (GDS__tom_chacha_key = 335545250)
|
||||
INTEGER*4 GDS__bad_repl_handle
|
||||
PARAMETER (GDS__bad_repl_handle = 335545251)
|
||||
INTEGER*4 GDS__tra_snapshot_does_not_exist
|
||||
PARAMETER (GDS__tra_snapshot_does_not_exist = 335545252)
|
||||
INTEGER*4 GDS__gfix_db_name
|
||||
PARAMETER (GDS__gfix_db_name = 335740929)
|
||||
INTEGER*4 GDS__gfix_invalid_sw
|
||||
|
@ -1909,6 +1909,8 @@ const
|
||||
gds_tom_chacha_key = 335545250;
|
||||
isc_bad_repl_handle = 335545251;
|
||||
gds_bad_repl_handle = 335545251;
|
||||
isc_tra_snapshot_does_not_exist = 335545252;
|
||||
gds_tra_snapshot_does_not_exist = 335545252;
|
||||
isc_gfix_db_name = 335740929;
|
||||
gds_gfix_db_name = 335740929;
|
||||
isc_gfix_invalid_sw = 335740930;
|
||||
|
@ -64,6 +64,12 @@ public:
|
||||
appendUShort(val >> 16);
|
||||
}
|
||||
|
||||
void appendUInt64(FB_UINT64 val)
|
||||
{
|
||||
appendULong(val);
|
||||
appendULong(val >> 32);
|
||||
}
|
||||
|
||||
void appendUCharRepeated(UCHAR byte, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
|
@ -600,7 +600,7 @@ int Parser::yylexAux()
|
||||
*
|
||||
* This code recognizes the following token types:
|
||||
*
|
||||
* NUMBER: string of digits which fits into a 32-bit integer
|
||||
* NUMBER32BIT: string of digits which fits into a 32-bit integer
|
||||
*
|
||||
* NUMBER64BIT: string of digits whose value might fit into an SINT64,
|
||||
* depending on whether or not there is a preceding '-', which is to
|
||||
@ -792,7 +792,7 @@ int Parser::yylexAux()
|
||||
// by a set of nibbles, using 0-9, a-f, or A-F. Odd numbers
|
||||
// of nibbles assume a leading '0'. The result is converted
|
||||
// to an integer, and the result returned to the caller. The
|
||||
// token is identified as a NUMBER if it's a 32-bit or less
|
||||
// token is identified as a NUMBER32BIT if it's a 32-bit or less
|
||||
// value, or a NUMBER64INT if it requires a 64-bit number.
|
||||
if (c == '0' && lex.ptr + 1 < lex.end && (*lex.ptr == 'x' || *lex.ptr == 'X') &&
|
||||
(classes(lex.ptr[1]) & CHR_HEX))
|
||||
@ -833,7 +833,7 @@ int Parser::yylexAux()
|
||||
}
|
||||
|
||||
// we have a valid hex token. Now give it back, either as
|
||||
// an NUMBER or NUMBER64BIT.
|
||||
// an NUMBER32BIT or NUMBER64BIT.
|
||||
if (!hexerror)
|
||||
{
|
||||
// if charlen > 8 (something like FFFF FFFF 0, w/o the spaces)
|
||||
@ -888,7 +888,7 @@ int Parser::yylexAux()
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have an integer value. we'll return NUMBER.
|
||||
// we have an integer value. we'll return NUMBER32BIT.
|
||||
// but we have to make a number value to be compatible
|
||||
// with existing code.
|
||||
|
||||
@ -933,7 +933,7 @@ int Parser::yylexAux()
|
||||
}
|
||||
|
||||
yylval.int32Val = (SLONG) value;
|
||||
return TOK_NUMBER;
|
||||
return TOK_NUMBER32BIT;
|
||||
} // integer value
|
||||
} // if (!hexerror)...
|
||||
|
||||
@ -1111,7 +1111,7 @@ int Parser::yylexAux()
|
||||
{
|
||||
yylval.int32Val = (SLONG) number;
|
||||
//printf ("parse.y %p %d\n", yylval.legacyStr, number);
|
||||
return TOK_NUMBER;
|
||||
return TOK_NUMBER32BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -8191,6 +8191,14 @@ SetTransactionNode* SetTransactionNode::dsqlPass(DsqlCompilerScratch* dsqlScratc
|
||||
for (RestrictionOption** i = reserveList.begin(); i != reserveList.end(); ++i)
|
||||
genTableLock(dsqlScratch, **i, lockLevel);
|
||||
|
||||
if (atSnapshotNumber.specified)
|
||||
{
|
||||
dsqlScratch->appendUChar(isc_tpb_at_snapshot_number);
|
||||
static_assert(sizeof(CommitNumber) == sizeof(FB_UINT64), "sizeof(CommitNumber) == sizeof(FB_UINT64)");
|
||||
dsqlScratch->appendUChar(sizeof(CommitNumber));
|
||||
dsqlScratch->appendUInt64(atSnapshotNumber.value);
|
||||
}
|
||||
|
||||
if (dsqlScratch->getBlrData().getCount() > 1) // 1 -> isc_tpb_version1
|
||||
tpb.add(dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount());
|
||||
|
||||
|
@ -1619,6 +1619,7 @@ public:
|
||||
Nullable<USHORT> lockTimeout;
|
||||
Firebird::Array<RestrictionOption*> reserveList;
|
||||
Firebird::UCharBuffer tpb;
|
||||
Nullable<CommitNumber> atSnapshotNumber;
|
||||
};
|
||||
|
||||
|
||||
|
@ -1 +1 @@
|
||||
56 shift/reduce conflicts, 17 reduce/reduce conflicts.
|
||||
58 shift/reduce conflicts, 17 reduce/reduce conflicts.
|
||||
|
@ -335,7 +335,7 @@ using namespace Firebird;
|
||||
%token <stringPtr> FLOAT_NUMBER DECIMAL_NUMBER LIMIT64_INT
|
||||
%token <lim64ptr> LIMIT64_NUMBER
|
||||
%token <metaNamePtr> SYMBOL
|
||||
%token <int32Val> NUMBER
|
||||
%token <int32Val> NUMBER32BIT
|
||||
|
||||
%token <intlStringPtr> STRING
|
||||
%token <metaNamePtr> INTRODUCER
|
||||
@ -5358,7 +5358,7 @@ tran_option($setTransactionNode)
|
||||
| NO WAIT
|
||||
{ setClause($setTransactionNode->wait, "[NO] WAIT", false); }
|
||||
// isolation mode
|
||||
| isolation_mode
|
||||
| isolation_mode($setTransactionNode)
|
||||
{ setClause($setTransactionNode->isoLevel, "ISOLATION LEVEL", $1); }
|
||||
// misc options
|
||||
| NO AUTO UNDO
|
||||
@ -5378,24 +5378,38 @@ tran_option($setTransactionNode)
|
||||
restr_list($setTransactionNode)
|
||||
;
|
||||
|
||||
%type <uintVal> isolation_mode
|
||||
isolation_mode
|
||||
: ISOLATION LEVEL iso_mode { $$ = $3;}
|
||||
%type <uintVal> isolation_mode(<setTransactionNode>)
|
||||
isolation_mode($setTransactionNode)
|
||||
: ISOLATION LEVEL iso_mode($setTransactionNode) { $$ = $3;}
|
||||
| iso_mode
|
||||
;
|
||||
|
||||
%type <uintVal> iso_mode
|
||||
iso_mode
|
||||
: snap_shot
|
||||
%type <uintVal> iso_mode(<setTransactionNode>)
|
||||
iso_mode($setTransactionNode)
|
||||
: snap_shot($setTransactionNode) { $$ = $1; }
|
||||
| READ UNCOMMITTED version_mode { $$ = $3; }
|
||||
| READ COMMITTED version_mode { $$ = $3; }
|
||||
;
|
||||
|
||||
%type <uintVal> snap_shot
|
||||
snap_shot
|
||||
: SNAPSHOT { $$ = SetTransactionNode::ISO_LEVEL_CONCURRENCY; }
|
||||
| SNAPSHOT TABLE { $$ = SetTransactionNode::ISO_LEVEL_CONSISTENCY; }
|
||||
| SNAPSHOT TABLE STABILITY { $$ = SetTransactionNode::ISO_LEVEL_CONSISTENCY; }
|
||||
%type <uintVal> snap_shot(<setTransactionNode>)
|
||||
snap_shot($setTransactionNode)
|
||||
: SNAPSHOT
|
||||
{ $$ = SetTransactionNode::ISO_LEVEL_CONCURRENCY; }
|
||||
| SNAPSHOT AT NUMBER snapshot_number
|
||||
{
|
||||
setClause($setTransactionNode->atSnapshotNumber, "SNAPSHOT AT NUMBER", (CommitNumber) $4);
|
||||
$$ = SetTransactionNode::ISO_LEVEL_CONCURRENCY;
|
||||
}
|
||||
| SNAPSHOT TABLE
|
||||
{ $$ = SetTransactionNode::ISO_LEVEL_CONSISTENCY; }
|
||||
| SNAPSHOT TABLE STABILITY
|
||||
{ $$ = SetTransactionNode::ISO_LEVEL_CONSISTENCY; }
|
||||
;
|
||||
|
||||
%type <int64Val> snapshot_number
|
||||
snapshot_number
|
||||
: NUMBER32BIT { $$ = $1; }
|
||||
| NUMBER64BIT { $$ = $1.number; }
|
||||
;
|
||||
|
||||
%type <uintVal> version_mode
|
||||
@ -7371,7 +7385,7 @@ u_numeric_constant
|
||||
|
||||
%type <valueExprNode> ul_numeric_constant
|
||||
ul_numeric_constant
|
||||
: NUMBER
|
||||
: NUMBER32BIT
|
||||
{ $$ = MAKE_const_slong($1); }
|
||||
| FLOAT_NUMBER
|
||||
{ $$ = MAKE_constant($1->c_str(), CONSTANT_DOUBLE); }
|
||||
@ -7511,7 +7525,7 @@ signed_short_integer
|
||||
|
||||
%type <int32Val> nonneg_short_integer
|
||||
nonneg_short_integer
|
||||
: NUMBER
|
||||
: NUMBER32BIT
|
||||
{
|
||||
if ($1 > SHRT_POS_MAX)
|
||||
yyabandon(YYPOSNARG(1), -842, isc_expec_short); // Short integer expected
|
||||
@ -7522,7 +7536,7 @@ nonneg_short_integer
|
||||
|
||||
%type <int32Val> neg_short_integer
|
||||
neg_short_integer
|
||||
: NUMBER
|
||||
: NUMBER32BIT
|
||||
{
|
||||
if ($1 > SHRT_NEG_MAX)
|
||||
yyabandon(YYPOSNARG(1), -842, isc_expec_short); // Short integer expected
|
||||
@ -7544,7 +7558,7 @@ pos_short_integer
|
||||
|
||||
%type <int32Val> unsigned_short_integer
|
||||
unsigned_short_integer
|
||||
: NUMBER
|
||||
: NUMBER32BIT
|
||||
{
|
||||
if ($1 > SHRT_UNSIGNED_MAX)
|
||||
yyabandon(YYPOSNARG(1), -842, isc_expec_ushort); // Unsigned short integer expected
|
||||
@ -7561,7 +7575,7 @@ signed_long_integer
|
||||
|
||||
%type <int32Val> long_integer
|
||||
long_integer
|
||||
: NUMBER { $$ = $1;}
|
||||
: NUMBER32BIT { $$ = $1;}
|
||||
;
|
||||
|
||||
|
||||
@ -8796,6 +8810,7 @@ non_reserved_word
|
||||
| NATIVE
|
||||
| NORMALIZE_DECFLOAT
|
||||
| NTILE
|
||||
| NUMBER
|
||||
| OLDEST
|
||||
| OTHERS
|
||||
| OVERRIDING
|
||||
|
@ -244,6 +244,7 @@
|
||||
#define isc_tpb_no_auto_undo 20
|
||||
#define isc_tpb_lock_timeout 21
|
||||
#define isc_tpb_read_consistency 22
|
||||
#define isc_tpb_at_snapshot_number 23
|
||||
|
||||
|
||||
/************************/
|
||||
|
@ -953,6 +953,7 @@ static const struct {
|
||||
{"tom_rsa_verify", 335545249},
|
||||
{"tom_chacha_key", 335545250},
|
||||
{"bad_repl_handle", 335545251},
|
||||
{"tra_snapshot_does_not_exist", 335545252},
|
||||
{"gfix_db_name", 335740929},
|
||||
{"gfix_invalid_sw", 335740930},
|
||||
{"gfix_incmp_sw", 335740932},
|
||||
|
@ -987,6 +987,7 @@ const ISC_STATUS isc_tom_rsa_sign = 335545248L;
|
||||
const ISC_STATUS isc_tom_rsa_verify = 335545249L;
|
||||
const ISC_STATUS isc_tom_chacha_key = 335545250L;
|
||||
const ISC_STATUS isc_bad_repl_handle = 335545251L;
|
||||
const ISC_STATUS isc_tra_snapshot_does_not_exist = 335545252L;
|
||||
const ISC_STATUS isc_gfix_db_name = 335740929L;
|
||||
const ISC_STATUS isc_gfix_invalid_sw = 335740930L;
|
||||
const ISC_STATUS isc_gfix_incmp_sw = 335740932L;
|
||||
@ -1461,7 +1462,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 = 1405;
|
||||
const ISC_STATUS isc_err_max = 1406;
|
||||
|
||||
#else /* c definitions */
|
||||
|
||||
@ -2418,6 +2419,7 @@ const ISC_STATUS isc_err_max = 1405;
|
||||
#define isc_tom_rsa_verify 335545249L
|
||||
#define isc_tom_chacha_key 335545250L
|
||||
#define isc_bad_repl_handle 335545251L
|
||||
#define isc_tra_snapshot_does_not_exist 335545252L
|
||||
#define isc_gfix_db_name 335740929L
|
||||
#define isc_gfix_invalid_sw 335740930L
|
||||
#define isc_gfix_incmp_sw 335740932L
|
||||
@ -2892,7 +2894,7 @@ const ISC_STATUS isc_err_max = 1405;
|
||||
#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 1405
|
||||
#define isc_err_max 1406
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -956,6 +956,7 @@ Data source : @4"}, /* eds_statement */
|
||||
{335545249, "Verifying RSA-signed data"}, /* tom_rsa_verify */
|
||||
{335545250, "Invalid key length @1, need 16 or 32"}, /* tom_chacha_key */
|
||||
{335545251, "invalid replicator handle"}, /* bad_repl_handle */
|
||||
{335545252, "Transaction's base snapshot number does not exist"}, /* tra_snapshot_does_not_exist */
|
||||
{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 */
|
||||
|
@ -952,6 +952,7 @@ static const struct {
|
||||
{335545249, -901}, /* 929 tom_rsa_verify */
|
||||
{335545250, -901}, /* 930 tom_chacha_key */
|
||||
{335545251, -901}, /* 931 bad_repl_handle */
|
||||
{335545252, -901}, /* 932 tra_snapshot_does_not_exist */
|
||||
{335740929, -901}, /* 1 gfix_db_name */
|
||||
{335740930, -901}, /* 2 gfix_invalid_sw */
|
||||
{335740932, -901}, /* 4 gfix_incmp_sw */
|
||||
|
@ -952,6 +952,7 @@ static const struct {
|
||||
{335545249, "22023"}, // 929 tom_rsa_verify
|
||||
{335545250, "22023"}, // 930 tom_chacha_key
|
||||
{335545251, "08003"}, // 931 bad_repl_handle
|
||||
{335545252, "0B000"}, // 932 tra_snapshot_does_not_exist
|
||||
{335740929, "00000"}, // 1 gfix_db_name
|
||||
{335740930, "00000"}, // 2 gfix_invalid_sw
|
||||
{335740932, "00000"}, // 4 gfix_incmp_sw
|
||||
|
@ -1166,6 +1166,10 @@ void INF_transaction_info(const jrd_tra* transaction,
|
||||
memcpy(buffer, transaction->tra_attachment->att_database->dbb_database_name.c_str(), length);
|
||||
break;
|
||||
|
||||
case fb_info_tra_snapshot_number:
|
||||
length = INF_convert(static_cast<SINT64>(transaction->tra_snapshot_number), buffer);
|
||||
break;
|
||||
|
||||
default:
|
||||
buffer[0] = item;
|
||||
item = isc_info_error;
|
||||
|
@ -394,6 +394,7 @@ enum info_db_provider
|
||||
#define isc_info_tra_access 9
|
||||
#define isc_info_tra_lock_timeout 10
|
||||
#define fb_info_tra_dbpath 11
|
||||
#define fb_info_tra_snapshot_number 12
|
||||
|
||||
// isc_info_tra_isolation responses
|
||||
#define isc_info_tra_consistency 1
|
||||
|
@ -759,8 +759,7 @@ void TipCache::remapSnapshots(bool sync)
|
||||
}
|
||||
|
||||
|
||||
|
||||
SnapshotHandle TipCache::beginSnapshot(thread_db* tdbb, AttNumber attachmentId, CommitNumber *commitNumber_out)
|
||||
SnapshotHandle TipCache::beginSnapshot(thread_db* tdbb, AttNumber attachmentId, CommitNumber& commitNumber)
|
||||
{
|
||||
// Can only be called on initialized TipCache
|
||||
fb_assert(m_tpcHeader);
|
||||
@ -772,16 +771,38 @@ SnapshotHandle TipCache::beginSnapshot(thread_db* tdbb, AttNumber attachmentId,
|
||||
// Remap snapshot list if it has been grown by someone else
|
||||
remapSnapshots(false);
|
||||
|
||||
if (commitNumber != 0)
|
||||
{
|
||||
SnapshotList* snapshots = m_snapshots->getHeader();
|
||||
ULONG slotsUsed = snapshots->slots_used.load(std::memory_order_relaxed);
|
||||
bool found = false;
|
||||
|
||||
for (SnapshotHandle slotNumber = 0; slotNumber < slotsUsed; ++slotNumber)
|
||||
{
|
||||
if (snapshots->slots[slotNumber].attachment_id.load(std::memory_order_relaxed) != 0 &&
|
||||
snapshots->slots[slotNumber].snapshot.load(std::memory_order_relaxed) == commitNumber)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
ERR_post(Arg::Gds(isc_tra_snapshot_does_not_exist));
|
||||
}
|
||||
|
||||
SnapshotHandle slotNumber = allocateSnapshotSlot();
|
||||
|
||||
// Note, that allocateSnapshotSlot might remap memory and thus invalidate pointers
|
||||
SnapshotList* snapshots = m_snapshots->getHeader();
|
||||
|
||||
// Store snapshot commit number and return handle
|
||||
SnapshotData *slot = snapshots->slots + slotNumber;
|
||||
SnapshotData* slot = snapshots->slots + slotNumber;
|
||||
|
||||
*commitNumber_out = m_tpcHeader->getHeader()->latest_commit_number.load(std::memory_order_acquire);
|
||||
slot->snapshot.store(*commitNumber_out, std::memory_order_release);
|
||||
if (commitNumber == 0)
|
||||
commitNumber = m_tpcHeader->getHeader()->latest_commit_number.load(std::memory_order_acquire);
|
||||
|
||||
slot->snapshot.store(commitNumber, std::memory_order_release);
|
||||
|
||||
// Only assign attachment_id after we completed all other work
|
||||
slot->attachment_id.store(attachmentId, std::memory_order_release);
|
||||
|
@ -118,9 +118,9 @@ public:
|
||||
void updateOldestTransaction(thread_db* tdbb, TraNumber oldest, TraNumber oldestSnapshot);
|
||||
|
||||
// Create snapshot. The snapshot shall use only versions committed
|
||||
// before commitNumber_out. Snapshots inhibit GC to some extent.
|
||||
// before commitNumber (the latest CN when it's 0). Snapshots inhibit GC to some extent.
|
||||
// When snapshot is no longer needed you call endSnapshot.
|
||||
SnapshotHandle beginSnapshot(thread_db* tdbb, AttNumber attachmentId, CommitNumber *commitNumber_out);
|
||||
SnapshotHandle beginSnapshot(thread_db* tdbb, AttNumber attachmentId, CommitNumber& commitNumber);
|
||||
|
||||
// Deallocate snapshot.
|
||||
void endSnapshot(thread_db* tdbb, SnapshotHandle handle, AttNumber attachmentId);
|
||||
|
@ -171,10 +171,11 @@ void TRA_setup_request_snapshot(Jrd::thread_db* tdbb, Jrd::jrd_req* request, boo
|
||||
// we need to set up statement snapshot for read consistency and own it
|
||||
|
||||
request->req_snapshot.m_owner = request;
|
||||
request->req_snapshot.m_number = 0;
|
||||
|
||||
request->req_snapshot.m_handle =
|
||||
tdbb->getDatabase()->dbb_tip_cache->beginSnapshot(tdbb,
|
||||
tdbb->getAttachment()->att_attachment_id, &request->req_snapshot.m_number);
|
||||
tdbb->getAttachment()->att_attachment_id, request->req_snapshot.m_number);
|
||||
}
|
||||
|
||||
|
||||
@ -1283,8 +1284,10 @@ void TRA_release_transaction(thread_db* tdbb, jrd_tra* transaction, Jrd::TraceTr
|
||||
}
|
||||
|
||||
++transaction->tra_use_count;
|
||||
|
||||
if (transaction->tra_lock)
|
||||
LCK_release(tdbb, transaction->tra_lock);
|
||||
|
||||
--transaction->tra_use_count;
|
||||
|
||||
// release the sparse bit map used for commit retain transaction
|
||||
@ -2732,6 +2735,7 @@ static void transaction_options(thread_db* tdbb,
|
||||
TriState wait, lock_timeout;
|
||||
TriState isolation, read_only, rec_version, read_consistency;
|
||||
bool anylock_write = false;
|
||||
bool shared_snapshot = false;
|
||||
|
||||
++tpb;
|
||||
|
||||
@ -2745,6 +2749,14 @@ static void transaction_options(thread_db* tdbb,
|
||||
ERR_post(Arg::Gds(isc_bad_tpb_content) <<
|
||||
Arg::Gds(isc_tpb_multiple_txn_isolation));
|
||||
|
||||
if (shared_snapshot)
|
||||
{
|
||||
ERR_post(
|
||||
Arg::Gds(isc_bad_tpb_content) <<
|
||||
Arg::Gds(isc_tpb_conflicting_options) <<
|
||||
Arg::Str("isc_tpb_consistency") << Arg::Str("isc_tpb_at_snapshot_number"));
|
||||
}
|
||||
|
||||
transaction->tra_flags |= TRA_degree3;
|
||||
transaction->tra_flags &= ~TRA_read_committed;
|
||||
break;
|
||||
@ -2763,6 +2775,14 @@ static void transaction_options(thread_db* tdbb,
|
||||
ERR_post(Arg::Gds(isc_bad_tpb_content) <<
|
||||
Arg::Gds(isc_tpb_multiple_txn_isolation));
|
||||
|
||||
if (shared_snapshot)
|
||||
{
|
||||
ERR_post(
|
||||
Arg::Gds(isc_bad_tpb_content) <<
|
||||
Arg::Gds(isc_tpb_conflicting_options) <<
|
||||
Arg::Str("isc_tpb_read_committed") << Arg::Str("isc_tpb_at_snapshot_number"));
|
||||
}
|
||||
|
||||
transaction->tra_flags &= ~TRA_degree3;
|
||||
transaction->tra_flags |= TRA_read_committed;
|
||||
break;
|
||||
@ -3143,6 +3163,70 @@ static void transaction_options(thread_db* tdbb,
|
||||
}
|
||||
break;
|
||||
|
||||
case isc_tpb_at_snapshot_number:
|
||||
{
|
||||
const char* option_name = "isc_tpb_at_snapshot_number";
|
||||
|
||||
if (shared_snapshot)
|
||||
{
|
||||
ERR_post(
|
||||
Arg::Gds(isc_bad_tpb_content) <<
|
||||
Arg::Gds(isc_tpb_multiple_spec) << Arg::Str(option_name));
|
||||
}
|
||||
|
||||
if (transaction->tra_flags & TRA_read_committed)
|
||||
{
|
||||
ERR_post(
|
||||
Arg::Gds(isc_bad_tpb_content) <<
|
||||
Arg::Gds(isc_tpb_conflicting_options) <<
|
||||
Arg::Str(option_name) << Arg::Str("isc_tpb_read_committed"));
|
||||
}
|
||||
|
||||
if (transaction->tra_flags & TRA_degree3)
|
||||
{
|
||||
ERR_post(
|
||||
Arg::Gds(isc_bad_tpb_content) <<
|
||||
Arg::Gds(isc_tpb_conflicting_options) <<
|
||||
Arg::Str(option_name) << Arg::Str("isc_tpb_consistency"));
|
||||
}
|
||||
|
||||
if (tpb >= end)
|
||||
{
|
||||
ERR_post(
|
||||
Arg::Gds(isc_bad_tpb_content) <<
|
||||
Arg::Gds(isc_tpb_missing_len) << Arg::Str(option_name));
|
||||
}
|
||||
|
||||
const USHORT len = *tpb++;
|
||||
|
||||
if (tpb >= end && len > 0)
|
||||
{
|
||||
ERR_post(
|
||||
Arg::Gds(isc_bad_tpb_content) <<
|
||||
Arg::Gds(isc_tpb_missing_value) << Arg::Num(len) << Arg::Str(option_name));
|
||||
}
|
||||
|
||||
if (end - tpb < len || len == 0)
|
||||
{
|
||||
ERR_post(
|
||||
Arg::Gds(isc_bad_tpb_content) <<
|
||||
Arg::Gds(isc_tpb_corrupt_len) << Arg::Num(len) << Arg::Str(option_name));
|
||||
}
|
||||
|
||||
shared_snapshot = true;
|
||||
transaction->tra_snapshot_number = isc_portable_integer(tpb, len);
|
||||
|
||||
if (transaction->tra_snapshot_number == 0)
|
||||
{
|
||||
ERR_post(
|
||||
Arg::Gds(isc_bad_tpb_content) <<
|
||||
Arg::Str(option_name));
|
||||
}
|
||||
|
||||
tpb += len;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ERR_post(Arg::Gds(isc_bad_tpb_form));
|
||||
}
|
||||
@ -3326,9 +3410,16 @@ static void transaction_start(thread_db* tdbb, jrd_tra* trans)
|
||||
|
||||
if (!(trans->tra_flags & TRA_read_committed))
|
||||
{
|
||||
trans->tra_snapshot_handle =
|
||||
dbb->dbb_tip_cache->beginSnapshot(tdbb,
|
||||
attachment->att_attachment_id, &trans->tra_snapshot_number);
|
||||
try
|
||||
{
|
||||
trans->tra_snapshot_handle = dbb->dbb_tip_cache->beginSnapshot(
|
||||
tdbb, attachment->att_attachment_id, trans->tra_snapshot_number);
|
||||
}
|
||||
catch (const Firebird::Exception&)
|
||||
{
|
||||
LCK_release(tdbb, lock);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Next task is to find the oldest active transaction on the system. This
|
||||
|
@ -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 (?, ?, ?, ?);
|
||||
--
|
||||
('2019-01-14 20:23:00', 'JRD', 0, 932)
|
||||
('2019-03-01 12:35:00', 'JRD', 0, 933)
|
||||
('2015-03-17 18:33:00', 'QLI', 1, 533)
|
||||
('2018-03-17 12:00:00', 'GFIX', 3, 136)
|
||||
('1996-11-07 13:39:40', 'GPRE', 4, 1)
|
||||
|
@ -1039,6 +1039,7 @@ Data source : @4', NULL, NULL)
|
||||
('tom_rsa_verify', NULL, 'SysFunction.cpp', NULL, 0, 929, NULL, 'Verifying RSA-signed data', NULL, NULL);
|
||||
('tom_chacha_key', NULL, 'SysFunction.cpp', NULL, 0, 930, NULL, 'Invalid key length @1, need 16 or 32', NULL, NULL);
|
||||
('bad_repl_handle', NULL, 'jrd.cpp', NULL, 0, 931, NULL, 'invalid replicator handle', NULL, NULL);
|
||||
('tra_snapshot_does_not_exist', NULL, 'tpc.cpp', NULL, 0, 932, NULL, 'Transaction''s base snapshot number does not exist', 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);
|
||||
|
@ -938,6 +938,7 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA
|
||||
(-901, '22', '023', 0, 929, 'tom_rsa_verify', NULL, NULL)
|
||||
(-901, '22', '023', 0, 930, 'tom_chacha_key', NULL, NULL)
|
||||
(-901, '08', '003', 0, 931, 'bad_repl_handle', NULL, NULL)
|
||||
(-901, '0B', '000', 0, 932, 'tra_snapshot_does_not_exist', NULL, NULL)
|
||||
-- GFIX
|
||||
(-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL)
|
||||
(-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)
|
||||
|
@ -317,6 +317,7 @@ static const TOK tokens[] =
|
||||
{TOK_NULLIF, "NULLIF", true},
|
||||
{TOK_NULL, "NULL", false},
|
||||
{TOK_NULLS, "NULLS", true},
|
||||
{TOK_NUMBER, "NUMBER", true},
|
||||
{TOK_NUMERIC, "NUMERIC", false},
|
||||
{TOK_OCTET_LENGTH, "OCTET_LENGTH", false},
|
||||
{TOK_OF, "OF", false},
|
||||
|
Loading…
Reference in New Issue
Block a user