Fix ecpg's mishandling of B'...' and X'...' literals.
authorTom Lane
Sat, 7 Nov 2020 20:03:44 +0000 (15:03 -0500)
committerTom Lane
Sat, 7 Nov 2020 20:03:44 +0000 (15:03 -0500)
These were broken in multiple ways:

* The xbstart and xhstart lexer actions neglected to set
"state_before_str_start" before transitioning to the xb/xh states,
thus possibly resulting in "internal error: unreachable state" later.

* The test for valid string contents at the end of xb state was flat out
wrong, as it accounted incorrectly for the "b" prefix that the xbstart
action had injected.  Meanwhile, the xh state had no such check at all.

* The generated literal value failed to include any quote marks.

* The grammar did the wrong thing anyway, typically ignoring the
literal value and emitting something else, since BCONST and XCONST
tokens were handled randomly differently from SCONST tokens.

The first of these problems is evidently an oversight in commit
7f380c59f, but the others seem to be very ancient.  The lack of
complaints shows that ECPG users aren't using these syntaxes much
(although I do vaguely remember one previous complaint).

As written, this patch is dependent on 7f380c59f, so it can't go
back further than v13.  Given the shortage of complaints, I'm not
excited about adapting the patch to prior branches.

Report and patch by Shenhao Wang (test case adjusted by me)

Discussion: https://postgr.es/m/d6402f1bacb74ecba22ef715dbba17fd@G08CNEXMBPEKD06.g08.fujitsu.local

src/interfaces/ecpg/preproc/ecpg.trailer
src/interfaces/ecpg/preproc/ecpg.type
src/interfaces/ecpg/preproc/parse.pl
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

index 0dbdfdc1223c4fb305483b2a4b0b14997590338f..769265b900b4abeb90018234deb323d4cfaa3897 100644 (file)
@@ -1715,13 +1715,13 @@ cvariable:  CVARIABLE
 
 ecpg_param:    PARAM       { $$ = make_name(); } ;
 
-ecpg_bconst:   BCONST      { $$ = make_name(); } ;
+ecpg_bconst:   BCONST      { $$ = $1; } ;
 
 ecpg_fconst:   FCONST      { $$ = make_name(); } ;
 
 ecpg_sconst:   SCONST      { $$ = $1; } ;
 
-ecpg_xconst:   XCONST      { $$ = make_name(); } ;
+ecpg_xconst:   XCONST      { $$ = $1; } ;
 
 ecpg_ident:    IDENT       { $$ = $1; }
        | CSTRING   { $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\"")); }
index ffafa82af9c1e5a4f46478bccf08c7a3aa6717df..eca298bdb80a9585c86b62ef8d53cccc02b81801 100644 (file)
 %type  CSTRING
 %type  CPP_LINE
 %type  CVARIABLE
+%type  BCONST
 %type  SCONST
+%type  XCONST
 %type  IDENT
 
 %type   s_struct_union_symbol
index 1a76b2d326b4271f02c24227bcf78871fbcee3b9..52ba7dfa0cdcc6b7ee317decf986a4c5f633a802 100644 (file)
@@ -38,6 +38,7 @@ my %replace_token = (
    'BCONST' => 'ecpg_bconst',
    'FCONST' => 'ecpg_fconst',
    'Sconst' => 'ecpg_sconst',
+   'XCONST' => 'ecpg_xconst',
    'IDENT'  => 'ecpg_ident',
    'PARAM'  => 'ecpg_param',);
 
index 466bbac6a7b01406862876b4d20dd069e5e9033c..a6ef1ac115e338df30b2d75a35e971f30437a7fd 100644 (file)
@@ -505,9 +505,9 @@ cppline         {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
 {
 {xbstart}      {
                    token_start = yytext;
+                   state_before_str_start = YYSTATE;
                    BEGIN(xb);
                    startlit();
-                   addlitchar('b');
                }
 } /*  */
 
@@ -519,9 +519,9 @@ cppline         {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
 
 {xhstart} {
                    token_start = yytext;
+                   state_before_str_start = YYSTATE;
                    BEGIN(xh);
                    startlit();
-                   addlitchar('x');
                }
 <>        { mmfatal(PARSE_ERROR, "unterminated hexadecimal string literal"); }
 
@@ -597,12 +597,14 @@ cppline           {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                    switch (state_before_str_stop)
                    {
                        case xb:
-                           if (literalbuf[strspn(literalbuf, "01") + 1] != '\0')
+                           if (literalbuf[strspn(literalbuf, "01")] != '\0')
                                mmerror(PARSE_ERROR, ET_ERROR, "invalid bit string literal");
-                           base_yylval.str = mm_strdup(literalbuf);
+                           base_yylval.str = psprintf("b'%s'", literalbuf);
                            return BCONST;
                        case xh:
-                           base_yylval.str = mm_strdup(literalbuf);
+                           if (literalbuf[strspn(literalbuf, "0123456789abcdefABCDEF")] != '\0')
+                               mmerror(PARSE_ERROR, ET_ERROR, "invalid hex string literal");
+                           base_yylval.str = psprintf("x'%s'", literalbuf);
                            return XCONST;
                        case xq:
                            /* fallthrough */
index e695007b1336e1588818a6dbbac4980a9ab54f72..f64ffbc3c2511c2e078a11d0a8aa7ec6bd50e5cc 100644 (file)
@@ -63,8 +63,18 @@ int main(void)
 
   printf("%s %s %s %s %s %s\n", s1, s2, s3, s4, s5, s6);
 
+  { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select b'0010' , x'019ABcd'", 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), 
+   ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}
+#line 26 "strings.pgc"
+
+
+  printf("%s %s\n", s1, s2);
+
   { ECPGdisconnect(__LINE__, "CURRENT");}
-#line 25 "strings.pgc"
+#line 30 "strings.pgc"
 
   return 0;
 }
index dbc9e5c0b8dba1d5964b3ec3e79f3e5b4750b3fc..607f8bc832620edfbcd7bd9f33a552441da6bd17 100644 (file)
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_get_data on line 15: RESULT: abc$def offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 25: query: select b'0010' , x'019ABcd'; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 25: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 25: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 25: RESULT: 0010 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 25: RESULT: 0000000110011010101111001101 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection ecpg1_regression closed
 [NO_PID]: sqlca: code: 0, state: 00000
index 730d72dd64e1330300cc0076e095acde656a09aa..3d4dd79e3d274dc59cab054e36a6a35949bbf01d 100644 (file)
@@ -1 +1,2 @@
 abcdef abcdef abc\bdef data data abc$def
+0010 0000000110011010101111001101
index f004ddf6dc1e1c28183c73dbd67ffa808948f6a5..3666c3e865bd419b8c9abf1bab45fcda575fb3c5 100644 (file)
@@ -22,6 +22,11 @@ int main(void)
 
   printf("%s %s %s %s %s %s\n", s1, s2, s3, s4, s5, s6);
 
+  exec sql select b'0010', X'019ABcd'
+                  into :s1, :s2;
+
+  printf("%s %s\n", s1, s2);
+
   exec sql disconnect;
   return 0;
 }