diff --git a/doc/sql.extensions/README.skip_locked.md b/doc/sql.extensions/README.skip_locked.md index d8c4be7af7..197bbfbc85 100644 --- a/doc/sql.extensions/README.skip_locked.md +++ b/doc/sql.extensions/README.skip_locked.md @@ -46,9 +46,9 @@ DELETE FROM ## 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 diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 78b9400bc2..48c1784f2b 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -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 regEraseNode({blr_erase}); +static RegisterNode 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 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 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(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; } } diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index ac183e1dd0..b593528a11 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -568,6 +568,7 @@ public: dsql_ctx* dsqlContext = nullptr; NestConst statement; NestConst subStatement; + NestConst returningStatement; NestConst forNode; // parent implicit cursor, if present StreamType stream = 0; unsigned marks = 0; // see StmtNode::IUD_MARK_xxx diff --git a/src/include/firebird/impl/blr.h b/src/include/firebird/impl/blr.h index ad2d56b190..0f666cc4e1 100644 --- a/src/include/firebird/impl/blr.h +++ b/src/include/firebird/impl/blr.h @@ -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 */ diff --git a/src/jrd/Savepoint.cpp b/src/jrd/Savepoint.cpp index 0bef0cc985..852df16f18 100644 --- a/src/jrd/Savepoint.cpp +++ b/src/jrd/Savepoint.cpp @@ -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(); } diff --git a/src/jrd/Savepoint.h b/src/jrd/Savepoint.h index 62ae3a6873..b71976ae22 100644 --- a/src/jrd/Savepoint.h +++ b/src/jrd/Savepoint.h @@ -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(); diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index d503b47b1f..ca24368d77 100644 --- a/src/jrd/Statement.cpp +++ b/src/jrd/Statement.cpp @@ -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; diff --git a/src/jrd/blp.h b/src/jrd/blp.h index cda079e900..759b9206dd 100644 --- a/src/jrd/blp.h +++ b/src/jrd/blp.h @@ -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}, diff --git a/src/jrd/exe.h b/src/jrd/exe.h index 4782fbfff7..dda4cd4c12 100644 --- a/src/jrd/exe.h +++ b/src/jrd/exe.h @@ -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) diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index 7bfe9e5cb9..022615784d 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -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); diff --git a/src/jrd/par.cpp b/src/jrd/par.cpp index d959bb675c..314170a947 100644 --- a/src/jrd/par.cpp +++ b/src/jrd/par.cpp @@ -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; diff --git a/src/jrd/recsrc/AggregatedStream.cpp b/src/jrd/recsrc/AggregatedStream.cpp index 4cfd13a374..9032110c34 100644 --- a/src/jrd/recsrc/AggregatedStream.cpp +++ b/src/jrd/recsrc/AggregatedStream.cpp @@ -109,7 +109,7 @@ bool BaseAggWinStream::refetchRecord(thread_db* tdbb) const } template -WriteLockResult BaseAggWinStream::lockRecord(thread_db* /*tdbb*/, bool /*skipLocked*/) const +WriteLockResult BaseAggWinStream::lockRecord(thread_db* /*tdbb*/) const { status_exception::raise(Arg::Gds(isc_record_lock_not_supp)); } diff --git a/src/jrd/recsrc/BufferedStream.cpp b/src/jrd/recsrc/BufferedStream.cpp index 9711567199..7f6e1381d1 100644 --- a/src/jrd/recsrc/BufferedStream.cpp +++ b/src/jrd/recsrc/BufferedStream.cpp @@ -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& children) const diff --git a/src/jrd/recsrc/ConditionalStream.cpp b/src/jrd/recsrc/ConditionalStream.cpp index 8ef48f48f1..8c61b874e6 100644 --- a/src/jrd/recsrc/ConditionalStream.cpp +++ b/src/jrd/recsrc/ConditionalStream.cpp @@ -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(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& children) const diff --git a/src/jrd/recsrc/ExternalTableScan.cpp b/src/jrd/recsrc/ExternalTableScan.cpp index 4137a7a5e0..433178d3e1 100644 --- a/src/jrd/recsrc/ExternalTableScan.cpp +++ b/src/jrd/recsrc/ExternalTableScan.cpp @@ -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); diff --git a/src/jrd/recsrc/FilteredStream.cpp b/src/jrd/recsrc/FilteredStream.cpp index f540a6f911..2c56853b69 100644 --- a/src/jrd/recsrc/FilteredStream.cpp +++ b/src/jrd/recsrc/FilteredStream.cpp @@ -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& children) const diff --git a/src/jrd/recsrc/FirstRowsStream.cpp b/src/jrd/recsrc/FirstRowsStream.cpp index 19df50092f..0f545b3ae1 100644 --- a/src/jrd/recsrc/FirstRowsStream.cpp +++ b/src/jrd/recsrc/FirstRowsStream.cpp @@ -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& children) const diff --git a/src/jrd/recsrc/FullOuterJoin.cpp b/src/jrd/recsrc/FullOuterJoin.cpp index ae87b72db2..ba1c8d9ecc 100644 --- a/src/jrd/recsrc/FullOuterJoin.cpp +++ b/src/jrd/recsrc/FullOuterJoin.cpp @@ -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); diff --git a/src/jrd/recsrc/HashJoin.cpp b/src/jrd/recsrc/HashJoin.cpp index e3d21b608b..271cac933d 100644 --- a/src/jrd/recsrc/HashJoin.cpp +++ b/src/jrd/recsrc/HashJoin.cpp @@ -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)); } diff --git a/src/jrd/recsrc/LocalTableStream.cpp b/src/jrd/recsrc/LocalTableStream.cpp index 86e2df624e..f4eb8a1e97 100644 --- a/src/jrd/recsrc/LocalTableStream.cpp +++ b/src/jrd/recsrc/LocalTableStream.cpp @@ -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)); } diff --git a/src/jrd/recsrc/LockedStream.cpp b/src/jrd/recsrc/LockedStream.cpp index e392c93021..767832cf13 100644 --- a/src/jrd/recsrc/LockedStream.cpp +++ b/src/jrd/recsrc/LockedStream.cpp @@ -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& children) const diff --git a/src/jrd/recsrc/MergeJoin.cpp b/src/jrd/recsrc/MergeJoin.cpp index ddafb39ebf..0d448d719d 100644 --- a/src/jrd/recsrc/MergeJoin.cpp +++ b/src/jrd/recsrc/MergeJoin.cpp @@ -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)); } diff --git a/src/jrd/recsrc/NestedLoopJoin.cpp b/src/jrd/recsrc/NestedLoopJoin.cpp index b267c5b366..d2abcc4d60 100644 --- a/src/jrd/recsrc/NestedLoopJoin.cpp +++ b/src/jrd/recsrc/NestedLoopJoin.cpp @@ -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)); } diff --git a/src/jrd/recsrc/ProcedureScan.cpp b/src/jrd/recsrc/ProcedureScan.cpp index fce5434a95..1166d1e089 100644 --- a/src/jrd/recsrc/ProcedureScan.cpp +++ b/src/jrd/recsrc/ProcedureScan.cpp @@ -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)); } diff --git a/src/jrd/recsrc/RecordSource.cpp b/src/jrd/recsrc/RecordSource.cpp index cdc96663de..b9054f61a3 100644 --- a/src/jrd/recsrc/RecordSource.cpp +++ b/src/jrd/recsrc/RecordSource.cpp @@ -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() diff --git a/src/jrd/recsrc/RecordSource.h b/src/jrd/recsrc/RecordSource.h index 24e9bf86c3..db70b71635 100644 --- a/src/jrd/recsrc/RecordSource.h +++ b/src/jrd/recsrc/RecordSource.h @@ -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& 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& 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& 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& 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& children) const override; @@ -460,7 +460,6 @@ namespace Jrd private: NestConst 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& children) const override; @@ -1314,7 +1313,7 @@ namespace Jrd void getChildren(Firebird::Array& 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& 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& 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& children) const override; diff --git a/src/jrd/recsrc/RecursiveStream.cpp b/src/jrd/recsrc/RecursiveStream.cpp index d40f3b3e24..df4c6aa065 100644 --- a/src/jrd/recsrc/RecursiveStream.cpp +++ b/src/jrd/recsrc/RecursiveStream.cpp @@ -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)); } diff --git a/src/jrd/recsrc/SingularStream.cpp b/src/jrd/recsrc/SingularStream.cpp index 99c0218e1c..fd97401f75 100644 --- a/src/jrd/recsrc/SingularStream.cpp +++ b/src/jrd/recsrc/SingularStream.cpp @@ -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& children) const diff --git a/src/jrd/recsrc/SkipRowsStream.cpp b/src/jrd/recsrc/SkipRowsStream.cpp index 297db0b7fb..67a2f6a49e 100644 --- a/src/jrd/recsrc/SkipRowsStream.cpp +++ b/src/jrd/recsrc/SkipRowsStream.cpp @@ -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& children) const diff --git a/src/jrd/recsrc/SortedStream.cpp b/src/jrd/recsrc/SortedStream.cpp index eacd72954b..8b1877061e 100644 --- a/src/jrd/recsrc/SortedStream.cpp +++ b/src/jrd/recsrc/SortedStream.cpp @@ -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& 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()); diff --git a/src/jrd/recsrc/Union.cpp b/src/jrd/recsrc/Union.cpp index 804133f2eb..3f47113aa4 100644 --- a/src/jrd/recsrc/Union.cpp +++ b/src/jrd/recsrc/Union.cpp @@ -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(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& children) const diff --git a/src/jrd/recsrc/VirtualTableScan.cpp b/src/jrd/recsrc/VirtualTableScan.cpp index 82dad5f80d..c8f00e7574 100644 --- a/src/jrd/recsrc/VirtualTableScan.cpp +++ b/src/jrd/recsrc/VirtualTableScan.cpp @@ -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)); } diff --git a/src/jrd/recsrc/WindowedStream.cpp b/src/jrd/recsrc/WindowedStream.cpp index ce795f76bc..9c2c482cc0 100644 --- a/src/jrd/recsrc/WindowedStream.cpp +++ b/src/jrd/recsrc/WindowedStream.cpp @@ -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& 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& 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)); } diff --git a/src/jrd/req.h b/src/jrd/req.h index 78142bf8d8..673bb04bd4 100644 --- a/src/jrd/req.h +++ b/src/jrd/req.h @@ -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 diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index d91b711a3b..6c235e2845 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -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; diff --git a/src/jrd/vio_proto.h b/src/jrd/vio_proto.h index a9a07a941e..6538f5c038 100644 --- a/src/jrd/vio_proto.h +++ b/src/jrd/vio_proto.h @@ -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); diff --git a/src/yvalve/gds.cpp b/src/yvalve/gds.cpp index 55fceb803e..3f77850f92 100644 --- a/src/yvalve/gds.cpp +++ b/src/yvalve/gds.cpp @@ -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};