You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@age.apache.org by jo...@apache.org on 2021/11/30 22:36:16 UTC

[incubator-age] branch master updated: Fix wrong parse tree when use EXISTS PATTERN (#141)

This is an automated email from the ASF dual-hosted git repository.

joshinnis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-age.git


The following commit(s) were added to refs/heads/master by this push:
     new 0b9205a  Fix wrong parse tree when use EXISTS PATTERN (#141)
0b9205a is described below

commit 0b9205a72ad0363eb052301bb8ba775da4cc650f
Author: Alex Kwak <ta...@kakao.com>
AuthorDate: Wed Dec 1 07:36:09 2021 +0900

    Fix wrong parse tree when use EXISTS PATTERN (#141)
    
    EXISTS PATTERN used ParseState as it was and affected the upper query.
    Use a temporary ParseState to solve this problem.
---
 regress/expected/cypher_match.out  | 88 ++++++++++++++++++++++++++++++++++++++
 regress/sql/cypher_match.sql       | 43 +++++++++++++++++++
 src/backend/parser/cypher_clause.c | 26 ++++++-----
 3 files changed, 147 insertions(+), 10 deletions(-)

diff --git a/regress/expected/cypher_match.out b/regress/expected/cypher_match.out
index 1590af9..f1bb3d7 100644
--- a/regress/expected/cypher_match.out
+++ b/regress/expected/cypher_match.out
@@ -890,6 +890,94 @@ $$) AS (i agtype);
 (3 rows)
 
 --
+-- JIRA: AGE2-544
+--
+-- Clean up
+SELECT DISTINCT * FROM cypher('cypher_match', $$
+    MATCH (u) DETACH DELETE (u)
+$$) AS (i agtype);
+ i 
+---
+(0 rows)
+
+-- Prepare
+SELECT * FROM cypher('cypher_match', $$
+    CREATE (u {name: "orphan"})
+    CREATE (u1 {name: "F"})-[u2:e1]->(u3 {name: "T"})
+    RETURN u1, u2, u3
+$$) as (u1 agtype, u2 agtype, u3 agtype);
+                                    u1                                     |                                                           u2                                                            |                                    u3                                     
+---------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------
+ {"id": 281474976710660, "label": "", "properties": {"name": "F"}}::vertex | {"id": 1407374883553283, "label": "e1", "end_id": 281474976710661, "start_id": 281474976710660, "properties": {}}::edge | {"id": 281474976710661, "label": "", "properties": {"name": "T"}}::vertex
+(1 row)
+
+-- Querying NOT EXISTS syntax
+SELECT * FROM cypher('cypher_match', $$
+     MATCH (f),(t)
+     WHERE NOT EXISTS((f)-[]->(t))
+     RETURN f.name, t.name
+ $$) as (f agtype, t agtype);
+    f     |    t     
+----------+----------
+ "orphan" | "orphan"
+ "orphan" | "F"
+ "orphan" | "T"
+ "F"      | "orphan"
+ "F"      | "F"
+ "T"      | "orphan"
+ "T"      | "F"
+ "T"      | "T"
+(8 rows)
+
+-- Querying EXISTS syntax
+SELECT * FROM cypher('cypher_match', $$
+    MATCH (f),(t)
+    WHERE EXISTS((f)-[]->(t))
+    RETURN f.name, t.name
+ $$) as (f agtype, t agtype);
+  f  |  t  
+-----+-----
+ "F" | "T"
+(1 row)
+
+-- Querying ALL
+SELECT * FROM cypher('cypher_match', $$
+    MATCH (f),(t)
+    WHERE NOT EXISTS((f)-[]->(t)) or true
+    RETURN f.name, t.name
+$$) as (f agtype, t agtype);
+    f     |    t     
+----------+----------
+ "orphan" | "orphan"
+ "orphan" | "F"
+ "orphan" | "T"
+ "F"      | "orphan"
+ "F"      | "F"
+ "F"      | "T"
+ "T"      | "orphan"
+ "T"      | "F"
+ "T"      | "T"
+(9 rows)
+
+-- Querying ALL
+SELECT * FROM cypher('cypher_match', $$
+    MATCH (f),(t)
+    RETURN f.name, t.name
+$$) as (f agtype, t agtype);
+    f     |    t     
+----------+----------
+ "orphan" | "orphan"
+ "orphan" | "F"
+ "orphan" | "T"
+ "F"      | "orphan"
+ "F"      | "F"
+ "F"      | "T"
+ "T"      | "orphan"
+ "T"      | "F"
+ "T"      | "T"
+(9 rows)
+
+--
 -- Clean up
 --
 SELECT drop_graph('cypher_match', true);
diff --git a/regress/sql/cypher_match.sql b/regress/sql/cypher_match.sql
index 377dc40..686d6ee 100644
--- a/regress/sql/cypher_match.sql
+++ b/regress/sql/cypher_match.sql
@@ -457,6 +457,49 @@ SELECT * FROM cypher('cypher_match', $$
 $$) AS (i agtype);
 
 --
+-- JIRA: AGE2-544
+--
+
+-- Clean up
+SELECT DISTINCT * FROM cypher('cypher_match', $$
+    MATCH (u) DETACH DELETE (u)
+$$) AS (i agtype);
+
+-- Prepare
+SELECT * FROM cypher('cypher_match', $$
+    CREATE (u {name: "orphan"})
+    CREATE (u1 {name: "F"})-[u2:e1]->(u3 {name: "T"})
+    RETURN u1, u2, u3
+$$) as (u1 agtype, u2 agtype, u3 agtype);
+
+-- Querying NOT EXISTS syntax
+SELECT * FROM cypher('cypher_match', $$
+     MATCH (f),(t)
+     WHERE NOT EXISTS((f)-[]->(t))
+     RETURN f.name, t.name
+ $$) as (f agtype, t agtype);
+
+-- Querying EXISTS syntax
+SELECT * FROM cypher('cypher_match', $$
+    MATCH (f),(t)
+    WHERE EXISTS((f)-[]->(t))
+    RETURN f.name, t.name
+ $$) as (f agtype, t agtype);
+
+-- Querying ALL
+SELECT * FROM cypher('cypher_match', $$
+    MATCH (f),(t)
+    WHERE NOT EXISTS((f)-[]->(t)) or true
+    RETURN f.name, t.name
+$$) as (f agtype, t agtype);
+
+-- Querying ALL
+SELECT * FROM cypher('cypher_match', $$
+    MATCH (f),(t)
+    RETURN f.name, t.name
+$$) as (f agtype, t agtype);
+
+--
 -- Clean up
 --
 SELECT drop_graph('cypher_match', true);
diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c
index a7fbf13..e9aaaff 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -1279,6 +1279,10 @@ static Query *transform_cypher_sub_pattern(cypher_parsestate *cpstate,
     ParseState *pstate = (ParseState *)cpstate;
     cypher_sub_pattern *subpat = (cypher_sub_pattern*)clause->self;
 
+    cypher_parsestate *child_parse_state = make_cypher_parsestate(cpstate);
+    ParseState *p_child_parse_state = (ParseState *) child_parse_state;
+    p_child_parse_state->p_expr_kind = pstate->p_expr_kind;
+
     /* create a cypher match node and assign it the sub pattern */
     match = make_ag_node(cypher_match);
     match->pattern = subpat->pattern;
@@ -1293,25 +1297,27 @@ static Query *transform_cypher_sub_pattern(cypher_parsestate *cpstate,
     qry = makeNode(Query);
     qry->commandType = CMD_SELECT;
 
-    rte = transform_cypher_clause_as_subquery(cpstate, transform_cypher_clause,
+    rte = transform_cypher_clause_as_subquery(child_parse_state, transform_cypher_clause,
                                               c);
 
-    qry->targetList = makeTargetListFromRTE(pstate, rte);
+    qry->targetList = makeTargetListFromRTE(p_child_parse_state, rte);
 
-    markTargetListOrigins(pstate, qry->targetList);
+    markTargetListOrigins(p_child_parse_state, qry->targetList);
 
-    qry->rtable = pstate->p_rtable;
-    qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+    qry->rtable = p_child_parse_state->p_rtable;
+    qry->jointree = makeFromExpr(p_child_parse_state->p_joinlist, NULL);
 
     /* the state will be destroyed so copy the data we need */
-    qry->hasSubLinks = pstate->p_hasSubLinks;
-    qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
-    qry->hasAggs = pstate->p_hasAggs;
+    qry->hasSubLinks = p_child_parse_state->p_hasSubLinks;
+    qry->hasTargetSRFs = p_child_parse_state->p_hasTargetSRFs;
+    qry->hasAggs = p_child_parse_state->p_hasAggs;
 
     if (qry->hasAggs)
-        parse_check_aggregates(pstate, qry);
+        parse_check_aggregates(p_child_parse_state, qry);
+
+    assign_query_collations(p_child_parse_state, qry);
 
-    assign_query_collations(pstate, qry);
+    free_cypher_parsestate(child_parse_state);
 
     return qry;
 }