mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 18:43:02 +01:00
Backported feature #7810 : Improve SKIP LOCKED implementation
(#7811 and #7872)
This commit is contained in:
parent
b596b35599
commit
dc2d85c17b
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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},
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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};
|
||||
|
Loading…
Reference in New Issue
Block a user