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 a21120bf Support doubled-quote escaping in Cypher string literals 
(issue #2222) (#2342)
a21120bf is described below

commit a21120bf139b156661ef10374630e688c0cc46fb
Author: Greg Felice <[email protected]>
AuthorDate: Mon Mar 2 11:27:04 2026 -0500

    Support doubled-quote escaping in Cypher string literals (issue #2222) 
(#2342)
    
    SQL drivers (psycopg2, JDBC, etc.) escape single quotes by doubling
    them ('isn''t') when substituting parameters into queries. When these
    substitutions land inside Cypher's $$ block, the Cypher scanner rejects
    them because it only recognizes backslash escaping (\'). This makes it
    difficult to pass strings containing apostrophes through SQL drivers.
    
    Add '' and "" as escape sequences in the Cypher scanner, following the
    same pattern already used for backtick-quoted identifiers (``). Flex
    picks the longer two-character match over the one-character closing
    quote, so the change is backwards-compatible -- '' was previously a
    syntax error.
    
    Co-authored-by: Claude Opus 4.6 <[email protected]>
---
 regress/expected/scan.out       | 41 +++++++++++++++++++++++++++++++++++++++++
 regress/sql/scan.sql            | 21 +++++++++++++++++++++
 src/backend/parser/ag_scanner.l | 10 ++++++++++
 3 files changed, 72 insertions(+)

diff --git a/regress/expected/scan.out b/regress/expected/scan.out
index 46d5676d..fb7c626a 100644
--- a/regress/expected/scan.out
+++ b/regress/expected/scan.out
@@ -294,6 +294,47 @@ $$) AS t(a agtype, b agtype, c agtype);
  " \" \" ' ' " | " ' ' \" \" " | " / / \\ \b \f \n \r \t "
 (1 row)
 
+-- doubled-quote escape sequences for SQL driver compatibility (issue #2222)
+SELECT * FROM cypher('scan', $$
+RETURN 'isn''t'
+$$) AS t(a agtype);
+    a    
+---------
+ "isn't"
+(1 row)
+
+SELECT * FROM cypher('scan', $$
+RETURN '''hello'
+$$) AS t(a agtype);
+    a     
+----------
+ "'hello"
+(1 row)
+
+SELECT * FROM cypher('scan', $$
+RETURN 'hello'''
+$$) AS t(a agtype);
+    a     
+----------
+ "hello'"
+(1 row)
+
+SELECT * FROM cypher('scan', $$
+RETURN 'it''s a ''test'''
+$$) AS t(a agtype);
+        a        
+-----------------
+ "it's a 'test'"
+(1 row)
+
+SELECT * FROM cypher('scan', $$
+RETURN "she said ""hello"""
+$$) AS t(a agtype);
+          a           
+----------------------
+ "she said \"hello\""
+(1 row)
+
 -- invalid escape sequence
 SELECT * FROM cypher('scan', $$
 RETURN "\a"
diff --git a/regress/sql/scan.sql b/regress/sql/scan.sql
index 4d35fe0f..614447e3 100644
--- a/regress/sql/scan.sql
+++ b/regress/sql/scan.sql
@@ -202,6 +202,27 @@ SELECT * FROM cypher('scan', $$
 RETURN " \" \" ' \' ", ' \' \' " \" ', " / \/ \\ \b \f \n \r \t "
 $$) AS t(a agtype, b agtype, c agtype);
 
+-- doubled-quote escape sequences for SQL driver compatibility (issue #2222)
+SELECT * FROM cypher('scan', $$
+RETURN 'isn''t'
+$$) AS t(a agtype);
+
+SELECT * FROM cypher('scan', $$
+RETURN '''hello'
+$$) AS t(a agtype);
+
+SELECT * FROM cypher('scan', $$
+RETURN 'hello'''
+$$) AS t(a agtype);
+
+SELECT * FROM cypher('scan', $$
+RETURN 'it''s a ''test'''
+$$) AS t(a agtype);
+
+SELECT * FROM cypher('scan', $$
+RETURN "she said ""hello"""
+$$) AS t(a agtype);
+
 -- invalid escape sequence
 SELECT * FROM cypher('scan', $$
 RETURN "\a"
diff --git a/src/backend/parser/ag_scanner.l b/src/backend/parser/ag_scanner.l
index d5d72b92..c1e156c3 100644
--- a/src/backend/parser/ag_scanner.l
+++ b/src/backend/parser/ag_scanner.l
@@ -195,6 +195,8 @@ dquote        \"
 dqchars       [^"\\]+
 squote        '
 sqchars       [^'\\]+
+esdquote      {dquote}{dquote}
+essquote      {squote}{squote}
 esascii       \\["'/\\bfnrt]
 esasciifail   \\[^Uu]?
 esunicode     \\(U{hexdigit}{8}|u{hexdigit}{4})
@@ -420,6 +422,14 @@ ag_token token;
     strbuf_append_buf(&yyextra.literal_buf, yytext, yyleng);
 }
 
+<dqstr>{esdquote} {
+    strbuf_append_char(&yyextra.literal_buf, '"');
+}
+
+<sqstr>{essquote} {
+    strbuf_append_char(&yyextra.literal_buf, '\'');
+}
+
 <dqstr,sqstr>{esascii} {
     char c;
 

Reply via email to