Fix JsonExpr deparsing to emit QUOTES and WRAPPER correctly
Currently, get_json_expr_options() does not emit the default values for QUOTES (KEEP QUOTES) and WRAPPER (WITHOUT WRAPPER). That causes the deparsed JSON_TABLE() columns, such as those contained in a a view's query, to behave differently when executed than the original definition. That's because the rules encoded in transformJsonTableColumns() will choose either JSON_VALUE() or JSON_QUERY() as implementation to execute a given column's path expression depending on the QUOTES and WRAPPER specificationd and they have slightly different semantics. Reported-by: Jian He <jian.universality@gmail.com> Discussion: https://postgr.es/m/CACJufxEqhqsfrg_p7EMyo5zak3d767iFDL8vz_4%3DZBHpOtrghw%40mail.gmail.com
This commit is contained in:
parent
561b74ddb8
commit
f6a2529920
|
@ -8848,9 +8848,15 @@ get_json_expr_options(JsonExpr *jsexpr, deparse_context *context,
|
|||
appendStringInfo(context->buf, " WITH CONDITIONAL WRAPPER");
|
||||
else if (jsexpr->wrapper == JSW_UNCONDITIONAL)
|
||||
appendStringInfo(context->buf, " WITH UNCONDITIONAL WRAPPER");
|
||||
/* The default */
|
||||
else if (jsexpr->wrapper == JSW_NONE || jsexpr->wrapper == JSW_UNSPEC)
|
||||
appendStringInfo(context->buf, " WITHOUT WRAPPER");
|
||||
|
||||
if (jsexpr->omit_quotes)
|
||||
appendStringInfo(context->buf, " OMIT QUOTES");
|
||||
/* The default */
|
||||
else
|
||||
appendStringInfo(context->buf, " KEEP QUOTES");
|
||||
}
|
||||
|
||||
if (jsexpr->on_empty && jsexpr->on_empty->btype != default_behavior)
|
||||
|
|
|
@ -302,11 +302,11 @@ CREATE OR REPLACE VIEW public.jsonb_table_view3 AS
|
|||
1 + 2 AS a,
|
||||
'"foo"'::json AS "b c"
|
||||
COLUMNS (
|
||||
js json PATH '$',
|
||||
jb jsonb PATH '$',
|
||||
jst text FORMAT JSON PATH '$',
|
||||
jsc character(4) FORMAT JSON PATH '$',
|
||||
jsv character varying(4) FORMAT JSON PATH '$'
|
||||
js json PATH '$' WITHOUT WRAPPER KEEP QUOTES,
|
||||
jb jsonb PATH '$' WITHOUT WRAPPER KEEP QUOTES,
|
||||
jst text FORMAT JSON PATH '$' WITHOUT WRAPPER KEEP QUOTES,
|
||||
jsc character(4) FORMAT JSON PATH '$' WITHOUT WRAPPER KEEP QUOTES,
|
||||
jsv character varying(4) FORMAT JSON PATH '$' WITHOUT WRAPPER KEEP QUOTES
|
||||
)
|
||||
)
|
||||
\sv jsonb_table_view4
|
||||
|
@ -321,8 +321,8 @@ CREATE OR REPLACE VIEW public.jsonb_table_view4 AS
|
|||
1 + 2 AS a,
|
||||
'"foo"'::json AS "b c"
|
||||
COLUMNS (
|
||||
jsb jsonb PATH '$',
|
||||
jsbq jsonb PATH '$' OMIT QUOTES,
|
||||
jsb jsonb PATH '$' WITHOUT WRAPPER KEEP QUOTES,
|
||||
jsbq jsonb PATH '$' WITHOUT WRAPPER OMIT QUOTES,
|
||||
aaa integer PATH '$."aaa"',
|
||||
aaa1 integer PATH '$."aaa"'
|
||||
)
|
||||
|
@ -357,12 +357,12 @@ CREATE OR REPLACE VIEW public.jsonb_table_view6 AS
|
|||
1 + 2 AS a,
|
||||
'"foo"'::json AS "b c"
|
||||
COLUMNS (
|
||||
js2 json PATH '$',
|
||||
jsb2w jsonb PATH '$' WITH UNCONDITIONAL WRAPPER,
|
||||
jsb2q jsonb PATH '$' OMIT QUOTES,
|
||||
ia integer[] PATH '$',
|
||||
ta text[] PATH '$',
|
||||
jba jsonb[] PATH '$'
|
||||
js2 json PATH '$' WITHOUT WRAPPER KEEP QUOTES,
|
||||
jsb2w jsonb PATH '$' WITH UNCONDITIONAL WRAPPER KEEP QUOTES,
|
||||
jsb2q jsonb PATH '$' WITHOUT WRAPPER OMIT QUOTES,
|
||||
ia integer[] PATH '$' WITHOUT WRAPPER KEEP QUOTES,
|
||||
ta text[] PATH '$' WITHOUT WRAPPER KEEP QUOTES,
|
||||
jba jsonb[] PATH '$' WITHOUT WRAPPER KEEP QUOTES
|
||||
)
|
||||
)
|
||||
EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM jsonb_table_view2;
|
||||
|
@ -374,19 +374,19 @@ EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM jsonb_table_view2;
|
|||
(3 rows)
|
||||
|
||||
EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM jsonb_table_view3;
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
QUERY PLAN
|
||||
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
Table Function Scan on "json_table"
|
||||
Output: "json_table".js, "json_table".jb, "json_table".jst, "json_table".jsc, "json_table".jsv
|
||||
Table Function Call: JSON_TABLE('null'::jsonb, '$[*]' AS json_table_path_0 PASSING 3 AS a, '"foo"'::jsonb AS "b c" COLUMNS (js json PATH '$', jb jsonb PATH '$', jst text FORMAT JSON PATH '$', jsc character(4) FORMAT JSON PATH '$', jsv character varying(4) FORMAT JSON PATH '$'))
|
||||
Table Function Call: JSON_TABLE('null'::jsonb, '$[*]' AS json_table_path_0 PASSING 3 AS a, '"foo"'::jsonb AS "b c" COLUMNS (js json PATH '$' WITHOUT WRAPPER KEEP QUOTES, jb jsonb PATH '$' WITHOUT WRAPPER KEEP QUOTES, jst text FORMAT JSON PATH '$' WITHOUT WRAPPER KEEP QUOTES, jsc character(4) FORMAT JSON PATH '$' WITHOUT WRAPPER KEEP QUOTES, jsv character varying(4) FORMAT JSON PATH '$' WITHOUT WRAPPER KEEP QUOTES))
|
||||
(3 rows)
|
||||
|
||||
EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM jsonb_table_view4;
|
||||
QUERY PLAN
|
||||
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
Table Function Scan on "json_table"
|
||||
Output: "json_table".jsb, "json_table".jsbq, "json_table".aaa, "json_table".aaa1
|
||||
Table Function Call: JSON_TABLE('null'::jsonb, '$[*]' AS json_table_path_0 PASSING 3 AS a, '"foo"'::jsonb AS "b c" COLUMNS (jsb jsonb PATH '$', jsbq jsonb PATH '$' OMIT QUOTES, aaa integer PATH '$."aaa"', aaa1 integer PATH '$."aaa"'))
|
||||
Table Function Call: JSON_TABLE('null'::jsonb, '$[*]' AS json_table_path_0 PASSING 3 AS a, '"foo"'::jsonb AS "b c" COLUMNS (jsb jsonb PATH '$' WITHOUT WRAPPER KEEP QUOTES, jsbq jsonb PATH '$' WITHOUT WRAPPER OMIT QUOTES, aaa integer PATH '$."aaa"', aaa1 integer PATH '$."aaa"'))
|
||||
(3 rows)
|
||||
|
||||
EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM jsonb_table_view5;
|
||||
|
@ -398,11 +398,11 @@ EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM jsonb_table_view5;
|
|||
(3 rows)
|
||||
|
||||
EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM jsonb_table_view6;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
Table Function Scan on "json_table"
|
||||
Output: "json_table".js2, "json_table".jsb2w, "json_table".jsb2q, "json_table".ia, "json_table".ta, "json_table".jba
|
||||
Table Function Call: JSON_TABLE('null'::jsonb, '$[*]' AS json_table_path_0 PASSING 3 AS a, '"foo"'::jsonb AS "b c" COLUMNS (js2 json PATH '$', jsb2w jsonb PATH '$' WITH UNCONDITIONAL WRAPPER, jsb2q jsonb PATH '$' OMIT QUOTES, ia integer[] PATH '$', ta text[] PATH '$', jba jsonb[] PATH '$'))
|
||||
Table Function Call: JSON_TABLE('null'::jsonb, '$[*]' AS json_table_path_0 PASSING 3 AS a, '"foo"'::jsonb AS "b c" COLUMNS (js2 json PATH '$' WITHOUT WRAPPER KEEP QUOTES, jsb2w jsonb PATH '$' WITH UNCONDITIONAL WRAPPER KEEP QUOTES, jsb2q jsonb PATH '$' WITHOUT WRAPPER OMIT QUOTES, ia integer[] PATH '$' WITHOUT WRAPPER KEEP QUOTES, ta text[] PATH '$' WITHOUT WRAPPER KEEP QUOTES, jba jsonb[] PATH '$' WITHOUT WRAPPER KEEP QUOTES))
|
||||
(3 rows)
|
||||
|
||||
-- JSON_TABLE() with alias
|
||||
|
|
|
@ -1070,27 +1070,27 @@ CREATE TABLE test_jsonb_constraints (
|
|||
CHECK (JSON_QUERY(js::jsonb, '$.a' RETURNING char(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) > 'a' COLLATE "C")
|
||||
);
|
||||
\d test_jsonb_constraints
|
||||
Table "public.test_jsonb_constraints"
|
||||
Column | Type | Collation | Nullable | Default
|
||||
--------+---------+-----------+----------+--------------------------------------------------------------------------------
|
||||
Table "public.test_jsonb_constraints"
|
||||
Column | Type | Collation | Nullable | Default
|
||||
--------+---------+-----------+----------+--------------------------------------------------------------------------------------------
|
||||
js | text | | |
|
||||
i | integer | | |
|
||||
x | jsonb | | | JSON_QUERY('[1, 2]'::jsonb, '$[*]' RETURNING jsonb WITH UNCONDITIONAL WRAPPER)
|
||||
x | jsonb | | | JSON_QUERY('[1, 2]'::jsonb, '$[*]' RETURNING jsonb WITH UNCONDITIONAL WRAPPER KEEP QUOTES)
|
||||
Check constraints:
|
||||
"test_jsonb_constraint1" CHECK (js IS JSON)
|
||||
"test_jsonb_constraint2" CHECK (JSON_EXISTS(js::jsonb, '$."a"' PASSING i + 5 AS int, i::text AS txt, ARRAY[1, 2, 3] AS arr))
|
||||
"test_jsonb_constraint3" CHECK (JSON_VALUE(js::jsonb, '$."a"' RETURNING integer DEFAULT 12 ON EMPTY ERROR ON ERROR) > i)
|
||||
"test_jsonb_constraint4" CHECK (JSON_QUERY(js::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < '[10]'::jsonb)
|
||||
"test_jsonb_constraint5" CHECK (JSON_QUERY(js::jsonb, '$."a"' RETURNING character(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) > ('a'::bpchar COLLATE "C"))
|
||||
"test_jsonb_constraint4" CHECK (JSON_QUERY(js::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER KEEP QUOTES EMPTY OBJECT ON ERROR) < '[10]'::jsonb)
|
||||
"test_jsonb_constraint5" CHECK (JSON_QUERY(js::jsonb, '$."a"' RETURNING character(5) WITHOUT WRAPPER OMIT QUOTES EMPTY ARRAY ON EMPTY) > ('a'::bpchar COLLATE "C"))
|
||||
|
||||
SELECT check_clause
|
||||
FROM information_schema.check_constraints
|
||||
WHERE constraint_name LIKE 'test_jsonb_constraint%'
|
||||
ORDER BY 1;
|
||||
check_clause
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
(JSON_QUERY((js)::jsonb, '$."a"' RETURNING character(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) > ('a'::bpchar COLLATE "C"))
|
||||
(JSON_QUERY((js)::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < '[10]'::jsonb)
|
||||
check_clause
|
||||
----------------------------------------------------------------------------------------------------------------------------------------
|
||||
(JSON_QUERY((js)::jsonb, '$."a"' RETURNING character(5) WITHOUT WRAPPER OMIT QUOTES EMPTY ARRAY ON EMPTY) > ('a'::bpchar COLLATE "C"))
|
||||
(JSON_QUERY((js)::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER KEEP QUOTES EMPTY OBJECT ON ERROR) < '[10]'::jsonb)
|
||||
(JSON_VALUE((js)::jsonb, '$."a"' RETURNING integer DEFAULT 12 ON EMPTY ERROR ON ERROR) > i)
|
||||
(js IS JSON)
|
||||
JSON_EXISTS((js)::jsonb, '$."a"' PASSING (i + 5) AS int, (i)::text AS txt, ARRAY[1, 2, 3] AS arr)
|
||||
|
@ -1100,9 +1100,9 @@ SELECT pg_get_expr(adbin, adrelid)
|
|||
FROM pg_attrdef
|
||||
WHERE adrelid = 'test_jsonb_constraints'::regclass
|
||||
ORDER BY 1;
|
||||
pg_get_expr
|
||||
--------------------------------------------------------------------------------
|
||||
JSON_QUERY('[1, 2]'::jsonb, '$[*]' RETURNING jsonb WITH UNCONDITIONAL WRAPPER)
|
||||
pg_get_expr
|
||||
--------------------------------------------------------------------------------------------
|
||||
JSON_QUERY('[1, 2]'::jsonb, '$[*]' RETURNING jsonb WITH UNCONDITIONAL WRAPPER KEEP QUOTES)
|
||||
(1 row)
|
||||
|
||||
INSERT INTO test_jsonb_constraints VALUES ('', 1);
|
||||
|
|
Loading…
Reference in New Issue