errors when there is a bit literal in ecpg

Started by Wang, Shenhaoabout 5 years ago2 messages
#1Wang, Shenhao
wangsh.fnst@cn.fujitsu.com
2 attachment(s)

Hi, hacker

I met an error as below when I use ecpg

a.pgc:5: ERROR: invalid bit string literal
a.pgc:5: ERROR: internal error: unreachable state; please report this to <pgsql-bugs@lists.postgresql.org>

the test source is attached.

After investigating the code, I think the process of pgc.l is:
Step 1: <SQL>{xbstart}, addlitchar('b') is called, literalbuf contains a char 'b';
Step 2: <xb>{xbinside}, the rest of char is added in literalbuf
Step 3: <xqs>{other}, the condition literalbuf[strspn(literalbuf, "01") + 1] != '\0' will always be true;
error is occurred here

I try to fix this bug by deleting 'addlitchar('b');' from source I also add a test case to test all const str in ecpg.

The patch is also attached.

Best regards, Shenhao Wang

Attachments:

0001-Fix-error-when-handle-bit-string.patchapplication/octet-stream; name=0001-Fix-error-when-handle-bit-string.patchDownload
From 8736ca4925b83433fc723333b7a11f98e306e4cc Mon Sep 17 00:00:00 2001
From: Shenhao Wang <wangsh.fnst@cn.fujitsu.com>
Date: Fri, 6 Nov 2020 11:08:35 +0800
Subject: [PATCH] Fix error when handle bit string


diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer
index 6ccc8ab916..14ad27c359 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 ffafa82af9..eca298bdb8 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 1a76b2d326..52ba7dfa0c 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 91d8b63578..1aebac89cd 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/ecpg_schedule b/src/interfaces/ecpg/test/ecpg_schedule
index 1e67d2b162..68016e32de 100644
--- a/src/interfaces/ecpg/test/ecpg_schedule
+++ b/src/interfaces/ecpg/test/ecpg_schedule
@@ -34,6 +34,7 @@ test: sql/array
 test: sql/binary
 test: sql/bytea
 test: sql/code100
+test: sql/const
 test: sql/copystdout
 test: sql/createtableas
 test: sql/define
diff --git a/src/interfaces/ecpg/test/expected/sql-const.c b/src/interfaces/ecpg/test/expected/sql-const.c
new file mode 100644
index 0000000000..d2591d3d52
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-const.c
@@ -0,0 +1,193 @@
+/* Processed by ecpg (regression mode) */
+/* These include files are added by the preprocessor */
+#include <ecpglib.h>
+#include <ecpgerrno.h>
+#include <sqlca.h>
+/* End of automatic include section */
+#define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))
+
+#line 1 "const.pgc"
+#include <stdio.h>
+
+/* exec sql whenever sqlerror  sqlprint ; */
+#line 3 "const.pgc"
+
+
+
+#line 1 "sqlca.h"
+#ifndef POSTGRES_SQLCA_H
+#define POSTGRES_SQLCA_H
+
+#ifndef PGDLLIMPORT
+#if  defined(WIN32) || defined(__CYGWIN__)
+#define PGDLLIMPORT __declspec (dllimport)
+#else
+#define PGDLLIMPORT
+#endif							/* __CYGWIN__ */
+#endif							/* PGDLLIMPORT */
+
+#define SQLERRMC_LEN	150
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+struct sqlca_t
+{
+	char		sqlcaid[8];
+	long		sqlabc;
+	long		sqlcode;
+	struct
+	{
+		int			sqlerrml;
+		char		sqlerrmc[SQLERRMC_LEN];
+	}			sqlerrm;
+	char		sqlerrp[8];
+	long		sqlerrd[6];
+	/* Element 0: empty						*/
+	/* 1: OID of processed tuple if applicable			*/
+	/* 2: number of rows processed				*/
+	/* after an INSERT, UPDATE or				*/
+	/* DELETE statement					*/
+	/* 3: empty						*/
+	/* 4: empty						*/
+	/* 5: empty						*/
+	char		sqlwarn[8];
+	/* Element 0: set to 'W' if at least one other is 'W'	*/
+	/* 1: if 'W' at least one character string		*/
+	/* value was truncated when it was			*/
+	/* stored into a host variable.             */
+
+	/*
+	 * 2: if 'W' a (hopefully) non-fatal notice occurred
+	 */	/* 3: empty */
+	/* 4: empty						*/
+	/* 5: empty						*/
+	/* 6: empty						*/
+	/* 7: empty						*/
+
+	char		sqlstate[5];
+};
+
+struct sqlca_t *ECPGget_sqlca(void);
+
+#ifndef POSTGRES_ECPG_INTERNAL
+#define sqlca (*ECPGget_sqlca())
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+#line 5 "const.pgc"
+
+
+#line 1 "regression.h"
+
+
+
+
+
+
+#line 6 "const.pgc"
+
+
+int
+main (void)
+{
+/* exec sql begin declare section */
+	 
+	 
+
+#line 12 "const.pgc"
+ int b , hb ;
+ 
+#line 13 "const.pgc"
+ char s [ 100 ] , es [ 100 ] , ns [ 100 ] , us [ 100 ] ;
+/* exec sql end declare section */
+#line 14 "const.pgc"
+
+
+	ECPGdebug(1, stderr);
+
+	{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); 
+#line 18 "const.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 18 "const.pgc"
+
+
+	{ ECPGtrans(__LINE__, NULL, "begin work");
+#line 20 "const.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 20 "const.pgc"
+
+
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table test ( b bit ( 4 ) , hb bit ( 12 ) , s text , es text , ns text , us text )", ECPGt_EOIT, ECPGt_EORT);
+#line 22 "const.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 22 "const.pgc"
+
+
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( b'1010' , x'9aE' , 'test' , E'test\\\\ab' , N'test' , U&'\\0047bc' )", ECPGt_EOIT, ECPGt_EORT);
+#line 25 "const.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 25 "const.pgc"
+
+
+	{ ECPGtrans(__LINE__, NULL, "commit");
+#line 27 "const.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 27 "const.pgc"
+
+
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select b :: int , hb :: int , s , es , ns , us from test limit 1", ECPGt_EOIT, 
+	ECPGt_int,&(b),(long)1,(long)1,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
+	ECPGt_int,&(hb),(long)1,(long)1,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
+	ECPGt_char,(s),(long)100,(long)1,(100)*sizeof(char), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
+	ECPGt_char,(es),(long)100,(long)1,(100)*sizeof(char), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
+	ECPGt_char,(ns),(long)100,(long)1,(100)*sizeof(char), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
+	ECPGt_char,(us),(long)100,(long)1,(100)*sizeof(char), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 30 "const.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 30 "const.pgc"
+
+
+	printf("b=%d, hb=%d, s=%s, es=%s, ns=%s, us=%s\n",
+		b, hb, s, es, ns, us);
+
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table test", ECPGt_EOIT, ECPGt_EORT);
+#line 35 "const.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 35 "const.pgc"
+
+	{ ECPGtrans(__LINE__, NULL, "commit");
+#line 36 "const.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 36 "const.pgc"
+
+	{ ECPGdisconnect(__LINE__, "CURRENT");
+#line 37 "const.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 37 "const.pgc"
+
+
+	return 0;
+}
diff --git a/src/interfaces/ecpg/test/expected/sql-const.stderr b/src/interfaces/ecpg/test/expected/sql-const.stderr
new file mode 100644
index 0000000000..8b85c7dfb7
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-const.stderr
@@ -0,0 +1,48 @@
+[NO_PID]: ECPGdebug: set to 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGconnect: opening database ecpg1_regression on <DEFAULT> port <DEFAULT>  
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGtrans on line 20: action "begin work"; connection "ecpg1_regression"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 22: query: create table test ( b bit ( 4 ) , hb bit ( 12 ) , s text , es text , ns text , us text ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 22: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 22: OK: CREATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 24: query: insert into test values ( b'1010' , x'9aE' , 'test' , E'test\\ab' , N'test' , U&'\0047bc' ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 24: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 24: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGtrans on line 27: action "commit"; connection "ecpg1_regression"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 29: query: select b :: int , hb :: int , s , es , ns , us from test limit 1; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 29: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 29: correctly got 1 tuples with 6 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 29: RESULT: 10 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 29: RESULT: 2478 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 29: RESULT: test offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 29: RESULT: test\ab offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 29: RESULT: test offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 29: RESULT: Gbc offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 35: query: drop table test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 35: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 35: OK: DROP TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGtrans on line 36: action "commit"; connection "ecpg1_regression"
+[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/sql-const.stdout b/src/interfaces/ecpg/test/expected/sql-const.stdout
new file mode 100644
index 0000000000..85d542ded7
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-const.stdout
@@ -0,0 +1 @@
+b=10, hb=2478, s=test, es=test\ab, ns=test, us=Gbc
diff --git a/src/interfaces/ecpg/test/sql/.gitignore b/src/interfaces/ecpg/test/sql/.gitignore
index 613bdebc96..9715b27a8b 100644
--- a/src/interfaces/ecpg/test/sql/.gitignore
+++ b/src/interfaces/ecpg/test/sql/.gitignore
@@ -6,6 +6,8 @@
 /bytea.c
 /code100
 /code100.c
+/const
+/const.c
 /copystdout
 /copystdout.c
 /createtableas
diff --git a/src/interfaces/ecpg/test/sql/Makefile b/src/interfaces/ecpg/test/sql/Makefile
index 170bcd72c4..0e6cfdd164 100644
--- a/src/interfaces/ecpg/test/sql/Makefile
+++ b/src/interfaces/ecpg/test/sql/Makefile
@@ -6,7 +6,8 @@ include $(top_srcdir)/$(subdir)/../Makefile.regress
 TESTS = array array.c \
 	binary binary.c \
 	code100 code100.c \
-        copystdout copystdout.c \
+	const const.c \
+	copystdout copystdout.c \
 	createtableas createtableas.c \
 	define define.c \
         desc desc.c \
diff --git a/src/interfaces/ecpg/test/sql/const.pgc b/src/interfaces/ecpg/test/sql/const.pgc
new file mode 100644
index 0000000000..c16ee11dc4
--- /dev/null
+++ b/src/interfaces/ecpg/test/sql/const.pgc
@@ -0,0 +1,40 @@
+#include <stdio.h>
+
+exec sql whenever sqlerror sqlprint;
+
+exec sql include sqlca;
+exec sql include ../regression;
+
+int
+main (void)
+{
+EXEC SQL BEGIN DECLARE SECTION;
+	int b,hb;
+	char s[100],es[100],ns[100],us[100];
+EXEC SQL END DECLARE SECTION;
+
+	ECPGdebug(1, stderr);
+
+	EXEC SQL CONNECT TO REGRESSDB1;
+
+	EXEC SQL BEGIN WORK;
+
+	EXEC SQL CREATE TABLE test(b bit(4), hb bit(12), s text, es text, ns text, us text);
+
+	EXEC SQL INSERT INTO test VALUES(b'1010', x'9aE', 'test', e'test\\ab',
+			n'test', u&'\0047bc');
+
+	EXEC SQL COMMIT;
+
+	EXEC SQL SELECT b::int, hb::int, s, es, ns, us INTO
+		:b, :hb, :s, :es, :ns, :us FROM test LIMIT 1;
+
+	printf("b=%d, hb=%d, s=%s, es=%s, ns=%s, us=%s\n",
+		b, hb, s, es, ns, us);
+
+	EXEC SQL DROP TABLE test;
+	EXEC SQL COMMIT;
+	EXEC SQL DISCONNECT;
+
+	return 0;
+}
-- 
2.26.2

a.pgcapplication/octet-stream; name=a.pgcDownload
#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Wang, Shenhao (#1)
Re: errors when there is a bit literal in ecpg

"Wang, Shenhao" <wangsh.fnst@cn.fujitsu.com> writes:

I met an error as below when I use ecpg

a.pgc:5: ERROR: invalid bit string literal
a.pgc:5: ERROR: internal error: unreachable state; please report this to <pgsql-bugs@lists.postgresql.org>

Indeed. This has apparently been broken for a very long time (though
the "unreachable state" part is fairly new).

I try to fix this bug by deleting 'addlitchar('b');' from source I also add a test case to test all const str in ecpg.

I thought that a whole new test case was overkill when we could just add a
couple of lines to an existing test. Other than that, looks good, pushed.

regards, tom lane