You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@age.apache.org by jg...@apache.org on 2023/01/03 22:26:58 UTC

[age] branch master updated: Fix bug #339 - entities in WHERE clause have wrong Expr (#391)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new e396815  Fix bug #339 - entities in WHERE clause have wrong Expr (#391)
e396815 is described below

commit e39681551f403addbfb74c51596350521d5b780a
Author: Rafsun Masud <ra...@gmail.com>
AuthorDate: Tue Jan 3 17:26:52 2023 -0500

    Fix bug #339 - entities in WHERE clause have wrong Expr (#391)
    
    Whenever a node or an edge within a WHERE clause uses a Var type
    expr retrieved from a different cypher parsestate, the Var->varlevelsup
    is adjusted.
---
 regress/expected/cypher_match.out            | 80 ++++++++++++++++++++++++++++
 regress/sql/cypher_match.sql                 | 44 +++++++++++++++
 src/backend/parser/cypher_clause.c           |  5 +-
 src/backend/parser/cypher_transform_entity.c | 31 +++++++++++
 src/include/parser/cypher_transform_entity.h |  2 +
 5 files changed, 159 insertions(+), 3 deletions(-)

diff --git a/regress/expected/cypher_match.out b/regress/expected/cypher_match.out
index 4757392..6ff9a3c 100644
--- a/regress/expected/cypher_match.out
+++ b/regress/expected/cypher_match.out
@@ -1000,6 +1000,73 @@ SELECT * FROM cypher('cypher_match', $$
  "nobody"   |               |            | "anybody"  |               | 
 (12 rows)
 
+--
+-- Tests retrieving Var from some parent's cpstate during transformation
+--
+SELECT create_graph('test_retrieve_var');
+NOTICE:  graph "test_retrieve_var" has been created
+ create_graph 
+--------------
+ 
+(1 row)
+
+SELECT * FROM cypher('test_retrieve_var', $$ CREATE (:A)-[:incs]->(:C) $$) as (a agtype);
+ a 
+---
+(0 rows)
+
+-- Tests with node Var
+-- both queries should return the same result
+-- first query does not retrieve any variable from any parent's cpstate
+-- second query retrieves variable 'a', inside WHERE, from parent's parent's cpstate
+SELECT * FROM cypher('test_retrieve_var', $$
+    MATCH (a:A) WITH a
+    OPTIONAL MATCH (a)-[:incs]->(c)
+    WHERE EXISTS((c)<-[:incs]-())
+    RETURN a, c
+$$) AS (a agtype, c agtype);
+                                a                                |                                c                                 
+-----------------------------------------------------------------+------------------------------------------------------------------
+ {"id": 844424930131969, "label": "A", "properties": {}}::vertex | {"id": 1407374883553281, "label": "C", "properties": {}}::vertex
+(1 row)
+
+SELECT * FROM cypher('test_retrieve_var', $$
+    MATCH (a:A) WITH a
+    OPTIONAL MATCH (a)-[:incs]->(c)
+    WHERE EXISTS((c)<-[:incs]-(a))
+    RETURN a, c
+$$) AS (a agtype, c agtype);
+                                a                                |                                c                                 
+-----------------------------------------------------------------+------------------------------------------------------------------
+ {"id": 844424930131969, "label": "A", "properties": {}}::vertex | {"id": 1407374883553281, "label": "C", "properties": {}}::vertex
+(1 row)
+
+-- Tests with edge Var
+-- both queries should return the same result
+-- first query does not retrieve any variable from any parent's cpstate
+-- second query retrieves variable 'r', inside WHERE, from parent's parent's cpstate
+SELECT * FROM cypher('test_retrieve_var', $$
+    MATCH (a:A)-[r:incs]->() WITH a, r
+    OPTIONAL MATCH (a)-[r]->(c)
+    WHERE EXISTS(()<-[]-(c))
+    RETURN a, r
+$$) AS (a agtype, r agtype);
+                                a                                |                                                             r                                                              
+-----------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "A", "properties": {}}::vertex | {"id": 1125899906842625, "label": "incs", "end_id": 1407374883553281, "start_id": 844424930131969, "properties": {}}::edge
+(1 row)
+
+SELECT * FROM cypher('test_retrieve_var', $$
+    MATCH (a:A)-[r:incs]->() WITH a, r
+    OPTIONAL MATCH (a)-[r]->(c)
+    WHERE EXISTS(()<-[r]-(c))
+    RETURN a, r
+$$) AS (a agtype, r agtype);
+                                a                                |                                                             r                                                              
+-----------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "A", "properties": {}}::vertex | {"id": 1125899906842625, "label": "incs", "end_id": 1407374883553281, "start_id": 844424930131969, "properties": {}}::edge
+(1 row)
+
 --
 -- JIRA: AGE2-544
 --
@@ -1135,6 +1202,19 @@ NOTICE:  graph "cypher_match" has been dropped
  
 (1 row)
 
+SELECT drop_graph('test_retrieve_var', true);
+NOTICE:  drop cascades to 5 other objects
+DETAIL:  drop cascades to table test_retrieve_var._ag_label_vertex
+drop cascades to table test_retrieve_var._ag_label_edge
+drop cascades to table test_retrieve_var."A"
+drop cascades to table test_retrieve_var.incs
+drop cascades to table test_retrieve_var."C"
+NOTICE:  graph "test_retrieve_var" has been dropped
+ drop_graph 
+------------
+ 
+(1 row)
+
 --
 -- End
 --
diff --git a/regress/sql/cypher_match.sql b/regress/sql/cypher_match.sql
index 1128296..c4c48bf 100644
--- a/regress/sql/cypher_match.sql
+++ b/regress/sql/cypher_match.sql
@@ -501,6 +501,49 @@ SELECT * FROM cypher('cypher_match', $$
     ORDER BY n, p, m, q
  $$) AS (n agtype, r agtype, p agtype, m agtype, s agtype, q agtype);
 
+
+--
+-- Tests retrieving Var from some parent's cpstate during transformation
+--
+SELECT create_graph('test_retrieve_var');
+SELECT * FROM cypher('test_retrieve_var', $$ CREATE (:A)-[:incs]->(:C) $$) as (a agtype);
+
+-- Tests with node Var
+-- both queries should return the same result
+-- first query does not retrieve any variable from any parent's cpstate
+-- second query retrieves variable 'a', inside WHERE, from parent's parent's cpstate
+SELECT * FROM cypher('test_retrieve_var', $$
+    MATCH (a:A) WITH a
+    OPTIONAL MATCH (a)-[:incs]->(c)
+    WHERE EXISTS((c)<-[:incs]-())
+    RETURN a, c
+$$) AS (a agtype, c agtype);
+
+SELECT * FROM cypher('test_retrieve_var', $$
+    MATCH (a:A) WITH a
+    OPTIONAL MATCH (a)-[:incs]->(c)
+    WHERE EXISTS((c)<-[:incs]-(a))
+    RETURN a, c
+$$) AS (a agtype, c agtype);
+
+-- Tests with edge Var
+-- both queries should return the same result
+-- first query does not retrieve any variable from any parent's cpstate
+-- second query retrieves variable 'r', inside WHERE, from parent's parent's cpstate
+SELECT * FROM cypher('test_retrieve_var', $$
+    MATCH (a:A)-[r:incs]->() WITH a, r
+    OPTIONAL MATCH (a)-[r]->(c)
+    WHERE EXISTS(()<-[]-(c))
+    RETURN a, r
+$$) AS (a agtype, r agtype);
+
+SELECT * FROM cypher('test_retrieve_var', $$
+    MATCH (a:A)-[r:incs]->() WITH a, r
+    OPTIONAL MATCH (a)-[r]->(c)
+    WHERE EXISTS(()<-[r]-(c))
+    RETURN a, r
+$$) AS (a agtype, r agtype);
+
 --
 -- JIRA: AGE2-544
 --
@@ -561,6 +604,7 @@ $$) as (n agtype);
 -- Clean up
 --
 SELECT drop_graph('cypher_match', true);
+SELECT drop_graph('test_retrieve_var', true);
 
 --
 -- End
diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c
index 9b0c76e..a541fb4 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -4146,7 +4146,7 @@ static Expr *transform_cypher_edge(cypher_parsestate *cpstate,
 
             if (entity != NULL)
             {
-                return entity->expr;
+                return get_relative_expr(entity, 2);
             }
             else
             {
@@ -4294,10 +4294,9 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate,
              */
             transform_entity *entity = find_variable(parent_cpstate, node->name);
 
-
             if (entity != NULL)
             {
-                return entity->expr;
+                return get_relative_expr(entity, 2);
             }
             else
             {
diff --git a/src/backend/parser/cypher_transform_entity.c b/src/backend/parser/cypher_transform_entity.c
index 6d4b2c7..b80c228 100644
--- a/src/backend/parser/cypher_transform_entity.c
+++ b/src/backend/parser/cypher_transform_entity.c
@@ -152,3 +152,34 @@ char *get_entity_name(transform_entity *entity)
 
     return NULL;
 }
+
+/*
+ * Returns entity->expr relative to the current cpstate.
+ *
+ * For example,
+ * If entity is from current cpstate, its levelsup = 0.
+ * If entity is from immediate parent cpstate, its levelsup = 1.
+ * If entity is from parent's parent's cpstate, its levelsup = 2.
+ *
+ * Relative Expr is necessary when entity->expr is a Var and the entity
+ * is not from the current cpstate. In this case, Var->varlevelsup must
+ * reflect the distance between source cpstate of the entity and the
+ * cpstate where the Var is being used.
+ */
+Expr *get_relative_expr(transform_entity *entity, Index levelsup)
+{
+    Var *var;
+    Var *updated_var;
+
+    if (!IsA(entity->expr, Var))
+    {
+        return entity->expr;
+    }
+
+    var = (Var *)entity->expr;
+    updated_var = makeVar(var->varno, var->varattno, var->vartype,
+                          var->vartypmod, var->varcollid,
+                          var->varlevelsup + levelsup);
+
+    return (Expr *)updated_var;
+}
diff --git a/src/include/parser/cypher_transform_entity.h b/src/include/parser/cypher_transform_entity.h
index 5732c83..79a8a33 100644
--- a/src/include/parser/cypher_transform_entity.h
+++ b/src/include/parser/cypher_transform_entity.h
@@ -24,6 +24,7 @@
 #include "parser/parse_node.h"
 
 #include "nodes/cypher_nodes.h"
+#include "nodes/makefuncs.h"
 #include "parser/cypher_parse_node.h"
 
 enum transform_entity_type
@@ -93,5 +94,6 @@ transform_entity *make_transform_entity(cypher_parsestate *cpstate,
                                         enum transform_entity_type type,
                                         Node *node, Expr *expr);
 char *get_entity_name(transform_entity *entity);
+Expr *get_relative_expr(transform_entity *entity, Index levelsup);
 
 #endif