[PATCH] pg_input_is_valid() reuses the first type name inside a PL/pgSQL loop

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

Browse pgsql-hackers by date

  From Date Subject
Previous Message Michael Paquier 2026-11-10 07:43:15 Re: Add wait events for server logging destination writes