From 4d8131c9ad26a939c170d74b7e9a2df2edd73a8b Mon Sep 17 00:00:00 2001 From: Hayato Kuroda Date: Thu, 1 Oct 2026 16:59:00 +0900 Subject: [PATCH v5] Track the depth of scans of system tables --- src/backend/access/index/genam.c | 30 ++----------- src/backend/access/transam/xact.c | 51 ++++++++++++++++++++++- src/backend/replication/logical/logical.c | 2 +- src/include/access/tableam.h | 28 +++++++------ src/include/access/xact.h | 6 ++- 5 files changed, 76 insertions(+), 41 deletions(-) diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c index 98ac5bc79c9..35e73ce9513 100644 --- a/src/backend/access/index/genam.c +++ b/src/backend/access/index/genam.c @@ -432,13 +432,7 @@ systable_beginscan(Relation heapRelation, sysscan->snapshot = NULL; } - /* - * If CheckXidAlive is set then set a flag to indicate that system table - * scan is in-progress. See detailed comments in xact.c where these - * variables are declared. - */ - if (TransactionIdIsValid(CheckXidAlive)) - bsysscan = true; + BeginSystemTableScan(); if (irel) { @@ -633,12 +627,7 @@ systable_endscan(SysScanDesc sysscan) if (sysscan->snapshot) UnregisterSnapshot(sysscan->snapshot); - /* - * Reset the bsysscan flag at the end of the systable scan. See detailed - * comments in xact.c where these variables are declared. - */ - if (TransactionIdIsValid(CheckXidAlive)) - bsysscan = false; + EndSystemTableScan(); pfree(sysscan); } @@ -721,13 +710,7 @@ systable_beginscan_ordered(Relation heapRelation, elog(ERROR, "column is not in index"); } - /* - * If CheckXidAlive is set then set a flag to indicate that system table - * scan is in-progress. See detailed comments in xact.c where these - * variables are declared. - */ - if (TransactionIdIsValid(CheckXidAlive)) - bsysscan = true; + BeginSystemTableScan(); sysscan->iscan = index_beginscan(heapRelation, indexRelation, false, snapshot, NULL, nkeys, 0, @@ -782,12 +765,7 @@ systable_endscan_ordered(SysScanDesc sysscan) if (sysscan->snapshot) UnregisterSnapshot(sysscan->snapshot); - /* - * Reset the bsysscan flag at the end of the systable scan. See detailed - * comments in xact.c where these variables are declared. - */ - if (TransactionIdIsValid(CheckXidAlive)) - bsysscan = false; + EndSystemTableScan(); pfree(sysscan); } diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 0121a0c8fa0..45af6cce946 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -100,7 +100,13 @@ int synchronous_commit = SYNCHRONOUS_COMMIT_ON; * concurrent aborts only in systable_* APIs. */ TransactionId CheckXidAlive = InvalidTransactionId; -bool bsysscan = false; + +/* + * Number of nested systable scans while CheckXidAlive is valid. A counter is + * needed because systable scans can be nested. Transaction abort resets it, + * since ERROR can bypass the matching systable_endscan(). + */ +static int sysscan_depth = 0; /* * When running as a parallel worker, we place only a single @@ -6531,3 +6537,46 @@ xact_redo(XLogReaderState *record) else elog(PANIC, "xact_redo: unknown op code %u", info); } + +/* + * Record entry into a system table scan while CheckXidAlive is valid. + */ +void +BeginSystemTableScan(void) +{ + if (!TransactionIdIsValid(CheckXidAlive)) + return; + + sysscan_depth++; +} + +/* + * Record normal exit from a tracked system table scan. + */ +void +EndSystemTableScan(void) +{ + if (!TransactionIdIsValid(CheckXidAlive)) + return; + + Assert(IsSystemTableScanInProgress()); + sysscan_depth--; +} + +/* + * Reset the system table scan state during transaction abort. + */ +void +ResetSystemTableScanState(void) +{ + sysscan_depth = 0; +} + +/* + * Return whether any tracked system table scan is in progress. + */ +bool +IsSystemTableScanInProgress(void) +{ + return sysscan_depth > 0; +} diff --git a/src/backend/replication/logical/logical.c b/src/backend/replication/logical/logical.c index 98e5f1dd8f9..f579fe934c7 100644 --- a/src/backend/replication/logical/logical.c +++ b/src/backend/replication/logical/logical.c @@ -2030,7 +2030,7 @@ void ResetLogicalStreamingState(void) { CheckXidAlive = InvalidTransactionId; - bsysscan = false; + ResetSystemTableScanState(); } /* diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h index ea3f2a6be99..f66faaacf9b 100644 --- a/src/include/access/tableam.h +++ b/src/include/access/tableam.h @@ -924,10 +924,11 @@ table_beginscan_common(Relation rel, Snapshot snapshot, int nkeys, /* * We don't allow scans to be started while CheckXidAlive is set, except - * via systable_beginscan() et al. See detailed comments in xact.c where - * these variables are declared. + * via systable_beginscan() et al. See comments for CheckXidAlive in + * xact.c. */ - if (unlikely(TransactionIdIsValid(CheckXidAlive) && !bsysscan)) + if (unlikely(TransactionIdIsValid(CheckXidAlive) && + !IsSystemTableScanInProgress())) elog(ERROR, "scan started during logical decoding"); return rel->rd_tableam->scan_begin(rel, snapshot, nkeys, key, pscan, flags); @@ -1248,10 +1249,11 @@ table_index_scan_begin(IndexScanDesc scan, uint32 flags) /* * We don't allow scans to be started while CheckXidAlive is set, except - * via systable_beginscan() et al. See detailed comments in xact.c where - * these variables are declared. + * via systable_beginscan() et al. See comments for CheckXidAlive in + * xact.c. */ - if (unlikely(TransactionIdIsValid(CheckXidAlive) && !bsysscan)) + if (unlikely(TransactionIdIsValid(CheckXidAlive) && + !IsSystemTableScanInProgress())) elog(ERROR, "scan started during logical decoding"); scan->heapRelation->rd_tableam->index_scan_begin(scan, flags); @@ -1341,10 +1343,11 @@ table_fetch_tid(Relation rel, { /* * We don't expect direct calls to table_fetch_tid with valid - * CheckXidAlive for catalog or regular tables. See detailed comments in - * xact.c where these variables are declared. + * CheckXidAlive for catalog or regular tables. See comments for + * CheckXidAlive in xact.c. */ - if (unlikely(TransactionIdIsValid(CheckXidAlive) && !bsysscan)) + if (unlikely(TransactionIdIsValid(CheckXidAlive) && + !IsSystemTableScanInProgress())) elog(ERROR, "unexpected table_fetch_tid call during logical decoding"); return rel->rd_tableam->fetch_tid(rel, tid, snapshot, all_dead); @@ -1366,10 +1369,11 @@ table_tuple_fetch_row_version(Relation rel, { /* * We don't expect direct calls to table_tuple_fetch_row_version with - * valid CheckXidAlive for catalog or regular tables. See detailed - * comments in xact.c where these variables are declared. + * valid CheckXidAlive for catalog or regular tables. See comments for + * CheckXidAlive in xact.c. */ - if (unlikely(TransactionIdIsValid(CheckXidAlive) && !bsysscan)) + if (unlikely(TransactionIdIsValid(CheckXidAlive) && + !IsSystemTableScanInProgress())) elog(ERROR, "unexpected table_tuple_fetch_row_version call during logical decoding"); return rel->rd_tableam->tuple_fetch_row_version(rel, tid, snapshot, slot); diff --git a/src/include/access/xact.h b/src/include/access/xact.h index a8cbdf247c8..cc91388a798 100644 --- a/src/include/access/xact.h +++ b/src/include/access/xact.h @@ -85,7 +85,6 @@ extern PGDLLIMPORT int synchronous_commit; /* used during logical streaming of a transaction */ extern PGDLLIMPORT TransactionId CheckXidAlive; -extern PGDLLIMPORT bool bsysscan; /* * Miscellaneous flag bits to record events which occur on the top level @@ -535,4 +534,9 @@ extern void EnterParallelMode(void); extern void ExitParallelMode(void); extern bool IsInParallelMode(void); +extern void BeginSystemTableScan(void); +extern void EndSystemTableScan(void); +extern void ResetSystemTableScanState(void); +extern bool IsSystemTableScanInProgress(void); + #endif /* XACT_H */ -- 2.52.0