public inbox for [email protected]  
help / color / mirror / Atom feed
From: Ewan Young <[email protected]>
To: PostgreSQL Hackers <[email protected]>
Cc: [email protected]
Cc: [email protected]
Subject: GRAPH_TABLE: lateral reference with label disjunction fails with "plan should not reference subplan's variable"
Date: Thu, 4 Jun 2026 16:28:13 +0800
Message-ID: <CAON2xHOYJ+dGZznY+oPyBHdLfRtzThQid5iEc-HOxODs3pb3AA@mail.gmail.com> (raw)

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



view thread (2+ messages)  latest in thread

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected], [email protected], [email protected]
  Subject: Re: GRAPH_TABLE: lateral reference with label disjunction fails with "plan should not reference subplan's variable"
  In-Reply-To: <CAON2xHOYJ+dGZznY+oPyBHdLfRtzThQid5iEc-HOxODs3pb3AA@mail.gmail.com>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

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