From 54b8ab93459f882dccd415b9709bdccdd68c730f Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Mon, 16 Feb 2026 18:58:07 +0200 Subject: [PATCH] Allow to use RDB$RECORD_VERSION in triggers, indices and RETURNING. It fixes #8749 : Computed index on RDB$RECORD_VERSION don't work as expected. --- src/dsql/StmtNodes.cpp | 8 ++++++++ src/jrd/Savepoint.cpp | 17 ++++++++++++++--- src/jrd/btr.cpp | 6 +++++- src/jrd/exe.cpp | 6 ++++++ src/jrd/replication/Applier.cpp | 6 ++++++ src/jrd/vio.cpp | 12 ++++++++++++ 6 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 2412e609d87..b99724589fe 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -7247,6 +7247,7 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg { const Format* const orgFormat = newFormat; orgRecord = VIO_record(tdbb, orgRpb, orgFormat, tdbb->getDefaultPool()); + orgRecord->setTransactionNumber(orgRpb->rpb_transaction_nr); orgRpb->rpb_address = orgRecord->getData(); orgRpb->rpb_length = orgFormat->fmt_length; orgRpb->rpb_format_number = orgFormat->fmt_version; @@ -7259,6 +7260,10 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, Request* request, WhichTrigg newRpb->rpb_number = orgRpb->rpb_number; newRpb->rpb_number.setValid(true); + // This allows to use NEW.RDB$RECORD_VERSION in triggers and indices. + // Value for OLD context is already set in VIO_data(). + newRpb->rpb_record->setTransactionNumber(transaction->tra_number); + if (mapView) { impure->sta_state = 1; @@ -8278,6 +8283,9 @@ const StmtNode* StoreNode::store(thread_db* tdbb, Request* request, WhichTrigger // even if the valid stream marker is present for OLD/NEW trigger contexts rpb->rpb_number.setValue(BOF_NUMBER); + // This allows to use NEW.RDB$RECORD_VERSION in triggers and indices + rpb->rpb_record->setTransactionNumber(transaction->tra_number); + // CVC: This small block added by Ann Harrison to // start with a clean empty buffer and so avoid getting // new record buffer with misleading information. Fixes diff --git a/src/jrd/Savepoint.cpp b/src/jrd/Savepoint.cpp index 852df16f181..64bae3b0516 100644 --- a/src/jrd/Savepoint.cpp +++ b/src/jrd/Savepoint.cpp @@ -39,8 +39,15 @@ UndoItem::UndoItem(jrd_tra* transaction, RecordNumber recordNumber, const Record : m_number(recordNumber.getValue()), m_format(record->getFormat()) { fb_assert(m_format); - m_offset = transaction->getUndoSpace()->allocateSpace(m_format->fmt_length); - transaction->getUndoSpace()->write(m_offset, record->getData(), record->getLength()); + + // If assert below became wrong at some day, than tx number should be put + // into (and read from) undo space also. + fb_assert(transaction->tra_number == record->getTransactionNumber()); + + auto undoSpace = transaction->getUndoSpace(); + + m_offset = undoSpace->allocateSpace(m_format->fmt_length); + undoSpace->write(m_offset, record->getData(), record->getLength()); } Record* UndoItem::setupRecord(jrd_tra* transaction) const @@ -48,7 +55,11 @@ Record* UndoItem::setupRecord(jrd_tra* transaction) const if (m_format) { Record* const record = transaction->getUndoRecord(m_format); - transaction->getUndoSpace()->read(m_offset, record->getData(), record->getLength()); + + auto undoSpace = transaction->getUndoSpace(); + undoSpace->read(m_offset, record->getData(), record->getLength()); + + record->setTransactionNumber(transaction->tra_number); return record; } diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index 931445aa4f1..ab459e7c1be 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -505,7 +505,11 @@ dsc* IndexExpression::evaluate(Record* record) const const auto orgRequest = m_tdbb->getRequest(); m_tdbb->setRequest(m_request); - m_request->req_rpb[0].rpb_record = record; + auto& rpb = m_request->req_rpb[0]; + rpb.rpb_record = record; + // Necessary for evaluation of expressions with RDB$RECORD_VERSION + rpb.rpb_transaction_nr = record->getTransactionNumber(); + m_request->req_flags &= ~req_null; FbLocalStatus status; diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index dc5f7b371c8..13c1a3f50e7 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -1196,6 +1196,9 @@ void EXE_execute_triggers(thread_db* tdbb, { trigger->req_rpb[0].rpb_number = old_rpb->rpb_number; trigger->req_rpb[0].rpb_number.setValid(true); + + // This allows to use OLD.RDB$RECORD_VERSION in triggers + trigger->req_rpb[0].rpb_transaction_nr = old_rec->getTransactionNumber(); } else trigger->req_rpb[0].rpb_number.setValid(false); @@ -1214,6 +1217,9 @@ void EXE_execute_triggers(thread_db* tdbb, { trigger->req_rpb[1].rpb_number = new_rpb->rpb_number; trigger->req_rpb[1].rpb_number.setValid(true); + + // This allows to use NEW.RDB$RECORD_VERSION in triggers + trigger->req_rpb[1].rpb_transaction_nr = new_rec->getTransactionNumber(); } else trigger->req_rpb[1].rpb_number.setValid(false); diff --git a/src/jrd/replication/Applier.cpp b/src/jrd/replication/Applier.cpp index 6d0cfa67e97..f38eaae6191 100644 --- a/src/jrd/replication/Applier.cpp +++ b/src/jrd/replication/Applier.cpp @@ -1289,6 +1289,9 @@ void Applier::doInsert(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) Savepoint::ChangeMarker marker(transaction->tra_save_point); + // This allows to use RDB$RECORD_VERSION in indices. + rpb->rpb_record->setTransactionNumber(transaction->tra_number); + VIO_store(tdbb, rpb, transaction); IDX_store(tdbb, rpb, transaction); if (m_enableCascade) @@ -1386,6 +1389,9 @@ void Applier::doUpdate(thread_db* tdbb, record_param* orgRpb, record_param* newR Savepoint::ChangeMarker marker(transaction->tra_save_point); + // This allows to use NEW.RDB$RECORD_VERSION in indices. + newRpb->rpb_record->setTransactionNumber(transaction->tra_number); + VIO_modify(tdbb, orgRpb, newRpb, transaction); IDX_modify(tdbb, orgRpb, newRpb, transaction); if (m_enableCascade) diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 1ea0d83ae24..a23b082e3eb 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -2430,6 +2430,9 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) } } + // Restore transaction number that can be used for OLD.RDB$RECORD_VERSION evaluation later. + rpb->rpb_transaction_nr = tid_fetch; + if (transaction->tra_save_point && transaction->tra_save_point->isChanging()) verb_post(tdbb, transaction, rpb, 0); @@ -3711,6 +3714,8 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j // Old record was restored and re-fetched for write. Now replace it. + const auto orgTranum = org_rpb->rpb_transaction_nr; + org_rpb->rpb_transaction_nr = new_rpb->rpb_transaction_nr; org_rpb->rpb_format_number = new_rpb->rpb_format_number; org_rpb->rpb_b_page = temp.rpb_page; @@ -3724,6 +3729,9 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j replace_record(tdbb, org_rpb, &stack, transaction); + // Restore transaction number that can be used for OLD.RDB$RECORD_VERSION evaluation later. + org_rpb->rpb_transaction_nr = orgTranum; + if (!(transaction->tra_flags & TRA_system) && transaction->tra_save_point && transaction->tra_save_point->isChanging()) { @@ -5210,7 +5218,10 @@ static void garbage_collect(thread_db* tdbb, record_param* rpb, ULONG prior_page delete_record(tdbb, rpb, prior_page, tdbb->getDefaultPool()); if (rpb->rpb_record) + { + rpb->rpb_record->setTransactionNumber(rpb->rpb_transaction_nr); going.push(rpb->rpb_record); + } ++backversions; @@ -5619,6 +5630,7 @@ static UndoDataRet get_undo_data(thread_db* tdbb, jrd_tra* transaction, record->copyFrom(undoRecord); rpb->rpb_flags &= ~rpb_deleted; + rpb->rpb_transaction_nr = undoRecord->getTransactionNumber(); return udExists; }