From 5bca7fa1df0b31f8b2e1c02562449ebdae637324 Mon Sep 17 00:00:00 2001 From: Zsolt Parragi Date: Thu, 15 Oct 2026 11:53:28 +0000 Subject: [PATCH v1 1/2] Don't let pg_upgrade carry over temporal keys using gist_inet_ops. check_for_gist_inet_ops() ignores gist_inet_ops/gist_cidr_ops indexes if those opclasses have been marked not-opcdefault in the old cluster, on the theory that pg_dump will then print the opclass explicitly. That's true for plain indexes and exclusion constraints, but not for the index of a PRIMARY KEY or UNIQUE constraint using WITHOUT OVERLAPS: pg_dump emits those as constraint clauses listing only column names. So the restored index gets v19's default inet_ops, while the transferred index files still contain btree_gist's keys. The upgrade reports success, and the first scan of the index fails an assertion in network_gist.c (or, in a non-assert build, quietly misreads the keys). To fix, also complain about unique indexes. GiST has no plain unique indexes, so indisunique identifies exactly the temporal-key case. While at it, document what to do with such constraints. The advice to replace the index with an inet_ops one doesn't work for them, since a PRIMARY KEY/UNIQUE constraint cannot specify an opclass and ADD CONSTRAINT ... USING INDEX does not accept GiST indexes; they have to be dropped before upgrading and re-created afterwards. Oversight in commit b352d3d80. Back-patch to v19. --- doc/src/sgml/btree-gist.sgml | 9 +++++++++ src/bin/pg_upgrade/check.c | 6 +++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/btree-gist.sgml b/doc/src/sgml/btree-gist.sgml index cc09ec83733..17965d9ad58 100644 --- a/doc/src/sgml/btree-gist.sgml +++ b/doc/src/sgml/btree-gist.sgml @@ -153,6 +153,15 @@ DROP INDEX bad_index; + + That is not possible for the index of a PRIMARY KEY + or UNIQUE constraint using WITHOUT + OVERLAPS, since such a constraint cannot specify an operator + class. Such constraints (and any foreign keys referencing them) must be + dropped before upgrading, and re-created afterwards, when they will + use inet_ops. + + diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c index 150c4fc34b9..1c54c9f4c8e 100644 --- a/src/bin/pg_upgrade/check.c +++ b/src/bin/pg_upgrade/check.c @@ -1770,6 +1770,10 @@ process_gist_inet_ops_check(DbInfo *dbinfo, PGresult *res, void *arg) * Verify that no indexes use gist_inet_ops/gist_cidr_ops, unless the * opclasses have been changed to not-opcdefault (which would allow * the old server to dump the index definitions with explicit opclasses). + * That escape hatch does not work for the indexes of PRIMARY KEY/UNIQUE + * ... WITHOUT OVERLAPS constraints, since pg_dump emits those as + * constraint clauses that list only column names. GiST does not support + * plain unique indexes, so indisunique identifies those. */ static void check_for_gist_inet_ops(ClusterInfo *cluster) @@ -1781,7 +1785,7 @@ check_for_gist_inet_ops(ClusterInfo *cluster) " pg_catalog.pg_class cc, pg_catalog.pg_namespace nc " "WHERE oc.opcmethod = " CppAsString2(GIST_AM_OID) " AND oc.opcname IN ('gist_inet_ops', 'gist_cidr_ops')" - " AND oc.opcdefault" + " AND (oc.opcdefault OR i.indisunique)" " AND oc.oid = any(i.indclass)" " AND i.indexrelid = cc.oid AND cc.relnamespace = nc.oid"; -- 2.56.0