From 50c670d26ff7c38e56009ade58cd1e1dd83ff545 Mon Sep 17 00:00:00 2001 From: Ashutosh Bapat Date: Wed, 25 Mar 2026 17:49:02 +0530 Subject: [PATCH v20260326 3/4] Consecutive element patterns of same kind Adding an implicit empty vertex pattern when a path pattern starts or ends with an edge pattern or when two consecutive edge patterns appear in the pattern is not supported right not. Prohibit such path patterns. Author: Ashutosh Bapat Reported by: Alexander Lakhin Investigation by: Henson Choi Reviewed by: Henson Choi --- src/backend/parser/parse_graphtable.c | 58 +++++++++++++++++++++-- src/backend/rewrite/rewriteGraphTable.c | 10 ++++ src/test/regress/expected/graph_table.out | 19 ++++++++ src/test/regress/sql/graph_table.sql | 6 +++ 4 files changed, 88 insertions(+), 5 deletions(-) diff --git a/src/backend/parser/parse_graphtable.c b/src/backend/parser/parse_graphtable.c index 626d3b681d0..d1d0076789a 100644 --- a/src/backend/parser/parse_graphtable.c +++ b/src/backend/parser/parse_graphtable.c @@ -238,11 +238,6 @@ transformGraphElementPattern(ParseState *pstate, GraphElementPattern *gep) { GraphTableParseState *gpstate = pstate->p_graph_table_pstate; - if (gep->kind != VERTEX_PATTERN && !IS_EDGE_PATTERN(gep->kind)) - ereport(ERROR, - errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("unsupported element pattern kind: \"%s\"", get_gep_kind_name(gep->kind))); - if (gep->quantifier) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -273,10 +268,63 @@ static Node * transformPathTerm(ParseState *pstate, List *path_term) { List *result = NIL; + GraphElementPattern *prev_gep = NULL; foreach_node(GraphElementPattern, gep, path_term) + { + if (gep->kind != VERTEX_PATTERN && !IS_EDGE_PATTERN(gep->kind)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("unsupported element pattern kind: \"%s\"", get_gep_kind_name(gep->kind)), + parser_errposition(pstate, gep->location))); + + if (IS_EDGE_PATTERN(gep->kind)) + { + /* An edge pattern should always be preceded by a vertex pattern */ + if (!prev_gep) + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("a path pattern cannot start with an edge pattern"), + parser_errposition(pstate, gep->location))); + } + + if (prev_gep->kind != VERTEX_PATTERN) + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("path pattern with two consecutive edge patterns without a vertex pattern in between is not supported"), + parser_errposition(pstate, gep->location))); + } + } + else + { + if (prev_gep && !IS_EDGE_PATTERN(prev_gep->kind)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("path pattern with two consecutive vertex patterns without an edge pattern in between is not supported"), + parser_errposition(pstate, gep->location))); + } + result = lappend(result, transformGraphElementPattern(pstate, gep)); + prev_gep = gep; + } + + /* Path pattern should have at least one element pattern. */ + Assert(prev_gep); + + /* + * Add an implicit vertex pattern at the end if the last element is an + * edge pattern. + */ + if (IS_EDGE_PATTERN(prev_gep->kind)) + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("a path pattern cannot end with an edge pattern"), + parser_errposition(pstate, prev_gep->location))); + } return (Node *) result; } diff --git a/src/backend/rewrite/rewriteGraphTable.c b/src/backend/rewrite/rewriteGraphTable.c index 9c293586e44..2c3199d3230 100644 --- a/src/backend/rewrite/rewriteGraphTable.c +++ b/src/backend/rewrite/rewriteGraphTable.c @@ -300,6 +300,11 @@ generate_queries_for_path_pattern(RangeTblEntry *rte, List *path_pattern) errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern")); prev_pf->src_pf = pf; } + else + { + Assert(prev_pf->kind == VERTEX_PATTERN); + Assert(IS_EDGE_PATTERN(pf->kind)); + } if (pf->kind == EDGE_PATTERN_RIGHT || pf->kind == EDGE_PATTERN_ANY) { @@ -319,6 +324,11 @@ generate_queries_for_path_pattern(RangeTblEntry *rte, List *path_pattern) errmsg("an edge cannot connect more than two vertexes even in a cyclic pattern")); pf->dest_pf = prev_pf; } + else + { + Assert(pf->kind == VERTEX_PATTERN); + Assert(IS_EDGE_PATTERN(prev_pf->kind)); + } } prev_pf = pf; diff --git a/src/test/regress/expected/graph_table.out b/src/test/regress/expected/graph_table.out index ca7b74e6912..ba433ce3713 100644 --- a/src/test/regress/expected/graph_table.out +++ b/src/test/regress/expected/graph_table.out @@ -103,6 +103,8 @@ SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers)->{1,2}(o IS ERROR: element pattern quantifier is not supported SELECT * FROM GRAPH_TABLE (myshop MATCH ((c IS customers)->(o IS orders)) COLUMNS (c.name)); ERROR: unsupported element pattern kind: "nested path pattern" +LINE 1: SELECT * FROM GRAPH_TABLE (myshop MATCH ((c IS customers)->(... + ^ -- a property graph can be referenced only from within GRAPH_TABLE clause. SELECT * FROM myshop; -- error ERROR: cannot open relation "myshop" @@ -443,6 +445,23 @@ SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.* IS NOT NULL)-[ ERROR: "*" not allowed here LINE 1: ...M GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.* IS NOT... ^ +-- consecutive element patterns with same kind +SELECT * FROM GRAPH_TABLE (g1 MATCH ()() COLUMNS (1 as one)); +ERROR: path pattern with two consecutive vertex patterns without an edge pattern in between is not supported +LINE 1: SELECT * FROM GRAPH_TABLE (g1 MATCH ()() COLUMNS (1 as one))... + ^ +SELECT * FROM GRAPH_TABLE (g1 MATCH -> COLUMNS (1 AS one)); +ERROR: a path pattern cannot start with an edge pattern +LINE 1: SELECT * FROM GRAPH_TABLE (g1 MATCH -> COLUMNS (1 AS one)); + ^ +SELECT * FROM GRAPH_TABLE (g1 MATCH ()-[]- COLUMNS (1 AS one)); +ERROR: a path pattern cannot end with an edge pattern +LINE 1: SELECT * FROM GRAPH_TABLE (g1 MATCH ()-[]- COLUMNS (1 AS one... + ^ +SELECT * FROM GRAPH_TABLE (g1 MATCH ()-> ->() COLUMNS (1 AS one)); +ERROR: path pattern with two consecutive edge patterns without a vertex pattern in between is not supported +LINE 1: SELECT * FROM GRAPH_TABLE (g1 MATCH ()-> ->() COLUMNS (1 AS ... + ^ -- select all the properties across all the labels associated with a given type -- of graph element SELECT * FROM GRAPH_TABLE (g1 MATCH (src)-[conn]->(dest) COLUMNS (src.vname AS svname, conn.ename AS cename, dest.vname AS dvname, src.vprop1 AS svp1, src.vprop2 AS svp2, src.lprop1 AS slp1, dest.vprop1 AS dvp1, dest.vprop2 AS dvp2, dest.lprop1 AS dlp1, conn.eprop1 AS cep1, conn.lprop2 AS clp2)); diff --git a/src/test/regress/sql/graph_table.sql b/src/test/regress/sql/graph_table.sql index 88215ea427a..bbff150a6d9 100644 --- a/src/test/regress/sql/graph_table.sql +++ b/src/test/regress/sql/graph_table.sql @@ -295,6 +295,12 @@ SELECT * FROM GRAPH_TABLE (g1 MATCH (src IS el1 | vl1)-[conn]->(dest) COLUMNS (c SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.*)); -- star anywhere else is not allowed as a property reference SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.* IS NOT NULL)-[IS customer_orders]->(o IS orders) COLUMNS (c.name)); +-- consecutive element patterns with same kind +SELECT * FROM GRAPH_TABLE (g1 MATCH ()() COLUMNS (1 as one)); +SELECT * FROM GRAPH_TABLE (g1 MATCH -> COLUMNS (1 AS one)); +SELECT * FROM GRAPH_TABLE (g1 MATCH ()-[]- COLUMNS (1 AS one)); +SELECT * FROM GRAPH_TABLE (g1 MATCH ()-> ->() COLUMNS (1 AS one)); + -- select all the properties across all the labels associated with a given type -- of graph element -- 2.34.1