8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 00:43:02 +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 ## Notes
As it happens with subclauses `FIRST`/`SKIP`/`ROWS`/`OFFSET`/`FETCH` record lock If statement have both `SKIP LOCKED` and `SKIP`/`ROWS` subclauses, some locked rows
(and "skip locked" check) is done in between of skip (`SKIP`/`ROWS`/`OFFSET`/`FETCH`) and could be skipped before `SKIP`/`ROWS` subclause would account it, thus skipping more
limit (`FIRST`/`ROWS`/`OFFSET`/`FETCH`) checks. rows than specified in `SKIP`/`ROWS`.
## Examples ## 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 } // 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(); 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) if (csb->csb_blr_reader.peekByte() == blr_marks)
node->marks |= PAR_marks(csb); node->marks |= PAR_marks(csb);
if (blrOp == blr_erase2)
node->returningStatement = PAR_parse_stmt(tdbb, csb);
return node; return node;
} }
@ -2308,7 +2345,7 @@ StmtNode* EraseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
PASS1_limit(dsqlScratch, dsqlRows->length, dsqlRows->skip, rse); PASS1_limit(dsqlScratch, dsqlRows->length, dsqlRows->skip, rse);
if (dsqlSkipLocked) if (dsqlSkipLocked)
rse->flags |= RseNode::FLAG_WRITELOCK | RseNode::FLAG_SKIP_LOCKED; rse->flags |= RseNode::FLAG_SKIP_LOCKED;
} }
if (dsqlReturning && dsqlScratch->isPsql()) if (dsqlReturning && dsqlScratch->isPsql())
@ -2342,22 +2379,29 @@ string EraseNode::internalPrint(NodePrinter& printer) const
NODE_PRINT(printer, dsqlReturning); NODE_PRINT(printer, dsqlReturning);
NODE_PRINT(printer, dsqlRse); NODE_PRINT(printer, dsqlRse);
NODE_PRINT(printer, dsqlContext); NODE_PRINT(printer, dsqlContext);
NODE_PRINT(printer, dsqlSkipLocked);
NODE_PRINT(printer, statement); NODE_PRINT(printer, statement);
NODE_PRINT(printer, subStatement); NODE_PRINT(printer, subStatement);
NODE_PRINT(printer, returningStatement);
NODE_PRINT(printer, stream); NODE_PRINT(printer, stream);
NODE_PRINT(printer, marks); NODE_PRINT(printer, marks);
return "EraseNode"; return "EraseNode";
} }
// The EraseNode::erase() depends on generated nodes layout in case when
// RETURNING specified.
void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch) void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
{ {
Nullable<USHORT> tableNumber; Nullable<USHORT> tableNumber;
const bool skipLocked = dsqlRse && dsqlRse->hasSkipLocked();
if (dsqlReturning && !dsqlScratch->isPsql()) if (dsqlReturning && !dsqlScratch->isPsql())
{ {
if (dsqlCursorName.isEmpty()) if (dsqlCursorName.isEmpty())
{ {
if (!skipLocked)
dsqlScratch->appendUChar(blr_begin); dsqlScratch->appendUChar(blr_begin);
tableNumber = dsqlScratch->localTableNumber++; tableNumber = dsqlScratch->localTableNumber++;
@ -2379,13 +2423,13 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
const auto* context = dsqlContext ? dsqlContext : dsqlRelation->dsqlContext; const auto* context = dsqlContext ? dsqlContext : dsqlRelation->dsqlContext;
if (dsqlReturning) if (dsqlReturning && !skipLocked)
{ {
dsqlScratch->appendUChar(blr_begin); dsqlScratch->appendUChar(blr_begin);
dsqlGenReturning(dsqlScratch, dsqlReturning, tableNumber); dsqlGenReturning(dsqlScratch, dsqlReturning, tableNumber);
} }
dsqlScratch->appendUChar(blr_erase); dsqlScratch->appendUChar(dsqlReturning && skipLocked ? blr_erase2 : blr_erase);
GEN_stuff_context(dsqlScratch, context); GEN_stuff_context(dsqlScratch, context);
if (marks) if (marks)
@ -2393,12 +2437,16 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
if (dsqlReturning) if (dsqlReturning)
{ {
if (!skipLocked)
dsqlScratch->appendUChar(blr_end); dsqlScratch->appendUChar(blr_end);
else
dsqlGenReturning(dsqlScratch, dsqlReturning, tableNumber);
if (!dsqlScratch->isPsql() && dsqlCursorName.isEmpty()) if (!dsqlScratch->isPsql() && dsqlCursorName.isEmpty())
{ {
dsqlGenReturningLocalTableCursor(dsqlScratch, dsqlReturning, tableNumber.value); dsqlGenReturningLocalTableCursor(dsqlScratch, dsqlReturning, tableNumber.value);
if (!skipLocked)
dsqlScratch->appendUChar(blr_end); dsqlScratch->appendUChar(blr_end);
} }
} }
@ -2411,6 +2459,9 @@ EraseNode* EraseNode::pass1(thread_db* tdbb, CompilerScratch* csb)
doPass1(tdbb, csb, statement.getAddress()); doPass1(tdbb, csb, statement.getAddress());
doPass1(tdbb, csb, subStatement.getAddress()); doPass1(tdbb, csb, subStatement.getAddress());
AutoSetRestore<bool> autoReturningExpr(&csb->csb_returning_expr, true);
doPass1(tdbb, csb, returningStatement.getAddress());
return this; return this;
} }
@ -2527,6 +2578,7 @@ void EraseNode::pass1Erase(thread_db* tdbb, CompilerScratch* csb, EraseNode* nod
EraseNode* EraseNode::pass2(thread_db* tdbb, CompilerScratch* csb) EraseNode* EraseNode::pass2(thread_db* tdbb, CompilerScratch* csb)
{ {
doPass2(tdbb, csb, statement.getAddress(), this); doPass2(tdbb, csb, statement.getAddress(), this);
doPass2(tdbb, csb, returningStatement.getAddress(), this);
doPass2(tdbb, csb, subStatement.getAddress(), this); doPass2(tdbb, csb, subStatement.getAddress(), this);
const jrd_rel* const relation = csb->csb_rpt[stream].csb_relation; 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. // Perform erase operation.
const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger whichTrig) const 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; jrd_tra* transaction = request->req_transaction;
record_param* rpb = &request->req_rpb[stream]; record_param* rpb = &request->req_rpb[stream];
jrd_rel* relation = rpb->rpb_relation; jrd_rel* relation = rpb->rpb_relation;
@ -2630,6 +2684,12 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
} }
case Request::req_return: case Request::req_return:
if (impure->sta_state == 1)
{
impure->sta_state = 0;
rpb->rpb_number.setValid(false);
return parentStmt;
}
break; break;
default: default:
@ -2659,7 +2719,7 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
if (rpb->rpb_runtime_flags & RPB_refetch) 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; rpb->rpb_runtime_flags &= ~RPB_refetch;
} }
@ -2668,6 +2728,12 @@ const StmtNode* EraseNode::erase(thread_db* tdbb, Request* request, WhichTrigger
SavepointChangeMarker scMarker(transaction); 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. // Handle pre-operation trigger.
preModifyEraseTriggers(tdbb, &relation->rel_pre_erase, whichTrig, rpb, NULL, TRIGGER_DELETE); 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); VirtualTable::erase(tdbb, rpb);
else if (!relation->rel_view_rse) else if (!relation->rel_view_rse)
{ {
// VIO_erase returns false if there is an update conflict in Read Consistency // VIO_erase returns false if:
// transaction. Before returning false it disables statement-level snapshot // a) there is an update conflict in Read Consistency transaction.
// (via setting req_update_conflict flag) so re-fetch should see new data. // 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)) 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); forceWriteLock(tdbb, rpb, transaction);
if (!forNode) if (!forNode)
restartRequest(request, transaction); restartRequest(request, transaction);
forNode->setWriteLockMode(request); forNode->setWriteLockMode(request);
return parentStmt; return forNode;
} }
REPL_erase(tdbb, rpb, transaction); REPL_erase(tdbb, rpb, transaction);
} }
spPreTriggers.release();
// Handle post operation trigger. // Handle post operation trigger.
if (relation->rel_post_erase && whichTrig != PRE_TRIG) 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); rpb->rpb_number.setValid(false);
return parentStmt; return parentStmt;
@ -6497,6 +6583,7 @@ StmtNode* ModifyNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, bool up
} }
node->dsqlCursorName = dsqlCursorName; node->dsqlCursorName = dsqlCursorName;
node->dsqlSkipLocked = dsqlSkipLocked;
if (dsqlCursorName.hasData() && dsqlScratch->isPsql()) if (dsqlCursorName.hasData() && dsqlScratch->isPsql())
{ {
@ -6603,7 +6690,7 @@ StmtNode* ModifyNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, bool up
PASS1_limit(dsqlScratch, dsqlRows->length, dsqlRows->skip, rse); PASS1_limit(dsqlScratch, dsqlRows->length, dsqlRows->skip, rse);
if (dsqlSkipLocked) if (dsqlSkipLocked)
rse->flags |= RseNode::FLAG_WRITELOCK | RseNode::FLAG_SKIP_LOCKED; rse->flags |= RseNode::FLAG_SKIP_LOCKED;
} }
node->dsqlReturning = dsqlProcessReturning(dsqlScratch, node->dsqlReturning = dsqlProcessReturning(dsqlScratch,
@ -6664,6 +6751,7 @@ string ModifyNode::internalPrint(NodePrinter& printer) const
NODE_PRINT(printer, dsqlRseFlags); NODE_PRINT(printer, dsqlRseFlags);
NODE_PRINT(printer, dsqlRse); NODE_PRINT(printer, dsqlRse);
NODE_PRINT(printer, dsqlContext); NODE_PRINT(printer, dsqlContext);
NODE_PRINT(printer, dsqlSkipLocked);
NODE_PRINT(printer, statement); NODE_PRINT(printer, statement);
NODE_PRINT(printer, statement2); NODE_PRINT(printer, statement2);
NODE_PRINT(printer, subMod); NODE_PRINT(printer, subMod);
@ -7013,6 +7101,12 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg
SavepointChangeMarker scMarker(transaction); 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, preModifyEraseTriggers(tdbb, &relation->rel_pre_modify, whichTrig, orgRpb, newRpb,
TRIGGER_UPDATE); TRIGGER_UPDATE);
@ -7025,24 +7119,31 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg
VirtualTable::modify(tdbb, orgRpb, newRpb); VirtualTable::modify(tdbb, orgRpb, newRpb);
else if (!relation->rel_view_rse) else if (!relation->rel_view_rse)
{ {
// VIO_modify returns false if there is an update conflict in Read Consistency // VIO_modify returns false if:
// transaction. Before returning false it disables statement-level snapshot // a) there is an update conflict in Read Consistency transaction.
// (via setting req_update_conflict flag) so re-fetch should see new data. // 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)) if (!VIO_modify(tdbb, orgRpb, newRpb, transaction))
{ {
if (!skipLocked)
{
spPreTriggers.release();
forceWriteLock(tdbb, orgRpb, transaction); forceWriteLock(tdbb, orgRpb, transaction);
if (!forNode) if (!forNode)
restartRequest(request, transaction); restartRequest(request, transaction);
forNode->setWriteLockMode(request); forNode->setWriteLockMode(request);
}
return parentStmt; return parentStmt;
} }
IDX_modify(tdbb, orgRpb, newRpb, transaction); IDX_modify(tdbb, orgRpb, newRpb, transaction);
REPL_modify(tdbb, orgRpb, newRpb, transaction); REPL_modify(tdbb, orgRpb, newRpb, transaction);
} }
spPreTriggers.release();
newRpb->rpb_number = orgRpb->rpb_number; newRpb->rpb_number = orgRpb->rpb_number;
newRpb->rpb_number.setValid(true); 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) 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; 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 // Try to set write lock on record until success or record exists
static void forceWriteLock(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) 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; rpb->rpb_runtime_flags &= ~RPB_refetch;
// VIO_writelock returns false if record has been deleted or modified // VIO_writelock returns false if record has been deleted or modified
// by someone else. // by someone else.
if (VIO_writelock(tdbb, rpb, transaction, false) == WriteLockResult::LOCKED) if (VIO_writelock(tdbb, rpb, transaction) == WriteLockResult::LOCKED)
break; break;
} }
} }

View File

@ -568,6 +568,7 @@ public:
dsql_ctx* dsqlContext = nullptr; dsql_ctx* dsqlContext = nullptr;
NestConst<StmtNode> statement; NestConst<StmtNode> statement;
NestConst<StmtNode> subStatement; NestConst<StmtNode> subStatement;
NestConst<StmtNode> returningStatement;
NestConst<ForNode> forNode; // parent implicit cursor, if present NestConst<ForNode> forNode; // parent implicit cursor, if present
StreamType stream = 0; StreamType stream = 0;
unsigned marks = 0; // see StmtNode::IUD_MARK_xxx 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 (unsigned char)170
#define blr_agg_list_distinct (unsigned char)171 #define blr_agg_list_distinct (unsigned char)171
#define blr_modify2 (unsigned char)172 #define blr_modify2 (unsigned char)172
#define blr_erase2 (unsigned char)173
// unused codes: 173
/* FB 1.0 specific BLR */ /* FB 1.0 specific BLR */

View File

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

View File

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

View File

@ -191,6 +191,9 @@ Statement::Statement(thread_db* tdbb, MemoryPool* p, CompilerScratch* csb)
if (tail->csb_flags & csb_unstable) if (tail->csb_flags & csb_unstable)
rpb->rpb_stream_flags |= RPB_s_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; rpb->rpb_relation = tail->csb_relation;
delete tail->csb_fields; delete tail->csb_fields;

View File

@ -199,7 +199,7 @@ static const struct
{"agg_list", two}, // 170 {"agg_list", two}, // 170
{"agg_list_distinct", two}, {"agg_list_distinct", two},
{"modify2", modify2}, {"modify2", modify2},
{NULL, NULL}, {"erase2", erase2},
// New BLR in FB1 // New BLR in FB1
{"current_role", zero}, {"current_role", zero},
{"skip", one}, {"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_unmatched = 512; // stream has conjuncts unmatched by any index
const int csb_update = 1024; // erase or modify for relation const int csb_update = 1024; // erase or modify for relation
const int csb_unstable = 2048; // unstable explicit cursor const int csb_unstable = 2048; // unstable explicit cursor
const int csb_skip_locked = 4096; // skip locked record
// Aggregate Sort Block (for DISTINCT aggregates) // Aggregate Sort Block (for DISTINCT aggregates)

View File

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

View File

@ -109,7 +109,7 @@ bool BaseAggWinStream<ThisType, NextType>::refetchRecord(thread_db* tdbb) const
} }
template <typename ThisType, typename NextType> 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)); 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); 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 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); 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(); Request* const request = tdbb->getRequest();
Impure* const impure = request->getImpure<Impure>(m_impure); 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)) if (!(impure->irsb_flags & irsb_open))
return WriteLockResult::CONFLICTED; return WriteLockResult::CONFLICTED;
return impure->irsb_next->lockRecord(tdbb, skipLocked); return impure->irsb_next->lockRecord(tdbb);
} }
void ConditionalStream::getChildren(Array<const RecordSource*>& children) const void ConditionalStream::getChildren(Array<const RecordSource*>& children) const

View File

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

View File

@ -122,9 +122,9 @@ bool FilteredStream::refetchRecord(thread_db* tdbb) const
m_boolean->execute(tdbb, request); 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 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); 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 void FirstRowsStream::getChildren(Array<const RecordSource*>& children) const

View File

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

View File

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

View File

@ -340,7 +340,7 @@ bool MergeJoin::refetchRecord(thread_db* /*tdbb*/) const
return true; 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)); 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; 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)); 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; 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)); 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 (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; rpb->rpb_runtime_flags &= ~RPB_refetch;
return true; return true;
@ -250,7 +250,7 @@ bool RecordStream::refetchRecord(thread_db* tdbb) const
return false; return false;
} }
WriteLockResult RecordStream::lockRecord(thread_db* tdbb, bool skipLocked) const WriteLockResult RecordStream::lockRecord(thread_db* tdbb) const
{ {
Request* const request = tdbb->getRequest(); Request* const request = tdbb->getRequest();
jrd_tra* const transaction = request->req_transaction; 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); RLCK_reserve_relation(tdbb, transaction, relation, true);
return VIO_writelock(tdbb, rpb, transaction, skipLocked); return VIO_writelock(tdbb, rpb, transaction);
} }
void RecordStream::markRecursive() void RecordStream::markRecursive()

View File

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

View File

@ -233,7 +233,7 @@ bool RecursiveStream::refetchRecord(thread_db* /*tdbb*/) const
return true; 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)); 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); 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 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); 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 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); 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 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); 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)) if (!(temp.rpb_runtime_flags & RPB_undo_data))
VIO_data(tdbb, &temp, tdbb->getDefaultPool()); 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); 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(); Request* const request = tdbb->getRequest();
Impure* const impure = request->getImpure<Impure>(m_impure); 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()) if (impure->irsb_count >= m_args.getCount())
return WriteLockResult::CONFLICTED; 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 void Union::getChildren(Array<const RecordSource*>& children) const

View File

@ -104,7 +104,7 @@ bool VirtualTableScan::refetchRecord(thread_db* /*tdbb*/) const
return true; 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)); 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 internalGetRecord(thread_db* tdbb) const override;
bool refetchRecord(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; void getChildren(Firebird::Array<const RecordSource*>& children) const override;
@ -142,9 +142,9 @@ namespace
return m_next->refetchRecord(tdbb); 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 void BufferedStreamWindow::getChildren(Array<const RecordSource*>& children) const
@ -399,7 +399,7 @@ bool WindowedStream::refetchRecord(thread_db* tdbb) const
return m_joinedStream->refetchRecord(tdbb); 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)); 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_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_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_bulk = 0x10; // bulk operation (currently insert only)
const USHORT RPB_s_skipLocked = 0x20; // skip locked record
// Runtime flags // 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*, 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, static void protect_system_table_insert(thread_db* tdbb, const Request* req, const jrd_rel* relation,
bool force_flag = false); 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); 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) 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, bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb,
jrd_tra* transaction, MemoryPool* pool, 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::Accumulator backversions(tdbb, relation,
RuntimeStatistics::RECORD_BACKVERSION_READS); 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 // First, save the record indentifying information to be restored on exit
while (true) 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 // If the transaction is a read committed and chooses the no version
// option, wait for reads also! // option, wait for reads also!
if (recordLock != RecordLock::SKIP && if ((transaction->tra_flags & TRA_read_committed) &&
(transaction->tra_flags & TRA_read_committed) &&
!(transaction->tra_flags & TRA_read_consistency) && !(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) if (state == tra_limbo)
{ {
CCH_RELEASE(tdbb, &rpb->getWindow(tdbb)); CCH_RELEASE(tdbb, &rpb->getWindow(tdbb));
state = wait(tdbb, transaction, rpb); state = wait(tdbb, transaction, rpb, false);
if (!DPM_get(tdbb, rpb, LCK_read)) if (!DPM_get(tdbb, rpb, LCK_read))
return false; return false;
@ -1297,7 +1305,7 @@ bool VIO_chase_record_version(thread_db* tdbb, record_param* rpb,
// of a dead record version. // of a dead record version.
CCH_RELEASE(tdbb, &rpb->getWindow(tdbb)); CCH_RELEASE(tdbb, &rpb->getWindow(tdbb));
state = wait(tdbb, transaction, rpb); state = wait(tdbb, transaction, rpb, false);
if (state == tra_committed) if (state == tra_committed)
state = check_precommitted(transaction, rpb); 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 * 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 * in update conflict mode. In latter case set TRA_ex_restart flag to correctly
* handle request restart. * handle request restart.
* If record should be skipped, return false also.
* *
**************************************/ **************************************/
fb_assert(prepare_result != PrepareResult::SKIP_LOCKED);
if (prepare_result == PrepareResult::SUCCESS) if (prepare_result == PrepareResult::SUCCESS)
return true; 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; Request* top_request = request->req_snapshot.m_owner;
const bool restart_ready = top_request && 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)) 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; rpb->rpb_runtime_flags &= ~RPB_refetch;
fb_assert(!(rpb->rpb_runtime_flags & RPB_undo_read)); 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 // Update stub didn't find one page -- do a long, hard update
PageStack stack; 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)) if (!check_prepare_result(prepare_result, transaction, request, rpb))
return false; 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; const USHORT lock_type = (rpb->rpb_stream_flags & RPB_s_update) ? LCK_write : LCK_read;
if (!DPM_get(tdbb, rpb, lock_type) || 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; 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 // Wait as long as it takes for an active transaction which has modified
// the record. // the record.
state = wait(tdbb, transaction, rpb); state = wait(tdbb, transaction, rpb, false);
if (state == tra_committed) if (state == tra_committed)
state = check_precommitted(transaction, rpb); 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); 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; org_rpb->rpb_runtime_flags &= ~RPB_refetch;
fb_assert(!(org_rpb->rpb_runtime_flags & RPB_undo_read)); 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; record_param temp;
PageStack stack; PageStack stack;
const auto prepare_result = prepare_update(tdbb, transaction, org_rpb->rpb_transaction_nr, org_rpb, 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)) if (!check_prepare_result(prepare_result, transaction, tdbb->getRequest(), org_rpb))
return false; return false;
@ -3772,7 +3784,7 @@ bool VIO_next_record(thread_db* tdbb,
{ {
return false; 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) if (rpb->rpb_runtime_flags & RPB_undo_data)
fb_assert(rpb->getWindow(tdbb).win_bdb == NULL); 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, 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; const TraNumber tid_fetch = rpb->rpb_transaction_nr;
if (!DPM_get(tdbb, rpb, LCK_read) || 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; return false;
ERR_post(Arg::Gds(isc_no_cur_rec)); 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 // make sure the record has not been updated. Also, punt after
// VIO_data() call which will release the page. // VIO_data() call which will release the page.
if (recordLock != RecordLock::LOCK && if (!writelock &&
(transaction->tra_flags & TRA_read_committed) && (transaction->tra_flags & TRA_read_committed) &&
(tid_fetch != rpb->rpb_transaction_nr) && (tid_fetch != rpb->rpb_transaction_nr) &&
// added to check that it was not current transaction, // 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); org_rpb->rpb_f_page, org_rpb->rpb_f_line);
#endif #endif
const bool skipLocked = org_rpb->rpb_stream_flags & RPB_s_skipLocked;
if (transaction->tra_flags & TRA_system) if (transaction->tra_flags & TRA_system)
{ {
// Explicit locks are not needed in system transactions // 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 (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; return WriteLockResult::CONFLICTED;
org_rpb->rpb_runtime_flags &= ~RPB_refetch; 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; record_param temp;
PageStack stack; PageStack stack;
switch (prepare_update(tdbb, transaction, org_rpb->rpb_transaction_nr, org_rpb, &temp, &new_rpb, switch (prepare_update(tdbb, transaction, org_rpb->rpb_transaction_nr, org_rpb, &temp, &new_rpb,
stack, TriState(skipLocked))) stack, true))
{ {
case PrepareResult::DELETED: case PrepareResult::DELETED:
if (skipLocked && (transaction->tra_flags & TRA_read_committed)) 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, 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. // was the same one we stored above.
record_param org_rpb; record_param org_rpb;
TraNumber update_conflict_trans = MAX_TRA_NUMBER; //-1; TraNumber update_conflict_trans = MAX_TRA_NUMBER; //-1;
const bool skipLocked = rpb->rpb_stream_flags & RPB_s_skipLocked;
while (true) while (true)
{ {
org_rpb.rpb_flags = rpb->rpb_flags; 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); 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); tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->rel_id);
return PrepareResult::DELETED; 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 // Wait as long as it takes (if not skipping locks) for an active
// transaction which has modified the record. // transaction which has modified the record.
state = writeLockSkipLocked == true ? state = wait(tdbb, transaction, rpb, skipLocked);
TRA_wait(tdbb, transaction, rpb->rpb_transaction_nr, jrd_tra::tra_probe) :
wait(tdbb, transaction, rpb);
if (state == tra_committed) if (state == tra_committed)
state = check_precommitted(transaction, rpb); 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); tdbb->bumpRelStats(RuntimeStatistics::RECORD_CONFLICTS, relation->rel_id);
if (writeLockSkipLocked == true) if (skipLocked)
return PrepareResult::SKIP_LOCKED; return PrepareResult::SKIP_LOCKED;
// Cannot use Arg::Num here because transaction number is 64-bit unsigned integer // 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 // fall thru
case tra_active: case tra_active:
return writeLockSkipLocked == true ? PrepareResult::SKIP_LOCKED : PrepareResult::LOCK_ERROR; return skipLocked ? PrepareResult::SKIP_LOCKED : PrepareResult::LOCK_ERROR;
case tra_dead: case tra_dead:
break; break;

View File

@ -44,13 +44,6 @@ namespace Jrd
DPM_next_pointer_page // data pages from one pointer page DPM_next_pointer_page // data pages from one pointer page
}; };
enum class RecordLock
{
NONE,
LOCK,
SKIP
};
enum class WriteLockResult enum class WriteLockResult
{ {
LOCKED, LOCKED,
@ -61,7 +54,7 @@ namespace Jrd
void VIO_backout(Jrd::thread_db*, Jrd::record_param*, const Jrd::jrd_tra*); void VIO_backout(Jrd::thread_db*, Jrd::record_param*, const Jrd::jrd_tra*);
bool VIO_chase_record_version(Jrd::thread_db*, Jrd::record_param*, 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_copy_record(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::Record*, Jrd::Record*);
void VIO_data(Jrd::thread_db*, Jrd::record_param*, MemoryPool*); void VIO_data(Jrd::thread_db*, Jrd::record_param*, MemoryPool*);
bool VIO_erase(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*); 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*, bool VIO_get_current(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*,
MemoryPool*, bool, bool&); MemoryPool*, bool, bool&);
void VIO_init(Jrd::thread_db*); 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_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); 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*); 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*); void VIO_store(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*);
bool VIO_sweep(Jrd::thread_db*, Jrd::jrd_tra*, Jrd::TraceSweepEvent*); 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); 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}, store3[] = { op_line, op_byte, op_line, op_verb, op_verb, op_verb, 0},
marks[] = { op_byte, op_literal, op_line, op_verb, 0}, marks[] = { op_byte, op_literal, op_line, op_verb, 0},
erase[] = { op_erase, 0}, erase[] = { op_erase, 0},
erase2[] = { op_erase, op_verb, 0},
local_table[] = { op_word, op_byte, op_literal, op_byte, op_line, 0}, local_table[] = { op_word, op_byte, op_literal, op_byte, op_line, 0},
outer_map[] = { op_outer_map, 0 }, outer_map[] = { op_outer_map, 0 },
in_list[] = { op_line, op_verb, op_indent, op_word, op_line, op_args, 0}; in_list[] = { op_line, op_verb, op_indent, op_word, op_line, op_args, 0};