| From: | Ewan Young <kdbase(dot)hack(at)gmail(dot)com> |
|---|---|
| To: | PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org> |
| Cc: | Amit Langote <amitlangote09(at)gmail(dot)com>, Peter Eisentraut <peter(at)eisentraut(dot)org> |
| Subject: | JSON_VALUE/JSON_TABLE DEFAULT expression ignores RETURNING typmod |
| Date: | 2026-06-30 07:01:31 |
| Message-ID: | CAON2xHPO9f4cAmyGn1mQ=VqoS7wN5rz4yOiqudxX78zninZpCw@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi,
While testing SQL/JSON on master I noticed that a DEFAULT expression in
JSON_VALUE / JSON_TABLE (ON EMPTY / ON ERROR) is not coerced to the
RETURNING type's type modifier when the expression's base type already
matches the RETURNING base type. The declared typmod is silently
dropped, so the result can violate its own declared type:
SELECT JSON_VALUE(jsonb '{}', '$.a'
RETURNING numeric(4,1) DEFAULT 99999.999 ON EMPTY);
json_value
------------
99999.999
even though the equivalent cast is rejected:
SELECT 99999.999::numeric(4,1);
ERROR: numeric field overflow
The same happens for varchar(n), bit(n), timestamp(p), and for
JSON_TABLE column DEFAULTs.
It is not limited to a query-time wrong result: because the returned
Datum is labeled numeric(4,1) but holds an out-of-range value, a later
assignment cast that sees the matching type trusts the label and skips
re-checking, so the value can be stored into a column whose typmod it
violates:
CREATE TABLE sink (c numeric(4,1));
INSERT INTO sink VALUES (99999.999); -- ERROR: numeric
field overflow
INSERT INTO sink
SELECT JSON_VALUE(jsonb '{}', '$.a'
RETURNING numeric(4,1) DEFAULT 99999.999 ON
EMPTY); -- succeeds
SELECT * FROM sink; -- 99999.999
Root cause is in transformJsonBehavior() (parse_expr.c), which gates the
DEFAULT coercion on a type-OID mismatch only:
if (expr && exprType(expr) != returning->typid)
The coerce_to_target_type() call inside that branch is what enforces the
typmod, so when the base type matches but the typmod differ
coercion is skipped entirely. (A DEFAULT whose type differs, e.g.
DEFAULT 99999 :: int, is coerced and correctly errors; a DO
numeric(4,1) also errors, since its OID differs.) The matching-OID
short-circuit dates back to 74c96699be3.
The attached patch coerces when the RETURNING type carries
well, excluding a NULL constant (which needs no enforcement).
coerce_to_target_type() is a no-op when the typmod already
conforming expressions are unaffected, and the jsonb-valued / NULL /
boolean runtime-coercion path (json_populate_type()) alread
typmod -- this only closes the gap in the parse-time cast path.
Note this is distinct from c0fc0751862, which fixed which expression
kinds are accepted in DEFAULT, not typmod enforcement.
make check passes with the added regression cases. Reproduc
fix verified on master (c776550e466).
--
Regards,
Ewan Young
| Attachment | Content-Type | Size |
|---|---|---|
| v1-0001-Enforce-RETURNING-typmod-on-SQL-JSON-DEFAULT-beha.patch | application/octet-stream | 6.4 KB |
| From | Date | Subject | |
|---|---|---|---|
| Previous Message | Ashutosh Sharma | 2026-06-30 07:01:06 | Re: Report bytes and transactions actually sent downtream |