From f70754fe071efd8ce490b0dd012d9a32a214f296 Mon Sep 17 00:00:00 2001 From: Hayato Kuroda Date: Thu, 1 Oct 2026 16:59:00 +0900 Subject: [PATCH v6-PG17] Track the depth of scans of system tables --- contrib/test_decoding/expected/stream.out | 44 +++++++++++++++++++- contrib/test_decoding/sql/stream.sql | 33 ++++++++++++++- src/backend/access/index/genam.c | 31 ++------------ src/backend/access/transam/xact.c | 49 +++++++++++++++++++++++ src/backend/replication/logical/logical.c | 3 +- src/include/access/xact.h | 4 ++ 6 files changed, 134 insertions(+), 30 deletions(-) diff --git a/contrib/test_decoding/expected/stream.out b/contrib/test_decoding/expected/stream.out index a76f77601e2..95dd8b1b68f 100644 --- a/contrib/test_decoding/expected/stream.out +++ b/contrib/test_decoding/expected/stream.out @@ -128,7 +128,49 @@ SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 5 (1 row) -DROP TABLE stream_test; +-- Create a table with a large constraint whose conbin value is stored out of +-- line. +DO $$ +DECLARE + large_literal text; +BEGIN + SELECT string_agg(md5(i::text), '') INTO large_literal FROM generate_series(1, 100) i; + + EXECUTE format( + 'CREATE TABLE nested_sys_scan_test ( + checked_value text, + CONSTRAINT a_toasted_check CHECK (checked_value <> %L), + CONSTRAINT z_following_check CHECK (true))', + large_literal); +END +$$; +-- consume DDL +SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); + data +------ +(0 rows) + +-- Verify that detoasting catalog data in a nested system table scan does not +-- prevent the outer scan from fetching subsequent tuples. +SET debug_logical_replication_streaming = immediate; +BEGIN; +INSERT INTO nested_sys_scan_test VALUES ('valid'); +CHECKPOINT; +SELECT count(*) > 0 AS streamed FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'stream-changes', '1'); + streamed +---------- + t +(1 row) + +COMMIT; +RESET debug_logical_replication_streaming; +SELECT count(*) > 0 AS consumed FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'stream-changes', '1'); + consumed +---------- + t +(1 row) + +DROP TABLE stream_test, nested_sys_scan_test; SELECT pg_drop_replication_slot('regression_slot'); pg_drop_replication_slot -------------------------- diff --git a/contrib/test_decoding/sql/stream.sql b/contrib/test_decoding/sql/stream.sql index 7f43f0c2ab7..0958d95e41e 100644 --- a/contrib/test_decoding/sql/stream.sql +++ b/contrib/test_decoding/sql/stream.sql @@ -59,5 +59,36 @@ ROLLBACK TO s1; COMMIT; SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1'); -DROP TABLE stream_test; +-- Create a table with a large constraint whose conbin value is stored out of +-- line. +DO $$ +DECLARE + large_literal text; +BEGIN + SELECT string_agg(md5(i::text), '') INTO large_literal FROM generate_series(1, 100) i; + + EXECUTE format( + 'CREATE TABLE nested_sys_scan_test ( + checked_value text, + CONSTRAINT a_toasted_check CHECK (checked_value <> %L), + CONSTRAINT z_following_check CHECK (true))', + large_literal); +END +$$; + +-- consume DDL +SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); + +-- Verify that detoasting catalog data in a nested system table scan does not +-- prevent the outer scan from fetching subsequent tuples. +SET debug_logical_replication_streaming = immediate; +BEGIN; +INSERT INTO nested_sys_scan_test VALUES ('valid'); +CHECKPOINT; +SELECT count(*) > 0 AS streamed FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'stream-changes', '1'); +COMMIT; +RESET debug_logical_replication_streaming; +SELECT count(*) > 0 AS consumed FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'stream-changes', '1'); + +DROP TABLE stream_test, nested_sys_scan_test; SELECT pg_drop_replication_slot('regression_slot'); diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c index b123acc5a60..ca35f0163be 100644 --- a/src/backend/access/index/genam.c +++ b/src/backend/access/index/genam.c @@ -37,7 +37,6 @@ #include "utils/ruleutils.h" #include "utils/snapmgr.h" - /* ---------------------------------------------------------------- * general access method routines * @@ -459,13 +458,7 @@ systable_beginscan(Relation heapRelation, sysscan->iscan = 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; + TrackSystemTableScanStart(); return sysscan; } @@ -614,12 +607,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; + TrackSystemTableScanEnd(); pfree(sysscan); } @@ -702,13 +690,7 @@ systable_beginscan_ordered(Relation heapRelation, index_rescan(sysscan->iscan, key, nkeys, NULL, 0); sysscan->scan = 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; + TrackSystemTableScanStart(); return sysscan; } @@ -755,12 +737,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; + TrackSystemTableScanEnd(); pfree(sysscan); } diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 96b9ff52728..441ba1b7b86 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -96,6 +96,18 @@ int synchronous_commit = SYNCHRONOUS_COMMIT_ON; * concurrent aborts only in systable_* APIs. */ TransactionId CheckXidAlive = InvalidTransactionId; + +/* + * 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 bsysscan_depth = 0; + +/* + * Indicates whether we are currently in a system table scan. Retained for the + * backwards-compatibility. + */ bool bsysscan = false; /* @@ -6401,3 +6413,40 @@ 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 +TrackSystemTableScanStart(void) +{ + if (!TransactionIdIsValid(CheckXidAlive)) + return; + + bsysscan_depth++; + bsysscan = true; +} + +/* + * Record normal exit from a tracked system table scan. + */ +void +TrackSystemTableScanEnd(void) +{ + if (!TransactionIdIsValid(CheckXidAlive)) + return; + + Assert(bsysscan_depth > 0); + bsysscan_depth--; + bsysscan = (bsysscan_depth > 0); +} + +/* + * Reset the system table scan state during transaction abort. + */ +void +ResetSystemTableScanState(void) +{ + bsysscan_depth = 0; + bsysscan = false; +} diff --git a/src/backend/replication/logical/logical.c b/src/backend/replication/logical/logical.c index f79557c7261..40406dca4b5 100644 --- a/src/backend/replication/logical/logical.c +++ b/src/backend/replication/logical/logical.c @@ -28,6 +28,7 @@ #include "postgres.h" +#include "access/genam.h" #include "access/xact.h" #include "access/xlog_internal.h" #include "access/xlogutils.h" @@ -2040,7 +2041,7 @@ void ResetLogicalStreamingState(void) { CheckXidAlive = InvalidTransactionId; - bsysscan = false; + ResetSystemTableScanState(); } /* diff --git a/src/include/access/xact.h b/src/include/access/xact.h index 6d4439f0524..2035d5e4338 100644 --- a/src/include/access/xact.h +++ b/src/include/access/xact.h @@ -527,4 +527,8 @@ extern void EnterParallelMode(void); extern void ExitParallelMode(void); extern bool IsInParallelMode(void); +extern void TrackSystemTableScanStart(void); +extern void TrackSystemTableScanEnd(void); +extern void ResetSystemTableScanState(void); + #endif /* XACT_H */ -- 2.52.0