Avoid ERROR at ON COMMIT DELETE ROWS after relhassubclass=f.
authorNoah Misch <noah@leadboat.com>
Sun, 20 Apr 2025 15:28:48 +0000 (08:28 -0700)
committerNoah Misch <noah@leadboat.com>
Sun, 20 Apr 2025 15:28:48 +0000 (08:28 -0700)
Commit 7102070329d8147246d2791321f9915c3b5abf31 fixed a similar bug, but
it missed the case of database-wide ANALYZE ("use_own_xacts" mode).
Commit a07e03fd8fa7daf4d1356f7cb501ffe784ea6257 changed consequences
from silent discard of a pg_class stats (relpages et al.) update to
ERROR "tuple to be updated was already modified".  Losing a relpages
update of an ON COMMIT DELETE ROWS table was negligible, but a
COMMIT-time error isn't negligible.  Back-patch to v13 (all supported
versions).

Reported-by: Richard Guo <guofenglinux@gmail.com
Reported-by: Robins Tharakan <tharakan@gmail.com>
Discussion: https://postgr.es/m/CAMbWs4-XwMKMKJ_GT=p3_-_=j9rQSEs1FbDFUnW9zHuKPsPNEQ@mail.gmail.com
Backpatch-through: 13

src/backend/commands/vacuum.c
src/test/regress/expected/maintain_every.out [new file with mode: 0644]
src/test/regress/parallel_schedule
src/test/regress/sql/maintain_every.sql [new file with mode: 0644]

index db5da3ce826210da7d9794357cb3d7a5f69efb47..33a33bf6b1cfae47d448cf18b2501de186164c62 100644 (file)
@@ -657,6 +657,8 @@ vacuum(List *relations, VacuumParams *params, BufferAccessStrategy bstrategy,
                if (use_own_xacts)
                {
                    PopActiveSnapshot();
+                   /* standard_ProcessUtility() does CCI if !use_own_xacts */
+                   CommandCounterIncrement();
                    CommitTransactionCommand();
                }
                else
diff --git a/src/test/regress/expected/maintain_every.out b/src/test/regress/expected/maintain_every.out
new file mode 100644 (file)
index 0000000..dea1089
--- /dev/null
@@ -0,0 +1,33 @@
+-- Test maintenance commands that visit every eligible relation.  Run as a
+-- non-superuser, to skip other users' tables.
+CREATE ROLE regress_maintain;
+SET ROLE regress_maintain;
+-- Test database-wide ANALYZE ("use_own_xacts" mode) setting relhassubclass=f
+-- for non-partitioning inheritance, w/ ON COMMIT DELETE ROWS building an
+-- empty index.
+CREATE TEMP TABLE past_inh_db_other (); -- need 2 tables for "use_own_xacts"
+CREATE TEMP TABLE past_inh_db_parent () ON COMMIT DELETE ROWS;
+CREATE TEMP TABLE past_inh_db_child () INHERITS (past_inh_db_parent);
+CREATE INDEX ON past_inh_db_parent ((1));
+ANALYZE past_inh_db_parent;
+SELECT reltuples, relhassubclass
+  FROM pg_class WHERE oid = 'past_inh_db_parent'::regclass;
+ reltuples | relhassubclass 
+-----------+----------------
+         0 | t
+(1 row)
+
+DROP TABLE past_inh_db_child;
+SET client_min_messages = error; -- hide WARNINGs for other users' tables
+ANALYZE;
+RESET client_min_messages;
+SELECT reltuples, relhassubclass
+  FROM pg_class WHERE oid = 'past_inh_db_parent'::regclass;
+ reltuples | relhassubclass 
+-----------+----------------
+         0 | f
+(1 row)
+
+DROP TABLE past_inh_db_parent, past_inh_db_other;
+RESET ROLE;
+DROP ROLE regress_maintain;
index 0f38caa0d240a174b3813885f0805de2bfe46170..a424be2a6bf0f42f35ab26ea858c09c803679848 100644 (file)
@@ -91,6 +91,10 @@ test: select_parallel
 test: write_parallel
 test: vacuum_parallel
 
+# Run this alone, because concurrent DROP TABLE would make non-superuser
+# "ANALYZE;" fail with "relation with OID $n does not exist".
+test: maintain_every
+
 # no relation related tests can be put in this group
 test: publication subscription
 
diff --git a/src/test/regress/sql/maintain_every.sql b/src/test/regress/sql/maintain_every.sql
new file mode 100644 (file)
index 0000000..263e972
--- /dev/null
@@ -0,0 +1,26 @@
+-- Test maintenance commands that visit every eligible relation.  Run as a
+-- non-superuser, to skip other users' tables.
+
+CREATE ROLE regress_maintain;
+SET ROLE regress_maintain;
+
+-- Test database-wide ANALYZE ("use_own_xacts" mode) setting relhassubclass=f
+-- for non-partitioning inheritance, w/ ON COMMIT DELETE ROWS building an
+-- empty index.
+CREATE TEMP TABLE past_inh_db_other (); -- need 2 tables for "use_own_xacts"
+CREATE TEMP TABLE past_inh_db_parent () ON COMMIT DELETE ROWS;
+CREATE TEMP TABLE past_inh_db_child () INHERITS (past_inh_db_parent);
+CREATE INDEX ON past_inh_db_parent ((1));
+ANALYZE past_inh_db_parent;
+SELECT reltuples, relhassubclass
+  FROM pg_class WHERE oid = 'past_inh_db_parent'::regclass;
+DROP TABLE past_inh_db_child;
+SET client_min_messages = error; -- hide WARNINGs for other users' tables
+ANALYZE;
+RESET client_min_messages;
+SELECT reltuples, relhassubclass
+  FROM pg_class WHERE oid = 'past_inh_db_parent'::regclass;
+DROP TABLE past_inh_db_parent, past_inh_db_other;
+
+RESET ROLE;
+DROP ROLE regress_maintain;