From 1c30243a8385d5ba3c977764d0d9ee493dcd8184 Mon Sep 17 00:00:00 2001 From: Ewan Young Date: Fri, 5 Jun 2026 00:07:10 +0800 Subject: [PATCH v1] Fix lateral references in GRAPH_TABLE with label disjunction When a graph pattern's label disjunction resolves to more than one element table, the rewritten path queries are combined with a UNION query, which places them one query level deeper than the GRAPH_TABLE's own subquery level. replace_property_refs() had already adjusted the varlevelsup of lateral references on the assumption that the path query would itself become the GRAPH_TABLE subquery, so such references ended up off by one, making the planner fail with ERROR: plan should not reference subplan's variable Compensate by incrementing the level of outer references in each path query once more when wrapping them in a UNION query. Also add a regression test that actually executes the customers_us view, which was created to cover exactly this combination of lateral references and label disjunction but was previously only exercised by the ruleutils deparsing test. --- src/backend/rewrite/rewriteGraphTable.c | 10 ++++++++++ src/test/regress/expected/graph_table.out | 9 +++++++++ src/test/regress/sql/graph_table.sql | 4 ++++ 3 files changed, 23 insertions(+) diff --git a/src/backend/rewrite/rewriteGraphTable.c b/src/backend/rewrite/rewriteGraphTable.c index 33d4e866d74..3d6c85a5ea8 100644 --- a/src/backend/rewrite/rewriteGraphTable.c +++ b/src/backend/rewrite/rewriteGraphTable.c @@ -638,6 +638,16 @@ generate_union_from_pathqueries(List **pathqueries) return sampleQuery; } + /* + * Each path query will be wrapped in a subquery RTE of the UNION query + * constructed below, which puts it one query level further away from the + * query containing the GRAPH_TABLE clause than replace_property_refs() + * assumed when it adjusted the levels of lateral references. Compensate + * by incrementing varlevelsup of any outer-query references once more. + */ + foreach_node(Query, pathquery, *pathqueries) + IncrementVarSublevelsUp((Node *) pathquery, 1, 1); + sostmt = castNode(SetOperationStmt, generate_setop_from_pathqueries(*pathqueries, &rtable, NULL)); diff --git a/src/test/regress/expected/graph_table.out b/src/test/regress/expected/graph_table.out index cc6d80afd82..924f0cff6db 100644 --- a/src/test/regress/expected/graph_table.out +++ b/src/test/regress/expected/graph_table.out @@ -942,6 +942,15 @@ SELECT pg_get_viewdef('customers_us'::regclass); ORDER BY g.customer_name, g.product_name; (1 row) +-- exercises lateral references combined with label disjunction, where the +-- rewritten path queries are wrapped in an extra UNION query level +SELECT * FROM customers_us; + customer_name | product_name | a +---------------+--------------+--- + customer1 | product1 | 1 + customer1 | product2 | 1 +(2 rows) + -- test view/graph nesting CREATE VIEW customers_view AS SELECT customer_id, 'redacted' || customer_id AS name_redacted, address FROM customers; SELECT * FROM customers; diff --git a/src/test/regress/sql/graph_table.sql b/src/test/regress/sql/graph_table.sql index 0e381ec72bc..f8207578d32 100644 --- a/src/test/regress/sql/graph_table.sql +++ b/src/test/regress/sql/graph_table.sql @@ -536,6 +536,10 @@ SELECT g.* FROM x1, ORDER BY customer_name, product_name; SELECT pg_get_viewdef('customers_us'::regclass); +-- exercises lateral references combined with label disjunction, where the +-- rewritten path queries are wrapped in an extra UNION query level +SELECT * FROM customers_us; + -- test view/graph nesting CREATE VIEW customers_view AS SELECT customer_id, 'redacted' || customer_id AS name_redacted, address FROM customers; -- 2.47.3