mirror of
https://github.com/postgres/postgres.git
synced 2026-07-08 09:10:57 -04:00
Enforce RETURNING typmod on SQL/JSON DEFAULT behavior expressions
transformJsonBehavior() coerced an ON EMPTY / ON ERROR DEFAULT
expression only when its type differed from the RETURNING type's OID.
When the base type matched but the RETURNING type carried a type
modifier (e.g. numeric(4,1) or varchar(3)), the coercion that enforces
the typmod was skipped, so the DEFAULT value could violate the
declared type:
SELECT JSON_VALUE(jsonb '{}', '$.a'
RETURNING numeric(4,1) DEFAULT 99999.999 ON EMPTY);
returned 99999.999, which 99999.999::numeric(4,1) would reject; the
value could even be stored into a numeric(4,1) column, as later
coercions trust its already-correct type label.
Fix by also coercing when the RETURNING type has a typmod, except for
a NULL constant. coerce_to_target_type() is a no-op when the typmod
already matches. The matching-OID short-circuit dates to 74c96699be.
Reported-by: Ewan Young <kdbase.hack@gmail.com>
Author: Ewan Young <kdbase.hack@gmail.com>
Discussion: https://postgr.es/m/CAON2xHPO9f4cAmyGn1mQ=VqoS7wN5rz4yOiqudxX78zninZpCw@mail.gmail.com
Backpatch-through: 17
This commit is contained in:
parent
da8889ccd7
commit
d30bfcbddc
5 changed files with 64 additions and 1 deletions
|
|
@ -4931,8 +4931,17 @@ transformJsonBehavior(ParseState *pstate, JsonExpr *jsexpr,
|
|||
*
|
||||
* For other non-NULL expressions, try to find a cast and error out if one
|
||||
* is not found.
|
||||
*
|
||||
* The DEFAULT expression's base type may already match the RETURNING type
|
||||
* yet still need coercion: when the RETURNING type carries a type
|
||||
* modifier (e.g. numeric(4,1)), the cast below is what enforces it, so
|
||||
* skipping it here would let the DEFAULT yield a value that violates its
|
||||
* declared RETURNING type. A NULL constant needs no such enforcement.
|
||||
*/
|
||||
if (expr && exprType(expr) != returning->typid)
|
||||
if (expr &&
|
||||
(exprType(expr) != returning->typid ||
|
||||
(returning->typmod >= 0 &&
|
||||
!(IsA(expr, Const) && ((Const *) expr)->constisnull))))
|
||||
{
|
||||
bool isnull = (IsA(expr, Const) && ((Const *) expr)->constisnull);
|
||||
|
||||
|
|
|
|||
|
|
@ -250,6 +250,22 @@ SELECT * FROM JSON_TABLE(jsonb '{"d1": "foo"}', '$'
|
|||
{1}
|
||||
(1 row)
|
||||
|
||||
-- A DEFAULT expression whose base type matches the column type must still be
|
||||
-- coerced to the column's typmod.
|
||||
SELECT * FROM JSON_TABLE(jsonb '{}', '$'
|
||||
COLUMNS (c numeric(4,1) PATH '$.x' DEFAULT 99999.999 ON EMPTY));
|
||||
ERROR: numeric field overflow
|
||||
DETAIL: A field with precision 4, scale 1 must round to an absolute value less than 10^3.
|
||||
SELECT * FROM JSON_TABLE(jsonb '{}', '$'
|
||||
COLUMNS (c bit(3) PATH '$.x' DEFAULT b'10101' ON EMPTY));
|
||||
ERROR: bit string length 5 does not match type bit(3)
|
||||
SELECT * FROM JSON_TABLE(jsonb '{}', '$'
|
||||
COLUMNS (c numeric(4,1) PATH '$.x' DEFAULT abs(NULL::numeric) ON EMPTY));
|
||||
c
|
||||
---
|
||||
|
||||
(1 row)
|
||||
|
||||
-- JSON_TABLE: Test backward parsing
|
||||
CREATE VIEW jsonb_table_view2 AS
|
||||
SELECT * FROM
|
||||
|
|
|
|||
|
|
@ -433,6 +433,27 @@ SELECT JSON_VALUE(jsonb '["1"]', '$[*]' RETURNING int FORMAT JSON); -- RETURNING
|
|||
ERROR: cannot specify FORMAT JSON in RETURNING clause of JSON_VALUE()
|
||||
LINE 1: ...CT JSON_VALUE(jsonb '["1"]', '$[*]' RETURNING int FORMAT JSO...
|
||||
^
|
||||
-- A DEFAULT expression must be coerced to the RETURNING type's typmod even
|
||||
-- when its base type already matches, but a matching NULL needs no coercion.
|
||||
SELECT JSON_VALUE(jsonb '{}', '$.a' RETURNING numeric(4,1) DEFAULT 99999.999 ON EMPTY);
|
||||
ERROR: numeric field overflow
|
||||
DETAIL: A field with precision 4, scale 1 must round to an absolute value less than 10^3.
|
||||
SELECT JSON_VALUE(jsonb '{}', '$.a' RETURNING varchar(3) DEFAULT 'toolong'::varchar(10) ON EMPTY);
|
||||
ERROR: value too long for type character varying(3)
|
||||
SELECT JSON_VALUE(jsonb '{}', '$.a' RETURNING numeric(4,1) DEFAULT NULL::numeric ON EMPTY);
|
||||
json_value
|
||||
------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT JSON_VALUE(jsonb '{}', '$.a' RETURNING bit(3) DEFAULT b'10101' ON EMPTY);
|
||||
ERROR: bit string length 5 does not match type bit(3)
|
||||
SELECT JSON_VALUE(jsonb '{}', '$.a' RETURNING numeric(4,1) DEFAULT abs(NULL::numeric) ON EMPTY);
|
||||
json_value
|
||||
------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- RETUGNING pseudo-types not allowed
|
||||
SELECT JSON_VALUE(jsonb '["1"]', '$[*]' RETURNING record);
|
||||
ERROR: returning pseudo-types is not supported in SQL/JSON functions
|
||||
|
|
|
|||
|
|
@ -132,6 +132,15 @@ SELECT * FROM JSON_TABLE(jsonb '{"d1": "foo"}', '$'
|
|||
SELECT * FROM JSON_TABLE(jsonb '{"d1": "foo"}', '$'
|
||||
COLUMNS (js1 oid[] PATH '$.d2' DEFAULT '{1}'::int[]::oid[] ON EMPTY));
|
||||
|
||||
-- A DEFAULT expression whose base type matches the column type must still be
|
||||
-- coerced to the column's typmod.
|
||||
SELECT * FROM JSON_TABLE(jsonb '{}', '$'
|
||||
COLUMNS (c numeric(4,1) PATH '$.x' DEFAULT 99999.999 ON EMPTY));
|
||||
SELECT * FROM JSON_TABLE(jsonb '{}', '$'
|
||||
COLUMNS (c bit(3) PATH '$.x' DEFAULT b'10101' ON EMPTY));
|
||||
SELECT * FROM JSON_TABLE(jsonb '{}', '$'
|
||||
COLUMNS (c numeric(4,1) PATH '$.x' DEFAULT abs(NULL::numeric) ON EMPTY));
|
||||
|
||||
-- JSON_TABLE: Test backward parsing
|
||||
|
||||
CREATE VIEW jsonb_table_view2 AS
|
||||
|
|
|
|||
|
|
@ -105,6 +105,14 @@ SELECT JSON_VALUE(jsonb '[" "]', '$[*]' RETURNING int DEFAULT 2 + 3 ON ERROR);
|
|||
SELECT JSON_VALUE(jsonb '["1"]', '$[*]' RETURNING int DEFAULT 2 + 3 ON ERROR);
|
||||
SELECT JSON_VALUE(jsonb '["1"]', '$[*]' RETURNING int FORMAT JSON); -- RETURNING FORMAT not allowed
|
||||
|
||||
-- A DEFAULT expression must be coerced to the RETURNING type's typmod even
|
||||
-- when its base type already matches, but a matching NULL needs no coercion.
|
||||
SELECT JSON_VALUE(jsonb '{}', '$.a' RETURNING numeric(4,1) DEFAULT 99999.999 ON EMPTY);
|
||||
SELECT JSON_VALUE(jsonb '{}', '$.a' RETURNING varchar(3) DEFAULT 'toolong'::varchar(10) ON EMPTY);
|
||||
SELECT JSON_VALUE(jsonb '{}', '$.a' RETURNING numeric(4,1) DEFAULT NULL::numeric ON EMPTY);
|
||||
SELECT JSON_VALUE(jsonb '{}', '$.a' RETURNING bit(3) DEFAULT b'10101' ON EMPTY);
|
||||
SELECT JSON_VALUE(jsonb '{}', '$.a' RETURNING numeric(4,1) DEFAULT abs(NULL::numeric) ON EMPTY);
|
||||
|
||||
-- RETUGNING pseudo-types not allowed
|
||||
SELECT JSON_VALUE(jsonb '["1"]', '$[*]' RETURNING record);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue