]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix JSON_TABLE PLAN deparse to keep parentheses around nested joins
authorAlexander Korotkov <akorotkov@postgresql.org>
Tue, 14 Jul 2026 21:46:56 +0000 (00:46 +0300)
committerAlexander Korotkov <akorotkov@postgresql.org>
Tue, 14 Jul 2026 21:46:56 +0000 (00:46 +0300)
get_json_table_plan() parenthesized the child of a parent/child
(OUTER/INNER) plan only when that child was a sibling (UNION/CROSS)
join, not when it was itself a parent/child join.  A plan such as
PLAN (p0 OUTER (p1 INNER p11)) was therefore deparsed as
PLAN (p0 OUTER p1 INNER p11), which does not parse back to the same
plan tree -- a dump/restore hazard.  Parenthesize the child whenever it
is not a bare path name, matching the logic already used for the
operands of sibling joins.

Reported-by: Thom Brown <thom@linux.com>
Discussion: https://postgr.es/m/CAA-aLv7aZGSExnbjJRw8eKkoXbu34TdoKLLA2gPye3aHjO5OSA@mail.gmail.com

src/backend/utils/adt/ruleutils.c
src/test/regress/expected/sqljson_jsontable.out
src/test/regress/sql/sqljson_jsontable.sql

index b83c8b9d7b509a626065886bbf56290e4e763980..0ea38c18ca44e9b2c4138e889a14258a7c7af932 100644 (file)
@@ -12768,7 +12768,8 @@ get_json_table_plan(TableFunc *tf, JsonTablePlan *plan, deparse_context *context
                        appendStringInfoString(context->buf,
                                                                   s->outerJoin ? " OUTER " : " INNER ");
                        get_json_table_plan(tf, s->child, context,
-                                                               IsA(s->child, JsonTableSiblingJoin));
+                                                               IsA(s->child, JsonTableSiblingJoin) ||
+                                                               castNode(JsonTablePathScan, s->child)->child);
                }
        }
        else if (IsA(plan, JsonTableSiblingJoin))
index 6ff2b1ab28b9b0ad865339fabeebc03236e6b63a..56e5a0524b23c84cc4794002897587e3e27433cd 100644 (file)
@@ -504,6 +504,35 @@ DROP VIEW jsonb_table_view4;
 DROP VIEW jsonb_table_view5;
 DROP VIEW jsonb_table_view6;
 DROP DOMAIN jsonb_test_domain;
+-- Parentheses around a nested parent/child plan must survive a dump, so the
+-- plan parses back to the same tree.
+CREATE VIEW jsonb_table_view_plan AS
+SELECT * FROM JSON_TABLE(
+       jsonb 'null', '$' AS p0
+       COLUMNS (
+               NESTED PATH '$.a[*]' AS p1 COLUMNS (
+                       NESTED PATH '$.b[*]' AS p11 COLUMNS (b int PATH '$')
+               )
+       )
+       PLAN (p0 OUTER (p1 INNER p11))
+);
+\sv jsonb_table_view_plan
+CREATE OR REPLACE VIEW public.jsonb_table_view_plan AS
+ SELECT b
+   FROM JSON_TABLE(
+            'null'::jsonb, '$' AS p0
+            COLUMNS (
+                NESTED PATH '$."a"[*]' AS p1
+                COLUMNS (
+                    NESTED PATH '$."b"[*]' AS p11
+                    COLUMNS (
+                        b integer PATH '$'
+                    )
+                )
+            )
+            PLAN (p0 OUTER (p1 INNER p11))
+        )
+DROP VIEW jsonb_table_view_plan;
 -- JSON_TABLE: only one FOR ORDINALITY columns allowed
 SELECT * FROM JSON_TABLE(jsonb '1', '$' COLUMNS (id FOR ORDINALITY, id2 FOR ORDINALITY, a int PATH '$.a' ERROR ON EMPTY)) jt;
 ERROR:  only one FOR ORDINALITY column is allowed
index 5dab93973dbe9d05bb008a157d71266af0018085..9cd4c5e7e1e101b178910c9945b1f1f89ac8ecf2 100644 (file)
@@ -237,6 +237,21 @@ DROP VIEW jsonb_table_view5;
 DROP VIEW jsonb_table_view6;
 DROP DOMAIN jsonb_test_domain;
 
+-- Parentheses around a nested parent/child plan must survive a dump, so the
+-- plan parses back to the same tree.
+CREATE VIEW jsonb_table_view_plan AS
+SELECT * FROM JSON_TABLE(
+       jsonb 'null', '$' AS p0
+       COLUMNS (
+               NESTED PATH '$.a[*]' AS p1 COLUMNS (
+                       NESTED PATH '$.b[*]' AS p11 COLUMNS (b int PATH '$')
+               )
+       )
+       PLAN (p0 OUTER (p1 INNER p11))
+);
+\sv jsonb_table_view_plan
+DROP VIEW jsonb_table_view_plan;
+
 -- JSON_TABLE: only one FOR ORDINALITY columns allowed
 SELECT * FROM JSON_TABLE(jsonb '1', '$' COLUMNS (id FOR ORDINALITY, id2 FOR ORDINALITY, a int PATH '$.a' ERROR ON EMPTY)) jt;
 SELECT * FROM JSON_TABLE(jsonb '1', '$' COLUMNS (id FOR ORDINALITY, a int PATH '$' ERROR ON EMPTY)) jt;