From 262f2eea958d21fdbe90a3e92ccc3e5c3f0d8059 Mon Sep 17 00:00:00 2001 From: Richard Guo Date: Wed, 2 Sep 2026 14:03:50 +0900 Subject: [PATCH] Fix OID-counter-sensitive row ordering in foreign_key test Several queries in the foreign_key test sort pg_constraint rows with "ORDER BY oid::regclass::text". These OIDs are constraint OIDs, not relation OIDs, so the regclass cast renders them as numeric strings, and text comparison of numeric strings depends on digit count. If the OID counter crosses a power of ten while the test runs, the output order flips, causing spurious test failures. This cannot happen in a fresh cluster, but installcheck against a long-lived cluster can hit it. To fix, sort by constraint name, which is unique within each of these queries, and use COLLATE "C" so that the order does not depend on the database's default collation either. Author: Richard Guo Reviewed-by: Tom Lane Reviewed-by: jian he Discussion: https://postgr.es/m/CAMbWs49KaJFGs4rfBWk+YmHPTXZ+pJTMJ12iaz7ruZdSK+sC5w@mail.gmail.com Backpatch-through: 18 --- src/test/regress/expected/foreign_key.out | 36 +++++++++++------------ src/test/regress/sql/foreign_key.sql | 18 ++++++------ 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index ac044eb40fa..333994e6505 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -887,11 +887,11 @@ CREATE TABLE FKTABLE ( -- this tests handling of duplicate entries in SET DEFAULT column list FOREIGN KEY (tid, fk_id_del_set_default) REFERENCES PKTABLE ON DELETE SET DEFAULT (fk_id_del_set_default, fk_id_del_set_default) ); -SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conrelid = 'fktable'::regclass::oid ORDER BY oid; +SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conrelid = 'fktable'::regclass::oid ORDER BY conname COLLATE "C"; pg_get_constraintdef -------------------------------------------------------------------------------------------------------------------- - FOREIGN KEY (tid, fk_id_del_set_null) REFERENCES pktable(tid, id) ON DELETE SET NULL (fk_id_del_set_null) FOREIGN KEY (tid, fk_id_del_set_default) REFERENCES pktable(tid, id) ON DELETE SET DEFAULT (fk_id_del_set_default) + FOREIGN KEY (tid, fk_id_del_set_null) REFERENCES pktable(tid, id) ON DELETE SET NULL (fk_id_del_set_null) (2 rows) INSERT INTO PKTABLE VALUES (1, 0), (1, 1), (1, 2); @@ -1936,22 +1936,22 @@ ALTER TABLE fk_partitioned_fk_1 ADD FOREIGN KEY (a, b) REFERENCES fk_notpartitio ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_1 FOR VALUES FROM (0,0) TO (1000,1000); -- Child constraint will remain valid. SELECT conname, convalidated, conrelid::regclass FROM pg_constraint -WHERE conrelid::regclass::text like 'fk_partitioned_fk%' ORDER BY oid::regclass::text; +WHERE conrelid::regclass::text like 'fk_partitioned_fk%' ORDER BY conname COLLATE "C"; conname | convalidated | conrelid ------------------------------+--------------+--------------------- - fk_partitioned_fk_a_b_fkey | f | fk_partitioned_fk fk_partitioned_fk_1_a_b_fkey | t | fk_partitioned_fk_1 + fk_partitioned_fk_a_b_fkey | f | fk_partitioned_fk (2 rows) -- Validate the constraint ALTER TABLE fk_partitioned_fk VALIDATE CONSTRAINT fk_partitioned_fk_a_b_fkey; -- All constraints are now valid. SELECT conname, convalidated, conrelid::regclass FROM pg_constraint -WHERE conrelid::regclass::text like 'fk_partitioned_fk%' ORDER BY oid::regclass::text; +WHERE conrelid::regclass::text like 'fk_partitioned_fk%' ORDER BY conname COLLATE "C"; conname | convalidated | conrelid ------------------------------+--------------+--------------------- - fk_partitioned_fk_a_b_fkey | t | fk_partitioned_fk fk_partitioned_fk_1_a_b_fkey | t | fk_partitioned_fk_1 + fk_partitioned_fk_a_b_fkey | t | fk_partitioned_fk (2 rows) -- Attaching a child with a NOT VALID constraint. @@ -1967,7 +1967,7 @@ TRUNCATE fk_partitioned_fk_2; ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_2 FOR VALUES FROM (1000,1000) TO (2000,2000); -- The child constraint will also be valid. SELECT conname, convalidated FROM pg_constraint -WHERE conrelid = 'fk_partitioned_fk_2'::regclass ORDER BY oid::regclass::text; +WHERE conrelid = 'fk_partitioned_fk_2'::regclass ORDER BY conname COLLATE "C"; conname | convalidated ------------------------------+-------------- fk_partitioned_fk_2_a_b_fkey | t @@ -1984,14 +1984,14 @@ ALTER TABLE fk_partitioned_fk_3 ATTACH PARTITION fk_partitioned_fk_3_1 FOR VALUE ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_3 FOR VALUES FROM (2000,2000) TO (3000,3000); -- All constraints are now valid. SELECT conname, convalidated, conrelid::regclass FROM pg_constraint -WHERE conrelid::regclass::text like 'fk_partitioned_fk%' ORDER BY oid::regclass::text; +WHERE conrelid::regclass::text like 'fk_partitioned_fk%' ORDER BY conname COLLATE "C"; conname | convalidated | conrelid --------------------------------+--------------+----------------------- - fk_partitioned_fk_a_b_fkey | t | fk_partitioned_fk fk_partitioned_fk_1_a_b_fkey | t | fk_partitioned_fk_1 fk_partitioned_fk_2_a_b_fkey | t | fk_partitioned_fk_2 - fk_partitioned_fk_3_a_b_fkey | t | fk_partitioned_fk_3 fk_partitioned_fk_3_1_a_b_fkey | t | fk_partitioned_fk_3_1 + fk_partitioned_fk_3_a_b_fkey | t | fk_partitioned_fk_3 + fk_partitioned_fk_a_b_fkey | t | fk_partitioned_fk (5 rows) DROP TABLE fk_partitioned_fk, fk_notpartitioned_pk; @@ -2009,30 +2009,30 @@ ALTER TABLE fk_notpartitioned_fk ADD CONSTRAINT fk_notpartitioned_fk_a_b_fkey2 FOREIGN KEY (a, b) REFERENCES fk_partitioned_pk NOT ENFORCED; -- All constraints will be invalid, and _fkey2 constraints will not be enforced. SELECT conname, conenforced, convalidated FROM pg_constraint -WHERE conrelid = 'fk_notpartitioned_fk'::regclass ORDER BY oid::regclass::text; +WHERE conrelid = 'fk_notpartitioned_fk'::regclass ORDER BY conname COLLATE "C"; conname | conenforced | convalidated ----------------------------------+-------------+-------------- fk_notpartitioned_fk_a_b_fkey | t | f - fk_notpartitioned_fk_a_b_fkey_1 | t | f - fk_notpartitioned_fk_a_b_fkey_2 | t | f fk_notpartitioned_fk_a_b_fkey2 | f | f fk_notpartitioned_fk_a_b_fkey2_1 | f | f fk_notpartitioned_fk_a_b_fkey2_2 | f | f + fk_notpartitioned_fk_a_b_fkey_1 | t | f + fk_notpartitioned_fk_a_b_fkey_2 | t | f (6 rows) ALTER TABLE fk_notpartitioned_fk VALIDATE CONSTRAINT fk_notpartitioned_fk_a_b_fkey; ALTER TABLE fk_notpartitioned_fk ALTER CONSTRAINT fk_notpartitioned_fk_a_b_fkey2 ENFORCED; -- All constraints are now valid and enforced. SELECT conname, conenforced, convalidated FROM pg_constraint -WHERE conrelid = 'fk_notpartitioned_fk'::regclass ORDER BY oid::regclass::text; +WHERE conrelid = 'fk_notpartitioned_fk'::regclass ORDER BY conname COLLATE "C"; conname | conenforced | convalidated ----------------------------------+-------------+-------------- fk_notpartitioned_fk_a_b_fkey | t | t - fk_notpartitioned_fk_a_b_fkey_1 | t | t - fk_notpartitioned_fk_a_b_fkey_2 | t | t fk_notpartitioned_fk_a_b_fkey2 | t | t fk_notpartitioned_fk_a_b_fkey2_1 | t | t fk_notpartitioned_fk_a_b_fkey2_2 | t | t + fk_notpartitioned_fk_a_b_fkey_1 | t | t + fk_notpartitioned_fk_a_b_fkey_2 | t | t (6 rows) -- test a self-referential FK @@ -2042,7 +2042,7 @@ CREATE TABLE fk_partitioned_pk_3 PARTITION OF fk_partitioned_pk FOR VALUES FROM CREATE TABLE fk_partitioned_pk_3_1 PARTITION OF fk_partitioned_pk_3 FOR VALUES FROM (2000) TO (2100); SELECT conname, conenforced, convalidated FROM pg_constraint WHERE conrelid = 'fk_partitioned_pk'::regclass AND contype = 'f' -ORDER BY oid::regclass::text; +ORDER BY conname COLLATE "C"; conname | conenforced | convalidated ------------+-------------+-------------- selffk | t | f @@ -2056,7 +2056,7 @@ ALTER TABLE fk_partitioned_pk_2 VALIDATE CONSTRAINT selffk; ALTER TABLE fk_partitioned_pk VALIDATE CONSTRAINT selffk; SELECT conname, conenforced, convalidated FROM pg_constraint WHERE conrelid = 'fk_partitioned_pk'::regclass AND contype = 'f' -ORDER BY oid::regclass::text; +ORDER BY conname COLLATE "C"; conname | conenforced | convalidated ------------+-------------+-------------- selffk | t | t diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index a93e81b42bc..21ef1931f31 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -585,7 +585,7 @@ CREATE TABLE FKTABLE ( FOREIGN KEY (tid, fk_id_del_set_default) REFERENCES PKTABLE ON DELETE SET DEFAULT (fk_id_del_set_default, fk_id_del_set_default) ); -SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conrelid = 'fktable'::regclass::oid ORDER BY oid; +SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conrelid = 'fktable'::regclass::oid ORDER BY conname COLLATE "C"; INSERT INTO PKTABLE VALUES (1, 0), (1, 1), (1, 2); INSERT INTO FKTABLE VALUES @@ -1449,14 +1449,14 @@ ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_1 FOR VALUES FR -- Child constraint will remain valid. SELECT conname, convalidated, conrelid::regclass FROM pg_constraint -WHERE conrelid::regclass::text like 'fk_partitioned_fk%' ORDER BY oid::regclass::text; +WHERE conrelid::regclass::text like 'fk_partitioned_fk%' ORDER BY conname COLLATE "C"; -- Validate the constraint ALTER TABLE fk_partitioned_fk VALIDATE CONSTRAINT fk_partitioned_fk_a_b_fkey; -- All constraints are now valid. SELECT conname, convalidated, conrelid::regclass FROM pg_constraint -WHERE conrelid::regclass::text like 'fk_partitioned_fk%' ORDER BY oid::regclass::text; +WHERE conrelid::regclass::text like 'fk_partitioned_fk%' ORDER BY conname COLLATE "C"; -- Attaching a child with a NOT VALID constraint. CREATE TABLE fk_partitioned_fk_2 (a int, b int); @@ -1472,7 +1472,7 @@ ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_2 FOR VALUES FR -- The child constraint will also be valid. SELECT conname, convalidated FROM pg_constraint -WHERE conrelid = 'fk_partitioned_fk_2'::regclass ORDER BY oid::regclass::text; +WHERE conrelid = 'fk_partitioned_fk_2'::regclass ORDER BY conname COLLATE "C"; -- Test case where the child constraint is invalid, the grandchild constraint -- is valid, and the validation for the grandchild should be skipped when a @@ -1486,7 +1486,7 @@ ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_3 FOR VALUES FR -- All constraints are now valid. SELECT conname, convalidated, conrelid::regclass FROM pg_constraint -WHERE conrelid::regclass::text like 'fk_partitioned_fk%' ORDER BY oid::regclass::text; +WHERE conrelid::regclass::text like 'fk_partitioned_fk%' ORDER BY conname COLLATE "C"; DROP TABLE fk_partitioned_fk, fk_notpartitioned_pk; @@ -1505,14 +1505,14 @@ ALTER TABLE fk_notpartitioned_fk ADD CONSTRAINT fk_notpartitioned_fk_a_b_fkey2 -- All constraints will be invalid, and _fkey2 constraints will not be enforced. SELECT conname, conenforced, convalidated FROM pg_constraint -WHERE conrelid = 'fk_notpartitioned_fk'::regclass ORDER BY oid::regclass::text; +WHERE conrelid = 'fk_notpartitioned_fk'::regclass ORDER BY conname COLLATE "C"; ALTER TABLE fk_notpartitioned_fk VALIDATE CONSTRAINT fk_notpartitioned_fk_a_b_fkey; ALTER TABLE fk_notpartitioned_fk ALTER CONSTRAINT fk_notpartitioned_fk_a_b_fkey2 ENFORCED; -- All constraints are now valid and enforced. SELECT conname, conenforced, convalidated FROM pg_constraint -WHERE conrelid = 'fk_notpartitioned_fk'::regclass ORDER BY oid::regclass::text; +WHERE conrelid = 'fk_notpartitioned_fk'::regclass ORDER BY conname COLLATE "C"; -- test a self-referential FK ALTER TABLE fk_partitioned_pk ADD CONSTRAINT selffk FOREIGN KEY (a, b) REFERENCES fk_partitioned_pk NOT VALID; @@ -1521,12 +1521,12 @@ CREATE TABLE fk_partitioned_pk_3 PARTITION OF fk_partitioned_pk FOR VALUES FROM CREATE TABLE fk_partitioned_pk_3_1 PARTITION OF fk_partitioned_pk_3 FOR VALUES FROM (2000) TO (2100); SELECT conname, conenforced, convalidated FROM pg_constraint WHERE conrelid = 'fk_partitioned_pk'::regclass AND contype = 'f' -ORDER BY oid::regclass::text; +ORDER BY conname COLLATE "C"; ALTER TABLE fk_partitioned_pk_2 VALIDATE CONSTRAINT selffk; ALTER TABLE fk_partitioned_pk VALIDATE CONSTRAINT selffk; SELECT conname, conenforced, convalidated FROM pg_constraint WHERE conrelid = 'fk_partitioned_pk'::regclass AND contype = 'f' -ORDER BY oid::regclass::text; +ORDER BY conname COLLATE "C"; DROP TABLE fk_notpartitioned_fk, fk_partitioned_pk;