PostgreSQL 19 adds a fast path for eligible foreign-key checks. When a row is inserted or updated in a referencing table, the server can look up the referenced key by probing the referenced table’s unique index directly, rather than asking the Server Programming Interface (SPI) to run a SQL lookup. It then takes a key-share lock on the matching tuple. The change is narrower than “foreign keys no longer run SQL”: only the referenced-row existence check is affected, and ineligible cases keep the old SPI route.
Version status: what the evidence supports
The PostgreSQL 19 release notes describe their documentation as an unsupported development version. They gave the release date as unknown as of 2026-09-14, and they list “quicker foreign-key checks” among the performance improvements. The implementation detail comes from a master-branch commit dated 2026-03-31, credited to Junwang Zhao as author and Amit Langote as co-author. Treat the details below as describing that commit. Confirm them against the final release before stating them as shipped behavior.
What “without running SQL” means
SPI is the interface that lets C code in the server run SQL commands through the parser, planner and executor (SPI documentation). Previously, the trigger that validates a foreign key went through SPI to run a lookup query against the referenced table. The commit describes its change as a “fast-path optimization for foreign key checks that bypasses SPI by directly probing the unique index on the referenced table.”
The SQL statement your application sends is unchanged, and the check still runs inside the normal transaction machinery. What disappears, on eligible checks, is the internal SQL lookup that was executed through SPI.
#1 Best Overall
How the fast path works
- The
RI_FKey_checktrigger receives the new foreign-key values to validate. - The fast-path code builds index scan keys from those values and probes the referenced table’s unique index.
- If a matching referenced tuple exists, it takes a key-share tuple lock. This keeps the concurrency protection the check has always provided, so the referenced key cannot be removed or changed out from under the new row.
- If the case is not eligible, PostgreSQL uses the existing SPI implementation.
It is not an unchecked index lookup. According to the commit, the direct scan uses GetTransactionSnapshot(), matching the snapshot behavior of the SPI path. It follows update chains and verifies that a chased tuple still has the expected key. The commit’s tests cover concurrent primary-key updates under READ COMMITTED and REPEATABLE READ, plus permission and row-level security checks.
Fast path versus retained SPI path
| Aspect | Fast path | SPI path |
|---|---|---|
| Mechanism | Direct probe of the referenced table’s unique index | SQL lookup run through SPI and the normal executor |
| Eligibility | Referenced table not partitioned; no temporal semantics in the constraint | Partitioned referenced tables, temporal constraints |
| Trigger coverage | RI_FKey_check only |
Fallback for the check, plus all action triggers |
| Locking | Key-share lock on the matching tuple | Existing behavior |
What is not covered
The optimization does not touch the referential action triggers: CASCADE, SET NULL, SET DEFAULT, RESTRICT and NO ACTION. Those triggers search the referencing side and may need to modify matching rows, which means running DML that can fire further triggers. The commit keeps them on SPI. So deletes and updates on the referenced table, which fire these triggers, do not gain from this change.
Rank #2
The performance number
The commit reports a “~1.8x speedup” for bulk foreign-key inserts. The benchmark used an integer primary key and integer foreign key, one million rows, with the primary-key table and its index cached. This is the commit’s own measurement, not an independent production result. Different key types, partitioned parents, cold caches or mixed transaction workloads may behave differently, so do not assume the same ratio.
Quick Recap
Rank #3
Practical takeaways
- Bulk loads into tables with foreign keys to ordinary, non-partitioned parents are the clearest beneficiary.
- If the referenced table is partitioned, or the constraint uses temporal semantics, expect the old behavior.
- Cascading and restrictive actions on parent-row changes are unchanged.
- Check the final PostgreSQL 19 release notes before relying on any of this in a migration plan.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →




