v1-0001-Add-nkeys-parameter-to-recheck_matched_pk_tuple.patch

application/octet-stream

Filename: v1-0001-Add-nkeys-parameter-to-recheck_matched_pk_tuple.patch
Type: application/octet-stream
Part: 0
Message: Re: Eliminating SPI / SQL from some RI triggers - take 3

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v1-0001
Subject: Add nkeys parameter to recheck_matched_pk_tuple()
File+
src/backend/utils/adt/ri_triggers.c 6 5
From 344ba694451fbaa57bbb2e12930fe20fd425e806 Mon Sep 17 00:00:00 2001
From: Amit Langote <amitlan@postgresql.org>
Date: Wed, 8 Apr 2026 10:20:15 +0900
Subject: [PATCH v1] Add nkeys parameter to recheck_matched_pk_tuple()

The function looped over ii_NumIndexKeyAttrs elements of the skeys
array, but one caller (ri_FastPathFlushArray) passes a one-element
array since it only handles single-column FKs.  The function
signature did not communicate this constraint, which static analysis
flags as a potential out-of-bounds read.

Add an nkeys parameter and assert that it matches
ii_NumIndexKeyAttrs, then use it in the loop.  The call sites
already know the key count.

Reported-by: Evan Montgomery-Recht <montge@mianetworks.net>
Discussion: https://postgr.es/m/CAEg7pwcKf01FmDqFAf-Hzu_pYnMYScY_Otid-pe9uw3BJ6gq9g@mail.gmail.com
---
 src/backend/utils/adt/ri_triggers.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 84f9fecdb4c..18ec858357d 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -329,7 +329,7 @@ static bool ri_LockPKTuple(Relation pk_rel, TupleTableSlot *slot, Snapshot snap,
 static bool ri_fastpath_is_applicable(const RI_ConstraintInfo *riinfo);
 static void ri_CheckPermissions(Relation query_rel);
 static bool recheck_matched_pk_tuple(Relation idxrel, ScanKeyData *skeys,
-									 TupleTableSlot *new_slot);
+									 int nkeys, TupleTableSlot *new_slot);
 static void build_index_scankeys(const RI_ConstraintInfo *riinfo,
 								 Relation idx_rel, Datum *pk_vals,
 								 char *pk_nulls, ScanKey skeys);
@@ -3138,7 +3138,7 @@ ri_FastPathFlushArray(RI_FastPathEntry *fpentry, TupleTableSlot *fk_slot,
 								   idx_rel->rd_indcollation[0],
 								   fpmeta->regops[0],
 								   found_val);
-			if (!recheck_matched_pk_tuple(idx_rel, recheck_skey, pk_slot))
+			if (!recheck_matched_pk_tuple(idx_rel, recheck_skey, 1, pk_slot))
 				continue;
 		}
 
@@ -3193,7 +3193,7 @@ ri_FastPathProbeOne(Relation pk_rel, Relation idx_rel,
 						   &concurrently_updated))
 		{
 			if (concurrently_updated)
-				found = recheck_matched_pk_tuple(idx_rel, skey, slot);
+				found = recheck_matched_pk_tuple(idx_rel, skey, nkeys, slot);
 			else
 				found = true;
 		}
@@ -3340,7 +3340,7 @@ ri_CheckPermissions(Relation query_rel)
  * not found.
  */
 static bool
-recheck_matched_pk_tuple(Relation idxrel, ScanKeyData *skeys,
+recheck_matched_pk_tuple(Relation idxrel, ScanKeyData *skeys, int nkeys,
 						 TupleTableSlot *new_slot)
 {
 	/*
@@ -3359,8 +3359,9 @@ recheck_matched_pk_tuple(Relation idxrel, ScanKeyData *skeys,
 		   indexInfo->ii_ExclusionOps == NULL);
 
 	/* Form the index values and isnull flags given the table tuple. */
+	Assert(nkeys == indexInfo->ii_NumIndexKeyAttrs);
 	FormIndexDatum(indexInfo, new_slot, NULL, values, isnull);
-	for (int i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
+	for (int i = 0; i < nkeys; i++)
 	{
 		ScanKeyData *skey = &skeys[i];
 
-- 
2.47.3