Fix fetching default toast value during decoding of in-progress transactions.
authorAmit Kapila <akapila@postgresql.org>
Mon, 7 Oct 2024 09:15:39 +0000 (14:45 +0530)
committerAmit Kapila <akapila@postgresql.org>
Mon, 7 Oct 2024 09:15:39 +0000 (14:45 +0530)
During logical decoding of in-progress transactions, we perform the toast
table scan while fetching the default toast value for an attribute. We
forgot to initialize the flag during this scan to indicate that the system
table scan is in progress. We need this flag to ensure that during logical
decoding we never directly access the tableam or heap APIs because we check
for concurrent aborts only in systable_* APIs.

Reported-by: Alexander Lakhin
Author: Takeshi Ideriha, Hou Zhijie
Reviewed-by: Amit Kapila, Hou Zhijie
Backpatch-through: 14
Discussion: https://postgr.es/m/18641-6687273b7f15269d@postgresql.org

contrib/test_decoding/expected/stream.out
contrib/test_decoding/sql/stream.sql
src/backend/access/index/genam.c

index 0f21dcb8e0e442c14079ea99426b0d3a27f952b5..2e319a89417a85cca3c1a03435e9b82ef686171a 100644 (file)
@@ -106,6 +106,26 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'incl
  committing streamed transaction
 (17 rows)
 
+-- Test that accessing a TOAST table in streaming mode is allowed.
+-- Create a table with a column that uses a TOASTed default value.
+-- (temporarily hide query, to avoid the long CREATE TABLE stmt)
+\set ECHO none
+BEGIN;
+INSERT INTO test_tab SELECT repeat('a', 6000) || g.i FROM generate_series(1, 350) g(i);
+-- Force WAL flush, so that the above changes will be streamed.
+SELECT 'force flush' FROM pg_switch_wal();
+  ?column?   
+-------------
+ force flush
+(1 row)
+
+SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
+ count 
+-------
+   315
+(1 row)
+
+COMMIT;
 DROP TABLE stream_test;
 SELECT pg_drop_replication_slot('regression_slot');
  pg_drop_replication_slot 
index 4feec62972a5e4b6ac40fddbaed3fc8836d645a7..9502a6d87cf6864510fe8c970667069596a94c97 100644 (file)
@@ -44,5 +44,23 @@ toasted-123456789012345678901234567890123456789012345678901234567890123456789012
 
 SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
 
+-- Test that accessing a TOAST table in streaming mode is allowed.
+
+-- Create a table with a column that uses a TOASTed default value.
+-- (temporarily hide query, to avoid the long CREATE TABLE stmt)
+\set ECHO none
+SELECT 'CREATE TABLE test_tab (a text DEFAULT ''' || string_agg('toast value', '') || ''');' FROM generate_series(1, 4000)
+\gexec
+\set ECHO all
+
+BEGIN;
+INSERT INTO test_tab SELECT repeat('a', 6000) || g.i FROM generate_series(1, 350) g(i);
+
+-- Force WAL flush, so that the above changes will be streamed.
+SELECT 'force flush' FROM pg_switch_wal();
+
+SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
+COMMIT;
+
 DROP TABLE stream_test;
 SELECT pg_drop_replication_slot('regression_slot');
index d2f5bf94a075676e1766ea44e8df4034edaa8339..ac531f071e23a390a870ea026a458ecec0782b75 100644 (file)
@@ -700,6 +700,14 @@ 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;
+
    return sysscan;
 }
 
@@ -744,6 +752,14 @@ systable_endscan_ordered(SysScanDesc sysscan)
    index_endscan(sysscan->iscan);
    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;
+
    pfree(sysscan);
 }