8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 16:43:03 +01:00

Merge branch 'query_restarts' of https://github.com/samofatov/firebird into query_restarts

This commit is contained in:
hvlad 2019-08-26 20:11:36 +03:00
commit 043f7b2361
13 changed files with 222 additions and 142 deletions

View File

@ -10,7 +10,7 @@ need to raise an exception but do not want the database changes to be rolled-bac
If exceptions are raised inside the body of an autonomous transaction block, the changes are
rolled-back. If the block runs till the end, the transaction is committed.
The new transaction is initiated with the same isolation level of the existing one.
The new transaction is initiated with snapshot isolation level and lock timeout of the existing one.
Should be used with caution to avoid deadlocks.
Author:

View File

@ -2643,7 +2643,19 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, jrd_req* request, WhichTrigger
VirtualTable::erase(tdbb, rpb);
else if (!relation->rel_view_rse)
{
VIO_erase(tdbb, rpb, transaction);
// VIO_erase returns false if there is an update conflict in Read Consistency
// transaction. Before returning false it disables statement-level snapshot
// (via setting req_update_conflict flag) so re-fetch should see new data.
// Deleting new version blindly is generally unsafe, but is ok in this situation
// because all changes made by this request will certainly be undone and request
// will be restarted.
while (!VIO_erase(tdbb, rpb, transaction))
{
// VIO_refetch_record returns false if record has been deleted by someone else.
// Gently ignore this situation and proceed further.
if (!VIO_refetch_record(tdbb, rpb, transaction, true, true))
return parentStmt;
}
REPL_erase(tdbb, rpb, transaction);
}
@ -3952,7 +3964,17 @@ const StmtNode* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* r
jrd_tra* const org_transaction = request->req_transaction;
fb_assert(tdbb->getTransaction() == org_transaction);
jrd_tra* const transaction = TRA_start(tdbb, org_transaction->tra_flags,
// Use SNAPSHOT isolation mode in autonomous transactions by default
ULONG transaction_flags = 0;
// Simulate legacy behavior if Read Consistency is not used
if (!dbb->dbb_config->getReadConsistency() &&
!(org_transaction->tra_flags & TRA_read_consistency))
{
transaction_flags = org_transaction->tra_flags;
}
jrd_tra* const transaction = TRA_start(tdbb, transaction_flags,
org_transaction->tra_lock_timeout,
org_transaction);
@ -3977,12 +3999,6 @@ const StmtNode* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* r
const Savepoint* const savepoint = transaction->startSavepoint();
impure->savNumber = savepoint->getNumber();
if ((transaction->tra_flags & TRA_read_committed) &&
(transaction->tra_flags & TRA_read_consistency))
{
TRA_setup_request_snapshot(tdbb, request, true);
}
return action;
}
@ -3994,16 +4010,6 @@ const StmtNode* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* r
fb_assert(transaction->tra_number == impure->traNumber);
if (request->req_operation == jrd_req::req_return ||
request->req_operation == jrd_req::req_unwind)
{
if ((transaction->tra_flags & TRA_read_committed) &&
(transaction->tra_flags & TRA_read_consistency))
{
TRA_release_request_snapshot(tdbb, request);
}
}
switch (request->req_operation)
{
case jrd_req::req_return:
@ -6452,7 +6458,19 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigg
VirtualTable::modify(tdbb, orgRpb, newRpb);
else if (!relation->rel_view_rse)
{
VIO_modify(tdbb, orgRpb, newRpb, transaction);
// VIO_modify returns false if there is an update conflict in Read Consistency
// transaction. Before returning false it disables statement-level snapshot
// (via setting req_update_conflict flag) so re-fetch should see new data.
// Updating new version blindly is generally unsafe, but is ok in this situation
// because all changes made by this request will certainly be undone and request
// will be restarted.
while (!VIO_modify(tdbb, orgRpb, newRpb, transaction))
{
// VIO_refetch_record returns false if record has been deleted by someone else.
// Gently ignore this situation and proceed further.
if (!VIO_refetch_record(tdbb, orgRpb, transaction, true, true))
return parentStmt;
}
IDX_modify(tdbb, orgRpb, newRpb, transaction);
REPL_modify(tdbb, orgRpb, newRpb, transaction);
}

View File

@ -286,7 +286,10 @@ bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer)
tdbb->checkCancelState(true);
UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number];
JRD_receive(tdbb, req_request, message->msg_number, message->msg_length, dsqlMsgBuffer);
if (prefetchedFirstRow)
prefetchedFirstRow = false;
else
JRD_receive(tdbb, req_request, message->msg_number, message->msg_length, dsqlMsgBuffer);
const dsql_par* const eof = statement->getEof();
const USHORT* eofPtr = eof ? (USHORT*) (dsqlMsgBuffer + (IPTR) eof->par_desc.dsc_address) : NULL;
@ -677,33 +680,14 @@ void DsqlDmlRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, boo
*destroyScratchPool = true;
}
// Execute a dynamic SQL statement.
void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle,
// Execute a dynamic SQL statement
void DsqlDmlRequest::doExecute(thread_db* tdbb, jrd_tra** traHandle,
Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg,
Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg,
bool singleton)
{
if (!req_request)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
Arg::Gds(isc_unprepared_stmt));
}
// If there is no data required, just start the request
prefetchedFirstRow = false;
const dsql_msg* message = statement->getSendMsg();
if (message)
mapInOut(tdbb, false, message, inMetadata, NULL, inMsg);
// we need to mapInOut() before tracing of execution start to let trace
// 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);
@ -805,6 +789,17 @@ void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle,
status_exception::raise(&localStatus);
}
}
else
{
// Prefetch first row of a query
if (reqTypeWithCursor(statement->getType())) {
dsql_msg* message = (dsql_msg*) statement->getReceiveMsg();
UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number];
JRD_receive(tdbb, req_request, message->msg_number, message->msg_length, dsqlMsgBuffer);
prefetchedFirstRow = true;
}
}
switch (statement->getType())
{
@ -826,6 +821,62 @@ void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle,
}
break;
}
}
// Execute a dynamic SQL statement with tracing, restart and timeout handler
void DsqlDmlRequest::execute(thread_db* tdbb, jrd_tra** traHandle,
Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg,
Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg,
bool singleton)
{
if (!req_request)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
Arg::Gds(isc_unprepared_stmt));
}
// If there is no data required, just start the request
const dsql_msg* message = statement->getSendMsg();
if (message)
mapInOut(tdbb, false, message, inMetadata, NULL, inMsg);
// we need to mapInOut() before tracing of execution start to let trace
// 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 (req_transaction && (req_transaction->tra_flags & TRA_read_consistency) &&
statement->getType() != DsqlCompiledStatement::TYPE_SAVEPOINT)
{
AutoSavePoint savePoint(tdbb, req_transaction);
int numTries = 0;
while (true)
{
doExecute(tdbb, traHandle, inMetadata, inMsg, outMetadata, outMsg, singleton);
if (!(req_request->req_flags & req_update_conflict))
break;
req_request->req_flags &= ~req_update_conflict;
if (numTries >= 10) {
gds__log("Update conflict: unable to get a stable set of rows in the source tables");
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) <<
Arg::Gds(isc_deadlock) <<
Arg::Gds(isc_update_conflict) <<
Arg::Gds(isc_concurrent_transaction) << Arg::Num(req_request->req_conflict_txn));
}
req_transaction->rollbackSavepoint(tdbb, true);
req_transaction->startSavepoint(tdbb);
numTries++;
}
savePoint.release(); // everything is ok
} else {
doExecute(tdbb, traHandle, inMetadata, inMsg, outMetadata, outMsg, singleton);
}
trace.finish(have_cursor, ITracePlugin::RESULT_SUCCESS);
}

View File

@ -645,7 +645,8 @@ public:
explicit DsqlDmlRequest(MemoryPool& pool, StmtNode* aNode)
: dsql_req(pool),
node(aNode),
needDelayedFormat(false)
needDelayedFormat(false),
prefetchedFirstRow(false)
{
}
@ -664,9 +665,14 @@ public:
virtual void setDelayedFormat(thread_db* tdbb, Firebird::IMessageMetadata* metadata);
private:
void doExecute(thread_db* tdbb, jrd_tra** traHandle,
Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg,
Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg,
bool singleton);
NestConst<StmtNode> node;
Firebird::RefPtr<Firebird::IMessageMetadata> delayedFormat;
bool needDelayedFormat;
bool prefetchedFirstRow;
};
class DsqlDdlRequest : public dsql_req

View File

@ -234,7 +234,7 @@ void VerbAction::mergeTo(thread_db* tdbb, jrd_tra* transaction, VerbAction* next
release(transaction);
}
void VerbAction::undo(thread_db* tdbb, jrd_tra* transaction)
void VerbAction::undo(thread_db* tdbb, jrd_tra* transaction, bool preserveLocks)
{
// Undo changes recorded for this verb action.
// After that, clear the verb action and prepare it for later reuse.
@ -258,7 +258,7 @@ void VerbAction::undo(thread_db* tdbb, jrd_tra* transaction)
if (!DPM_get(tdbb, &rpb, LCK_read))
BUGCHECK(186); // msg 186 record disappeared
if (have_undo && !(rpb.rpb_flags & rpb_deleted))
if ((have_undo || preserveLocks) && !(rpb.rpb_flags & rpb_deleted))
VIO_data(tdbb, &rpb, transaction->tra_pool);
else
CCH_RELEASE(tdbb, &rpb.getWindow(tdbb));
@ -267,7 +267,45 @@ void VerbAction::undo(thread_db* tdbb, jrd_tra* transaction)
BUGCHECK(185); // msg 185 wrong record version
if (!have_undo)
VIO_backout(tdbb, &rpb, transaction);
{
if (preserveLocks && rpb.rpb_b_page) {
// Fetch previous record version and update in place current version with it
record_param temp = rpb;
temp.rpb_page = rpb.rpb_b_page;
temp.rpb_line = rpb.rpb_b_line;
temp.rpb_record = NULL;
if (temp.rpb_flags & rpb_delta)
fb_assert(temp.rpb_prior != NULL);
else
fb_assert(temp.rpb_prior == NULL);
if (!DPM_fetch(tdbb, &temp, LCK_read))
BUGCHECK(291); // msg 291 cannot find record back version
if (!(temp.rpb_flags & rpb_chained) || (temp.rpb_flags & (rpb_blob | rpb_fragment)))
ERR_bugcheck_msg("invalid back version");
VIO_data(tdbb, &temp, tdbb->getDefaultPool());
Record* const save_record = rpb.rpb_record;
if (rpb.rpb_flags & rpb_deleted)
rpb.rpb_record = NULL;
Record* const dead_record = rpb.rpb_record;
VIO_update_in_place(tdbb, transaction, &rpb, &temp);
if (dead_record)
{
rpb.rpb_record = NULL; // VIO_garbage_collect_idx will play with this record dirty tricks
VIO_garbage_collect_idx(tdbb, transaction, &rpb, dead_record);
}
rpb.rpb_record = save_record;
delete temp.rpb_record;
} else
VIO_backout(tdbb, &rpb, transaction);
}
else
{
AutoUndoRecord record(vct_undo->current().setupRecord(transaction));
@ -378,7 +416,7 @@ void Savepoint::cleanupTempData()
}
}
Savepoint* Savepoint::rollback(thread_db* tdbb, Savepoint* prior)
Savepoint* Savepoint::rollback(thread_db* tdbb, Savepoint* prior, bool preserveLocks)
{
// Undo changes made in this savepoint.
// Perform index and BLOB cleanup if needed.
@ -399,7 +437,7 @@ Savepoint* Savepoint::rollback(thread_db* tdbb, Savepoint* prior)
{
VerbAction* const action = m_actions;
action->undo(tdbb, m_transaction);
action->undo(tdbb, m_transaction, preserveLocks);
m_actions = action->vct_next;
action->vct_next = m_freeActions;

View File

@ -94,7 +94,7 @@ namespace Jrd
UndoItemTree* vct_undo; // Data for undo records
void mergeTo(thread_db* tdbb, jrd_tra* transaction, VerbAction* nextAction);
void undo(thread_db* tdbb, jrd_tra* transaction);
void undo(thread_db* tdbb, jrd_tra* transaction, bool preserveLocks);
void garbageCollectIdxLite(thread_db* tdbb, jrd_tra* transaction, SINT64 recordNumber,
VerbAction* nextAction, Record* goingRecord);
@ -231,7 +231,7 @@ namespace Jrd
void cleanupTempData();
Savepoint* rollback(thread_db* tdbb, Savepoint* prior = NULL);
Savepoint* rollback(thread_db* tdbb, Savepoint* prior = NULL, bool preserveLocks = false);
Savepoint* rollforward(thread_db* tdbb, Savepoint* prior = NULL);
static void destroy(Savepoint*& savepoint)

View File

@ -8880,39 +8880,8 @@ void JRD_start(Jrd::thread_db* tdbb, jrd_req* request, jrd_tra* transaction)
* Get a record from the host program.
*
**************************************/
// Repeat execution to handle update conflicts, if any
int numTries = 0;
while (true)
{
try
{
EXE_unwind(tdbb, request);
EXE_start(tdbb, request, transaction);
break;
}
catch (const status_exception &ex)
{
const ISC_STATUS* v = ex.value();
if (// Update conflict error
v[0] == isc_arg_gds &&
v[1] == isc_update_conflict &&
// Read committed transaction with snapshots
(transaction->tra_flags & TRA_read_committed) &&
(transaction->tra_flags & TRA_read_consistency) &&
// Snapshot has been assigned to the request -
// it was top-level request
!TRA_get_prior_request(tdbb))
{
if (++numTries < 10)
{
fb_utils::init_status(tdbb->tdbb_status_vector);
continue;
}
}
throw;
}
}
EXE_unwind(tdbb, request);
EXE_start(tdbb, request, transaction);
check_autocommit(tdbb, request);
@ -9001,40 +8970,9 @@ void JRD_start_and_send(thread_db* tdbb, jrd_req* request, jrd_tra* transaction,
* Get a record from the host program.
*
**************************************/
// Repeat execution to handle update conflicts, if any
int numTries = 0;
while (true)
{
try
{
EXE_unwind(tdbb, request);
EXE_start(tdbb, request, transaction);
EXE_send(tdbb, request, msg_type, msg_length, msg);
break;
}
catch (const status_exception &ex)
{
const ISC_STATUS* v = ex.value();
if (// Update conflict error
v[0] == isc_arg_gds &&
v[1] == isc_update_conflict &&
// Read committed transaction with snapshots
(transaction->tra_flags & TRA_read_committed) &&
(transaction->tra_flags & TRA_read_consistency) &&
// Snapshot has been assigned to the request -
// it was top-level request
!TRA_get_prior_request(tdbb))
{
if (++numTries < 10)
{
fb_utils::init_status(tdbb->tdbb_status_vector);
continue;
}
}
throw;
}
}
EXE_unwind(tdbb, request);
EXE_start(tdbb, request, transaction);
EXE_send(tdbb, request, msg_type, msg_length, msg);
check_autocommit(tdbb, request);

View File

@ -275,6 +275,7 @@ public:
SINT64 req_fetch_rowcount; // Total number of rows returned by this request
jrd_req* req_proc_caller; // Procedure's caller request
const ValueListNode* req_proc_inputs; // and its node with input parameters
TraNumber req_conflict_txn; // Transaction number for update conflict in read consistency mode
ULONG req_src_line;
ULONG req_src_column;
@ -397,6 +398,7 @@ const ULONG req_continue_loop = 0x100L; // PSQL continue statement
const ULONG req_proc_fetch = 0x200L; // Fetch from procedure in progress
const ULONG req_same_tx_upd = 0x400L; // record was updated by same transaction
const ULONG req_reserved = 0x800L; // Request reserved for client
const ULONG req_update_conflict = 0x1000L; // We need to restart request due to update conflict
// Index lock block

View File

@ -145,7 +145,7 @@ jrd_req* TRA_get_prior_request(thread_db* tdbb)
return org_request;
}
void TRA_setup_request_snapshot(Jrd::thread_db* tdbb, Jrd::jrd_req* request, bool no_prior)
void TRA_setup_request_snapshot(Jrd::thread_db* tdbb, Jrd::jrd_req* request)
{
// This function is called whenever request is started in a transaction.
// Setup context to preserve read consistency in READ COMMITTED transactions.
@ -160,7 +160,7 @@ void TRA_setup_request_snapshot(Jrd::thread_db* tdbb, Jrd::jrd_req* request, boo
return;
// See if there is any request right above us in the call stack
jrd_req* org_request = no_prior ? nullptr : TRA_get_prior_request(tdbb);
jrd_req* org_request = TRA_get_prior_request(tdbb);
if (org_request && org_request->req_transaction == transaction)
{
@ -1616,8 +1616,11 @@ int TRA_snapshot_state(thread_db* tdbb, const jrd_tra* trans, TraNumber number,
// GC thread accesses data directly without any request
if (jrd_req* current_request = tdbb->getRequest())
{
// There is no request snapshot when we build expression index
if (jrd_req* snapshot_request = current_request->req_snapshot.m_owner)
// Notes:
// 1) There is no request snapshot when we build expression index
// 2) Disable read committed snapshot after we encountered update conflict
jrd_req* snapshot_request = current_request->req_snapshot.m_owner;
if (snapshot_request && !(snapshot_request->req_flags & req_update_conflict))
{
if (stateCn > snapshot_request->req_snapshot.m_number)
return tra_active;
@ -3847,7 +3850,7 @@ Savepoint* jrd_tra::startSavepoint(bool root)
return savepoint;
}
void jrd_tra::rollbackSavepoint(thread_db* tdbb)
void jrd_tra::rollbackSavepoint(thread_db* tdbb, bool preserveLocks)
/**************************************
*
* r o l l b a c k S a v e p o i n t
@ -3864,7 +3867,7 @@ void jrd_tra::rollbackSavepoint(thread_db* tdbb)
REPL_save_cleanup(tdbb, this, tra_save_point, true);
Jrd::ContextPoolHolder context(tdbb, tra_pool);
tra_save_point = tra_save_point->rollback(tdbb);
tra_save_point = tra_save_point->rollback(tdbb, NULL, preserveLocks);
}
}

View File

@ -386,7 +386,7 @@ public:
Record* findNextUndo(VerbAction* before_this, jrd_rel* relation, SINT64 number);
void listStayingUndo(jrd_rel* relation, SINT64 number, RecordStack &staying);
Savepoint* startSavepoint(bool root = false);
void rollbackSavepoint(thread_db* tdbb);
void rollbackSavepoint(thread_db* tdbb, bool preserveLocks = false);
void rollbackToSavepoint(thread_db* tdbb, SavNumber number);
void rollforwardSavepoint(thread_db* tdbb);
DbCreatorsList* getDbCreatorsList();

View File

@ -63,7 +63,7 @@ void TRA_update_counters(Jrd::thread_db*, Jrd::Database*);
int TRA_wait(Jrd::thread_db* tdbb, Jrd::jrd_tra* trans, TraNumber number, Jrd::jrd_tra::wait_t wait);
void TRA_attach_request(Jrd::jrd_tra* transaction, Jrd::jrd_req* request);
void TRA_detach_request(Jrd::jrd_req* request);
void TRA_setup_request_snapshot(Jrd::thread_db*, Jrd::jrd_req* request, bool no_prior = false);
void TRA_setup_request_snapshot(Jrd::thread_db*, Jrd::jrd_req* request);
void TRA_release_request_snapshot(Jrd::thread_db*, Jrd::jrd_req* request);
Jrd::jrd_req* TRA_get_prior_request(Jrd::thread_db*);

View File

@ -1441,7 +1441,7 @@ void VIO_data(thread_db* tdbb, record_param* rpb, MemoryPool* pool)
}
void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
{
/**************************************
*
@ -1497,7 +1497,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
// hvlad: what if record was created\modified by user tx also,
// i.e. if there is backversion ???
VIO_backout(tdbb, rpb, transaction);
return;
return true;
}
transaction->tra_flags |= TRA_write;
@ -1891,7 +1891,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
if (transaction->tra_save_point && transaction->tra_save_point->isChanging())
verb_post(tdbb, transaction, rpb, rpb->rpb_undo);
return;
return true;
}
const bool backVersion = (rpb->rpb_b_page != 0);
@ -1910,12 +1910,20 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
{
// Update stub didn't find one page -- do a long, hard update
PageStack stack;
if (prepare_update(tdbb, transaction, tid_fetch, rpb, &temp, 0, stack, false))
int prepare_result = prepare_update(tdbb, transaction, tid_fetch, rpb, &temp, 0, stack, false);
if (prepare_result &&
(!(transaction->tra_flags & TRA_read_consistency) || prepare_result == PREPARE_LOCKERR))
{
ERR_post(Arg::Gds(isc_deadlock) <<
Arg::Gds(isc_update_conflict) <<
Arg::Gds(isc_concurrent_transaction) << Arg::Num(rpb->rpb_transaction_nr));
}
if (prepare_result) {
jrd_req* top_request = request->req_snapshot.m_owner;
top_request->req_flags |= req_update_conflict;
top_request->req_conflict_txn = rpb->rpb_transaction_nr;
return false;
}
// Old record was restored and re-fetched for write. Now replace it.
@ -1974,6 +1982,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
{
notify_garbage_collector(tdbb, rpb, transaction->tra_number);
}
return true;
}
@ -2769,7 +2778,7 @@ void VIO_init(thread_db* tdbb)
}
}
void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, jrd_tra* transaction)
bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, jrd_tra* transaction)
{
/**************************************
*
@ -2835,7 +2844,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
{
VIO_update_in_place(tdbb, transaction, org_rpb, new_rpb);
tdbb->bumpRelStats(RuntimeStatistics::RECORD_UPDATES, relation->rel_id);
return;
return true;
}
check_gbak_cheating_insupd(tdbb, relation, "UPDATE");
@ -3198,19 +3207,27 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
}
tdbb->bumpRelStats(RuntimeStatistics::RECORD_UPDATES, relation->rel_id);
return;
return true;
}
const bool backVersion = (org_rpb->rpb_b_page != 0);
record_param temp;
PageStack stack;
if (prepare_update(tdbb, transaction, org_rpb->rpb_transaction_nr, org_rpb, &temp, new_rpb,
stack, false))
int prepare_result = prepare_update(tdbb, transaction, org_rpb->rpb_transaction_nr, org_rpb,
&temp, new_rpb, stack, false);
if (prepare_result &&
(!(transaction->tra_flags & TRA_read_consistency) || prepare_result == PREPARE_LOCKERR))
{
ERR_post(Arg::Gds(isc_deadlock) <<
Arg::Gds(isc_update_conflict) <<
Arg::Gds(isc_concurrent_transaction) << Arg::Num(org_rpb->rpb_transaction_nr));
}
if (prepare_result) {
jrd_req* top_request = tdbb->getRequest()->req_snapshot.m_owner;
top_request->req_flags |= req_update_conflict;
top_request->req_conflict_txn = org_rpb->rpb_transaction_nr;
return false;
}
IDX_modify_flag_uk_modified(tdbb, org_rpb, new_rpb, transaction);
@ -3259,6 +3276,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
{
notify_garbage_collector(tdbb, org_rpb, transaction->tra_number);
}
return true;
}
@ -4062,6 +4080,11 @@ bool VIO_writelock(thread_db* tdbb, record_param* org_rpb, jrd_tra* transaction)
{
case PREPARE_CONFLICT:
case PREPARE_DELETE:
if ((transaction->tra_flags & TRA_read_consistency)) {
jrd_req* top_request = tdbb->getRequest()->req_snapshot.m_owner;
top_request->req_flags |= req_update_conflict;
top_request->req_conflict_txn = org_rpb->rpb_transaction_nr;
}
org_rpb->rpb_runtime_flags |= RPB_refetch;
return false;
case PREPARE_LOCKERR:
@ -5662,7 +5685,7 @@ static int prepare_update( thread_db* tdbb,
delete_record(tdbb, temp, 0, NULL);
if (writelock)
if (writelock || (transaction->tra_flags & TRA_read_consistency))
{
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->rel_id);
return PREPARE_DELETE;
@ -5785,15 +5808,16 @@ static int prepare_update( thread_db* tdbb,
switch (state)
{
case tra_committed:
// We need to loop waiting in read committed with no read consistency transactions only
if (!(transaction->tra_flags & TRA_read_committed) ||
(transaction->tra_flags & TRA_read_consistency))
// For SNAPSHOT mode transactions raise error early
if (!(transaction->tra_flags & TRA_read_committed))
{
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->rel_id);
ERR_post(Arg::Gds(isc_update_conflict) <<
ERR_post(Arg::Gds(isc_deadlock) <<
Arg::Gds(isc_update_conflict) <<
Arg::Gds(isc_concurrent_transaction) << Arg::Num(update_conflict_trans));
}
return PREPARE_CONFLICT;
case tra_limbo:
if (!(transaction->tra_flags & TRA_ignore_limbo))

View File

@ -42,7 +42,7 @@ bool VIO_chase_record_version(Jrd::thread_db*, Jrd::record_param*,
Jrd::jrd_tra*, MemoryPool*, bool, bool);
void VIO_copy_record(Jrd::thread_db*, Jrd::record_param*, Jrd::record_param*);
void VIO_data(Jrd::thread_db*, Jrd::record_param*, MemoryPool*);
void VIO_erase(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*);
bool VIO_erase(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*);
void VIO_fini(Jrd::thread_db*);
bool VIO_garbage_collect(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*);
Jrd::Record* VIO_gc_record(Jrd::thread_db*, Jrd::jrd_rel*);
@ -51,7 +51,7 @@ bool VIO_get_current(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*,
MemoryPool*, bool, bool&);
void VIO_init(Jrd::thread_db*);
bool VIO_writelock(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*);
void VIO_modify(Jrd::thread_db*, Jrd::record_param*, Jrd::record_param*, Jrd::jrd_tra*);
bool VIO_modify(Jrd::thread_db*, Jrd::record_param*, Jrd::record_param*, Jrd::jrd_tra*);
bool VIO_next_record(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*, MemoryPool*, bool);
Jrd::Record* VIO_record(Jrd::thread_db*, Jrd::record_param*, const Jrd::Format*, MemoryPool*);
bool VIO_refetch_record(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*, bool, bool);