]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix replace_property_refs() ignoring the root of expression tree
authorPeter Eisentraut <peter@eisentraut.org>
Wed, 8 Jul 2026 07:40:46 +0000 (09:40 +0200)
committerPeter Eisentraut <peter@eisentraut.org>
Wed, 8 Jul 2026 07:56:44 +0000 (09:56 +0200)
replace_property_refs() called expression_tree_mutator() with the root
of the expression tree as the input node.  But
expression_tree_mutator() does not call the mutator function on the
root node, so the root node remains unchanged.  If the root node is a
property reference or a lateral reference -- the two node kinds that
replace_property_refs_mutator() rewrites -- it is returned unchanged.
Modules after the rewriter do not know about property reference nodes,
resulting in "ERROR: unrecognized node type: 63".  Since varlevelsup
of lateral references is not incremented, they are not resolved
correctly in the planner, leading to many different symptoms.  Fix
this by calling replace_property_refs_mutator() directly from
replace_property_refs(), similar to how other mutator functions do.

The only case when a property reference or a lateral reference can be
the root of a GRAPH_TABLE expression tree is when it is a bare
property reference or a bare lateral reference in the WHERE clause.
The COLUMNS clause is passed to replace_property_refs() as a
targetlist.  Every other expression has at least one expression node
covering the property reference or a lateral reference in the
expression tree.  That explains why this bug was not seen so far.

Author: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Reported-by: Noah Misch <noah@leadboat.com>
Discussion: https://www.postgresql.org/message-id/flat/20260630173053.51.noahmisch%40microsoft.com

src/backend/rewrite/rewriteGraphTable.c
src/test/regress/expected/graph_table.out
src/test/regress/sql/graph_table.sql

index 7db17bec312e97bfc774c0af9f2ce5fd5ff1de73..cdb1f4c0dca1e764ff4ed1aa21c055f6be70ddaa 100644 (file)
@@ -1159,7 +1159,7 @@ replace_property_refs(Oid propgraphid, Node *node, const List *mappings)
        context.mappings = mappings;
        context.propgraphid = propgraphid;
 
-       return expression_tree_mutator(node, replace_property_refs_mutator, &context);
+       return replace_property_refs_mutator(node, &context);
 }
 
 /*
index bd603ea0d77df719768d8e61f2a6f378af1d75d5..a3d78a7ac43f867fd54d50f8409664f3b9bb2205 100644 (file)
@@ -248,12 +248,12 @@ SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers)->(o IS orders) COLUMNS
 -- Use table with a column name same as a property in the property graph so as
 -- to test resolution preferences. Property references are preferred over
 -- lateral table references.
-CREATE TABLE x1 (a int, address text);
-INSERT INTO x1 VALUES (1, 'one'), (2, 'two');
+CREATE TABLE x1 (a int, address text, flag boolean);
+INSERT INTO x1 VALUES (1, 'one', true), (2, 'two', false);
 SELECT * FROM x1, GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US' AND c.customer_id = x1.a)-[IS customer_orders]->(o IS orders) COLUMNS (c.name AS customer_name, c.customer_id AS cid));
- a | address | customer_name | cid 
----+---------+---------------+-----
- 1 | one     | customer1     |   1
+ a | address | flag | customer_name | cid 
+---+---------+------+---------------+-----
+ 1 | one     | t    | customer1     |   1
 (1 row)
 
 SELECT x1.a, g.* FROM x1, GRAPH_TABLE (myshop MATCH (x1 IS customers WHERE x1.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (x1.name AS customer_name, x1.customer_id AS cid, o.order_id)) g;
@@ -263,6 +263,13 @@ SELECT x1.a, g.* FROM x1, GRAPH_TABLE (myshop MATCH (x1 IS customers WHERE x1.ad
  2 | customer1     |   1 |        1
 (2 rows)
 
+-- bare lateral reference in WHERE clause
+SELECT * FROM x1, GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.customer_id = x1.a) WHERE x1.flag COLUMNS (c.name AS customer_name));
+ a | address | flag | customer_name 
+---+---------+------+---------------
+ 1 | one     | t    | customer1
+(1 row)
+
 -- lateral reference with multi-label pattern, which is rewritten as UNION of
 -- path queries
 SELECT x1.a, g.* FROM x1,
@@ -864,12 +871,12 @@ CREATE TABLE cv2 () INHERITS (pv);
 INSERT INTO pv VALUES (1, 10);
 INSERT INTO cv1 VALUES (2, 20);
 INSERT INTO cv2 VALUES (3, 30);
-CREATE TABLE pe (id int, src int, dest int, val int);
+CREATE TABLE pe (id int, src int, dest int, val int, flag boolean);
 CREATE TABLE ce1 () INHERITS (pe);
 CREATE TABLE ce2 () INHERITS (pe);
-INSERT INTO pe VALUES (1, 1, 2, 100);
-INSERT INTO ce1 VALUES (2, 2, 3, 200);
-INSERT INTO ce2 VALUES (3, 3, 1, 300);
+INSERT INTO pe VALUES (1, 1, 2, 100, false);
+INSERT INTO ce1 VALUES (2, 2, 3, 200, false);
+INSERT INTO ce2 VALUES (3, 3, 1, 300, true);
 CREATE PROPERTY GRAPH g3
     NODE TABLES (
         pv KEY (id)
@@ -887,6 +894,19 @@ SELECT * FROM GRAPH_TABLE (g3 MATCH (s IS pv)-[e IS pe]->(d IS pv) COLUMNS (s.va
   30 | 300 |  10
 (3 rows)
 
+-- bare property reference in WHERE clause
+SELECT * FROM GRAPH_TABLE (g3 MATCH (s IS pv)-[e IS pe WHERE e.flag]->(d IS pv) COLUMNS (s.val, e.val, d.val)) ORDER BY 1, 2, 3;
+ val | val | val 
+-----+-----+-----
+  30 | 300 |  10
+(1 row)
+
+SELECT * FROM GRAPH_TABLE (g3 MATCH (s IS pv)-[e IS pe]->(d IS pv) WHERE e.flag COLUMNS (s.val, e.val, d.val)) ORDER BY 1, 2, 3;
+ val | val | val 
+-----+-----+-----
+  30 | 300 |  10
+(1 row)
+
 -- temporary property graph
 CREATE TEMPORARY PROPERTY GRAPH gtmp
     VERTEX TABLES (
index 5c8049ed242a14dd5b0942d07f84a11dc0acd6af..6aacc2d4aa53df56a6bf1ae30f6ba14649a52846 100644 (file)
@@ -156,10 +156,12 @@ SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers)->(o IS orders) COLUMNS
 -- Use table with a column name same as a property in the property graph so as
 -- to test resolution preferences. Property references are preferred over
 -- lateral table references.
-CREATE TABLE x1 (a int, address text);
-INSERT INTO x1 VALUES (1, 'one'), (2, 'two');
+CREATE TABLE x1 (a int, address text, flag boolean);
+INSERT INTO x1 VALUES (1, 'one', true), (2, 'two', false);
 SELECT * FROM x1, GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US' AND c.customer_id = x1.a)-[IS customer_orders]->(o IS orders) COLUMNS (c.name AS customer_name, c.customer_id AS cid));
 SELECT x1.a, g.* FROM x1, GRAPH_TABLE (myshop MATCH (x1 IS customers WHERE x1.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (x1.name AS customer_name, x1.customer_id AS cid, o.order_id)) g;
+-- bare lateral reference in WHERE clause
+SELECT * FROM x1, GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.customer_id = x1.a) WHERE x1.flag COLUMNS (c.name AS customer_name));
 -- lateral reference with multi-label pattern, which is rewritten as UNION of
 -- path queries
 SELECT x1.a, g.* FROM x1,
@@ -483,12 +485,12 @@ CREATE TABLE cv2 () INHERITS (pv);
 INSERT INTO pv VALUES (1, 10);
 INSERT INTO cv1 VALUES (2, 20);
 INSERT INTO cv2 VALUES (3, 30);
-CREATE TABLE pe (id int, src int, dest int, val int);
+CREATE TABLE pe (id int, src int, dest int, val int, flag boolean);
 CREATE TABLE ce1 () INHERITS (pe);
 CREATE TABLE ce2 () INHERITS (pe);
-INSERT INTO pe VALUES (1, 1, 2, 100);
-INSERT INTO ce1 VALUES (2, 2, 3, 200);
-INSERT INTO ce2 VALUES (3, 3, 1, 300);
+INSERT INTO pe VALUES (1, 1, 2, 100, false);
+INSERT INTO ce1 VALUES (2, 2, 3, 200, false);
+INSERT INTO ce2 VALUES (3, 3, 1, 300, true);
 CREATE PROPERTY GRAPH g3
     NODE TABLES (
         pv KEY (id)
@@ -499,6 +501,9 @@ CREATE PROPERTY GRAPH g3
             DESTINATION KEY(dest) REFERENCES pv(id)
     );
 SELECT * FROM GRAPH_TABLE (g3 MATCH (s IS pv)-[e IS pe]->(d IS pv) COLUMNS (s.val, e.val, d.val)) ORDER BY 1, 2, 3;
+-- bare property reference in WHERE clause
+SELECT * FROM GRAPH_TABLE (g3 MATCH (s IS pv)-[e IS pe WHERE e.flag]->(d IS pv) COLUMNS (s.val, e.val, d.val)) ORDER BY 1, 2, 3;
+SELECT * FROM GRAPH_TABLE (g3 MATCH (s IS pv)-[e IS pe]->(d IS pv) WHERE e.flag COLUMNS (s.val, e.val, d.val)) ORDER BY 1, 2, 3;
 -- temporary property graph
 CREATE TEMPORARY PROPERTY GRAPH gtmp
     VERTEX TABLES (