From 6b2ba82493599c4ca1084226cbafbba7c127b546 Mon Sep 17 00:00:00 2001 From: Amit Langote Date: Thu, 16 Jul 2026 18:17:01 +0900 Subject: [PATCH v1 3/6] Restrict RI fast-path FK check to btree referenced indexes The RI fast-path check probes the referenced index directly and, for single-column keys, uses SK_SEARCHARRAY. Both assume the index is a btree. A comment claimed "PK indexes are always btree", but a foreign key's referenced index need not be a primary key: transformFkeyCheckAttrs() accepts any unique (or, for temporal keys, exclusion) index, so an out-of-tree access method advertising amcanunique could supply a non-btree index reachable by the fast path. Add pk_index_is_btree to RI_ConstraintInfo, set from the referenced index's access method when the constraint is loaded, and make ri_fastpath_is_applicable() return false for non-btree indexes so such constraints fall back to the SPI path. Reported-by: Noah Misch Discussion: https://postgr.es/m/20260705210533.ee.noahmisch@microsoft.com Backpatch-through: 19 --- src/backend/utils/adt/ri_triggers.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index d2b969e31ae..f95f72348e1 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -32,6 +32,7 @@ #include "access/tableam.h" #include "access/xact.h" #include "catalog/index.h" +#include "catalog/pg_am_d.h" #include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_namespace.h" @@ -142,6 +143,7 @@ typedef struct RI_ConstraintInfo Oid conindid; bool pk_is_partitioned; + bool pk_index_is_btree; /* is conindid a btree index? */ FastPathMeta *fpmeta; } RI_ConstraintInfo; @@ -2503,6 +2505,8 @@ ri_LoadConstraintInfo(Oid constraintOid) riinfo->conindid = conForm->conindid; riinfo->pk_is_partitioned = (get_rel_relkind(riinfo->pk_relid) == RELKIND_PARTITIONED_TABLE); + riinfo->pk_index_is_btree = + (get_rel_relam(riinfo->conindid) == BTREE_AM_OID); ReleaseSysCache(tup); @@ -3153,7 +3157,8 @@ ri_FastPathFlushArray(RI_FastPathEntry *fpentry, TupleTableSlot *fk_slot, * Build scan key with SK_SEARCHARRAY. The index AM code will internally * sort and deduplicate, then walk leaf pages in order. * - * PK indexes are always btree, which supports SK_SEARCHARRAY. + * ri_fastpath_is_applicable() restricts the fast path to btree indexes, + * which support SK_SEARCHARRAY. * * This path handles single-column FKs only, so index_attnos[0] == 1. */ @@ -3372,6 +3377,17 @@ ri_fastpath_is_applicable(const RI_ConstraintInfo *riinfo) if (riinfo->hasperiod) return false; + /* + * The fast path probes the referenced index directly and, for + * single-column keys, uses SK_SEARCHARRAY. A foreign key's referenced + * index need not be a primary key; transformFkeyCheckAttrs() accepts any + * unique index, so an out-of-tree amcanunique access method could reach + * here. Restrict the fast path to btree, which is what the direct probe + * and SK_SEARCHARRAY assume; other access methods fall back to SPI. + */ + if (!riinfo->pk_index_is_btree) + return false; + return true; } -- 2.47.3