8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 21:23:04 +01:00

Backported feature #7810 : Improve SKIP LOCKED implementation

(#7811 and #7872)
This commit is contained in:
Vlad Khorsun 2023-11-27 20:12:40 +02:00
parent b596b35599
commit dc2d85c17b
37 changed files with 281 additions and 156 deletions

View File

@ -46,9 +46,9 @@ DELETE FROM <sometable>
## Notes
As it happens with subclauses `FIRST`/`SKIP`/`ROWS`/`OFFSET`/`FETCH` record lock
(and "skip locked" check) is done in between of skip (`SKIP`/`ROWS`/`OFFSET`/`FETCH`) and
limit (`FIRST`/`ROWS`/`OFFSET`/`FETCH`) checks.
If statement have both `SKIP LOCKED` and `SKIP`/`ROWS` subclauses, some locked rows
could be skipped before `SKIP`/`ROWS` subclause would account it, thus skipping more
rows than specified in `SKIP`/`ROWS`.
## Examples

View File

@ -152,6 +152,40 @@ namespace
{}
};
// Combined conditional savepoint and its change marker.
class CondSavepointAndMarker
{
public:
CondSavepointAndMarker(thread_db* tdbb, jrd_tra* trans, bool cond) :
m_savepoint(tdbb, trans, cond),
m_marker(cond ? trans->tra_save_point : nullptr)
{}
~CondSavepointAndMarker()
{
rollback();
}
void release()
{
m_marker.done();
m_savepoint.release();
}
void rollback()
{
m_marker.done();
m_savepoint.rollback();
}
// Prohibit unwanted creation/copying
CondSavepointAndMarker(const CondSavepointAndMarker&) = delete;
CondSavepointAndMarker& operator=(const CondSavepointAndMarker&) = delete;
private:
AutoSavePoint m_savepoint;
Savepoint::ChangeMarker m_marker;
};
} // namespace
@ -2233,9 +2267,9 @@ const StmtNode* DeclareVariableNode::execute(thread_db* tdbb, Request* request,
//--------------------
static RegisterNode<EraseNode> regEraseNode({blr_erase});
static RegisterNode<EraseNode> regEraseNode({blr_erase, blr_erase2});
DmlNode* EraseNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch* csb, const UCHAR /*blrOp*/)
DmlNode* EraseNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp)
{
const USHORT n = csb->csb_blr_reader.getByte();
@ -2248,6 +2282,9 @@ DmlNode* EraseNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch
if (csb->csb_blr_reader.peekByte() == blr_marks)
node->marks |= PAR_marks(csb);
if (blrOp == blr_erase2)
node->returningStatement = PAR_parse_stmt(tdbb, csb);
return node;
}
@ -2308,7 +2345,7 @@ StmtNode* EraseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
PASS1_limit(dsqlScratch, dsqlRows->length, dsqlRows->skip, rse);
if (dsqlSkipLocked)
rse->flags |= RseNode::FLAG_WRITELOCK | RseNode::FLAG_SKIP_LOCKED;
rse->flags |= RseNode::FLAG_SKIP_LOCKED;
}
if (dsqlReturning && dsqlScratch->isPsql())
@ -2342,23 +2379,30 @@ string EraseNode::internalPrint(NodePrinter& printer) const
NODE_PRINT(printer, dsqlReturning);
NODE_PRINT(printer, dsqlRse);
NODE_PRINT(printer, dsqlContext);
NODE_PRINT(printer, dsqlSkipLocked);
NODE_PRINT(printer, statement);
NODE_PRINT(printer, subStatement);
NODE_PRINT(printer, returningStatement);
NODE_PRINT(printer, stream);
NODE_PRINT(printer, marks);
return "EraseNode";
}
// The EraseNode::erase() depends on generated nodes layout in case when
// RETURNING specified.
void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
{
Nullable<USHORT> tableNumber;
const bool skipLocked = dsqlRse && dsqlRse->hasSkipLocked();
if (dsqlReturning && !dsqlScratch->isPsql())
{
if (dsqlCursorName.isEmpty())
{
dsqlScratch->appendUChar(blr_begin);
if (!skipLocked)
dsqlScratch->appendUChar(blr_begin);
tableNumber = dsqlScratch->localTableNumber++;
dsqlGenReturningLocalTableDecl(dsqlScratch, tableNumber.value);
@ -2379,13 +2423,13 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
const auto* context = dsqlContext ? dsqlContext : dsqlRelation->dsqlContext;
if (dsqlReturning)
if (dsqlReturning && !skipLocked)
{
dsqlScratch->appendUChar(blr_begin);
dsqlGenReturning(dsqlScratch, dsqlReturning, tableNumber);
}
dsqlScratch->appendUChar(blr_erase);
dsqlScratch->appendUChar(dsqlReturning && skipLocked ? blr_erase2 : blr_erase);
GEN_stuff_context(dsqlScratch, context);
if (marks)
@ -2393,13 +2437,17 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
if (dsqlReturning)
{
dsqlScratch->appendUChar(blr_end);
if (!skipLocked)
dsqlScratch->appendUChar(blr_end);
else
dsqlGenReturning(dsqlScratch, dsqlReturning, tableNumber);
if (!dsqlScratch->isPsql() && dsqlCursorName.isEmpty())
{
dsqlGenReturningLocalTableCursor(dsqlScratch, dsqlReturning, tableNumber.value);
dsqlScratch->appendUChar(blr_end);
if (!skipLocked)
dsqlScratch->appendUChar(blr_end);
}
}
}
@ -2411,6 +2459,9 @@ EraseNode* EraseNode::pass1(thread_db* tdbb, CompilerScratch* csb)
doPass1(tdbb, csb, statement.getAddress());
doPass1(tdbb, csb, subStatement.getAddress());
AutoSetRestore<bool> autoReturningExpr(&csb->csb_returning_expr, true);
doPass1(tdbb, csb, returningStatement.getAddress());
return this;
}
@ -2527,6 +2578,7 @@ void EraseNode::pass1Erase(thread_db* tdbb, CompilerScratch* csb, EraseNode* nod
EraseNode* EraseNode::pass2(thread_db* tdbb, CompilerScratch* csb)
{
doPass2(tdbb, csb, statement.getAddress(), this);
doPass2(tdbb, csb, returningStatement.getAddress(), this);
doPass2(tdbb, csb, subStatement.getAddress(), this);
const jrd_rel* const relation = csb->csb_rpt[stream].csb_relation;
@ -2605,6 +2657,8 @@ const StmtNode* EraseNode::execute(thread_db* tdbb, Request* request, ExeState*
// Perform erase operation.
const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger whichTrig) const
{
impure_state* impure = request->getImpure<impure_state>(impureOffset);
jrd_tra* transaction = request->req_transaction;
record_param* rpb = &request->req_rpb[stream];
jrd_rel* relation = rpb->rpb_relation;
@ -2630,6 +2684,12 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
}
case Request::req_return:
if (impure->sta_state == 1)
{
impure->sta_state = 0;
rpb->rpb_number.setValid(false);
return parentStmt;
}
break;
default:
@ -2659,7 +2719,7 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
if (rpb->rpb_runtime_flags & RPB_refetch)
{
VIO_refetch_record(tdbb, rpb, transaction, RecordLock::NONE, false);
VIO_refetch_record(tdbb, rpb, transaction, false, false);
rpb->rpb_runtime_flags &= ~RPB_refetch;
}
@ -2668,6 +2728,12 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
SavepointChangeMarker scMarker(transaction);
// Prepare to undo changes by PRE-triggers if record is locked by another
// transaction and delete should be skipped.
const bool skipLocked = rpb->rpb_stream_flags & RPB_s_skipLocked;
CondSavepointAndMarker spPreTriggers(tdbb, transaction,
skipLocked && !(transaction->tra_flags & TRA_system) && relation->rel_pre_erase);
// Handle pre-operation trigger.
preModifyEraseTriggers(tdbb, &relation->rel_pre_erase, whichTrig, rpb, NULL, TRIGGER_DELETE);
@ -2677,23 +2743,36 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
VirtualTable::erase(tdbb, rpb);
else if (!relation->rel_view_rse)
{
// 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.
// VIO_erase returns false if:
// a) 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.
// b) record is locked by another transaction and should be skipped.
if (!VIO_erase(tdbb, rpb, transaction))
{
// Record was not deleted, flow control should be passed to the parent
// ForNode. Note, If RETURNING clause was specified and SKIP LOCKED was
// not, then parent node is CompoundStmtNode, not ForNode. If\when this
// will be changed, the code below should be changed accordingly.
if (skipLocked)
return forNode;
spPreTriggers.release();
forceWriteLock(tdbb, rpb, transaction);
if (!forNode)
restartRequest(request, transaction);
forNode->setWriteLockMode(request);
return parentStmt;
return forNode;
}
REPL_erase(tdbb, rpb, transaction);
}
spPreTriggers.release();
// Handle post operation trigger.
if (relation->rel_post_erase && whichTrig != PRE_TRIG)
@ -2726,6 +2805,13 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
}
}
if (returningStatement)
{
impure->sta_state = 1;
request->req_operation = Request::req_evaluate;
return returningStatement;
}
rpb->rpb_number.setValid(false);
return parentStmt;
@ -6497,6 +6583,7 @@ StmtNode* ModifyNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, bool up
}
node->dsqlCursorName = dsqlCursorName;
node->dsqlSkipLocked = dsqlSkipLocked;
if (dsqlCursorName.hasData() && dsqlScratch->isPsql())
{
@ -6603,7 +6690,7 @@ StmtNode* ModifyNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, bool up
PASS1_limit(dsqlScratch, dsqlRows->length, dsqlRows->skip, rse);
if (dsqlSkipLocked)
rse->flags |= RseNode::FLAG_WRITELOCK | RseNode::FLAG_SKIP_LOCKED;
rse->flags |= RseNode::FLAG_SKIP_LOCKED;
}
node->dsqlReturning = dsqlProcessReturning(dsqlScratch,
@ -6664,6 +6751,7 @@ string ModifyNode::internalPrint(NodePrinter& printer) const
NODE_PRINT(printer, dsqlRseFlags);
NODE_PRINT(printer, dsqlRse);
NODE_PRINT(printer, dsqlContext);
NODE_PRINT(printer, dsqlSkipLocked);
NODE_PRINT(printer, statement);
NODE_PRINT(printer, statement2);
NODE_PRINT(printer, subMod);
@ -7013,6 +7101,12 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg
SavepointChangeMarker scMarker(transaction);
// Prepare to undo changes by PRE-triggers if record is locked by another
// transaction and update should be skipped.
const bool skipLocked = orgRpb->rpb_stream_flags & RPB_s_skipLocked;
CondSavepointAndMarker spPreTriggers(tdbb, transaction,
skipLocked && !(transaction->tra_flags & TRA_system) && relation->rel_pre_modify);
preModifyEraseTriggers(tdbb, &relation->rel_pre_modify, whichTrig, orgRpb, newRpb,
TRIGGER_UPDATE);
@ -7025,24 +7119,31 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg
VirtualTable::modify(tdbb, orgRpb, newRpb);
else if (!relation->rel_view_rse)
{
// 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.
// VIO_modify returns false if:
// a) 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.
// b) record is locked by another transaction and should be skipped.
if (!VIO_modify(tdbb, orgRpb, newRpb, transaction))
{
forceWriteLock(tdbb, orgRpb, transaction);
if (!skipLocked)
{
spPreTriggers.release();
forceWriteLock(tdbb, orgRpb, transaction);
if (!forNode)
restartRequest(request, transaction);
if (!forNode)
restartRequest(request, transaction);
forNode->setWriteLockMode(request);
forNode->setWriteLockMode(request);
}
return parentStmt;
}
IDX_modify(tdbb, orgRpb, newRpb, transaction);
REPL_modify(tdbb, orgRpb, newRpb, transaction);
}
spPreTriggers.release();
newRpb->rpb_number = orgRpb->rpb_number;
newRpb->rpb_number.setValid(true);
@ -7113,7 +7214,7 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg
if (orgRpb->rpb_runtime_flags & RPB_refetch)
{
VIO_refetch_record(tdbb, orgRpb, transaction, RecordLock::NONE, false);
VIO_refetch_record(tdbb, orgRpb, transaction, false, false);
orgRpb->rpb_runtime_flags &= ~RPB_refetch;
}
@ -10570,13 +10671,13 @@ static void cleanupRpb(thread_db* tdbb, record_param* rpb)
// Try to set write lock on record until success or record exists
static void forceWriteLock(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
{
while (VIO_refetch_record(tdbb, rpb, transaction, RecordLock::LOCK, true))
while (VIO_refetch_record(tdbb, rpb, transaction, true, true))
{
rpb->rpb_runtime_flags &= ~RPB_refetch;
// VIO_writelock returns false if record has been deleted or modified
// by someone else.
if (VIO_writelock(tdbb, rpb, transaction, false) == WriteLockResult::LOCKED)
if (VIO_writelock(tdbb, rpb, transaction) == WriteLockResult::LOCKED)
break;
}
}

View File

@ -568,6 +568,7 @@ public:
dsql_ctx* dsqlContext = nullptr;
NestConst<StmtNode> statement;
NestConst<StmtNode> subStatement;
NestConst<StmtNode> returningStatement;
NestConst<ForNode> forNode; // parent implicit cursor, if present
StreamType stream = 0;
unsigned marks = 0; // see StmtNode::IUD_MARK_xxx

View File

@ -309,8 +309,7 @@
#define blr_agg_list (unsigned char)170
#define blr_agg_list_distinct (unsigned char)171
#define blr_modify2 (unsigned char)172
// unused codes: 173
#define blr_erase2 (unsigned char)173
/* FB 1.0 specific BLR */

View File

@ -620,9 +620,12 @@ Savepoint* Savepoint::release(Savepoint* prior)
// AutoSavePoint implementation
AutoSavePoint::AutoSavePoint(thread_db* tdbb, jrd_tra* trans)
AutoSavePoint::AutoSavePoint(thread_db* tdbb, jrd_tra* trans, bool cond)
: m_tdbb(tdbb), m_transaction(trans), m_number(0)
{
if (!cond)
return;
const auto savepoint = trans->startSavepoint();
m_number = savepoint->getNumber();
}

View File

@ -291,6 +291,15 @@ namespace Jrd
++m_savepoint->m_count;
}
void done()
{
if (m_savepoint)
{
--m_savepoint->m_count;
m_savepoint = nullptr;
}
}
~ChangeMarker()
{
if (m_savepoint)
@ -301,7 +310,7 @@ namespace Jrd
ChangeMarker(const ChangeMarker&);
ChangeMarker& operator=(const ChangeMarker&);
Savepoint* const m_savepoint;
Savepoint* m_savepoint;
};
private:
@ -330,7 +339,7 @@ namespace Jrd
class AutoSavePoint
{
public:
AutoSavePoint(thread_db* tdbb, jrd_tra* trans);
AutoSavePoint(thread_db* tdbb, jrd_tra* trans, bool cond = true);
~AutoSavePoint();
void release();

View File

@ -191,6 +191,9 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb)
if (tail->csb_flags & csb_unstable)
rpb->rpb_stream_flags |= RPB_s_unstable;
if (tail->csb_flags & csb_skip_locked)
rpb->rpb_stream_flags |= RPB_s_skipLocked;
rpb->rpb_relation = tail->csb_relation;
delete tail->csb_fields;

View File

@ -199,7 +199,7 @@ static const struct
{"agg_list", two}, // 170
{"agg_list_distinct", two},
{"modify2", modify2},
{NULL, NULL},
{"erase2", erase2},
// New BLR in FB1
{"current_role", zero},
{"skip", one},

View File

@ -118,6 +118,7 @@ const int csb_erase = 256; // we are processing an erase
const int csb_unmatched = 512; // stream has conjuncts unmatched by any index
const int csb_update = 1024; // erase or modify for relation
const int csb_unstable = 2048; // unstable explicit cursor
const int csb_skip_locked = 4096; // skip locked record
// Aggregate Sort Block (for DISTINCT aggregates)

View File

@ -1024,13 +1024,20 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack)
if (invariantBoolean)
rsb = FB_NEW_POOL(getPool()) PreFilteredStream(csb, rsb, invariantBoolean);
// Handle SKIP, WITH LOCK and FIRST.
// The SKIP must (if present) appear in the rsb list deeper than FIRST.
// WITH LOCK must appear between them to work correct with SKIP LOCKED.
// Handle first and/or skip. The skip MUST (if present)
// appear in the rsb list AFTER the first. Since the gen_first and gen_skip
// functions add their nodes at the beginning of the rsb list we MUST call
// gen_skip before gen_first.
if (rse->rse_skip)
rsb = FB_NEW_POOL(getPool()) SkipRowsStream(csb, rsb, rse->rse_skip);
if (rse->rse_first)
rsb = FB_NEW_POOL(getPool()) FirstRowsStream(csb, rsb, rse->rse_first);
if (rse->isSingular())
rsb = FB_NEW_POOL(getPool()) SingularStream(csb, rsb);
if (rse->hasWriteLock())
{
for (const auto compileStream : compileStreams)
@ -1045,14 +1052,16 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack)
SCL_update, obj_relations, tail->csb_relation->rel_name);
}
rsb = FB_NEW_POOL(getPool()) LockedStream(csb, rsb, rse->hasSkipLocked());
rsb = FB_NEW_POOL(getPool()) LockedStream(csb, rsb);
}
if (rse->rse_first)
rsb = FB_NEW_POOL(getPool()) FirstRowsStream(csb, rsb, rse->rse_first);
if (rse->isSingular())
rsb = FB_NEW_POOL(getPool()) SingularStream(csb, rsb);
if (rse->hasSkipLocked())
{
for (const auto compileStream : compileStreams)
{
csb->csb_rpt[compileStream].csb_flags |= csb_skip_locked;
}
}
if (rse->isScrollable())
rsb = FB_NEW_POOL(getPool()) BufferedStream(csb, rsb);

View File

@ -1408,13 +1408,6 @@ RseNode* PAR_rse(thread_db* tdbb, CompilerScratch* csb, SSHORT rse_op)
break;
case blr_skip_locked:
if (!rse->hasWriteLock())
{
PAR_error(csb,
Arg::Gds(isc_random) <<
"blr_skip_locked cannot be used without previous blr_writelock",
false);
}
rse->flags |= RseNode::FLAG_SKIP_LOCKED;
break;

View File

@ -109,7 +109,7 @@ bool BaseAggWinStream<ThisType, NextType>::refetchRecord(thread_db* tdbb) const
}
template <typename ThisType, typename NextType>
WriteLockResult BaseAggWinStream<ThisType, NextType>::lockRecord(thread_db* /*tdbb*/, bool /*skipLocked*/) const
WriteLockResult BaseAggWinStream<ThisType, NextType>::lockRecord(thread_db* /*tdbb*/) const
{
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
}

View File

@ -309,9 +309,9 @@ bool BufferedStream::refetchRecord(thread_db* tdbb) const
return m_next->refetchRecord(tdbb);
}
WriteLockResult BufferedStream::lockRecord(thread_db* tdbb, bool skipLocked) const
WriteLockResult BufferedStream::lockRecord(thread_db* tdbb) const
{
return m_next->lockRecord(tdbb, skipLocked);
return m_next->lockRecord(tdbb);
}
void BufferedStream::getChildren(Array<const RecordSource*>& children) const

View File

@ -104,7 +104,7 @@ bool ConditionalStream::refetchRecord(thread_db* tdbb) const
return impure->irsb_next->refetchRecord(tdbb);
}
WriteLockResult ConditionalStream::lockRecord(thread_db* tdbb, bool skipLocked) const
WriteLockResult ConditionalStream::lockRecord(thread_db* tdbb) const
{
Request* const request = tdbb->getRequest();
Impure* const impure = request->getImpure<Impure>(m_impure);
@ -112,7 +112,7 @@ WriteLockResult ConditionalStream::lockRecord(thread_db* tdbb, bool skipLocked)
if (!(impure->irsb_flags & irsb_open))
return WriteLockResult::CONFLICTED;
return impure->irsb_next->lockRecord(tdbb, skipLocked);
return impure->irsb_next->lockRecord(tdbb);
}
void ConditionalStream::getChildren(Array<const RecordSource*>& children) const

View File

@ -108,7 +108,7 @@ bool ExternalTableScan::refetchRecord(thread_db* /*tdbb*/) const
return true;
}
WriteLockResult ExternalTableScan::lockRecord(thread_db* tdbb, bool skipLocked) const
WriteLockResult ExternalTableScan::lockRecord(thread_db* tdbb) const
{
SET_TDBB(tdbb);

View File

@ -122,9 +122,9 @@ bool FilteredStream::refetchRecord(thread_db* tdbb) const
m_boolean->execute(tdbb, request);
}
WriteLockResult FilteredStream::lockRecord(thread_db* tdbb, bool skipLocked) const
WriteLockResult FilteredStream::lockRecord(thread_db* tdbb) const
{
return m_next->lockRecord(tdbb, skipLocked);
return m_next->lockRecord(tdbb);
}
void FilteredStream::getChildren(Array<const RecordSource*>& children) const

View File

@ -115,9 +115,9 @@ bool FirstRowsStream::refetchRecord(thread_db* tdbb) const
return m_next->refetchRecord(tdbb);
}
WriteLockResult FirstRowsStream::lockRecord(thread_db* tdbb, bool skipLocked) const
WriteLockResult FirstRowsStream::lockRecord(thread_db* tdbb) const
{
return m_next->lockRecord(tdbb, skipLocked);
return m_next->lockRecord(tdbb);
}
void FirstRowsStream::getChildren(Array<const RecordSource*>& children) const

View File

@ -105,7 +105,7 @@ bool FullOuterJoin::refetchRecord(thread_db* /*tdbb*/) const
return true;
}
WriteLockResult FullOuterJoin::lockRecord(thread_db* tdbb, bool skipLocked) const
WriteLockResult FullOuterJoin::lockRecord(thread_db* tdbb) const
{
SET_TDBB(tdbb);

View File

@ -449,7 +449,7 @@ bool HashJoin::refetchRecord(thread_db* /*tdbb*/) const
return true;
}
WriteLockResult HashJoin::lockRecord(thread_db* /*tdbb*/, bool /*skipLocked*/) const
WriteLockResult HashJoin::lockRecord(thread_db* /*tdbb*/) const
{
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
}

View File

@ -108,7 +108,7 @@ bool LocalTableStream::refetchRecord(thread_db* tdbb) const
return true;
}
WriteLockResult LocalTableStream::lockRecord(thread_db* tdbb, bool skipLocked) const
WriteLockResult LocalTableStream::lockRecord(thread_db* tdbb) const
{
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
}

View File

@ -35,10 +35,9 @@ using namespace Jrd;
// Data access: stream locked for write
// ------------------------------------
LockedStream::LockedStream(CompilerScratch* csb, RecordSource* next, bool skipLocked)
LockedStream::LockedStream(CompilerScratch* csb, RecordSource* next)
: RecordSource(csb),
m_next(next),
m_skipLocked(skipLocked)
m_next(next)
{
fb_assert(m_next);
@ -86,7 +85,7 @@ bool LockedStream::internalGetRecord(thread_db* tdbb) const
{
do {
// Attempt to lock the record
const auto lockResult = m_next->lockRecord(tdbb, m_skipLocked);
const auto lockResult = m_next->lockRecord(tdbb);
if (lockResult == WriteLockResult::LOCKED)
return true; // locked
@ -106,9 +105,9 @@ bool LockedStream::refetchRecord(thread_db* tdbb) const
return m_next->refetchRecord(tdbb);
}
WriteLockResult LockedStream::lockRecord(thread_db* tdbb, bool skipLocked) const
WriteLockResult LockedStream::lockRecord(thread_db* tdbb) const
{
return m_next->lockRecord(tdbb, skipLocked);
return m_next->lockRecord(tdbb);
}
void LockedStream::getChildren(Array<const RecordSource*>& children) const

View File

@ -340,7 +340,7 @@ bool MergeJoin::refetchRecord(thread_db* /*tdbb*/) const
return true;
}
WriteLockResult MergeJoin::lockRecord(thread_db* /*tdbb*/, bool /*skipLocked*/) const
WriteLockResult MergeJoin::lockRecord(thread_db* /*tdbb*/) const
{
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
}

View File

@ -203,7 +203,7 @@ bool NestedLoopJoin::refetchRecord(thread_db* /*tdbb*/) const
return true;
}
WriteLockResult NestedLoopJoin::lockRecord(thread_db* /*tdbb*/, bool /*skipLocked*/) const
WriteLockResult NestedLoopJoin::lockRecord(thread_db* /*tdbb*/) const
{
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
}

View File

@ -244,7 +244,7 @@ bool ProcedureScan::refetchRecord(thread_db* /*tdbb*/) const
return true;
}
WriteLockResult ProcedureScan::lockRecord(thread_db* /*tdbb*/, bool /*skipLocked*/) const
WriteLockResult ProcedureScan::lockRecord(thread_db* /*tdbb*/) const
{
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
}

View File

@ -240,7 +240,7 @@ bool RecordStream::refetchRecord(thread_db* tdbb) const
if (rpb->rpb_runtime_flags & RPB_refetch)
{
if (VIO_refetch_record(tdbb, rpb, transaction, RecordLock::LOCK, false))
if (VIO_refetch_record(tdbb, rpb, transaction, false, false))
{
rpb->rpb_runtime_flags &= ~RPB_refetch;
return true;
@ -250,7 +250,7 @@ bool RecordStream::refetchRecord(thread_db* tdbb) const
return false;
}
WriteLockResult RecordStream::lockRecord(thread_db* tdbb, bool skipLocked) const
WriteLockResult RecordStream::lockRecord(thread_db* tdbb) const
{
Request* const request = tdbb->getRequest();
jrd_tra* const transaction = request->req_transaction;
@ -262,7 +262,7 @@ WriteLockResult RecordStream::lockRecord(thread_db* tdbb, bool skipLocked) const
RLCK_reserve_relation(tdbb, transaction, relation, true);
return VIO_writelock(tdbb, rpb, transaction, skipLocked);
return VIO_writelock(tdbb, rpb, transaction);
}
void RecordStream::markRecursive()

View File

@ -88,7 +88,7 @@ namespace Jrd
virtual void close(thread_db* tdbb) const = 0;
virtual bool refetchRecord(thread_db* tdbb) const = 0;
virtual WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const = 0;
virtual WriteLockResult lockRecord(thread_db* tdbb) const = 0;
virtual void markRecursive() = 0;
virtual void invalidateRecords(Request* request) const = 0;
@ -160,7 +160,7 @@ namespace Jrd
RecordStream(CompilerScratch* csb, StreamType stream, const Format* format = NULL);
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void markRecursive() override;
void invalidateRecords(Request* request) const override;
@ -315,7 +315,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -340,7 +340,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -376,7 +376,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -409,7 +409,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -436,12 +436,12 @@ namespace Jrd
class LockedStream : public RecordSource
{
public:
LockedStream(CompilerScratch* csb, RecordSource* next, bool skipLocked);
LockedStream(CompilerScratch* csb, RecordSource* next);
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -460,7 +460,6 @@ namespace Jrd
private:
NestConst<RecordSource> m_next;
const bool m_skipLocked;
};
class FirstRowsStream : public RecordSource
@ -476,7 +475,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -516,7 +515,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -552,7 +551,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -673,7 +672,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -829,7 +828,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void markRecursive() override;
void invalidateRecords(Request* request) const override;
@ -1011,7 +1010,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -1079,7 +1078,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -1123,7 +1122,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -1156,7 +1155,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -1210,7 +1209,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -1276,7 +1275,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -1314,7 +1313,7 @@ namespace Jrd
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void print(thread_db* tdbb, Firebird::string& plan,
bool detailed, unsigned level, bool recurse) const override;
@ -1342,7 +1341,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -1387,7 +1386,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -1429,7 +1428,7 @@ namespace Jrd
void close(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;

View File

@ -233,7 +233,7 @@ bool RecursiveStream::refetchRecord(thread_db* /*tdbb*/) const
return true;
}
WriteLockResult RecursiveStream::lockRecord(thread_db* /*tdbb*/, bool /*skipLocked*/) const
WriteLockResult RecursiveStream::lockRecord(thread_db* /*tdbb*/) const
{
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
}

View File

@ -141,9 +141,9 @@ bool SingularStream::refetchRecord(thread_db* tdbb) const
return m_next->refetchRecord(tdbb);
}
WriteLockResult SingularStream::lockRecord(thread_db* tdbb, bool skipLocked) const
WriteLockResult SingularStream::lockRecord(thread_db* tdbb) const
{
return m_next->lockRecord(tdbb, skipLocked);
return m_next->lockRecord(tdbb);
}
void SingularStream::getChildren(Array<const RecordSource*>& children) const

View File

@ -111,9 +111,9 @@ bool SkipRowsStream::refetchRecord(thread_db* tdbb) const
return m_next->refetchRecord(tdbb);
}
WriteLockResult SkipRowsStream::lockRecord(thread_db* tdbb, bool skipLocked) const
WriteLockResult SkipRowsStream::lockRecord(thread_db* tdbb) const
{
return m_next->lockRecord(tdbb, skipLocked);
return m_next->lockRecord(tdbb);
}
void SkipRowsStream::getChildren(Array<const RecordSource*>& children) const

View File

@ -116,9 +116,9 @@ bool SortedStream::refetchRecord(thread_db* tdbb) const
return m_next->refetchRecord(tdbb);
}
WriteLockResult SortedStream::lockRecord(thread_db* tdbb, bool skipLocked) const
WriteLockResult SortedStream::lockRecord(thread_db* tdbb) const
{
return m_next->lockRecord(tdbb, skipLocked);
return m_next->lockRecord(tdbb);
}
void SortedStream::getChildren(Array<const RecordSource*>& children) const
@ -478,7 +478,7 @@ void SortedStream::mapData(thread_db* tdbb, Request* request, UCHAR* data) const
tdbb->bumpRelStats(RuntimeStatistics::RECORD_RPT_READS, relation->rel_id);
if (VIO_chase_record_version(tdbb, &temp, transaction, tdbb->getDefaultPool(), RecordLock::NONE, false))
if (VIO_chase_record_version(tdbb, &temp, transaction, tdbb->getDefaultPool(), false, false))
{
if (!(temp.rpb_runtime_flags & RPB_undo_data))
VIO_data(tdbb, &temp, tdbb->getDefaultPool());

View File

@ -151,7 +151,7 @@ bool Union::refetchRecord(thread_db* tdbb) const
return m_args[impure->irsb_count]->refetchRecord(tdbb);
}
WriteLockResult Union::lockRecord(thread_db* tdbb, bool skipLocked) const
WriteLockResult Union::lockRecord(thread_db* tdbb) const
{
Request* const request = tdbb->getRequest();
Impure* const impure = request->getImpure<Impure>(m_impure);
@ -159,7 +159,7 @@ WriteLockResult Union::lockRecord(thread_db* tdbb, bool skipLocked) const
if (impure->irsb_count >= m_args.getCount())
return WriteLockResult::CONFLICTED;
return m_args[impure->irsb_count]->lockRecord(tdbb, skipLocked);
return m_args[impure->irsb_count]->lockRecord(tdbb);
}
void Union::getChildren(Array<const RecordSource*>& children) const

View File

@ -104,7 +104,7 @@ bool VirtualTableScan::refetchRecord(thread_db* /*tdbb*/) const
return true;
}
WriteLockResult VirtualTableScan::lockRecord(thread_db* /*tdbb*/, bool /*skipLocked*/) const
WriteLockResult VirtualTableScan::lockRecord(thread_db* /*tdbb*/) const
{
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
}

View File

@ -57,7 +57,7 @@ namespace
bool internalGetRecord(thread_db* tdbb) const override;
bool refetchRecord(thread_db* tdbb) const override;
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
WriteLockResult lockRecord(thread_db* tdbb) const override;
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -142,9 +142,9 @@ namespace
return m_next->refetchRecord(tdbb);
}
WriteLockResult BufferedStreamWindow::lockRecord(thread_db* tdbb, bool skipLocked) const
WriteLockResult BufferedStreamWindow::lockRecord(thread_db* tdbb) const
{
return m_next->lockRecord(tdbb, skipLocked);
return m_next->lockRecord(tdbb);
}
void BufferedStreamWindow::getChildren(Array<const RecordSource*>& children) const
@ -399,7 +399,7 @@ bool WindowedStream::refetchRecord(thread_db* tdbb) const
return m_joinedStream->refetchRecord(tdbb);
}
WriteLockResult WindowedStream::lockRecord(thread_db* /*tdbb*/, bool /*skipLocked*/) const
WriteLockResult WindowedStream::lockRecord(thread_db* /*tdbb*/) const
{
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
}

View File

@ -127,6 +127,7 @@ const USHORT RPB_s_no_data = 0x02; // nobody is going to access the data
const USHORT RPB_s_sweeper = 0x04; // garbage collector - skip swept pages
const USHORT RPB_s_unstable = 0x08; // don't use undo log, used with unstable explicit cursors
const USHORT RPB_s_bulk = 0x10; // bulk operation (currently insert only)
const USHORT RPB_s_skipLocked = 0x20; // skip locked record
// Runtime flags

View File

@ -166,7 +166,7 @@ enum class PrepareResult
};
static PrepareResult prepare_update(thread_db*, jrd_tra*, TraNumber commit_tid_read, record_param*,
record_param*, record_param*, PageStack&, TriState writeLockSkipLocked = {});
record_param*, record_param*, PageStack&, bool);
static void protect_system_table_insert(thread_db* tdbb, const Request* req, const jrd_rel* relation,
bool force_flag = false);
@ -688,12 +688,13 @@ inline void check_gbak_cheating_delete(thread_db* tdbb, const jrd_rel* relation)
}
}
inline int wait(thread_db* tdbb, jrd_tra* transaction, const record_param* rpb)
inline int wait(thread_db* tdbb, jrd_tra* transaction, const record_param* rpb, bool probe)
{
if (transaction->getLockWait())
if (!probe && transaction->getLockWait())
tdbb->bumpRelStats(RuntimeStatistics::RECORD_WAITS, rpb->rpb_relation->rel_id);
return TRA_wait(tdbb, transaction, rpb->rpb_transaction_nr, jrd_tra::tra_wait);
return TRA_wait(tdbb, transaction, rpb->rpb_transaction_nr,
probe ? jrd_tra::tra_probe : jrd_tra::tra_wait);
}
inline bool checkGCActive(thread_db* tdbb, record_param* rpb, int& state)
@ -1109,7 +1110,7 @@ void VIO_backout(thread_db* tdbb, record_param* rpb, const jrd_tra* transaction)
bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb,
jrd_tra* transaction, MemoryPool* pool,
RecordLock recordLock, bool noundo)
bool writelock, bool noundo)
{
/**************************************
*
@ -1211,6 +1212,14 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb,
RuntimeStatistics::Accumulator backversions(tdbb, relation,
RuntimeStatistics::RECORD_BACKVERSION_READS);
const bool skipLocked = rpb->rpb_stream_flags & RPB_s_skipLocked;
if (skipLocked && (state == tra_active || state == tra_limbo))
{
CCH_RELEASE(tdbb, &rpb->getWindow(tdbb));
return false;
}
// First, save the record indentifying information to be restored on exit
while (true)
@ -1258,15 +1267,14 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb,
// If the transaction is a read committed and chooses the no version
// option, wait for reads also!
if (recordLock != RecordLock::SKIP &&
(transaction->tra_flags & TRA_read_committed) &&
if ((transaction->tra_flags & TRA_read_committed) &&
!(transaction->tra_flags & TRA_read_consistency) &&
(!(transaction->tra_flags & TRA_rec_version) || recordLock == RecordLock::LOCK))
(!(transaction->tra_flags & TRA_rec_version) || writelock))
{
if (state == tra_limbo)
{
CCH_RELEASE(tdbb, &rpb->getWindow(tdbb));
state = wait(tdbb, transaction, rpb);
state = wait(tdbb, transaction, rpb, false);
if (!DPM_get(tdbb, rpb, LCK_read))
return false;
@ -1297,7 +1305,7 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb,
// of a dead record version.
CCH_RELEASE(tdbb, &rpb->getWindow(tdbb));
state = wait(tdbb, transaction, rpb);
state = wait(tdbb, transaction, rpb, false);
if (state == tra_committed)
state = check_precommitted(transaction, rpb);
@ -1896,13 +1904,17 @@ static bool check_prepare_result(PrepareResult prepare_result, jrd_tra* transact
* read consistency transaction or lock error happens or if request is already
* in update conflict mode. In latter case set TRA_ex_restart flag to correctly
* handle request restart.
* If record should be skipped, return false also.
*
**************************************/
fb_assert(prepare_result != PrepareResult::SKIP_LOCKED);
if (prepare_result == PrepareResult::SUCCESS)
return true;
if ((rpb->rpb_stream_flags & RPB_s_skipLocked) && prepare_result == PrepareResult::SKIP_LOCKED)
return false;
fb_assert(prepare_result != PrepareResult::SKIP_LOCKED);
Request* top_request = request->req_snapshot.m_owner;
const bool restart_ready = top_request &&
@ -1976,7 +1988,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
if (rpb->rpb_runtime_flags & (RPB_refetch | RPB_undo_read))
{
VIO_refetch_record(tdbb, rpb, transaction, RecordLock::NONE, true);
VIO_refetch_record(tdbb, rpb, transaction, false, true);
rpb->rpb_runtime_flags &= ~RPB_refetch;
fb_assert(!(rpb->rpb_runtime_flags & RPB_undo_read));
}
@ -2363,7 +2375,7 @@ bool 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;
const auto prepare_result = prepare_update(tdbb, transaction, tid_fetch, rpb, &temp, 0, stack);
const auto prepare_result = prepare_update(tdbb, transaction, tid_fetch, rpb, &temp, 0, stack, false);
if (!check_prepare_result(prepare_result, transaction, request, rpb))
return false;
@ -2914,7 +2926,7 @@ bool VIO_get(thread_db* tdbb, record_param* rpb, jrd_tra* transaction, MemoryPoo
const USHORT lock_type = (rpb->rpb_stream_flags & RPB_s_update) ? LCK_write : LCK_read;
if (!DPM_get(tdbb, rpb, lock_type) ||
!VIO_chase_record_version(tdbb, rpb, transaction, pool, RecordLock::NONE, false))
!VIO_chase_record_version(tdbb, rpb, transaction, pool, false, false))
{
return false;
}
@ -3082,7 +3094,7 @@ bool VIO_get_current(thread_db* tdbb,
// Wait as long as it takes for an active transaction which has modified
// the record.
state = wait(tdbb, transaction, rpb);
state = wait(tdbb, transaction, rpb, false);
if (state == tra_committed)
state = check_precommitted(transaction, rpb);
@ -3289,7 +3301,7 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
old_record->copyFrom(org_rpb->rpb_record);
}
VIO_refetch_record(tdbb, org_rpb, transaction, RecordLock::NONE, true);
VIO_refetch_record(tdbb, org_rpb, transaction, false, true);
org_rpb->rpb_runtime_flags &= ~RPB_refetch;
fb_assert(!(org_rpb->rpb_runtime_flags & RPB_undo_read));
@ -3671,7 +3683,7 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
record_param temp;
PageStack stack;
const auto prepare_result = prepare_update(tdbb, transaction, org_rpb->rpb_transaction_nr, org_rpb,
&temp, new_rpb, stack);
&temp, new_rpb, stack, false);
if (!check_prepare_result(prepare_result, transaction, tdbb->getRequest(), org_rpb))
return false;
@ -3772,7 +3784,7 @@ bool VIO_next_record(thread_db* tdbb,
{
return false;
}
} while (!VIO_chase_record_version(tdbb, rpb, transaction, pool, RecordLock::NONE, false));
} while (!VIO_chase_record_version(tdbb, rpb, transaction, pool, false, false));
if (rpb->rpb_runtime_flags & RPB_undo_data)
fb_assert(rpb->getWindow(tdbb).win_bdb == NULL);
@ -3850,7 +3862,7 @@ Record* VIO_record(thread_db* tdbb, record_param* rpb, const Format* format, Mem
bool VIO_refetch_record(thread_db* tdbb, record_param* rpb, jrd_tra* transaction,
RecordLock recordLock, bool noundo)
bool writelock, bool noundo)
{
/**************************************
*
@ -3873,9 +3885,9 @@ bool VIO_refetch_record(thread_db* tdbb, record_param* rpb, jrd_tra* transaction
const TraNumber tid_fetch = rpb->rpb_transaction_nr;
if (!DPM_get(tdbb, rpb, LCK_read) ||
!VIO_chase_record_version(tdbb, rpb, transaction, tdbb->getDefaultPool(), recordLock, noundo))
!VIO_chase_record_version(tdbb, rpb, transaction, tdbb->getDefaultPool(), writelock, noundo))
{
if (recordLock == RecordLock::LOCK)
if (writelock)
return false;
ERR_post(Arg::Gds(isc_no_cur_rec));
@ -3899,7 +3911,7 @@ bool VIO_refetch_record(thread_db* tdbb, record_param* rpb, jrd_tra* transaction
// make sure the record has not been updated. Also, punt after
// VIO_data() call which will release the page.
if (recordLock != RecordLock::LOCK &&
if (!writelock &&
(transaction->tra_flags & TRA_read_committed) &&
(tid_fetch != rpb->rpb_transaction_nr) &&
// added to check that it was not current transaction,
@ -4459,7 +4471,7 @@ bool VIO_sweep(thread_db* tdbb, jrd_tra* transaction, TraceSweepEvent* traceSwee
}
WriteLockResult VIO_writelock(thread_db* tdbb, record_param* org_rpb, jrd_tra* transaction, bool skipLocked)
WriteLockResult VIO_writelock(thread_db* tdbb, record_param* org_rpb, jrd_tra* transaction)
{
/**************************************
*
@ -4487,6 +4499,8 @@ WriteLockResult VIO_writelock(thread_db* tdbb, record_param* org_rpb, jrd_tra* t
org_rpb->rpb_f_page, org_rpb->rpb_f_line);
#endif
const bool skipLocked = org_rpb->rpb_stream_flags & RPB_s_skipLocked;
if (transaction->tra_flags & TRA_system)
{
// Explicit locks are not needed in system transactions
@ -4495,7 +4509,7 @@ WriteLockResult VIO_writelock(thread_db* tdbb, record_param* org_rpb, jrd_tra* t
if (org_rpb->rpb_runtime_flags & (RPB_refetch | RPB_undo_read))
{
if (!VIO_refetch_record(tdbb, org_rpb, transaction, (skipLocked ? RecordLock::SKIP : RecordLock::LOCK), true))
if (!VIO_refetch_record(tdbb, org_rpb, transaction, true, true))
return WriteLockResult::CONFLICTED;
org_rpb->rpb_runtime_flags &= ~RPB_refetch;
@ -4551,7 +4565,7 @@ WriteLockResult VIO_writelock(thread_db* tdbb, record_param* org_rpb, jrd_tra* t
record_param temp;
PageStack stack;
switch (prepare_update(tdbb, transaction, org_rpb->rpb_transaction_nr, org_rpb, &temp, &new_rpb,
stack, TriState(skipLocked)))
stack, true))
{
case PrepareResult::DELETED:
if (skipLocked && (transaction->tra_flags & TRA_read_committed))
@ -5997,7 +6011,7 @@ static void notify_garbage_collector(thread_db* tdbb, record_param* rpb, TraNumb
static PrepareResult prepare_update(thread_db* tdbb, jrd_tra* transaction, TraNumber commit_tid_read,
record_param* rpb, record_param* temp, record_param* new_rpb, PageStack& stack, TriState writeLockSkipLocked)
record_param* rpb, record_param* temp, record_param* new_rpb, PageStack& stack, bool writelock)
{
/**************************************
*
@ -6121,6 +6135,7 @@ static PrepareResult prepare_update(thread_db* tdbb, jrd_tra* transaction, TraNu
// was the same one we stored above.
record_param org_rpb;
TraNumber update_conflict_trans = MAX_TRA_NUMBER; //-1;
const bool skipLocked = rpb->rpb_stream_flags & RPB_s_skipLocked;
while (true)
{
org_rpb.rpb_flags = rpb->rpb_flags;
@ -6186,7 +6201,7 @@ static PrepareResult prepare_update(thread_db* tdbb, jrd_tra* transaction, TraNu
delete_record(tdbb, temp, 0, NULL);
if (writeLockSkipLocked.isAssigned() || (transaction->tra_flags & TRA_read_consistency))
if (writelock || skipLocked || (transaction->tra_flags & TRA_read_consistency))
{
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->rel_id);
return PrepareResult::DELETED;
@ -6282,9 +6297,7 @@ static PrepareResult prepare_update(thread_db* tdbb, jrd_tra* transaction, TraNu
// Wait as long as it takes (if not skipping locks) for an active
// transaction which has modified the record.
state = writeLockSkipLocked == true ?
TRA_wait(tdbb, transaction, rpb->rpb_transaction_nr, jrd_tra::tra_probe) :
wait(tdbb, transaction, rpb);
state = wait(tdbb, transaction, rpb, skipLocked);
if (state == tra_committed)
state = check_precommitted(transaction, rpb);
@ -6316,7 +6329,7 @@ static PrepareResult prepare_update(thread_db* tdbb, jrd_tra* transaction, TraNu
{
tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->rel_id);
if (writeLockSkipLocked == true)
if (skipLocked)
return PrepareResult::SKIP_LOCKED;
// Cannot use Arg::Num here because transaction number is 64-bit unsigned integer
@ -6335,7 +6348,7 @@ static PrepareResult prepare_update(thread_db* tdbb, jrd_tra* transaction, TraNu
// fall thru
case tra_active:
return writeLockSkipLocked == true ? PrepareResult::SKIP_LOCKED : PrepareResult::LOCK_ERROR;
return skipLocked ? PrepareResult::SKIP_LOCKED : PrepareResult::LOCK_ERROR;
case tra_dead:
break;

View File

@ -44,13 +44,6 @@ namespace Jrd
DPM_next_pointer_page // data pages from one pointer page
};
enum class RecordLock
{
NONE,
LOCK,
SKIP
};
enum class WriteLockResult
{
LOCKED,
@ -61,7 +54,7 @@ namespace Jrd
void VIO_backout(Jrd::thread_db*, Jrd::record_param*, const Jrd::jrd_tra*);
bool VIO_chase_record_version(Jrd::thread_db*, Jrd::record_param*,
Jrd::jrd_tra*, MemoryPool*, Jrd::RecordLock, bool);
Jrd::jrd_tra*, MemoryPool*, bool, bool);
void VIO_copy_record(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::Record*, Jrd::Record*);
void VIO_data(Jrd::thread_db*, Jrd::record_param*, MemoryPool*);
bool VIO_erase(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*);
@ -72,11 +65,11 @@ bool VIO_get(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*, MemoryPool*);
bool VIO_get_current(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*,
MemoryPool*, bool, bool&);
void VIO_init(Jrd::thread_db*);
Jrd::WriteLockResult VIO_writelock(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*, bool skipLocked);
Jrd::WriteLockResult VIO_writelock(Jrd::thread_db*, 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*, Jrd::FindNextRecordScope);
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*, Jrd::RecordLock, bool);
bool VIO_refetch_record(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*, bool, bool);
void VIO_store(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*);
bool VIO_sweep(Jrd::thread_db*, Jrd::jrd_tra*, Jrd::TraceSweepEvent*);
void VIO_intermediate_gc(Jrd::thread_db* tdbb, Jrd::record_param* rpb, Jrd::jrd_tra* transaction);

View File

@ -411,6 +411,7 @@ static const UCHAR
store3[] = { op_line, op_byte, op_line, op_verb, op_verb, op_verb, 0},
marks[] = { op_byte, op_literal, op_line, op_verb, 0},
erase[] = { op_erase, 0},
erase2[] = { op_erase, op_verb, 0},
local_table[] = { op_word, op_byte, op_literal, op_byte, op_line, 0},
outer_map[] = { op_outer_map, 0 },
in_list[] = { op_line, op_verb, op_indent, op_word, op_line, op_args, 0};