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

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


The following commit(s) were added to refs/heads/PG15 by this push:
     new 29588a25 Fix Issue 1988: How to update a property which is a keyword 
(#2005) (#2015)
29588a25 is described below

commit 29588a25a12da1d36b6d251f415f28d7415e79b1
Author: John Gemignani <[email protected]>
AuthorDate: Tue Aug 6 16:14:30 2024 -0700

    Fix Issue 1988: How to update a property which is a keyword (#2005) (#2015)
    
    Fixed issue 1988: Reserved Keyword Handling: How to update a property
    which is a keyword. Basically, how to use a keyword, aka reserved
    word, in a query.
    
    Added the appropriate scanner and parser rules to allow keywords to
    be back ticked and used in queries as their name. For example, like
    in the following commands -
    
        MATCH (n:Person { id: 123 }) SET n.`match` = 'matched' RETURN n;
        MATCH (n:Person { id: 123 }) SET n.`create` = 'created' RETURN n;
    
    The changes did not break any regression tests.
    Added additional regression tests.
---
 regress/expected/expr.out          | 137 +++++++++++++++++++++++++++++++++++++
 regress/sql/expr.sql               |  47 +++++++++++++
 src/backend/parser/ag_scanner.l    |   2 +-
 src/backend/parser/cypher_gram.y   |   8 ++-
 src/backend/parser/cypher_parser.c |  16 ++++-
 src/include/parser/ag_scanner.h    |   1 +
 6 files changed, 206 insertions(+), 5 deletions(-)

diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index e83928f7..fde0cb4c 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -8465,9 +8465,146 @@ SELECT * FROM cypher('issue_1953', $$ RETURN 
is_valid_label_name('issue_1953')[0
 ERROR:  A_indirection could not convert type boolean to agtype
 LINE 1: ...cypher('issue_1953', $$ RETURN is_valid_label_name('issue_19...
                                                              ^
+--
+-- Issue 1988: How to update a property which is a keyword.
+--
+SELECT * FROM create_graph('issue_1988');
+NOTICE:  graph "issue_1988" has been created
+ create_graph 
+--------------
+ 
+(1 row)
+
+SELECT * from cypher('issue_1988', $$
+    CREATE (p1:Part {part_num: 123}),
+           (p2:Part {part_num: 345}),
+           (p3:Part {part_num: 456}),
+           (p4:Part {part_num: 789}) $$) as (a agtype);
+ a 
+---
+(0 rows)
+
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p) RETURN p $$) as (p agtype);
+                                         p                                     
    
+-----------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "Part", "properties": {"part_num": 
123}}::vertex
+ {"id": 844424930131970, "label": "Part", "properties": {"part_num": 
345}}::vertex
+ {"id": 844424930131971, "label": "Part", "properties": {"part_num": 
456}}::vertex
+ {"id": 844424930131972, "label": "Part", "properties": {"part_num": 
789}}::vertex
+(4 rows)
+
+SELECT * from cypher('issue_1988', $$
+    MATCH (p1:Part {part_num: 123}), (p2:Part {part_num: 345})
+    CREATE (p1)-[u:used_by { quantity: 1 }]->(p2) RETURN p1, u, p2 $$) as (p1 
agtype, u agtype, p2 agtype);
+                                        p1                                     
    |                                                                     u     
                                                                |               
                         p2                                         
+-----------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "Part", "properties": {"part_num": 
123}}::vertex | {"id": 1125899906842625, "label": "used_by", "end_id": 
844424930131970, "start_id": 844424930131969, "properties": {"quantity": 
1}}::edge | {"id": 844424930131970, "label": "Part", "properties": {"part_num": 
345}}::vertex
+(1 row)
+
+-- should fail
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.match = 'xyz' RETURN p $$) as (p 
agtype);
+ERROR:  syntax error at or near "="
+LINE 2:     MATCH (p:Part { part_num: 123 }) SET p.match = 'xyz' RET...
+                                                         ^
+-- should succeed
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`match` = 'xyz' RETURN p $$) as (p 
agtype);
+                                                 p                             
                    
+---------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "Part", "properties": {"match": "xyz", 
"part_num": 123}}::vertex
+(1 row)
+
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`set` = 'xyz' RETURN p $$) as (p 
agtype);
+                                                        p                      
                                  
+-----------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "Part", "properties": {"set": "xyz", 
"match": "xyz", "part_num": 123}}::vertex
+(1 row)
+
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`delete` = 'xyz' RETURN p $$) as (p 
agtype);
+                                                                p              
                                                   
+----------------------------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "Part", "properties": {"set": "xyz", 
"match": "xyz", "delete": "xyz", "part_num": 123}}::vertex
+(1 row)
+
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`merge` = 'xyz' RETURN p $$) as (p 
agtype);
+                                                                        p      
                                                                   
+--------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "Part", "properties": {"set": "xyz", 
"match": "xyz", "merge": "xyz", "delete": "xyz", "part_num": 123}}::vertex
+(1 row)
+
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`create` = 'xyz' RETURN p $$) as (p 
agtype);
+                                                                               
  p                                                                             
    
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "Part", "properties": {"set": "xyz", 
"match": "xyz", "merge": "xyz", "create": "xyz", "delete": "xyz", "part_num": 
123}}::vertex
+(1 row)
+
+-- should succeed
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`match` = 'match' RETURN p $$) as 
(p agtype);
+                                                                               
   p                                                                            
      
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "Part", "properties": {"set": "xyz", 
"match": "match", "merge": "xyz", "create": "xyz", "delete": "xyz", "part_num": 
123}}::vertex
+(1 row)
+
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`set` = 'set' RETURN p $$) as (p 
agtype);
+                                                                               
   p                                                                            
      
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "Part", "properties": {"set": "set", 
"match": "match", "merge": "xyz", "create": "xyz", "delete": "xyz", "part_num": 
123}}::vertex
+(1 row)
+
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`delete` = 'delete' RETURN p $$) as 
(p agtype);
+                                                                               
    p                                                                           
         
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "Part", "properties": {"set": "set", 
"match": "match", "merge": "xyz", "create": "xyz", "delete": "delete", 
"part_num": 123}}::vertex
+(1 row)
+
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`merge` = 'merge' RETURN p $$) as 
(p agtype);
+                                                                               
     p                                                                          
           
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "Part", "properties": {"set": "set", 
"match": "match", "merge": "merge", "create": "xyz", "delete": "delete", 
"part_num": 123}}::vertex
+(1 row)
+
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`create` = 'create' RETURN p $$) as 
(p agtype);
+                                                                               
       p                                                                        
              
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "Part", "properties": {"set": "set", 
"match": "match", "merge": "merge", "create": "create", "delete": "delete", 
"part_num": 123}}::vertex
+(1 row)
+
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p) RETURN p $$) as (p agtype);
+                                                                               
       p                                                                        
              
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131970, "label": "Part", "properties": {"part_num": 
345}}::vertex
+ {"id": 844424930131971, "label": "Part", "properties": {"part_num": 
456}}::vertex
+ {"id": 844424930131972, "label": "Part", "properties": {"part_num": 
789}}::vertex
+ {"id": 844424930131969, "label": "Part", "properties": {"set": "set", 
"match": "match", "merge": "merge", "create": "create", "delete": "delete", 
"part_num": 123}}::vertex
+(4 rows)
+
 --
 -- Cleanup
 --
+SELECT * FROM drop_graph('issue_1988', true);
+NOTICE:  drop cascades to 4 other objects
+DETAIL:  drop cascades to table issue_1988._ag_label_vertex
+drop cascades to table issue_1988._ag_label_edge
+drop cascades to table issue_1988."Part"
+drop cascades to table issue_1988.used_by
+NOTICE:  graph "issue_1988" has been dropped
+ drop_graph 
+------------
+ 
+(1 row)
+
 SELECT * FROM drop_graph('issue_1953', true);
 NOTICE:  drop cascades to 2 other objects
 DETAIL:  drop cascades to table issue_1953._ag_label_vertex
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index 7985f45c..4f1f1af0 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -3445,9 +3445,56 @@ SELECT * FROM cypher('issue_1953', $$ RETURN 
is_valid_label_name('issue_1953')[{
 SELECT * FROM cypher('issue_1953', $$ RETURN 
is_valid_label_name('issue_1953')[0] $$) AS (result agtype);
 SELECT * FROM cypher('issue_1953', $$ RETURN 
is_valid_label_name('issue_1953')[0..1] $$) AS (result agtype);
 
+--
+-- Issue 1988: How to update a property which is a keyword.
+--
+SELECT * FROM create_graph('issue_1988');
+SELECT * from cypher('issue_1988', $$
+    CREATE (p1:Part {part_num: 123}),
+           (p2:Part {part_num: 345}),
+           (p3:Part {part_num: 456}),
+           (p4:Part {part_num: 789}) $$) as (a agtype);
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p) RETURN p $$) as (p agtype);
+
+SELECT * from cypher('issue_1988', $$
+    MATCH (p1:Part {part_num: 123}), (p2:Part {part_num: 345})
+    CREATE (p1)-[u:used_by { quantity: 1 }]->(p2) RETURN p1, u, p2 $$) as (p1 
agtype, u agtype, p2 agtype);
+
+-- should fail
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.match = 'xyz' RETURN p $$) as (p 
agtype);
+
+-- should succeed
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`match` = 'xyz' RETURN p $$) as (p 
agtype);
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`set` = 'xyz' RETURN p $$) as (p 
agtype);
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`delete` = 'xyz' RETURN p $$) as (p 
agtype);
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`merge` = 'xyz' RETURN p $$) as (p 
agtype);
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`create` = 'xyz' RETURN p $$) as (p 
agtype);
+-- should succeed
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`match` = 'match' RETURN p $$) as 
(p agtype);
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`set` = 'set' RETURN p $$) as (p 
agtype);
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`delete` = 'delete' RETURN p $$) as 
(p agtype);
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`merge` = 'merge' RETURN p $$) as 
(p agtype);
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p:Part { part_num: 123 }) SET p.`create` = 'create' RETURN p $$) as 
(p agtype);
+
+SELECT * FROM cypher('issue_1988', $$
+    MATCH (p) RETURN p $$) as (p agtype);
+
 --
 -- Cleanup
 --
+SELECT * FROM drop_graph('issue_1988', true);
 SELECT * FROM drop_graph('issue_1953', true);
 SELECT * FROM drop_graph('expanded_map', true);
 SELECT * FROM drop_graph('issue_1124', true);
diff --git a/src/backend/parser/ag_scanner.l b/src/backend/parser/ag_scanner.l
index 256e662d..35eb37ac 100644
--- a/src/backend/parser/ag_scanner.l
+++ b/src/backend/parser/ag_scanner.l
@@ -628,7 +628,7 @@ ag_token token;
                         scan_errposition()));
     }
 
-    token.type = AG_TOKEN_IDENTIFIER;
+    token.type = AG_TOKEN_BQIDENT;
     token.value.s = strbuf_get_str(&yyextra.literal_buf);
     token.location = get_location();
     return token;
diff --git a/src/backend/parser/cypher_gram.y b/src/backend/parser/cypher_gram.y
index b990b820..11e2f392 100644
--- a/src/backend/parser/cypher_gram.y
+++ b/src/backend/parser/cypher_gram.y
@@ -66,6 +66,8 @@
 
 %token <string> IDENTIFIER
 %token <string> PARAMETER
+%token <string> BQIDENT
+%token <character> CHAR
 
 /* operators that have more than 1 character */
 %token NOT_EQ LT_EQ GT_EQ DOT_DOT TYPECAST PLUS_EQ EQ_TILDE CONCAT
@@ -656,7 +658,7 @@ subquery_stmt_no_return:
 single_subquery:
     subquery_part_init reading_clause_list return
         {
-            $$ = list_concat($1, lappend($2, $3));  
+            $$ = list_concat($1, lappend($2, $3));
         }
     ;
 
@@ -3281,7 +3283,7 @@ static Node *verify_rule_as_list_comprehension(Node 
*expr, Node *expr2,
                                                int where_loc, int mapping_loc)
 {
     Node *result = NULL;
-    
+
     /*
      * If the first expression is a ColumnRef, then we can build a
      * list_comprehension node.
@@ -3360,4 +3362,4 @@ static Node *build_list_comprehension_node(ColumnRef 
*cref, Node *expr,
 
     /* return the UNWIND node */
     return (Node *)unwind;
-}
\ No newline at end of file
+}
diff --git a/src/backend/parser/cypher_parser.c 
b/src/backend/parser/cypher_parser.c
index 4ec56fb9..d2b64ffe 100644
--- a/src/backend/parser/cypher_parser.c
+++ b/src/backend/parser/cypher_parser.c
@@ -50,7 +50,9 @@ int cypher_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, ag_scanner_t 
scanner)
         ACCESS_PATH,
         ANY_EXISTS,
         ALL_EXISTS,
-        CONCAT
+        CONCAT,
+        CHAR,
+        BQIDENT
     };
 
     ag_token token;
@@ -93,6 +95,18 @@ int cypher_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, 
ag_scanner_t scanner)
         lvalp->string = ident;
         break;
     }
+    case AG_TOKEN_BQIDENT:
+    {
+        char *ident;
+
+        /* these are identifiers, just back ticked */
+        token.type = AG_TOKEN_IDENTIFIER;
+
+        ident = pstrdup(token.value.s);
+        truncate_identifier(ident, strlen(ident), true);
+        lvalp->string = ident;
+        break;
+    }
     case AG_TOKEN_PARAMETER:
         lvalp->string = pstrdup(token.value.s);
         break;
diff --git a/src/include/parser/ag_scanner.h b/src/include/parser/ag_scanner.h
index 7351b89b..3dd89abd 100644
--- a/src/include/parser/ag_scanner.h
+++ b/src/include/parser/ag_scanner.h
@@ -53,6 +53,7 @@ typedef enum ag_token_type
     AG_TOKEN_ALL_EXISTS,
     AG_TOKEN_CONCAT,
     AG_TOKEN_CHAR,
+    AG_TOKEN_BQIDENT
 } ag_token_type;
 
 /*

Reply via email to