public inbox for [email protected]  
help / color / mirror / Atom feed
GRAPH_TABLE: lateral reference with label disjunction fails with "plan should not reference subplan's variable"
2+ messages / 2 participants
[nested] [flat]

* GRAPH_TABLE: lateral reference with label disjunction fails with "plan should not reference subplan's variable"
@ 2026-06-04 08:28 Ewan Young <[email protected]>
  2026-06-04 09:55 ` Re: GRAPH_TABLE: lateral reference with label disjunction fails with "plan should not reference subplan's variable" Ashutosh Bapat <[email protected]>
  0 siblings, 1 reply; 2+ messages in thread

From: Ewan Young @ 2026-06-04 08:28 UTC (permalink / raw)
  To: PostgreSQL Hackers <[email protected]>; +Cc: [email protected]; [email protected]

Hi hackers,

While testing with master I ran into an internal error in the
SQL/PGQ code (commit 2f094e7ac69).  A lateral reference into a
GRAPH_TABLE clause whose graph pattern contains a label disjunction
resolving to more than one element table fails at plan time:

    CREATE TABLE v1 (id int);
    CREATE TABLE v2 (id int);
    CREATE TABLE x (a int);
    CREATE PROPERTY GRAPH g
      VERTEX TABLES (v1 KEY (id) LABEL l1, v2 KEY (id) LABEL l2);

    SELECT 1 FROM x,
      GRAPH_TABLE (g MATCH (s IS l1|l2 WHERE s.id = x.a) COLUMNS (s.id));
    ERROR:  plan should not reference subplan's variable

The same pattern with a single label works fine, as does a label
disjunction without the lateral reference.  Reproduces on any build of
master and 19beta1; the equivalent hand-written query (LATERAL over a
UNION ALL) plans fine.

The cause is in rewriteGraphTable.c.  replace_property_refs_mutator()
increments varlevelsup of lateral references by one, assuming the path
query will directly become the GRAPH_TABLE's subquery.  That holds for
a single path query, but when the disjunction produces several path
queries, generate_union_from_pathqueries() wraps them in subquery RTEs
of a new UNION query, one level deeper, and nothing compensates for
that.  The planner then attributes the parameters to the wrong query
level, which finalize_plan() detects.

The attached patch increments the level of outer references in each
path query once more at the point where they are wrapped in the UNION
query, using IncrementVarSublevelsUp().  With the fix, the query above
returns the same results as the hand-written UNION ALL equivalent.

make check passes with the patch applied.

Regards,
Ewan Young


Attachments:

  [application/octet-stream] v1-0001-Fix-lateral-references-in-GRAPH_TABLE-with-label-.patch (3.7K, 2-v1-0001-Fix-lateral-references-in-GRAPH_TABLE-with-label-.patch)
  download | inline diff:
From 1c30243a8385d5ba3c977764d0d9ee493dcd8184 Mon Sep 17 00:00:00 2001
From: Ewan Young <[email protected]>
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



^ permalink  raw  reply  [nested|flat] 2+ messages in thread

* Re: GRAPH_TABLE: lateral reference with label disjunction fails with "plan should not reference subplan's variable"
  2026-06-04 08:28 GRAPH_TABLE: lateral reference with label disjunction fails with "plan should not reference subplan's variable" Ewan Young <[email protected]>
@ 2026-06-04 09:55 ` Ashutosh Bapat <[email protected]>
  0 siblings, 0 replies; 2+ messages in thread

From: Ashutosh Bapat @ 2026-06-04 09:55 UTC (permalink / raw)
  To: Ewan Young <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>; [email protected]

Hi Ewan,
Thanks for trying out SQL/PGQ and the report.

On Thu, Jun 4, 2026 at 1:58 PM Ewan Young <[email protected]> wrote:
>
> Hi hackers,
>
> While testing with master I ran into an internal error in the
> SQL/PGQ code (commit 2f094e7ac69).  A lateral reference into a
> GRAPH_TABLE clause whose graph pattern contains a label disjunction
> resolving to more than one element table fails at plan time:
>
>     CREATE TABLE v1 (id int);
>     CREATE TABLE v2 (id int);
>     CREATE TABLE x (a int);
>     CREATE PROPERTY GRAPH g
>       VERTEX TABLES (v1 KEY (id) LABEL l1, v2 KEY (id) LABEL l2);
>
>     SELECT 1 FROM x,
>       GRAPH_TABLE (g MATCH (s IS l1|l2 WHERE s.id = x.a) COLUMNS (s.id));
>     ERROR:  plan should not reference subplan's variable
>
> The same pattern with a single label works fine, as does a label
> disjunction without the lateral reference.  Reproduces on any build of
> master and 19beta1; the equivalent hand-written query (LATERAL over a
> UNION ALL) plans fine.
>
> The cause is in rewriteGraphTable.c.  replace_property_refs_mutator()
> increments varlevelsup of lateral references by one, assuming the path
> query will directly become the GRAPH_TABLE's subquery.  That holds for
> a single path query, but when the disjunction produces several path
> queries, generate_union_from_pathqueries() wraps them in subquery RTEs
> of a new UNION query, one level deeper, and nothing compensates for
> that.  The planner then attributes the parameters to the wrong query
> level, which finalize_plan() detects.
>
> The attached patch increments the level of outer references in each
> path query once more at the point where they are wrapped in the UNION
> query, using IncrementVarSublevelsUp().  With the fix, the query above
> returns the same results as the hand-written UNION ALL equivalent.
>
> make check passes with the patch applied.

This bug is the same as the one being discussed in [1]. Let's continue
the discussion there.

[1] http://postgr.es/m/[email protected]

-- 
Best Wishes,
Ashutosh Bapat






^ permalink  raw  reply  [nested|flat] 2+ messages in thread


end of thread, other threads:[~2026-06-04 09:55 UTC | newest]

Thread overview: 2+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-06-04 08:28 GRAPH_TABLE: lateral reference with label disjunction fails with "plan should not reference subplan's variable" Ewan Young <[email protected]>
2026-06-04 09:55 ` Ashutosh Bapat <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox