Avoid premature de-doubling of quote marks in ECPG strings.
authorTom Lane
Thu, 22 Oct 2020 21:34:32 +0000 (17:34 -0400)
committerTom Lane
Thu, 22 Oct 2020 22:29:46 +0000 (18:29 -0400)
If you write the literal 'abc''def' in an EXEC SQL command, that will
come out the other end as 'abc'def', triggering a syntax error in the
backend.  Likewise, "abc""def" is reduced to "abc"def" which is wrong
syntax for a quoted identifier.

The cause is that the lexer thinks it should emit just one quote
mark, whereas what it really should do is keep the string as-is.

Add some docs and test cases, too.

Although this seems clearly a bug, I fear users wouldn't appreciate
changing it in minor releases.  Some may well be working around it
by applying an extra doubling of affected quotes, as for example
sql/dyntest.pgc has been doing.

Per investigation of a report from 1250kv, although this isn't
exactly what he/she was on about.

Discussion: https://postgr.es/m/673825.1603223178@sss.pgh.pa.us

doc/src/sgml/ecpg.sgml
src/interfaces/ecpg/preproc/pgc.l
src/interfaces/ecpg/test/expected/preproc-strings.c
src/interfaces/ecpg/test/expected/preproc-strings.stderr
src/interfaces/ecpg/test/expected/preproc-strings.stdout
src/interfaces/ecpg/test/preproc/strings.pgc
src/interfaces/ecpg/test/sql/dyntest.pgc

index 6e3ca788f6ecdf6eda1cc949ead227cf75a574e6..419574e9ea68db38df2e90f14d8b4e315f419c72 100644 (file)
@@ -31,7 +31,7 @@
    specially marked sections.  To build the program, the source code (*.pgc)
    is first passed through the embedded SQL preprocessor, which converts it
    to an ordinary C program (*.c), and afterwards it can be processed by a C
-   compiler.  (For details about the compiling and linking see ).
+   compiler.  (For details about the compiling and linking see .)
    Converted ECPG applications call functions in the libpq library
    through the embedded SQL library (ecpglib), and communicate with
    the PostgreSQL server using the normal frontend-backend protocol.
@@ -63,11 +63,22 @@ EXEC SQL ...;
 
    These statements syntactically take the place of a C statement.
    Depending on the particular statement, they can appear at the
-   global level or within a function.  Embedded
+   global level or within a function.
+  
+
+  
+   Embedded
    SQL statements follow the case-sensitivity rules of
    normal SQL code, and not those of C. Also they allow nested
-   C-style comments that are part of the SQL standard. The C part of the
+   C-style comments as per the SQL standard. The C part of the
    program, however, follows the C standard of not accepting nested comments.
+   Embedded SQL statements likewise use SQL rules, not
+   C rules, for parsing quoted strings and identifiers.
+   (See  and
+    respectively.  Note that
+   ECPG assumes that standard_conforming_strings
+   is on.)
+   Of course, the C part of the program follows C quoting rules.
   
 
   
index 466bbac6a7b01406862876b4d20dd069e5e9033c..e98aa6c48628ef76fa8b5b8b0ed81cac13ca8646 100644 (file)
@@ -623,11 +623,8 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                    }
                }
 
-{xqdouble}   { addlitchar('\''); }
-{xqcquote}    {
-                   addlitchar('\\');
-                   addlitchar('\'');
-               }
+{xqdouble}   { addlit(yytext, yyleng); }
+{xqcquote}                { addlit(yytext, yyleng); }
 {xqinside}  { addlit(yytext, yyleng); }
 {xeinside}  {
                    addlit(yytext, yyleng);
@@ -736,7 +733,7 @@ cppline         {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                    return UIDENT;
                }
 {xddouble} {
-                   addlitchar('"');
+                   addlit(yytext, yyleng);
                }
 {xdinside} {
                    addlit(yytext, yyleng);
index e695007b1336e1588818a6dbbac4980a9ab54f72..1e50cd36c3820fe5af673b99554b2b1e01b7f695 100644 (file)
@@ -45,7 +45,7 @@ int main(void)
 #line 13 "strings.pgc"
 
 
-  { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select 'abcdef' , N'abcdef' as foo , E'abc\\bdef' as \"foo\" , U&'d\\0061t\\0061' as U&\"foo\" , U&'d!+000061t!+000061' UESCAPE '!' , $foo$abc$def$foo$", ECPGt_EOIT, 
+  { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select 'abc''d\\ef' , N'abc''d\\ef' as foo , E'abc''d\\\\ef' as \"foo\"\"bar\" , U&'d\\0061t\\0061' as U&\"foo\"\"bar\" , U&'d!+000061t!+000061' UESCAPE '!' , $foo$abc$def$foo$", ECPGt_EOIT, 
    ECPGt_char,&(s1),(long)0,(long)1,(1)*sizeof(char), 
    ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
    ECPGt_char,&(s2),(long)0,(long)1,(1)*sizeof(char), 
index dbc9e5c0b8dba1d5964b3ec3e79f3e5b4750b3fc..4c3a8eee5aad1228a913918abf793110be819ab3 100644 (file)
@@ -8,7 +8,7 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_process_output on line 13: OK: SET
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 15: query: select 'abcdef' , N'abcdef' as foo , E'abc\bdef' as "foo" , U&'d\0061t\0061' as U&"foo" , U&'d!+000061t!+000061' UESCAPE '!' , $foo$abc$def$foo$; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 15: query: select 'abc''d\ef' , N'abc''d\ef' as foo , E'abc''d\\ef' as "foo""bar" , U&'d\0061t\0061' as U&"foo""bar" , U&'d!+000061t!+000061' UESCAPE '!' , $foo$abc$def$foo$; with 0 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_execute on line 15: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_store_result on line 15: allocating memory for 1 tuples
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 15: RESULT: abcdef offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 15: RESULT: abc'd\ef offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_store_result on line 15: allocating memory for 1 tuples
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 15: RESULT: abcdef offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 15: RESULT: abc'd\ef offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_store_result on line 15: allocating memory for 1 tuples
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 15: RESULT: abc\bdef offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 15: RESULT: abc'd\ef offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_store_result on line 15: allocating memory for 1 tuples
 [NO_PID]: sqlca: code: 0, state: 00000
index 730d72dd64e1330300cc0076e095acde656a09aa..1456b152d78e972e74380b6ec892067031ef5904 100644 (file)
@@ -1 +1 @@
-abcdef abcdef abc\bdef data data abc$def
+abc'd\ef abc'd\ef abc'd\ef data data abc$def
index f004ddf6dc1e1c28183c73dbd67ffa808948f6a5..25157f136c2ae83d709da8b5f9916be92ea1ad55 100644 (file)
@@ -12,10 +12,10 @@ int main(void)
 
   exec sql set standard_conforming_strings to on;
 
-  exec sql select 'abcdef',
-                  N'abcdef' AS foo,
-                  E'abc\bdef' AS "foo",
-                  U&'d\0061t\0061' AS U&"foo",
+  exec sql select 'abc''d\ef',
+                  N'abc''d\ef' AS foo,
+                  E'abc''d\\ef' AS "foo""bar",
+                  U&'d\0061t\0061' AS U&"foo""bar",
                   U&'d!+000061t!+000061' uescape '!',
                   $foo$abc$def$foo$
                   into :s1, :s2, :s3, :s4, :s5, :s6;
index 5f02fd5dd6951120efc9849aa5b0a119094214cc..0222c89851547bbdbbe7f1fc931abfefd0bca5c6 100644 (file)
@@ -51,7 +51,7 @@ main ()
   exec sql create table dyntest (name char (14), d float8, i int,
                 bignumber int8, b boolean, comment text,
                 day date);
-  exec sql insert into dyntest values ('first entry', 14.7, 14, 123045607890, true, 'The world''''s most advanced open source database.', '1987-07-14');
+  exec sql insert into dyntest values ('first entry', 14.7, 14, 123045607890, true, 'The world''s most advanced open source database.', '1987-07-14');
   exec sql insert into dyntest values ('second entry', 1407.87, 1407, 987065403210, false, 'The elephant never forgets.', '1999-11-5');
 
   exec sql prepare MYQUERY from :QUERY;