From 755f7e2c921f16e6bc09bdc8d286c495b2cee93b Mon Sep 17 00:00:00 2001 From: Masahiko Sawada Date: Tue, 18 Aug 2026 13:55:10 -0700 Subject: [PATCH] test_decoding: Don't print virtual generated columns. Virtual generated columns are stored in tuples as null values, so heap_getattr() in tuple_to_stringinfo() always returns null for them. test_decoding printed such a column as null, which is indistinguishable from a column that genuinely holds null. This commit skips virtual generated columns in tuple_to_stringinfo(), the only place that prints a tuple. Stored generated columns continue to be printed, since their values do live in the heap tuple. This is a behavior change rather than a bug fix, and it changes test_decoding's output for tables that have virtual generated columns, which could affect consumers that parse it. Therefore it is not back-patched. Author: SATYANARAYANA NARLAPURAM Co-authored-by: Bharath Rupireddy Reviewed-by: Euler Taveira Reviewed-by: Masahiko Sawada Discussion: https://postgr.es/m/CAHg+QDfTh3UbB-Ed--o2Bd=SBDJoEiG-qp3C0+ETDibF63y=dw@mail.gmail.com --- contrib/test_decoding/expected/ddl.out | 18 ++++++++++++++++++ contrib/test_decoding/sql/ddl.sql | 12 ++++++++++++ contrib/test_decoding/test_decoding.c | 10 ++++++++++ 3 files changed, 40 insertions(+) diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out index 6819812e806..a129c016d2b 100644 --- a/contrib/test_decoding/expected/ddl.out +++ b/contrib/test_decoding/expected/ddl.out @@ -895,6 +895,24 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc data (0 rows) \pset format aligned +-- check that virtual generated columns are not printed +CREATE TABLE gtest1 ( + a int PRIMARY KEY, + b int, + c int GENERATED ALWAYS AS (a + b) VIRTUAL, + d int GENERATED ALWAYS AS (a * 2) STORED, + e int +); +INSERT INTO gtest1 (a, b) VALUES (1, 10); +SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); + data +-------------------------------------------------------------------------------------- + BEGIN + table public.gtest1: INSERT: a[integer]:1 b[integer]:10 d[integer]:2 e[integer]:null + COMMIT +(3 rows) + +DROP TABLE gtest1; SELECT pg_drop_replication_slot('regression_slot'); pg_drop_replication_slot -------------------------- diff --git a/contrib/test_decoding/sql/ddl.sql b/contrib/test_decoding/sql/ddl.sql index 6d0b7d77778..de8a7cdc0d2 100644 --- a/contrib/test_decoding/sql/ddl.sql +++ b/contrib/test_decoding/sql/ddl.sql @@ -467,6 +467,18 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); \pset format aligned +-- check that virtual generated columns are not printed +CREATE TABLE gtest1 ( + a int PRIMARY KEY, + b int, + c int GENERATED ALWAYS AS (a + b) VIRTUAL, + d int GENERATED ALWAYS AS (a * 2) STORED, + e int +); +INSERT INTO gtest1 (a, b) VALUES (1, 10); +SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); +DROP TABLE gtest1; + SELECT pg_drop_replication_slot('regression_slot'); /* check that the slot is gone */ diff --git a/contrib/test_decoding/test_decoding.c b/contrib/test_decoding/test_decoding.c index d5cf0fa02b0..78185837aef 100644 --- a/contrib/test_decoding/test_decoding.c +++ b/contrib/test_decoding/test_decoding.c @@ -554,6 +554,16 @@ tuple_to_stringinfo(StringInfo s, TupleDesc tupdesc, HeapTuple tuple, bool skip_ if (attr->attnum < 0) continue; + /* + * Virtual generated columns are always stored as null in the tuple, + * so don't print them at all. A printed null would not be + * distinguishable from a column that really contains a null. Stored + * generated columns are printed as usual since their values are + * actually on disk. + */ + if (attr->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL) + continue; + typid = attr->atttypid; /* get Datum from tuple */