You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@age.apache.org by de...@apache.org on 2021/10/25 23:15:55 UTC

[incubator-age] branch master updated: Implement XOR Operator

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

dehowef 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 163e064  Implement XOR Operator
163e064 is described below

commit 163e064086eac52eb68dbc55a77897a74df01ce6
Author: Dehowe Feng <de...@gmail.com>
AuthorDate: Fri Oct 22 17:40:19 2021 -0700

    Implement XOR Operator
    
    Implemented XOR in the grammar using logical and, or and not.
    
    This is ticket AGE2-478
    
    Added regression tests.
---
 regress/expected/expr.out            | 34 +++++++++++++++++++++++++++++++++-
 regress/sql/expr.sql                 | 18 +++++++++++++++++-
 src/backend/parser/cypher_gram.y     | 22 ++++++++++++++++++++++
 src/backend/parser/cypher_keywords.c |  3 ++-
 4 files changed, 74 insertions(+), 3 deletions(-)

diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index 33a72aa..b90d4eb 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -492,7 +492,7 @@ $$) AS r(result boolean);
 (1 row)
 
 --
--- Test transform logic for AND, OR, and NOT
+-- Test transform logic for AND, OR, NOT and XOR
 --
 SELECT * FROM cypher('expr', $$
 RETURN NOT false
@@ -582,6 +582,38 @@ $$) AS r(result boolean);
  f
 (1 row)
 
+SELECT * FROM cypher('expr', $$
+RETURN true XOR true
+$$) AS r(result boolean);
+ result 
+--------
+ f
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN true XOR false
+$$) AS r(result boolean);
+ result 
+--------
+ t
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN false XOR true
+$$) AS r(result boolean);
+ result 
+--------
+ t
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN false XOR false
+$$) AS r(result boolean);
+ result 
+--------
+ f
+(1 row)
+
 --
 -- Test indirection transform logic for object.property, object["property"],
 -- and array[element]
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index f26ba14..f2f5b8e 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -256,7 +256,7 @@ RETURN null IS NOT NULL
 $$) AS r(result boolean);
 
 --
--- Test transform logic for AND, OR, and NOT
+-- Test transform logic for AND, OR, NOT and XOR
 --
 
 SELECT * FROM cypher('expr', $$
@@ -303,6 +303,22 @@ SELECT * FROM cypher('expr', $$
 RETURN NOT ((true OR false) AND (false OR true))
 $$) AS r(result boolean);
 
+SELECT * FROM cypher('expr', $$
+RETURN true XOR true
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN true XOR false
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN false XOR true
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN false XOR false
+$$) AS r(result boolean);
+
 --
 -- Test indirection transform logic for object.property, object["property"],
 -- and array[element]
diff --git a/src/backend/parser/cypher_gram.y b/src/backend/parser/cypher_gram.y
index 3298456..6c1591b 100644
--- a/src/backend/parser/cypher_gram.y
+++ b/src/backend/parser/cypher_gram.y
@@ -93,6 +93,7 @@
                  THEN TRUE_P
                  VERBOSE
                  WHEN WHERE WITH
+                 XOR
 
 /* query */
 %type <list> single_query query_part_init query_part_last
@@ -148,6 +149,7 @@
 /* precedence: lowest to highest */
 %left OR
 %left AND
+%left XOR
 %right NOT
 %nonassoc '=' NOT_EQ '<' LT_EQ '>' GT_EQ
 %left '+' '-'
@@ -164,6 +166,7 @@
 // logical operators
 static Node *make_or_expr(Node *lexpr, Node *rexpr, int location);
 static Node *make_and_expr(Node *lexpr, Node *rexpr, int location);
+static Node *make_xor_expr(Node *lexpr, Node *rexpr, int location);
 static Node *make_not_expr(Node *expr, int location);
 
 // arithmetic operators
@@ -1010,6 +1013,10 @@ expr:
         {
             $$ = make_and_expr($1, $3, @2);
         }
+    | expr XOR expr
+        {
+            $$ = make_xor_expr($1, $3, @2);
+        }
     | NOT expr
         {
             $$ = make_not_expr($2, @1);
@@ -1607,6 +1614,7 @@ safe_keywords:
     | VERBOSE    { $$ = pnstrdup($1, 7); }
     | WHERE      { $$ = pnstrdup($1, 5); }
     | WITH       { $$ = pnstrdup($1, 4); }
+    | XOR        { $$ = pnstrdup($1, 3); }
     ;
 
 conflicted_keywords:
@@ -1658,6 +1666,20 @@ static Node *make_and_expr(Node *lexpr, Node *rexpr, int location)
     return (Node *)makeBoolExpr(AND_EXPR, list_make2(lexpr, rexpr), location);
 }
 
+static Node *make_xor_expr(Node *lexpr, Node *rexpr, int location)
+{
+    Expr *aorb;
+    Expr *notaandb;
+
+    // XOR is (A OR B) AND (NOT (A AND B))
+    aorb = makeBoolExpr(OR_EXPR, list_make2(lexpr, rexpr), location);
+
+    notaandb = makeBoolExpr(AND_EXPR, list_make2(lexpr, rexpr), location);
+    notaandb = makeBoolExpr(NOT_EXPR, list_make1(notaandb), location);
+
+    return (Node *)makeBoolExpr(AND_EXPR, list_make2(aorb, notaandb), location);
+}
+
 static Node *make_not_expr(Node *expr, int location)
 {
     return (Node *)makeBoolExpr(NOT_EXPR, list_make1(expr), location);
diff --git a/src/backend/parser/cypher_keywords.c b/src/backend/parser/cypher_keywords.c
index 64c6711..350683e 100644
--- a/src/backend/parser/cypher_keywords.c
+++ b/src/backend/parser/cypher_keywords.c
@@ -73,7 +73,8 @@ const ScanKeyword cypher_keywords[] = {
     {"verbose", VERBOSE, RESERVED_KEYWORD},
     {"when", WHEN, RESERVED_KEYWORD},
     {"where", WHERE, RESERVED_KEYWORD},
-    {"with", WITH, RESERVED_KEYWORD}
+    {"with", WITH, RESERVED_KEYWORD},
+    {"xor", XOR, RESERVED_KEYWORD}
 };
 
 const int num_cypher_keywords = lengthof(cypher_keywords);