v5-0004-Refine-fast-path-FK-validation-path.patch
application/octet-stream
Filename: v5-0004-Refine-fast-path-FK-validation-path.patch
Type: application/octet-stream
Part: 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 v5-0004
Subject: Refine fast-path FK validation path
| File | + | − |
|---|---|---|
| src/backend/utils/adt/ri_triggers.c | 23 | 50 |
From 1923dbce2127f91f0ded31ed1f2465a2961cedca Mon Sep 17 00:00:00 2001
From: Junwang Zhao <zhjwpku@gmail.com>
Date: Tue, 10 Mar 2026 16:06:56 +0800
Subject: [PATCH v5 4/6] Refine fast-path FK validation path
The `use_cache` branch in `ri_FastPathCheck` is no longer needed.
Remove it and update the comments that are no longer applicable.
---
src/backend/utils/adt/ri_triggers.c | 73 +++++++++--------------------
1 file changed, 23 insertions(+), 50 deletions(-)
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 7929d6f8c85..200c4094861 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -200,7 +200,7 @@ typedef struct RI_CompareHashEntry
/*
* RI_FastPathEntry
- * Per-constraint cache of resources needed by ri_FastPathCheck().
+ * Per-constraint cache of resources needed by ri_FastPathBatchFlush().
*
* One entry per constraint, keyed by pg_constraint OID. Created lazily
* by ri_FastPathGetEntry() on first use within a trigger-firing batch
@@ -2722,10 +2722,14 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
/*
* ri_FastPathCheck
- * Perform FK existence check via direct index probe, bypassing SPI.
+ * Perform per row FK existence check via direct index probe,
+ * bypassing SPI.
*
* If no matching PK row exists, report the violation via ri_ReportViolation(),
* otherwise, the function returns normally.
+ *
+ * Note: This is only used by the ALTER TABLE validation path. Other paths use
+ * ri_FastPathBatchAdd().
*/
static void
ri_FastPathCheck(const RI_ConstraintInfo *riinfo,
@@ -2744,8 +2748,6 @@ ri_FastPathCheck(const RI_ConstraintInfo *riinfo,
int saved_sec_context;
Snapshot snapshot;
Snapshot xact_snap = NULL;
- bool use_cache;
- RI_FastPathEntry *fpentry = NULL;
/*
* Use the per-batch cache only if we're inside the after-trigger
@@ -2753,10 +2755,7 @@ ri_FastPathCheck(const RI_ConstraintInfo *riinfo,
* ... ADD FOREIGN KEY validation, triggers are called directly and the
* callback would never run, leaking resources.
*/
- use_cache = AfterTriggerBatchIsActive();
-
- if (use_cache)
- fpentry = ri_FastPathGetEntry(riinfo);
+ Assert(!AfterTriggerBatchIsActive());
/*
* Advance the command counter so the snapshot sees the effects of prior
@@ -2764,36 +2763,14 @@ ri_FastPathCheck(const RI_ConstraintInfo *riinfo,
* ri_PerformCheck().
*/
CommandCounterIncrement();
- if (use_cache)
- {
- /*
- * The snapshot was registered once when the cache entry was created.
- * Patch curcid so it reflects the effects of prior triggers in this
- * statement. We deliberately do not call GetLatestSnapshot() again:
- * the xmin/xmax/xip fields do not need refreshing because any PK row
- * we need to see was either already visible when the batch started or
- * will be found via the tuple-lock wait (LockTupleKeyShare).
- */
- Assert(fpentry && fpentry->snapshot != NULL);
- snapshot = fpentry->snapshot;
- snapshot->curcid = GetCurrentCommandId(false);
- xact_scan = fpentry->xact_scan;
- xact_snap = fpentry->xact_snap;
- pk_rel = fpentry->pk_rel;
- idx_rel = fpentry->idx_rel;
- scandesc = fpentry->scandesc;
- slot = fpentry->slot;
- }
- else
- {
- snapshot = RegisterSnapshot(GetLatestSnapshot());
- pk_rel = table_open(riinfo->pk_relid, RowShareLock);
- idx_rel = index_open(riinfo->conindid, AccessShareLock);
- scandesc = index_beginscan(pk_rel, idx_rel,
- snapshot, NULL,
- riinfo->nkeys, 0);
- slot = table_slot_create(pk_rel, NULL);
- }
+
+ snapshot = RegisterSnapshot(GetLatestSnapshot());
+ pk_rel = table_open(riinfo->pk_relid, RowShareLock);
+ idx_rel = index_open(riinfo->conindid, AccessShareLock);
+ scandesc = index_beginscan(pk_rel, idx_rel,
+ snapshot, NULL,
+ riinfo->nkeys, 0);
+ slot = table_slot_create(pk_rel, NULL);
if (riinfo->fpmeta == NULL)
ri_populate_fastpath_metadata((RI_ConstraintInfo *) riinfo,
@@ -2805,14 +2782,13 @@ ri_FastPathCheck(const RI_ConstraintInfo *riinfo,
saved_sec_context |
SECURITY_LOCAL_USERID_CHANGE |
SECURITY_NOFORCE_RLS);
- if (!use_cache)
- ri_CheckPermissions(pk_rel);
+ ri_CheckPermissions(pk_rel);
ri_ExtractValues(fk_rel, newslot, riinfo, false, pk_vals, pk_nulls);
build_index_scankeys(riinfo, idx_rel, pk_vals, pk_nulls, skey);
found = ri_FastPathProbeOne(pk_rel, idx_rel, scandesc, xact_scan,
slot, snapshot, xact_snap, riinfo,
- skey, riinfo->nkeys, use_cache);
+ skey, riinfo->nkeys, false);
if (!found)
ri_ReportViolation(riinfo, pk_rel, fk_rel,
newslot, NULL,
@@ -2820,14 +2796,11 @@ ri_FastPathCheck(const RI_ConstraintInfo *riinfo,
SetUserIdAndSecContext(saved_userid, saved_sec_context);
/* Non-cached path: clean up per-invocation resources */
- if (!use_cache)
- {
- index_endscan(scandesc);
- index_close(idx_rel, NoLock);
- table_close(pk_rel, NoLock);
- ExecDropSingleTupleTableSlot(slot);
- UnregisterSnapshot(snapshot);
- }
+ index_endscan(scandesc);
+ index_close(idx_rel, NoLock);
+ table_close(pk_rel, NoLock);
+ ExecDropSingleTupleTableSlot(slot);
+ UnregisterSnapshot(snapshot);
}
/*
@@ -3987,7 +3960,7 @@ ri_FastPathGetEntry(const RI_ConstraintInfo *riinfo)
/*
* Register an initial snapshot. Its curcid will be patched in place
- * on each subsequent row (see ri_FastPathCheck()), avoiding per-row
+ * on each subsequent row (see ri_FastPathBatchFlush()), avoiding per-row
* GetSnapshotData() overhead.
*/
entry->snapshot = RegisterSnapshot(GetLatestSnapshot());
--
2.41.0