Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
325 changes: 325 additions & 0 deletions src/test/regress/expected/sqljson_jsontable.out
Original file line number Diff line number Diff line change
Expand Up @@ -1926,3 +1926,328 @@ CREATE OR REPLACE VIEW public.json_table_view_on_empty AS
) ERROR ON ERROR
)
DROP VIEW json_table_view_on_empty;
-- ==========================================================================
-- Persisted JSON_TABLE views for dump/restore round-trip coverage
--
-- The views above are all dropped, so their deparsed definitions are never
-- carried through a dump/restore (or pg_upgrade) cycle. The views below are
-- deliberately left in place at the end of this file so that
-- pg_upgrade's dump comparison round-trips them, pinning the backward
-- parsing of the trickier JSON_TABLE PLAN constructs: nested parent/child
-- (OUTER/INNER) joins that must stay parenthesized, sibling UNION/CROSS
-- joins, PLAN DEFAULT, generated-vs-user path-name collisions, and per-column
-- and table-level ON ERROR/ON EMPTY. Each view is accompanied by its pinned
-- pg_get_viewdef() output and a SELECT that checks the resulting rows, so a
-- regression that silently drops a column or mangles a plan is caught here.
-- ==========================================================================
-- Nested parent/child plan two levels deep, with an explicit parenthesized
-- child join: PLAN (p0 OUTER (p1 INNER p11)). The parentheses around
-- (p1 INNER p11) must survive deparsing, otherwise the plan reparses to a
-- different tree (a dump/restore hazard). The INNER join means array
-- elements of "a" whose "b" is empty (x = 2) are dropped from the output,
-- while the OUTER-joined parent still contributes its other rows.
CREATE VIEW jsonb_table_view_pc_plan AS
SELECT * FROM JSON_TABLE(
jsonb '{"a":[{"x":1,"b":[10,20]},{"x":2,"b":[]},{"x":3,"b":[30]}]}', '$' AS p0
COLUMNS (
NESTED PATH '$.a[*]' AS p1 COLUMNS (
x int PATH '$.x',
NESTED PATH '$.b[*]' AS p11 COLUMNS (b int PATH '$')
)
)
PLAN (p0 OUTER (p1 INNER p11))
);
SELECT pg_get_viewdef('jsonb_table_view_pc_plan'::regclass);
pg_get_viewdef
----------------------------------------------------------------------------------------------------------
SELECT x, +
b +
FROM JSON_TABLE( +
'{"a": [{"b": [10, 20], "x": 1}, {"b": [], "x": 2}, {"b": [30], "x": 3}]}'::jsonb, '$' AS p0+
COLUMNS ( +
NESTED PATH '$."a"[*]' AS p1 +
COLUMNS ( +
x integer PATH '$."x"', +
NESTED PATH '$."b"[*]' AS p11 +
COLUMNS ( +
b integer PATH '$' +
) +
) +
) +
PLAN (p0 OUTER (p1 INNER p11)) +
);
(1 row)

SELECT * FROM jsonb_table_view_pc_plan ORDER BY x, b;
x | b
---+----
1 | 10
1 | 20
3 | 30
(3 rows)

-- Sibling paths joined with an explicit CROSS (not the default UNION), under
-- a parent/child OUTER join. CROSS produces the cartesian product of the two
-- sibling arrays, so the deparsed PLAN must keep CROSS and the parentheses.
CREATE VIEW jsonb_table_view_sibling_plan AS
SELECT * FROM JSON_TABLE(
jsonb '{"a":[1,2],"b":["x","y"]}', '$' AS p0
COLUMNS (
NESTED PATH '$.a[*]' AS pa COLUMNS (a int PATH '$'),
NESTED PATH '$.b[*]' AS pb COLUMNS (b text PATH '$')
)
PLAN (p0 OUTER (pa CROSS pb))
);
SELECT pg_get_viewdef('jsonb_table_view_sibling_plan'::regclass);
pg_get_viewdef
----------------------------------------------------------------
SELECT a, +
b +
FROM JSON_TABLE( +
'{"a": [1, 2], "b": ["x", "y"]}'::jsonb, '$' AS p0+
COLUMNS ( +
NESTED PATH '$."a"[*]' AS pa +
COLUMNS ( +
a integer PATH '$' +
), +
NESTED PATH '$."b"[*]' AS pb +
COLUMNS ( +
b text PATH '$' +
) +
) +
PLAN (p0 OUTER (pa CROSS pb)) +
);
(1 row)

SELECT * FROM jsonb_table_view_sibling_plan ORDER BY a, b;
a | b
---+---
1 | x
1 | y
2 | x
2 | y
(4 rows)

-- The trickiest shape: a parent OUTER-joined to a CROSS of two sibling
-- parent/child subplans, each of which is itself a parent/child join that
-- must stay parenthesized: PLAN (p OUTER ((pb INNER pb1) CROSS (pc OUTER pc1))).
-- Exercises both the "parenthesize a parent/child child" fix and the sibling
-- operand parenthesization together.
CREATE VIEW jsonb_table_view_mixed_plan AS
SELECT * FROM JSON_TABLE(
jsonb '{"b":[{"b1":[1,2]}],"c":[{"c1":[3]},{"c1":[]}]}', '$' AS p
COLUMNS (
NESTED PATH '$.b[*]' AS pb COLUMNS (
NESTED PATH '$.b1[*]' AS pb1 COLUMNS (b1 int PATH '$')
),
NESTED PATH '$.c[*]' AS pc COLUMNS (
NESTED PATH '$.c1[*]' AS pc1 COLUMNS (c1 int PATH '$')
)
)
PLAN (p OUTER ((pb INNER pb1) CROSS (pc OUTER pc1)))
);
SELECT pg_get_viewdef('jsonb_table_view_mixed_plan'::regclass);
pg_get_viewdef
----------------------------------------------------------------------------------------
SELECT b1, +
c1 +
FROM JSON_TABLE( +
'{"b": [{"b1": [1, 2]}], "c": [{"c1": [3]}, {"c1": []}]}'::jsonb, '$' AS p+
COLUMNS ( +
NESTED PATH '$."b"[*]' AS pb +
COLUMNS ( +
NESTED PATH '$."b1"[*]' AS pb1 +
COLUMNS ( +
b1 integer PATH '$' +
) +
), +
NESTED PATH '$."c"[*]' AS pc +
COLUMNS ( +
NESTED PATH '$."c1"[*]' AS pc1 +
COLUMNS ( +
c1 integer PATH '$' +
) +
) +
) +
PLAN (p OUTER ((pb INNER pb1) CROSS (pc OUTER pc1))) +
);
(1 row)

SELECT * FROM jsonb_table_view_mixed_plan ORDER BY b1, c1;
b1 | c1
----+----
1 | 3
1 |
2 | 3
2 |
(4 rows)

-- Deparse-reparse round-trip stability check: recreate the trickiest view
-- from its own deparsed definition and confirm the deparse is a fixed point
-- (the second view's definition is byte-for-byte identical). This proves the
-- PLAN clause parses back to the same tree without relying on an external
-- dump/restore.
SELECT format('CREATE VIEW jsonb_table_view_mixed_plan2 AS %s',
pg_get_viewdef('jsonb_table_view_mixed_plan'::regclass))
\gexec
CREATE VIEW jsonb_table_view_mixed_plan2 AS SELECT b1,
c1
FROM JSON_TABLE(
'{"b": [{"b1": [1, 2]}], "c": [{"c1": [3]}, {"c1": []}]}'::jsonb, '$' AS p
COLUMNS (
NESTED PATH '$."b"[*]' AS pb
COLUMNS (
NESTED PATH '$."b1"[*]' AS pb1
COLUMNS (
b1 integer PATH '$'
)
),
NESTED PATH '$."c"[*]' AS pc
COLUMNS (
NESTED PATH '$."c1"[*]' AS pc1
COLUMNS (
c1 integer PATH '$'
)
)
)
PLAN (p OUTER ((pb INNER pb1) CROSS (pc OUTER pc1)))
);
SELECT pg_get_viewdef('jsonb_table_view_mixed_plan'::regclass) =
pg_get_viewdef('jsonb_table_view_mixed_plan2'::regclass) AS deparse_roundtrip_stable;
deparse_roundtrip_stable
--------------------------
t
(1 row)

-- PLAN DEFAULT with explicit non-default choices (INNER, UNION). The default
-- plan is normally implied by the NESTED structure and omitted, but INNER is
-- not the default parent/child join, so this deparses back as an explicit
-- PLAN with INNER joins over the (unnamed) generated path names. The INNER
-- join drops the "c"-less element (x = 2).
CREATE VIEW jsonb_table_view_default_plan AS
SELECT * FROM JSON_TABLE(
jsonb '{"a":[{"x":1,"c":[10]},{"x":2,"c":[]}]}', '$'
COLUMNS (
NESTED PATH '$.a[*]' COLUMNS (
x int PATH '$.x',
NESTED PATH '$.c[*]' COLUMNS (c int PATH '$')
)
)
PLAN DEFAULT (INNER, UNION)
);
SELECT pg_get_viewdef('jsonb_table_view_default_plan'::regclass);
pg_get_viewdef
------------------------------------------------------------------------------------------------
SELECT x, +
c +
FROM JSON_TABLE( +
'{"a": [{"c": [10], "x": 1}, {"c": [], "x": 2}]}'::jsonb, '$' AS json_table_path_0+
COLUMNS ( +
NESTED PATH '$."a"[*]' AS json_table_path_1 +
COLUMNS ( +
x integer PATH '$."x"', +
NESTED PATH '$."c"[*]' AS json_table_path_2 +
COLUMNS ( +
c integer PATH '$' +
) +
) +
) +
PLAN (json_table_path_0 INNER (json_table_path_1 INNER json_table_path_2)) +
);
(1 row)

SELECT * FROM jsonb_table_view_default_plan ORDER BY x, c;
x | c
---+----
1 | 10
(1 row)

-- User path/column names deliberately colliding with the generated
-- "json_table_path_N" pattern, mixed with unnamed paths. A generated name
-- must skip any name already used, and the unnamed row-pattern (root) path is
-- named only after the user-supplied names are collected, so none of the
-- columns is silently dropped. All of json_table_path_0 (a column),
-- json_table_path_1 (a NESTED path) and the unnamed sibling path's rows must
-- appear.
CREATE VIEW jsonb_table_view_name_collision AS
SELECT * FROM JSON_TABLE(
jsonb '{"json_table_path_1":[{"v":11},{"v":12}],"y":[{"w":21}]}', '$'
COLUMNS (
json_table_path_0 int PATH '$.nosuch' DEFAULT -1 ON EMPTY,
NESTED PATH '$.json_table_path_1[*]' AS json_table_path_1
COLUMNS (v int PATH '$.v'),
NESTED PATH '$.y[*]' COLUMNS (w int PATH '$.w')
)
);
SELECT pg_get_viewdef('jsonb_table_view_name_collision'::regclass);
pg_get_viewdef
----------------------------------------------------------------------------------------------------------------
SELECT json_table_path_0, +
v, +
w +
FROM JSON_TABLE( +
'{"y": [{"w": 21}], "json_table_path_1": [{"v": 11}, {"v": 12}]}'::jsonb, '$' AS json_table_path_2+
COLUMNS ( +
json_table_path_0 integer PATH '$."nosuch"' DEFAULT '-1'::integer ON EMPTY, +
NESTED PATH '$."json_table_path_1"[*]' AS json_table_path_1 +
COLUMNS ( +
v integer PATH '$."v"' +
), +
NESTED PATH '$."y"[*]' AS json_table_path_3 +
COLUMNS ( +
w integer PATH '$."w"' +
) +
) +
);
(1 row)

SELECT * FROM jsonb_table_view_name_collision ORDER BY v, w;
json_table_path_0 | v | w
-------------------+----+----
-1 | 11 |
-1 | 12 |
-1 | | 21
(3 rows)

-- Per-column and table-level ON ERROR / ON EMPTY under an explicit PLAN. A
-- non-default per-column ON EMPTY (and EXISTS ... ON ERROR) must be preserved
-- verbatim through the deparse, independently of the table-level ERROR ON
-- ERROR (which does not cascade to the columns).
CREATE VIEW jsonb_table_view_on_behavior AS
SELECT * FROM JSON_TABLE(
jsonb '{"a":[{"n":5}]}', '$' AS p0
COLUMNS (
a int PATH '$.nosuch' DEFAULT -7 ON EMPTY,
e bool EXISTS PATH '$.nosuch' FALSE ON ERROR,
NESTED PATH '$.a[*]' AS p1 COLUMNS (
n int PATH '$.n' ERROR ON EMPTY
)
)
PLAN (p0 OUTER p1)
ERROR ON ERROR
);
SELECT pg_get_viewdef('jsonb_table_view_on_behavior'::regclass);
pg_get_viewdef
-----------------------------------------------------------------------------
SELECT a, +
e, +
n +
FROM JSON_TABLE( +
'{"a": [{"n": 5}]}'::jsonb, '$' AS p0 +
COLUMNS ( +
a integer PATH '$."nosuch"' DEFAULT '-7'::integer ON EMPTY,+
e boolean EXISTS PATH '$."nosuch"', +
NESTED PATH '$."a"[*]' AS p1 +
COLUMNS ( +
n integer PATH '$."n"' ERROR ON EMPTY +
) +
) ERROR ON ERROR +
);
(1 row)

SELECT * FROM jsonb_table_view_on_behavior ORDER BY n;
a | e | n
----+---+---
-7 | f | 5
(1 row)

Loading