| From: | Xnerv Wang <xnervwang(at)gmail(dot)com> |
|---|---|
| To: | pgsql-hackers(at)lists(dot)postgresql(dot)org |
| Cc: | John Naylor <johncnaylorls(at)gmail(dot)com> |
| Subject: | [PATCH] pg_input_is_valid() reuses the first type name inside a PL/pgSQL loop |
| Date: | 2026-11-10 08:11:52 |
| Message-ID: | CAFEh72DTdqrHwe01V+Y2xdL=fj3LJwvyp6yYzTCCUWWdDP19UA@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi hackers,
One of our tests checks exported values with pg_input_is_valid() in a
PL/pgSQL loop and it returned wrong results. I can reproduce the
problem on master and 19rc1:
DO $$
DECLARE r record;
BEGIN
FOR r IN SELECT * FROM (VALUES ('42', 'integer'), ('hello', 'text'),
('100000', 'smallint')) AS v(val, typ)
LOOP
RAISE NOTICE '% as %: % %', r.val, r.typ,
pg_input_is_valid(r.val, r.typ),
(pg_input_error_info(r.val, r.typ)).message;
END LOOP;
END $$;
NOTICE: 42 as integer: t <NULL>
NOTICE: hello as text: f invalid input syntax for type integer: "hello"
NOTICE: 100000 as smallint: t <NULL>
The second and third calls should return t and f, but both validate
against integer, the type from the first call. pg_input_error_info()
has the same problem. The typmod is reused too: varchar(5) followed
by varchar(10) still checks against varchar(5).
In misc.c, pg_input_is_valid_common() parses the type name only on the
first call when get_fn_expr_arg_stable() says the argument is stable.
That function returns true for a PARAM_EXTERN Param, which is how
PL/pgSQL passes variables. But a PL/pgSQL simple expression keeps its
ExprState, and so fn_extra, across evaluations within a transaction
while the variable changes. This logic came in with commit 1939d26282b,
in PostgreSQL 16.
For a stable argument, the attached patch keeps the last type name
string it parsed and parses again when a later call passes a different
one. The new test in plpgsql_simple.sql (a similar loop plus varchar
typmod cases) fails without the fix, and check-world passes with it. A
constant type name now costs one extra name comparison per call: 5
million calls with 'int4' took 722 ms instead of 691 ms (-O2, no
assertions, median of 11 runs).
Dropping typname_constant and always comparing names would be simpler
and would also fix this bug. A type name from a column would then be
parsed only when its text changes, rather than on every call as today,
so it would behave like a constant name does now. If the text stays
the same, a search_path change between two FETCHes from a cursor would
go unnoticed. I kept the flag so that non-constant names behave as
before, and can send the simpler version if others prefer it.
Thanks,
Xnerv Wang
Amazon Web Services
| Attachment | Content-Type | Size |
|---|---|---|
| v1-0001-Fix-pg_input_is_valid-reusing-a-stale-type-in-PL-.patch | text/x-patch | 6.1 KB |
| From | Date | Subject | |
|---|---|---|---|
| Previous Message | Michael Paquier | 2026-11-10 07:43:15 | Re: Add wait events for server logging destination writes |