| From: | Ayush Tiwari <ayushtiwari(dot)slg01(at)gmail(dot)com> |
|---|---|
| To: | Nathan Bossart <nathandbossart(at)gmail(dot)com> |
| Cc: | PostgreSQL Hackers <pgsql-hackers(at)postgresql(dot)org> |
| Subject: | Re: [PATCH] refint: Avoid reusing cascade UPDATE plans. |
| Date: | 2026-05-15 06:43:21 |
| Message-ID: | CAJTYsWWEvcVsnJWXgijh5Sb8+odYs7ZS4pNPN2Va1Un5w5jATg@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi,
On Fri, 15 May 2026 at 02:44, Nathan Bossart <nathandbossart(at)gmail(dot)com>
wrote:
>
> That being said, the plan cache still has problems. There's no
> invalidation mechanism, so you could still end up with the wrong plan.
> Here's a quick example:
>
> CREATE EXTENSION refint;
>
> CREATE TABLE p AS SELECT 1 AS a;
> CREATE TABLE f1 AS SELECT 1 AS a;
> CREATE TABLE f2 AS SELECT 1 AS a;
> CREATE TRIGGER t
> AFTER DELETE OR UPDATE ON p
> FOR EACH ROW EXECUTE PROCEDURE
> check_foreign_key(2, 'c', 'a', 'f1', 'a', 'f2', 'a');
> UPDATE p SET a = 2;
>
> DROP TRIGGER t ON p;
> CREATE TRIGGER t
> AFTER DELETE OR UPDATE ON p
> FOR EACH ROW EXECUTE PROCEDURE
> check_foreign_key(1, 'c', 'a', 'f1', 'a');
> UPDATE p SET a = 3;
>
> The last UPDATE fails with:
>
> ERROR: t: check_foreign_key: # of plans changed in meantime
>
> A simple way to fix this could be to use the trigger OID instead of the
> trigger name in the plan cache key. That's not perfect because the OID
> could be reused, but IMHO it's better than what's there today. An even
> better approach would involve more sophisticated invalidation or removing
> the cache altogether.
>
>
Thanks for the review and the drop/recreate example.
After looking at it again, the root issue is that check_foreign_key()'s
private plan cache is not robust enough for the SQL it stores: the
cascade UPDATE query text embeds the current NEW key values in its SET
clause, and the cache itself has no invalidation mechanism, so the
trigger-name-keyed entries survive a drop and recreate. Rather than
keep teaching the cache more special cases, v2 just removes the cache
from check_foreign_key() entirely. Plans are prepared on each trigger
invocation and released by SPI_finish(). refint is example code, so I
think the simplicity is worth more than the per-call SPI_prepare.
check_primary_key() still uses the existing per-trigger cache; its
generated SELECT does not embed row values and the same invalidation
concern is far less interesting there, so I left it alone for now.
v2 keeps the cascade-UPDATE regression test from v1 and adds a second
test that drops and recreates a cascade trigger with a different number
of references, which reproduces your example.
Regards,
Ayush
| Attachment | Content-Type | Size |
|---|---|---|
| v2-0001-refint-Remove-plan-cache-from-check_foreign_key.patch | application/octet-stream | 10.6 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Michael Paquier | 2026-05-15 06:54:59 | Re: Re-add recently-removed tests for ltree and intarray |
| Previous Message | shveta malik | 2026-05-15 06:36:19 | Re: Bound memory usage during manual slot sync retries |