pg.ddx.io  pgsql-committers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
pgsql: Add fast path for foreign key constraint checks
5+ messages / 2 participants
[nested] [flat]

* pgsql: Add fast path for foreign key constraint checks
@ 2026-03-31 04:53 Amit Langote <amitlan@postgresql.org>
  2026-03-31 07:17 ` Re: pgsql: Add fast path for foreign key constraint checks Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 5+ messages in thread

From: Amit Langote @ 2026-03-31 04:53 UTC (permalink / raw)
  To: pgsql-committers@lists.postgresql.org

Add fast path for foreign key constraint checks

Add a fast-path optimization for foreign key checks that bypasses SPI
by directly probing the unique index on the referenced table.
Benchmarking shows ~1.8x speedup for bulk FK inserts (int PK/int FK,
1M rows, where PK table and index are cached).

The fast path applies when the referenced table is not partitioned and
the constraint does not involve temporal semantics.  Otherwise, the
existing SPI path is used.

This optimization covers only the referential check trigger
(RI_FKey_check).  The action triggers (CASCADE, SET NULL, SET DEFAULT,
RESTRICT, NO ACTION) must find rows on the FK side to modify, which
requires a table scan with no guaranteed index available, and then
execute DML against those rows through the full executor path including
any triggered actions.  Replicating that without substantial code
duplication is not feasible, so those triggers remain on the SPI path.
Extending the fast path to action triggers remains possible as future
work if the necessary infrastructure is built.

The new ri_FastPathCheck() function extracts the FK values, builds scan
keys, performs an index scan, and locks the matching tuple with
LockTupleKeyShare via ri_LockPKTuple(), which handles the RI-specific
subset of table_tuple_lock() results.

If the locked tuple was reached by chasing an update chain
(tmfd.traversed), recheck_matched_pk_tuple() verifies that the key
is still the same, emulating EvalPlanQual.

The scan uses GetTransactionSnapshot(), matching what the SPI path
uses (via _SPI_execute_plan pushing GetTransactionSnapshot() as the
active snapshot).  Under READ COMMITTED this is a fresh snapshot;
under REPEATABLE READ / SERIALIZABLE it is the frozen transaction-
start snapshot, so PK rows committed after the transaction started
are not visible.

The ri_CheckPermissions() function performs schema USAGE and table
SELECT checks, matching what the SPI path gets implicitly through
the executor's permission checks.  The fast path also switches to
the PK table owner's security context (with SECURITY_NOFORCE_RLS)
before the index probe, matching the SPI path where the query runs
as the table owner.

ri_HashCompareOp() is adjusted to handle cross-type equality operators
(e.g. int48eq for int4 PK / int8 FK) which can appear in conpfeqop.
The existing code asserted same-type operators only, which was correct
for its existing callers (ri_KeysEqual compares same-type FK column
values via ff_eq_oprs), but the fast path is the first caller to pass
pf_eq_oprs, which can be cross-type.

Per-key metadata (compare entries, operator procedures, strategy
numbers) is cached in RI_ConstraintInfo via
ri_populate_fastpath_metadata() on first use, eliminating repeated
calls to ri_HashCompareOp() and get_op_opfamily_properties().
conindid and pk_is_partitioned are also cached at constraint load
time, avoiding per-invocation syscache lookups and the need to open
pk_rel before deciding whether the fast path applies.

New regression tests cover RLS bypass and ACL enforcement for the
fast-path permission checks.  New isolation tests exercise concurrent
PK updates under both READ COMMITTED and REPEATABLE READ.

Author: Junwang Zhao <zhjwpku@gmail.com>
Co-authored-by: Amit Langote <amitlangote09@gmail.com>
Reviewed-by: Haibo Yan <tristan.yim@gmail.com>
Tested-by: Tomas Vondra <tomas@vondra.me>
Discussion: https://postgr.es/m/CA+HiwqF4C0ws3cO+z5cLkPuvwnAwkSp7sfvgGj3yQ=Li6KNMqA@mail.gmail.com

Branch
------
master

Details
-------
https://git.postgresql.org/pg/commitdiff/2da86c1ef9b5446e0e22c0b6a5846293e58d98e3

Modified Files
--------------
src/backend/utils/adt/ri_triggers.c                | 466 ++++++++++++++++++++-
.../isolation/expected/fk-concurrent-pk-upd.out    | 105 +++++
src/test/isolation/isolation_schedule              |   1 +
src/test/isolation/specs/fk-concurrent-pk-upd.spec |  53 +++
src/test/regress/expected/foreign_key.out          |  47 +++
src/test/regress/sql/foreign_key.sql               |  64 +++
src/tools/pgindent/typedefs.list                   |   1 +
7 files changed, 723 insertions(+), 14 deletions(-)



^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: pgsql: Add fast path for foreign key constraint checks
  2026-03-31 04:53 pgsql: Add fast path for foreign key constraint checks Amit Langote <amitlan@postgresql.org>
@ 2026-03-31 07:17 ` Amit Langote <amitlangote09@gmail.com>
  2026-03-31 08:08   ` Re: pgsql: Add fast path for foreign key constraint checks Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 5+ messages in thread

From: Amit Langote @ 2026-03-31 07:17 UTC (permalink / raw)
  To: Amit Langote <amitlan@postgresql.org>; +Cc: pgsql-committers@lists.postgresql.org

On Tue, Mar 31, 2026 at 1:54 PM Amit Langote <amitlan@postgresql.org> wrote:
> Add fast path for foreign key constraint checks
>
> Add a fast-path optimization for foreign key checks that bypasses SPI
> by directly probing the unique index on the referenced table.
> Benchmarking shows ~1.8x speedup for bulk FK inserts (int PK/int FK,
> 1M rows, where PK table and index are cached).
>
> The fast path applies when the referenced table is not partitioned and
> the constraint does not involve temporal semantics.  Otherwise, the
> existing SPI path is used.
>
> This optimization covers only the referential check trigger
> (RI_FKey_check).  The action triggers (CASCADE, SET NULL, SET DEFAULT,
> RESTRICT, NO ACTION) must find rows on the FK side to modify, which
> requires a table scan with no guaranteed index available, and then
> execute DML against those rows through the full executor path including
> any triggered actions.  Replicating that without substantial code
> duplication is not feasible, so those triggers remain on the SPI path.
> Extending the fast path to action triggers remains possible as future
> work if the necessary infrastructure is built.
>
> The new ri_FastPathCheck() function extracts the FK values, builds scan
> keys, performs an index scan, and locks the matching tuple with
> LockTupleKeyShare via ri_LockPKTuple(), which handles the RI-specific
> subset of table_tuple_lock() results.
>
> If the locked tuple was reached by chasing an update chain
> (tmfd.traversed), recheck_matched_pk_tuple() verifies that the key
> is still the same, emulating EvalPlanQual.
>
> The scan uses GetTransactionSnapshot(), matching what the SPI path
> uses (via _SPI_execute_plan pushing GetTransactionSnapshot() as the
> active snapshot).  Under READ COMMITTED this is a fresh snapshot;
> under REPEATABLE READ / SERIALIZABLE it is the frozen transaction-
> start snapshot, so PK rows committed after the transaction started
> are not visible.
>
> The ri_CheckPermissions() function performs schema USAGE and table
> SELECT checks, matching what the SPI path gets implicitly through
> the executor's permission checks.  The fast path also switches to
> the PK table owner's security context (with SECURITY_NOFORCE_RLS)
> before the index probe, matching the SPI path where the query runs
> as the table owner.
>
> ri_HashCompareOp() is adjusted to handle cross-type equality operators
> (e.g. int48eq for int4 PK / int8 FK) which can appear in conpfeqop.
> The existing code asserted same-type operators only, which was correct
> for its existing callers (ri_KeysEqual compares same-type FK column
> values via ff_eq_oprs), but the fast path is the first caller to pass
> pf_eq_oprs, which can be cross-type.
>
> Per-key metadata (compare entries, operator procedures, strategy
> numbers) is cached in RI_ConstraintInfo via
> ri_populate_fastpath_metadata() on first use, eliminating repeated
> calls to ri_HashCompareOp() and get_op_opfamily_properties().
> conindid and pk_is_partitioned are also cached at constraint load
> time, avoiding per-invocation syscache lookups and the need to open
> pk_rel before deciding whether the fast path applies.
>
> New regression tests cover RLS bypass and ACL enforcement for the
> fast-path permission checks.  New isolation tests exercise concurrent
> PK updates under both READ COMMITTED and REPEATABLE READ.
>
> Author: Junwang Zhao <zhjwpku@gmail.com>
> Co-authored-by: Amit Langote <amitlangote09@gmail.com>
> Reviewed-by: Haibo Yan <tristan.yim@gmail.com>
> Tested-by: Tomas Vondra <tomas@vondra.me>
> Discussion: https://postgr.es/m/CA+HiwqF4C0ws3cO+z5cLkPuvwnAwkSp7sfvgGj3yQ=Li6KNMqA@mail.gmail.com
>
> Branch
> ------
> master
>
> Details
> -------
> https://git.postgresql.org/pg/commitdiff/2da86c1ef9b5446e0e22c0b6a5846293e58d98e3
>
> Modified Files
> --------------
> src/backend/utils/adt/ri_triggers.c                | 466 ++++++++++++++++++++-
> .../isolation/expected/fk-concurrent-pk-upd.out    | 105 +++++
> src/test/isolation/isolation_schedule              |   1 +
> src/test/isolation/specs/fk-concurrent-pk-upd.spec |  53 +++
> src/test/regress/expected/foreign_key.out          |  47 +++
> src/test/regress/sql/foreign_key.sql               |  64 +++
> src/tools/pgindent/typedefs.list                   |   1 +
> 7 files changed, 723 insertions(+), 14 deletions(-)

I'm looking at the failures on prion:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=prion&dt=2026-03-31%2006%3A53%3A05

They all look like this:
+ERROR:  could not open relation with OID 2139062143

-- 
Thanks, Amit Langote





^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: pgsql: Add fast path for foreign key constraint checks
  2026-03-31 04:53 pgsql: Add fast path for foreign key constraint checks Amit Langote <amitlan@postgresql.org>
  2026-03-31 07:17 ` Re: pgsql: Add fast path for foreign key constraint checks Amit Langote <amitlangote09@gmail.com>
@ 2026-03-31 08:08   ` Amit Langote <amitlangote09@gmail.com>
  2026-03-31 12:21     ` Re: pgsql: Add fast path for foreign key constraint checks Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 5+ messages in thread

From: Amit Langote @ 2026-03-31 08:08 UTC (permalink / raw)
  To: Amit Langote <amitlan@postgresql.org>; +Cc: pgsql-committers@lists.postgresql.org

On Tue, Mar 31, 2026 at 4:17 PM Amit Langote <amitlangote09@gmail.com> wrote:
> On Tue, Mar 31, 2026 at 1:54 PM Amit Langote <amitlan@postgresql.org> wrote:
> > Add fast path for foreign key constraint checks
> >
> > Add a fast-path optimization for foreign key checks that bypasses SPI
> > by directly probing the unique index on the referenced table.
> > Benchmarking shows ~1.8x speedup for bulk FK inserts (int PK/int FK,
> > 1M rows, where PK table and index are cached).
> >
> > The fast path applies when the referenced table is not partitioned and
> > the constraint does not involve temporal semantics.  Otherwise, the
> > existing SPI path is used.
> >
> > This optimization covers only the referential check trigger
> > (RI_FKey_check).  The action triggers (CASCADE, SET NULL, SET DEFAULT,
> > RESTRICT, NO ACTION) must find rows on the FK side to modify, which
> > requires a table scan with no guaranteed index available, and then
> > execute DML against those rows through the full executor path including
> > any triggered actions.  Replicating that without substantial code
> > duplication is not feasible, so those triggers remain on the SPI path.
> > Extending the fast path to action triggers remains possible as future
> > work if the necessary infrastructure is built.
> >
> > The new ri_FastPathCheck() function extracts the FK values, builds scan
> > keys, performs an index scan, and locks the matching tuple with
> > LockTupleKeyShare via ri_LockPKTuple(), which handles the RI-specific
> > subset of table_tuple_lock() results.
> >
> > If the locked tuple was reached by chasing an update chain
> > (tmfd.traversed), recheck_matched_pk_tuple() verifies that the key
> > is still the same, emulating EvalPlanQual.
> >
> > The scan uses GetTransactionSnapshot(), matching what the SPI path
> > uses (via _SPI_execute_plan pushing GetTransactionSnapshot() as the
> > active snapshot).  Under READ COMMITTED this is a fresh snapshot;
> > under REPEATABLE READ / SERIALIZABLE it is the frozen transaction-
> > start snapshot, so PK rows committed after the transaction started
> > are not visible.
> >
> > The ri_CheckPermissions() function performs schema USAGE and table
> > SELECT checks, matching what the SPI path gets implicitly through
> > the executor's permission checks.  The fast path also switches to
> > the PK table owner's security context (with SECURITY_NOFORCE_RLS)
> > before the index probe, matching the SPI path where the query runs
> > as the table owner.
> >
> > ri_HashCompareOp() is adjusted to handle cross-type equality operators
> > (e.g. int48eq for int4 PK / int8 FK) which can appear in conpfeqop.
> > The existing code asserted same-type operators only, which was correct
> > for its existing callers (ri_KeysEqual compares same-type FK column
> > values via ff_eq_oprs), but the fast path is the first caller to pass
> > pf_eq_oprs, which can be cross-type.
> >
> > Per-key metadata (compare entries, operator procedures, strategy
> > numbers) is cached in RI_ConstraintInfo via
> > ri_populate_fastpath_metadata() on first use, eliminating repeated
> > calls to ri_HashCompareOp() and get_op_opfamily_properties().
> > conindid and pk_is_partitioned are also cached at constraint load
> > time, avoiding per-invocation syscache lookups and the need to open
> > pk_rel before deciding whether the fast path applies.
> >
> > New regression tests cover RLS bypass and ACL enforcement for the
> > fast-path permission checks.  New isolation tests exercise concurrent
> > PK updates under both READ COMMITTED and REPEATABLE READ.
> >
> > Author: Junwang Zhao <zhjwpku@gmail.com>
> > Co-authored-by: Amit Langote <amitlangote09@gmail.com>
> > Reviewed-by: Haibo Yan <tristan.yim@gmail.com>
> > Tested-by: Tomas Vondra <tomas@vondra.me>
> > Discussion: https://postgr.es/m/CA+HiwqF4C0ws3cO+z5cLkPuvwnAwkSp7sfvgGj3yQ=Li6KNMqA@mail.gmail.com
> >
> > Branch
> > ------
> > master
> >
> > Details
> > -------
> > https://git.postgresql.org/pg/commitdiff/2da86c1ef9b5446e0e22c0b6a5846293e58d98e3
> >
> > Modified Files
> > --------------
> > src/backend/utils/adt/ri_triggers.c                | 466 ++++++++++++++++++++-
> > .../isolation/expected/fk-concurrent-pk-upd.out    | 105 +++++
> > src/test/isolation/isolation_schedule              |   1 +
> > src/test/isolation/specs/fk-concurrent-pk-upd.spec |  53 +++
> > src/test/regress/expected/foreign_key.out          |  47 +++
> > src/test/regress/sql/foreign_key.sql               |  64 +++
> > src/tools/pgindent/typedefs.list                   |   1 +
> > 7 files changed, 723 insertions(+), 14 deletions(-)
>
> I'm looking at the failures on prion:
> https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=prion&dt=2026-03-31%2006%3A53%3A05
>
> They all look like this:
> +ERROR:  could not open relation with OID 2139062143

I've pushed a fix: 68a8601ee9ec.

--
Thanks, Amit Langote





^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: pgsql: Add fast path for foreign key constraint checks
  2026-03-31 04:53 pgsql: Add fast path for foreign key constraint checks Amit Langote <amitlan@postgresql.org>
  2026-03-31 07:17 ` Re: pgsql: Add fast path for foreign key constraint checks Amit Langote <amitlangote09@gmail.com>
  2026-03-31 08:08   ` Re: pgsql: Add fast path for foreign key constraint checks Amit Langote <amitlangote09@gmail.com>
@ 2026-03-31 12:21     ` Amit Langote <amitlangote09@gmail.com>
  2026-04-01 08:52       ` Re: pgsql: Add fast path for foreign key constraint checks Amit Langote <amitlangote09@gmail.com>
  0 siblings, 1 reply; 5+ messages in thread

From: Amit Langote @ 2026-03-31 12:21 UTC (permalink / raw)
  To: Amit Langote <amitlan@postgresql.org>; +Cc: pgsql-committers@lists.postgresql.org

On Tue, Mar 31, 2026 at 5:08 PM Amit Langote <amitlangote09@gmail.com> wrote:
> On Tue, Mar 31, 2026 at 4:17 PM Amit Langote <amitlangote09@gmail.com> wrote:
> > On Tue, Mar 31, 2026 at 1:54 PM Amit Langote <amitlan@postgresql.org> wrote:
> > > Add fast path for foreign key constraint checks
> > >
> > > Add a fast-path optimization for foreign key checks that bypasses SPI
> > > by directly probing the unique index on the referenced table.
> > > Benchmarking shows ~1.8x speedup for bulk FK inserts (int PK/int FK,
> > > 1M rows, where PK table and index are cached).
> > >
> > > The fast path applies when the referenced table is not partitioned and
> > > the constraint does not involve temporal semantics.  Otherwise, the
> > > existing SPI path is used.
> > >
> > > This optimization covers only the referential check trigger
> > > (RI_FKey_check).  The action triggers (CASCADE, SET NULL, SET DEFAULT,
> > > RESTRICT, NO ACTION) must find rows on the FK side to modify, which
> > > requires a table scan with no guaranteed index available, and then
> > > execute DML against those rows through the full executor path including
> > > any triggered actions.  Replicating that without substantial code
> > > duplication is not feasible, so those triggers remain on the SPI path.
> > > Extending the fast path to action triggers remains possible as future
> > > work if the necessary infrastructure is built.
> > >
> > > The new ri_FastPathCheck() function extracts the FK values, builds scan
> > > keys, performs an index scan, and locks the matching tuple with
> > > LockTupleKeyShare via ri_LockPKTuple(), which handles the RI-specific
> > > subset of table_tuple_lock() results.
> > >
> > > If the locked tuple was reached by chasing an update chain
> > > (tmfd.traversed), recheck_matched_pk_tuple() verifies that the key
> > > is still the same, emulating EvalPlanQual.
> > >
> > > The scan uses GetTransactionSnapshot(), matching what the SPI path
> > > uses (via _SPI_execute_plan pushing GetTransactionSnapshot() as the
> > > active snapshot).  Under READ COMMITTED this is a fresh snapshot;
> > > under REPEATABLE READ / SERIALIZABLE it is the frozen transaction-
> > > start snapshot, so PK rows committed after the transaction started
> > > are not visible.
> > >
> > > The ri_CheckPermissions() function performs schema USAGE and table
> > > SELECT checks, matching what the SPI path gets implicitly through
> > > the executor's permission checks.  The fast path also switches to
> > > the PK table owner's security context (with SECURITY_NOFORCE_RLS)
> > > before the index probe, matching the SPI path where the query runs
> > > as the table owner.
> > >
> > > ri_HashCompareOp() is adjusted to handle cross-type equality operators
> > > (e.g. int48eq for int4 PK / int8 FK) which can appear in conpfeqop.
> > > The existing code asserted same-type operators only, which was correct
> > > for its existing callers (ri_KeysEqual compares same-type FK column
> > > values via ff_eq_oprs), but the fast path is the first caller to pass
> > > pf_eq_oprs, which can be cross-type.
> > >
> > > Per-key metadata (compare entries, operator procedures, strategy
> > > numbers) is cached in RI_ConstraintInfo via
> > > ri_populate_fastpath_metadata() on first use, eliminating repeated
> > > calls to ri_HashCompareOp() and get_op_opfamily_properties().
> > > conindid and pk_is_partitioned are also cached at constraint load
> > > time, avoiding per-invocation syscache lookups and the need to open
> > > pk_rel before deciding whether the fast path applies.
> > >
> > > New regression tests cover RLS bypass and ACL enforcement for the
> > > fast-path permission checks.  New isolation tests exercise concurrent
> > > PK updates under both READ COMMITTED and REPEATABLE READ.
> > >
> > > Author: Junwang Zhao <zhjwpku@gmail.com>
> > > Co-authored-by: Amit Langote <amitlangote09@gmail.com>
> > > Reviewed-by: Haibo Yan <tristan.yim@gmail.com>
> > > Tested-by: Tomas Vondra <tomas@vondra.me>
> > > Discussion: https://postgr.es/m/CA+HiwqF4C0ws3cO+z5cLkPuvwnAwkSp7sfvgGj3yQ=Li6KNMqA@mail.gmail.com
> > >
> > > Branch
> > > ------
> > > master
> > >
> > > Details
> > > -------
> > > https://git.postgresql.org/pg/commitdiff/2da86c1ef9b5446e0e22c0b6a5846293e58d98e3
> > >
> > > Modified Files
> > > --------------
> > > src/backend/utils/adt/ri_triggers.c                | 466 ++++++++++++++++++++-
> > > .../isolation/expected/fk-concurrent-pk-upd.out    | 105 +++++
> > > src/test/isolation/isolation_schedule              |   1 +
> > > src/test/isolation/specs/fk-concurrent-pk-upd.spec |  53 +++
> > > src/test/regress/expected/foreign_key.out          |  47 +++
> > > src/test/regress/sql/foreign_key.sql               |  64 +++
> > > src/tools/pgindent/typedefs.list                   |   1 +
> > > 7 files changed, 723 insertions(+), 14 deletions(-)
> >
> > I'm looking at the failures on prion:
> > https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=prion&dt=2026-03-31%2006%3A53%3A05
> >
> > They all look like this:
> > +ERROR:  could not open relation with OID 2139062143
>
> I've pushed a fix: 68a8601ee9ec.

Found additional issues when testing locally with
CLOBBER_CACHE_ALWAYS: a dangling fpmeta pointer after constraint cache
invalidation, and riinfo going stale inside ri_FastPathCheck() after
relation opens. The attached patch fixes both. I'll apply it tomorrow
morning barring objections.

-- 
Thanks, Amit Langote

Attachments:

  [application/octet-stream] v1-0001-Fix-two-issues-in-fast-path-FK-check-introduced-b.patch (3.2K, ../../CA+HiwqGBU__7-VZZhQWQ3EQuwLYNPd9==ngnzduhGWKHMj9mvw@mail.gmail.com/2-v1-0001-Fix-two-issues-in-fast-path-FK-check-introduced-b.patch)
  download | inline diff:
From 84af557f2f6e5c181fec9efec3949f7876ea34ab Mon Sep 17 00:00:00 2001
From: Amit Langote <amitlan@postgresql.org>
Date: Tue, 31 Mar 2026 20:00:45 +0900
Subject: [PATCH v1] Fix two issues in fast-path FK check introduced by commit
 2da86c1ef9

First, under CLOBBER_CACHE_ALWAYS, the RI_ConstraintInfo entry can
be invalidated by relcache callbacks triggered inside table_open()
or index_open(), leaving ri_FastPathCheck() calling
ri_populate_fastpath_metadata() with a stale entry whose valid flag
is false.  Fix by reloading riinfo after the relation opens and
populating fpmeta immediately, then calling ri_ExtractValues() and
build_index_scankeys() before any further operations that could
trigger invalidation.

Second, fpmeta allocated in TopMemoryContext was not freed when the
entry was invalidated in InvalidateConstraintCacheCallBack(),
leaking memory each time the constraint cache entry was recycled.
Fix by freeing fpmeta at invalidation time.

Noticed locally when testing with CLOBBER_CACHE_ALWAYS.
---
 src/backend/utils/adt/ri_triggers.c | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index ffaa0e749cb..8673a4300a7 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2486,6 +2486,11 @@ InvalidateConstraintCacheCallBack(Datum arg, SysCacheIdentifier cacheid,
 			riinfo->rootHashValue == hashvalue)
 		{
 			riinfo->valid = false;
+			if (riinfo->fpmeta)
+			{
+				pfree(riinfo->fpmeta);
+				riinfo->fpmeta = NULL;
+			}
 			/* Remove invalidated entries from the list, too */
 			dclist_delete_from(&ri_constraint_cache_valid_list, iter.cur);
 		}
@@ -2714,17 +2719,23 @@ ri_FastPathCheck(const RI_ConstraintInfo *riinfo,
 	pk_rel = table_open(riinfo->pk_relid, RowShareLock);
 	idx_rel = index_open(riinfo->conindid, AccessShareLock);
 
+	if (riinfo->fpmeta == NULL)
+	{
+		/* Reload to ensure it's valid. */
+		riinfo = ri_LoadConstraintInfo(riinfo->constraint_id);
+		ri_populate_fastpath_metadata((RI_ConstraintInfo *) riinfo,
+									  fk_rel, idx_rel);
+	}
+	Assert(riinfo->fpmeta);
+	ri_ExtractValues(fk_rel, newslot, riinfo, false, pk_vals, pk_nulls);
+	build_index_scankeys(riinfo, idx_rel, pk_vals, pk_nulls, skey);
+
 	slot = table_slot_create(pk_rel, NULL);
 	scandesc = index_beginscan(pk_rel, idx_rel,
 							   snapshot, NULL,
 							   riinfo->nkeys, 0,
 							   SO_NONE);
 
-	if (riinfo->fpmeta == NULL)
-		ri_populate_fastpath_metadata((RI_ConstraintInfo *) riinfo,
-									  fk_rel, idx_rel);
-	Assert(riinfo->fpmeta);
-
 	GetUserIdAndSecContext(&saved_userid, &saved_sec_context);
 	SetUserIdAndSecContext(RelationGetForm(pk_rel)->relowner,
 						   saved_sec_context |
@@ -2732,8 +2743,6 @@ ri_FastPathCheck(const RI_ConstraintInfo *riinfo,
 						   SECURITY_NOFORCE_RLS);
 	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, slot,
 								snapshot, riinfo, skey, riinfo->nkeys);
 	SetUserIdAndSecContext(saved_userid, saved_sec_context);
-- 
2.47.3



^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: pgsql: Add fast path for foreign key constraint checks
  2026-03-31 04:53 pgsql: Add fast path for foreign key constraint checks Amit Langote <amitlan@postgresql.org>
  2026-03-31 07:17 ` Re: pgsql: Add fast path for foreign key constraint checks Amit Langote <amitlangote09@gmail.com>
  2026-03-31 08:08   ` Re: pgsql: Add fast path for foreign key constraint checks Amit Langote <amitlangote09@gmail.com>
  2026-03-31 12:21     ` Re: pgsql: Add fast path for foreign key constraint checks Amit Langote <amitlangote09@gmail.com>
@ 2026-04-01 08:52       ` Amit Langote <amitlangote09@gmail.com>
  0 siblings, 0 replies; 5+ messages in thread

From: Amit Langote @ 2026-04-01 08:52 UTC (permalink / raw)
  To: Amit Langote <amitlan@postgresql.org>; +Cc: pgsql-committers@lists.postgresql.org

On Tue, Mar 31, 2026 at 9:21 PM Amit Langote <amitlangote09@gmail.com> wrote:
> On Tue, Mar 31, 2026 at 5:08 PM Amit Langote <amitlangote09@gmail.com> wrote:
> > On Tue, Mar 31, 2026 at 4:17 PM Amit Langote <amitlangote09@gmail.com> wrote:
> > > I'm looking at the failures on prion:
> > > https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=prion&dt=2026-03-31%2006%3A53%3A05
> > >
> > > They all look like this:
> > > +ERROR:  could not open relation with OID 2139062143
> >
> > I've pushed a fix: 68a8601ee9ec.
>
> Found additional issues when testing locally with
> CLOBBER_CACHE_ALWAYS: a dangling fpmeta pointer after constraint cache
> invalidation, and riinfo going stale inside ri_FastPathCheck() after
> relation opens. The attached patch fixes both. I'll apply it tomorrow
> morning barring objections.

Pushed: e484b0eea6.

-- 
Thanks, Amit Langote





^ permalink  raw  reply  [nested|flat] 5+ messages in thread


end of thread, other threads:[~2026-04-01 08:52 UTC | newest]

Thread overview: 5+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-03-31 04:53 pgsql: Add fast path for foreign key constraint checks Amit Langote <amitlan@postgresql.org>
2026-03-31 07:17 ` Amit Langote <amitlangote09@gmail.com>
2026-03-31 08:08   ` Amit Langote <amitlangote09@gmail.com>
2026-03-31 12:21     ` Amit Langote <amitlangote09@gmail.com>
2026-04-01 08:52       ` Amit Langote <amitlangote09@gmail.com>

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox