From 4aae62b2682e5e8d9c5d5874e85c1a17f410a2b7 Mon Sep 17 00:00:00 2001 From: Ashutosh Bapat Date: Wed, 25 Mar 2026 17:15:10 +0530 Subject: [PATCH v20260326 2/4] Cross variable references in graph pattern causes segfault When converting the WHERE clause in an element pattern, generate_query_for_graph_path() calls replace_property_refs() to replace the property references in it. Only the current graph element pattern is passed as the context for replacement. If there are references to variables from other element patterns, it causes a segmentation fault (an assertion failure in an Assert enabled build) since it does not find path_element object corresponding to those variables. We do not support forward and backward variable references within a graph table clause. Hence prohibit all the cross references. Author: Ashutosh Bapat Reported by: Man Zeng Investigated by: Henson Choi Reviewed by: Junwang Zhao --- src/backend/parser/parse_graphtable.c | 24 +++++++++++++++++++++-- src/include/parser/parse_node.h | 4 ++++ src/test/regress/expected/graph_table.out | 23 ++++++++++++++++++++++ src/test/regress/sql/graph_table.sql | 6 ++++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/backend/parser/parse_graphtable.c b/src/backend/parser/parse_graphtable.c index bbe105b823d..626d3b681d0 100644 --- a/src/backend/parser/parse_graphtable.c +++ b/src/backend/parser/parse_graphtable.c @@ -89,6 +89,7 @@ transformGraphTablePropertyRef(ParseState *pstate, ColumnRef *cref) Node *field2 = lsecond(cref->fields); char *elvarname; char *propname; + bool is_prop_ref = false; if (IsA(field1, A_Star) || IsA(field2, A_Star)) { @@ -107,7 +108,19 @@ transformGraphTablePropertyRef(ParseState *pstate, ColumnRef *cref) elvarname = strVal(field1); propname = strVal(field2); - if (list_member(gpstate->variables, field1)) + /* + * If we are transforming expression in an element pattern, property + * references containing only that variable are allowed. + */ + if (gpstate->cur_variable) + { + if (equal(gpstate->cur_variable, field1)) + is_prop_ref = true; + } + else if (list_member(gpstate->variables, field1)) + is_prop_ref = true; + + if (is_prop_ref) { GraphPropertyRef *gpr = makeNode(GraphPropertyRef); HeapTuple pgptup; @@ -235,14 +248,21 @@ transformGraphElementPattern(ParseState *pstate, GraphElementPattern *gep) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("element pattern quantifier is not supported"))); + Assert(!gpstate->cur_variable); + if (gep->variable) - gpstate->variables = list_append_unique(gpstate->variables, makeString(pstrdup(gep->variable))); + { + gpstate->cur_variable = makeString(pstrdup(gep->variable)); + gpstate->variables = list_append_unique(gpstate->variables, gpstate->cur_variable); + } gep->labelexpr = transformLabelExpr(gpstate, gep->labelexpr); gep->whereClause = transformExpr(pstate, gep->whereClause, EXPR_KIND_WHERE); assign_expr_collations(pstate, gep->whereClause); + gpstate->cur_variable = NULL; + return (Node *) gep; } diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index fc2cbeb2083..d23d37dfaec 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -110,6 +110,10 @@ typedef struct GraphTableParseState Oid graphid; /* OID of the graph being referenced */ List *variables; /* list of element pattern variables in * GRAPH_TABLE */ + String *cur_variable; /* Variable in the element pattern being + * transformed. NULL if no element pattern is + * being transformed or the pattern has no + * variable. */ } GraphTableParseState; /* diff --git a/src/test/regress/expected/graph_table.out b/src/test/regress/expected/graph_table.out index 27c81ec6e42..ca7b74e6912 100644 --- a/src/test/regress/expected/graph_table.out +++ b/src/test/regress/expected/graph_table.out @@ -242,6 +242,19 @@ SELECT * FROM x1, GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'U 1 | one | customer1 | 1 (1 row) +SELECT x1.a, g.* FROM x1, GRAPH_TABLE (myshop MATCH (x1 IS customers)-[IS customer_orders]->(o IS orders WHERE o.order_id = x1.a) COLUMNS (x1.name AS customer_name, x1.customer_id AS cid, o.order_id)) g; + a | customer_name | cid | order_id +---+---------------+-----+---------- + 1 | customer1 | 1 | 1 + 2 | customer2 | 2 | 2 +(2 rows) + +SELECT x1.a, g.* FROM x1, GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US' AND c.customer_id = x1.a)-[IS customer_orders]->(x1 IS orders) COLUMNS (c.name AS customer_name, c.customer_id AS cid, x1.order_id)) g; + a | customer_name | cid | order_id +---+---------------+-----+---------- + 1 | customer1 | 1 | 1 +(1 row) + DROP TABLE x1; CREATE TABLE v1 ( id int PRIMARY KEY, @@ -395,6 +408,16 @@ SELECT * FROM GRAPH_TABLE (g1 MATCH (v1 IS vl2)-(v2) COLUMNS (v1.vname AS v1name v22 | v32 (3 rows) +-- cross variable references +SELECT * FROM GRAPH_TABLE (g1 MATCH (a)-[b WHERE a.vprop1 = 10]->(c) COLUMNS (a.vname AS aname, a.vprop1 AS ap1, b.ename AS bname, b.eprop1 AS bp1, c.vname AS cname, c.vprop1 AS cp1)) ORDER BY aname, bname, cname; +ERROR: missing FROM-clause entry for table "a" +LINE 1: SELECT * FROM GRAPH_TABLE (g1 MATCH (a)-[b WHERE a.vprop1 = ... + ^ +-- forward variable reference, not supported +SELECT * FROM GRAPH_TABLE (g1 MATCH (a WHERE b.eprop1 = 10001)-[b]->(c) COLUMNS (a.vname AS aname, a.vprop1 AS ap1, b.ename AS bname, b.eprop1 AS bp1, c.vname AS cname, c.vprop1 AS cp1)) ORDER BY aname, bname, cname; +ERROR: missing FROM-clause entry for table "b" +LINE 1: SELECT * FROM GRAPH_TABLE (g1 MATCH (a WHERE b.eprop1 = 1000... + ^ -- Errors -- vl1 is not associated with property vprop2 SELECT src, src_vprop2, conn, dest FROM GRAPH_TABLE (g1 MATCH (a IS vl1)-[b IS el1]->(c IS vl2 | vl3) COLUMNS (a.vname AS src, a.vprop2 AS src_vprop2, b.ename AS conn, c.vname AS dest)); diff --git a/src/test/regress/sql/graph_table.sql b/src/test/regress/sql/graph_table.sql index 6d2b08b997b..88215ea427a 100644 --- a/src/test/regress/sql/graph_table.sql +++ b/src/test/regress/sql/graph_table.sql @@ -154,6 +154,8 @@ SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers)->(o IS orders) COLUMNS CREATE TABLE x1 (a int, b text); INSERT INTO x1 VALUES (1, 'one'), (2, 'two'); 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)-[IS customer_orders]->(o IS orders WHERE o.order_id = x1.a) COLUMNS (x1.name AS customer_name, x1.customer_id AS cid, o.order_id)) g; +SELECT x1.a, g.* FROM x1, GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US' AND c.customer_id = x1.a)-[IS customer_orders]->(x1 IS orders) COLUMNS (c.name AS customer_name, c.customer_id AS cid, x1.order_id)) g; DROP TABLE x1; CREATE TABLE v1 ( @@ -274,6 +276,10 @@ SELECT src, conn, dest, lprop1, vprop2, vprop1 FROM GRAPH_TABLE (g1 MATCH (a IS -- edges directed in both ways - to and from v2 SELECT * FROM GRAPH_TABLE (g1 MATCH (v1 IS vl2)-[conn]-(v2) COLUMNS (v1.vname AS v1name, conn.ename AS cname, v2.vname AS v2name)); SELECT * FROM GRAPH_TABLE (g1 MATCH (v1 IS vl2)-(v2) COLUMNS (v1.vname AS v1name, v2.vname AS v2name)); +-- cross variable references +SELECT * FROM GRAPH_TABLE (g1 MATCH (a)-[b WHERE a.vprop1 = 10]->(c) COLUMNS (a.vname AS aname, a.vprop1 AS ap1, b.ename AS bname, b.eprop1 AS bp1, c.vname AS cname, c.vprop1 AS cp1)) ORDER BY aname, bname, cname; +-- forward variable reference, not supported +SELECT * FROM GRAPH_TABLE (g1 MATCH (a WHERE b.eprop1 = 10001)-[b]->(c) COLUMNS (a.vname AS aname, a.vprop1 AS ap1, b.ename AS bname, b.eprop1 AS bp1, c.vname AS cname, c.vprop1 AS cp1)) ORDER BY aname, bname, cname; -- Errors -- vl1 is not associated with property vprop2 -- 2.34.1