From 3aaf9784ea62232dccbac6bcb7f41e4b76c29cca Mon Sep 17 00:00:00 2001 From: shihao zhong Date: Tue, 20 Oct 2026 20:02:49 -0400 Subject: [PATCH v2 2/2] Add a test for CLUSTER with an aborted update An isolation test for a pg_class row whose update was rolled back while its inserting transaction was still open. VACUUM FULL pg_class used to lose the row. Discussion: https://postgr.es/m/19760-8f9a0eba17bc8c92@postgresql.org --- .../cluster-catalog-aborted-update.out | 22 ++++++++++++++++ src/test/isolation/isolation_schedule | 1 + .../specs/cluster-catalog-aborted-update.spec | 26 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 src/test/isolation/expected/cluster-catalog-aborted-update.out create mode 100644 src/test/isolation/specs/cluster-catalog-aborted-update.spec diff --git a/src/test/isolation/expected/cluster-catalog-aborted-update.out b/src/test/isolation/expected/cluster-catalog-aborted-update.out new file mode 100644 index 00000000000..399fb8728a4 --- /dev/null +++ b/src/test/isolation/expected/cluster-catalog-aborted-update.out @@ -0,0 +1,22 @@ +Parsed test spec with 2 sessions + +starting permutation: s1_create s1_alter s2_vacfull s1_commit s2_check +step s1_create: BEGIN; CREATE TABLE cluster_catalog_tab (a int); +step s1_alter: SAVEPOINT sp; ALTER TABLE cluster_catalog_tab ADD COLUMN b int; ROLLBACK TO sp; +step s2_vacfull: VACUUM FULL pg_class; +step s1_commit: COMMIT; +step s2_check: SELECT count(*) FROM pg_class WHERE relname = 'cluster_catalog_tab'; +count +----- + 1 +(1 row) + + +starting permutation: s1_plain s1_count +step s1_plain: BEGIN; CREATE TABLE cluster_plain_tab (a int, b int); CREATE INDEX cluster_plain_idx ON cluster_plain_tab (b); INSERT INTO cluster_plain_tab VALUES (1, 1); SAVEPOINT sp; UPDATE cluster_plain_tab SET a = 2; ROLLBACK TO sp; CLUSTER cluster_plain_tab USING cluster_plain_idx; COMMIT; +step s1_count: SELECT count(*) FROM cluster_plain_tab; +count +----- + 1 +(1 row) + diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule index bf9a2037c70..d47b59040c4 100644 --- a/src/test/isolation/isolation_schedule +++ b/src/test/isolation/isolation_schedule @@ -122,6 +122,7 @@ test: plpgsql-toast test: cluster-conflict test: cluster-conflict-partition test: cluster-toast-value-reuse +test: cluster-catalog-aborted-update test: truncate-conflict test: serializable-parallel test: serializable-parallel-2 diff --git a/src/test/isolation/specs/cluster-catalog-aborted-update.spec b/src/test/isolation/specs/cluster-catalog-aborted-update.spec new file mode 100644 index 00000000000..b76c37d064c --- /dev/null +++ b/src/test/isolation/specs/cluster-catalog-aborted-update.spec @@ -0,0 +1,26 @@ +# VACUUM FULL on a catalog, with an aborted update by a concurrent +# transaction +# +# s1 creates a table and then updates its pg_class row in a subtransaction +# that rolls back. s1 is still open when s2 rewrites pg_class, and no one +# has set HEAP_XMAX_INVALID on the pg_class row yet. The row must survive. + +teardown +{ + DROP TABLE IF EXISTS cluster_catalog_tab; + DROP TABLE IF EXISTS cluster_plain_tab; +} + +session s1 +step s1_create { BEGIN; CREATE TABLE cluster_catalog_tab (a int); } +step s1_alter { SAVEPOINT sp; ALTER TABLE cluster_catalog_tab ADD COLUMN b int; ROLLBACK TO sp; } +step s1_commit { COMMIT; } +step s1_plain { BEGIN; CREATE TABLE cluster_plain_tab (a int, b int); CREATE INDEX cluster_plain_idx ON cluster_plain_tab (b); INSERT INTO cluster_plain_tab VALUES (1, 1); SAVEPOINT sp; UPDATE cluster_plain_tab SET a = 2; ROLLBACK TO sp; CLUSTER cluster_plain_tab USING cluster_plain_idx; COMMIT; } +step s1_count { SELECT count(*) FROM cluster_plain_tab; } + +session s2 +step s2_vacfull { VACUUM FULL pg_class; } +step s2_check { SELECT count(*) FROM pg_class WHERE relname = 'cluster_catalog_tab'; } + +permutation s1_create s1_alter s2_vacfull s1_commit s2_check +permutation s1_plain s1_count -- 2.37.1 (Apple Git-137.1)