diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2020-11-07 15:03:44 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2020-11-07 15:03:44 -0500 |
commit | 1bccb159af5813b8f34fd177acdbdb2ad82cd596 (patch) | |
tree | ce331ebb5602de463a0d38986aeae5a28c73accb /src | |
parent | d94d37f8c0c77cf1b9c5ae924bb6cfc12f4bc692 (diff) | |
download | postgresql-1bccb159af5813b8f34fd177acdbdb2ad82cd596.tar.gz postgresql-1bccb159af5813b8f34fd177acdbdb2ad82cd596.zip |
Fix ecpg's mishandling of B'...' and X'...' literals.
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
Diffstat (limited to 'src')
-rw-r--r-- | src/interfaces/ecpg/preproc/ecpg.trailer | 4 | ||||
-rw-r--r-- | src/interfaces/ecpg/preproc/ecpg.type | 2 | ||||
-rw-r--r-- | src/interfaces/ecpg/preproc/parse.pl | 1 | ||||
-rw-r--r-- | src/interfaces/ecpg/preproc/pgc.l | 12 | ||||
-rw-r--r-- | src/interfaces/ecpg/test/expected/preproc-strings.c | 12 | ||||
-rw-r--r-- | src/interfaces/ecpg/test/expected/preproc-strings.stderr | 10 | ||||
-rw-r--r-- | src/interfaces/ecpg/test/expected/preproc-strings.stdout | 1 | ||||
-rw-r--r-- | src/interfaces/ecpg/test/preproc/strings.pgc | 5 |
8 files changed, 39 insertions, 8 deletions
diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer index 0dbdfdc1223..769265b900b 100644 --- a/src/interfaces/ecpg/preproc/ecpg.trailer +++ b/src/interfaces/ecpg/preproc/ecpg.trailer @@ -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("\"")); } diff --git a/src/interfaces/ecpg/preproc/ecpg.type b/src/interfaces/ecpg/preproc/ecpg.type index ffafa82af9c..eca298bdb80 100644 --- a/src/interfaces/ecpg/preproc/ecpg.type +++ b/src/interfaces/ecpg/preproc/ecpg.type @@ -122,7 +122,9 @@ %type <str> CSTRING %type <str> CPP_LINE %type <str> CVARIABLE +%type <str> BCONST %type <str> SCONST +%type <str> XCONST %type <str> IDENT %type <struct_union> s_struct_union_symbol diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl index 1a76b2d326b..52ba7dfa0cd 100644 --- a/src/interfaces/ecpg/preproc/parse.pl +++ b/src/interfaces/ecpg/preproc/parse.pl @@ -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',); diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l index 466bbac6a7b..a6ef1ac115e 100644 --- a/src/interfaces/ecpg/preproc/pgc.l +++ b/src/interfaces/ecpg/preproc/pgc.l @@ -505,9 +505,9 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+ <SQL>{ {xbstart} { token_start = yytext; + state_before_str_start = YYSTATE; BEGIN(xb); startlit(); - addlitchar('b'); } } /* <SQL> */ @@ -519,9 +519,9 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+ <SQL>{xhstart} { token_start = yytext; + state_before_str_start = YYSTATE; BEGIN(xh); startlit(); - addlitchar('x'); } <xh><<EOF>> { 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 */ diff --git a/src/interfaces/ecpg/test/expected/preproc-strings.c b/src/interfaces/ecpg/test/expected/preproc-strings.c index e695007b133..f64ffbc3c25 100644 --- a/src/interfaces/ecpg/test/expected/preproc-strings.c +++ b/src/interfaces/ecpg/test/expected/preproc-strings.c @@ -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; } diff --git a/src/interfaces/ecpg/test/expected/preproc-strings.stderr b/src/interfaces/ecpg/test/expected/preproc-strings.stderr index dbc9e5c0b8d..607f8bc8326 100644 --- a/src/interfaces/ecpg/test/expected/preproc-strings.stderr +++ b/src/interfaces/ecpg/test/expected/preproc-strings.stderr @@ -38,5 +38,15 @@ [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 diff --git a/src/interfaces/ecpg/test/expected/preproc-strings.stdout b/src/interfaces/ecpg/test/expected/preproc-strings.stdout index 730d72dd64e..3d4dd79e3d2 100644 --- a/src/interfaces/ecpg/test/expected/preproc-strings.stdout +++ b/src/interfaces/ecpg/test/expected/preproc-strings.stdout @@ -1 +1,2 @@ abcdef abcdef abcdef data data abc$def +0010 0000000110011010101111001101 diff --git a/src/interfaces/ecpg/test/preproc/strings.pgc b/src/interfaces/ecpg/test/preproc/strings.pgc index f004ddf6dc1..3666c3e865b 100644 --- a/src/interfaces/ecpg/test/preproc/strings.pgc +++ b/src/interfaces/ecpg/test/preproc/strings.pgc @@ -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; } |