| From: | PG Bug reporting form <noreply(at)postgresql(dot)org> |
|---|---|
| To: | pgsql-bugs(at)lists(dot)postgresql(dot)org |
| Cc: | marcos(dot)castedo(at)anachronics(dot)com |
| Subject: | BUG #19816: PL/Python SD dictionary is split by input collation in 19rc1 |
| Date: | 2026-10-29 22:25:07 |
| Message-ID: | 19816-921b5cbc3ee63146@postgresql.org |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
The following bug has been logged on the website:
Bug reference: 19816
Logged by: Marcos Castedo
Email address: marcos(dot)castedo(at)anachronics(dot)com
PostgreSQL version: 19rc1
Operating system: Debian GNU/Linux (x86_64)
Description:
Version: 19rc1, built from the postgresql-19rc1.tar.bz2 release tarball,
unmodified.
In 19rc1 PL/Python uses the generic function cache (funccache.c), so a
function gets one compiled entry per CachedFunctionHashKey, and that key
includes the call's input collation. Each entry builds its own PLyProcedure
and therefore its own SD dictionary. As a result, SD is no longer shared
between calls to the same function when the calls resolve to different input
collations.
The documentation describes SD as private data "between repeated calls to
the same function", and that is how it behaves in 17, where PL/Python's own
cache is keyed by function OID (plus the trigger relation). I have not
checked 18. Code that stores state in SD with one call and reads it back
with another now loses it whenever the two call sites differ in input
collation. The most common way to hit this is an optional text argument: a
call that passes it has the default collation, and a call that omits it
Steps to reproduce:
CREATE FUNCTION tt(set_ boolean, a text DEFAULT NULL) RETURNS int AS $$
if set_: SD['x'] = 1
return SD.get('x')
$$ LANGUAGE plpython3u;
SELECT tt(true, 'z'); -- 1
SELECT tt(false); -- 19rc1: NULL 17: 1
SELECT tt(false, 'z' COLLATE "C"); -- 19rc1: NULL
SELECT tt(false, 'w'); -- 1 (same collation as the first
call)
SELECT tt(false, NULL); -- 1 (unknown literal resolves to
text,
-- default collation)
Calls that agree in input collation see the same SD. With an optional
argument of a non-collatable type (int), all calls share SD. GD is not
affected.
Expected: SD is shared by all calls to the function in the session, as in
17.
Actual: SD is partitioned by input collation.
The collation is part of the key for good reason in PL/pgSQL, where it
affects the compiled code. PL/Python does not use the input collation when
compiling, so splitting the entry there only splits SD. A possible fix is to
keep the SD dictionary per function OID, outside the funccache entry, and
have every PLyProcedure for that function share it. Another is to leave the
collation out of the hash key for PL/Python. Plans that callers cache in SD
with plpy.prepare() also get duplicated per collation, but that only costs
memory; losing stored state is the visible regression.
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Michael Paquier | 2026-10-29 22:46:04 | Re: pg_stat_statements stores WITH HOLD cursor query with literals |
| Previous Message | Tom Lane | 2026-10-29 21:54:21 | Re: postgres_fdw returns wrong rows for age(timestamptz, timestamptz) filters |