diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml index 106ae0984e..1f9d35eeb3 100644 --- a/doc/src/sgml/ecpg.sgml +++ b/doc/src/sgml/ecpg.sgml @@ -5695,7 +5695,7 @@ EXEC SQL UPDATE Tbl SET col = MYNUMBER; - ifdef, ifndef, else, elif, and endif Directives + ifdef, ifndef, elif, else, and endif Directives You can use the following directives to compile code sections conditionally: @@ -5705,7 +5705,7 @@ EXEC SQL UPDATE Tbl SET col = MYNUMBER; Checks a name and processes subsequent lines if - name has been created with EXEC SQL define + name has been defined via EXEC SQL define name. @@ -5716,30 +5716,40 @@ EXEC SQL UPDATE Tbl SET col = MYNUMBER; Checks a name and processes subsequent lines if - name has not been created with + name has not been defined via EXEC SQL define name. - - EXEC SQL else; - - - Starts processing an alternative section to a section introduced by - either EXEC SQL ifdef name or - EXEC SQL ifndef name. - - - - EXEC SQL elif name; - Checks name and starts an alternative section if - name has been created with EXEC SQL define - name. + Begins an optional alternative section after an + EXEC SQL ifdef name or + EXEC SQL ifndef name + directive. Any number of elif sections can appear. + Lines following an elif will be processed + if name has been + defined and no previous section of the same + ifdef/ifndef...endif + construct has been processed. + + + + + + EXEC SQL else; + + + Begins an optional, final alternative section after an + EXEC SQL ifdef name or + EXEC SQL ifndef name + directive. Subsequent lines will be processed if no previous section + of the same + ifdef/ifndef...endif + construct has been processed. @@ -5748,7 +5758,9 @@ EXEC SQL UPDATE Tbl SET col = MYNUMBER; EXEC SQL endif; - Ends an alternative section. + Ends an + ifdef/ifndef...endif + construct. Subsequent lines are processed normally. @@ -5756,14 +5768,20 @@ EXEC SQL UPDATE Tbl SET col = MYNUMBER; - Example: + ifdef/ifndef...endif + constructs can be nested, up to 127 levels deep. + + + + This example will compile exactly one of the three SET + TIMEZONE commands: -EXEC SQL ifndef TZVAR; -EXEC SQL SET TIMEZONE TO 'GMT'; +EXEC SQL ifdef TZVAR; +EXEC SQL SET TIMEZONE TO TZVAR; EXEC SQL elif TZNAME; EXEC SQL SET TIMEZONE TO TZNAME; EXEC SQL else; -EXEC SQL SET TIMEZONE TO TZVAR; +EXEC SQL SET TIMEZONE TO 'GMT'; EXEC SQL endif; diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l index f6052798fd..466bbac6a7 100644 --- a/src/interfaces/ecpg/preproc/pgc.l +++ b/src/interfaces/ecpg/preproc/pgc.l @@ -79,13 +79,29 @@ struct _yy_buffer static char *old; +/* + * Vars for handling ifdef/elif/endif constructs. preproc_tos is the current + * nesting depth of such constructs, and stacked_if_value[preproc_tos] is the + * state for the innermost level. (For convenience, stacked_if_value[0] is + * initialized as though we are in the active branch of some outermost IF.) + * The active field is true if the current branch is active (being expanded). + * The saw_active field is true if we have found any successful branch, + * so that all subsequent branches of this level should be skipped. + * The else_branch field is true if we've found an 'else' (so that another + * 'else' or 'elif' at this level is an error.) + * For IFs nested within an inactive branch, all branches always have active + * set to false, but saw_active and else_branch are maintained normally. + * ifcond is valid only while evaluating an if-condition; it's true if we + * are doing ifdef, false if ifndef. + */ #define MAX_NESTED_IF 128 static short preproc_tos; -static short ifcond; +static bool ifcond; static struct _if_value { - short condition; - short else_branch; + bool active; + bool saw_active; + bool else_branch; } stacked_if_value[MAX_NESTED_IF]; %} @@ -1165,11 +1181,26 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+ return S_ANYTHING; } } -{exec_sql}{ifdef}{space}* { ifcond = true; BEGIN(xcond); } +{exec_sql}{ifdef}{space}* { + if (preproc_tos >= MAX_NESTED_IF-1) + mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions"); + preproc_tos++; + stacked_if_value[preproc_tos].active = false; + stacked_if_value[preproc_tos].saw_active = false; + stacked_if_value[preproc_tos].else_branch = false; + ifcond = true; + BEGIN(xcond); + } {informix_special}{ifdef}{space}* { /* are we simulating Informix? */ if (INFORMIX_MODE) { + if (preproc_tos >= MAX_NESTED_IF-1) + mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions"); + preproc_tos++; + stacked_if_value[preproc_tos].active = false; + stacked_if_value[preproc_tos].saw_active = false; + stacked_if_value[preproc_tos].else_branch = false; ifcond = true; BEGIN(xcond); } @@ -1179,11 +1210,26 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+ return S_ANYTHING; } } -{exec_sql}{ifndef}{space}* { ifcond = false; BEGIN(xcond); } +{exec_sql}{ifndef}{space}* { + if (preproc_tos >= MAX_NESTED_IF-1) + mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions"); + preproc_tos++; + stacked_if_value[preproc_tos].active = false; + stacked_if_value[preproc_tos].saw_active = false; + stacked_if_value[preproc_tos].else_branch = false; + ifcond = false; + BEGIN(xcond); + } {informix_special}{ifndef}{space}* { /* are we simulating Informix? */ if (INFORMIX_MODE) { + if (preproc_tos >= MAX_NESTED_IF-1) + mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions"); + preproc_tos++; + stacked_if_value[preproc_tos].active = false; + stacked_if_value[preproc_tos].saw_active = false; + stacked_if_value[preproc_tos].else_branch = false; ifcond = false; BEGIN(xcond); } @@ -1193,16 +1239,13 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+ return S_ANYTHING; } } -{exec_sql}{elif}{space}* { /* pop stack */ - if ( preproc_tos == 0 ) { +{exec_sql}{elif}{space}* { + if (preproc_tos == 0) mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\""); - } - else if ( stacked_if_value[preproc_tos].else_branch ) + if (stacked_if_value[preproc_tos].else_branch) mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\""); - else - preproc_tos--; - - ifcond = true; BEGIN(xcond); + ifcond = true; + BEGIN(xcond); } {informix_special}{elif}{space}* { /* are we simulating Informix? */ @@ -1210,11 +1253,8 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+ { if (preproc_tos == 0) mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\""); - else if (stacked_if_value[preproc_tos].else_branch) + if (stacked_if_value[preproc_tos].else_branch) mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\""); - else - preproc_tos--; - ifcond = true; BEGIN(xcond); } @@ -1226,16 +1266,19 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+ } {exec_sql}{else}{space}*";" { /* only exec sql endif pops the stack, so take care of duplicated 'else' */ - if (stacked_if_value[preproc_tos].else_branch) + if ( preproc_tos == 0 ) + mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\""); + else if (stacked_if_value[preproc_tos].else_branch) mmfatal(PARSE_ERROR, "more than one EXEC SQL ELSE"); else { stacked_if_value[preproc_tos].else_branch = true; - stacked_if_value[preproc_tos].condition = - (stacked_if_value[preproc_tos-1].condition && - !stacked_if_value[preproc_tos].condition); + stacked_if_value[preproc_tos].active = + (stacked_if_value[preproc_tos-1].active && + !stacked_if_value[preproc_tos].saw_active); + stacked_if_value[preproc_tos].saw_active = true; - if (stacked_if_value[preproc_tos].condition) + if (stacked_if_value[preproc_tos].active) BEGIN(C); else BEGIN(xskip); @@ -1245,16 +1288,19 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+ /* are we simulating Informix? */ if (INFORMIX_MODE) { - if (stacked_if_value[preproc_tos].else_branch) + if ( preproc_tos == 0 ) + mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\""); + else if (stacked_if_value[preproc_tos].else_branch) mmfatal(PARSE_ERROR, "more than one EXEC SQL ELSE"); else { stacked_if_value[preproc_tos].else_branch = true; - stacked_if_value[preproc_tos].condition = - (stacked_if_value[preproc_tos-1].condition && - !stacked_if_value[preproc_tos].condition); + stacked_if_value[preproc_tos].active = + (stacked_if_value[preproc_tos-1].active && + !stacked_if_value[preproc_tos].saw_active); + stacked_if_value[preproc_tos].saw_active = true; - if (stacked_if_value[preproc_tos].condition) + if (stacked_if_value[preproc_tos].active) BEGIN(C); else BEGIN(xskip); @@ -1272,7 +1318,7 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+ else preproc_tos--; - if (stacked_if_value[preproc_tos].condition) + if (stacked_if_value[preproc_tos].active) BEGIN(C); else BEGIN(xskip); @@ -1286,7 +1332,7 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+ else preproc_tos--; - if (stacked_if_value[preproc_tos].condition) + if (stacked_if_value[preproc_tos].active) BEGIN(C); else BEGIN(xskip); @@ -1301,12 +1347,10 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+ {other} { /* ignore */ } {identifier}{space}*";" { - if (preproc_tos >= MAX_NESTED_IF-1) - mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions"); - else { struct _defines *defptr; unsigned int i; + bool this_active; /* * Skip the ";" and trailing whitespace. Note that yytext @@ -1324,13 +1368,15 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+ defptr = defptr->next) /* skip */ ; - preproc_tos++; - stacked_if_value[preproc_tos].else_branch = false; - stacked_if_value[preproc_tos].condition = - (defptr ? ifcond : !ifcond) && stacked_if_value[preproc_tos-1].condition; + this_active = (defptr ? ifcond : !ifcond); + stacked_if_value[preproc_tos].active = + (stacked_if_value[preproc_tos-1].active && + !stacked_if_value[preproc_tos].saw_active && + this_active); + stacked_if_value[preproc_tos].saw_active |= this_active; } - if (stacked_if_value[preproc_tos].condition) + if (stacked_if_value[preproc_tos].active) BEGIN(C); else BEGIN(xskip); @@ -1442,10 +1488,12 @@ lex_init(void) parenths_open = 0; current_function = NULL; - preproc_tos = 0; yylineno = 1; - ifcond = true; - stacked_if_value[preproc_tos].condition = ifcond; + + /* initialize state for if/else/endif */ + preproc_tos = 0; + stacked_if_value[preproc_tos].active = true; + stacked_if_value[preproc_tos].saw_active = true; stacked_if_value[preproc_tos].else_branch = false; /* initialize literal buffer to a reasonable but expansible size */ diff --git a/src/interfaces/ecpg/test/expected/preproc-define.c b/src/interfaces/ecpg/test/expected/preproc-define.c index bde15b74a0..0256609b1f 100644 --- a/src/interfaces/ecpg/test/expected/preproc-define.c +++ b/src/interfaces/ecpg/test/expected/preproc-define.c @@ -40,73 +40,90 @@ main(void) { /* exec sql begin declare section */ + typedef char string [ 8 ]; -#line 21 "define.pgc" +#line 22 "define.pgc" - -#line 22 "define.pgc" + + + + + + + + + + + + + + + + + +#line 23 "define.pgc" intarray amount ; -#line 23 "define.pgc" +#line 24 "define.pgc" char name [ 6 ] [ 8 ] ; -#line 24 "define.pgc" +#line 37 "define.pgc" char letter [ 6 ] [ 1 ] ; #if 0 -#line 26 "define.pgc" +#line 39 "define.pgc" int not_used ; #endif /* exec sql end declare section */ -#line 29 "define.pgc" +#line 46 "define.pgc" int i,j; ECPGdebug(1, stderr); { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); -#line 34 "define.pgc" +#line 51 "define.pgc" if (sqlca.sqlcode < 0) sqlprint();} -#line 34 "define.pgc" +#line 51 "define.pgc" { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table test ( name char ( 8 ) , amount int , letter char ( 1 ) )", ECPGt_EOIT, ECPGt_EORT); -#line 36 "define.pgc" +#line 53 "define.pgc" if (sqlca.sqlcode < 0) sqlprint();} -#line 36 "define.pgc" +#line 53 "define.pgc" { ECPGtrans(__LINE__, NULL, "commit"); -#line 37 "define.pgc" +#line 54 "define.pgc" if (sqlca.sqlcode < 0) sqlprint();} -#line 37 "define.pgc" +#line 54 "define.pgc" { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into Test ( name , amount , letter ) values ( 'false' , 1 , 'f' )", ECPGt_EOIT, ECPGt_EORT); -#line 39 "define.pgc" +#line 56 "define.pgc" if (sqlca.sqlcode < 0) sqlprint();} -#line 39 "define.pgc" +#line 56 "define.pgc" { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test ( name , amount , letter ) values ( 'true' , 2 , 't' )", ECPGt_EOIT, ECPGt_EORT); -#line 40 "define.pgc" +#line 57 "define.pgc" if (sqlca.sqlcode < 0) sqlprint();} -#line 40 "define.pgc" +#line 57 "define.pgc" { ECPGtrans(__LINE__, NULL, "commit"); -#line 41 "define.pgc" +#line 58 "define.pgc" if (sqlca.sqlcode < 0) sqlprint();} -#line 41 "define.pgc" +#line 58 "define.pgc" { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select * from test", ECPGt_EOIT, @@ -116,10 +133,10 @@ if (sqlca.sqlcode < 0) sqlprint();} ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(letter),(long)1,(long)6,(1)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT); -#line 43 "define.pgc" +#line 60 "define.pgc" if (sqlca.sqlcode < 0) sqlprint();} -#line 43 "define.pgc" +#line 60 "define.pgc" for (i=0, j=sqlca.sqlerrd[2]; i port [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 36: query: create table test ( name char ( 8 ) , amount int , letter char ( 1 ) ); with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: ecpg_execute on line 53: query: create table test ( name char ( 8 ) , amount int , letter char ( 1 ) ); with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 36: using PQexec +[NO_PID]: ecpg_execute on line 53: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 36: OK: CREATE TABLE +[NO_PID]: ecpg_process_output on line 53: OK: CREATE TABLE [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ECPGtrans on line 37: action "commit"; connection "ecpg1_regression" +[NO_PID]: ECPGtrans on line 54: action "commit"; connection "ecpg1_regression" [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 39: query: insert into Test ( name , amount , letter ) values ( 'false' , 1 , 'f' ); with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: ecpg_execute on line 56: query: insert into Test ( name , amount , letter ) values ( 'false' , 1 , 'f' ); with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 39: using PQexec +[NO_PID]: ecpg_execute on line 56: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 39: OK: INSERT 0 1 +[NO_PID]: ecpg_process_output on line 56: OK: INSERT 0 1 [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 40: query: insert into test ( name , amount , letter ) values ( 'true' , 2 , 't' ); with 0 parameter(s) on connection ecpg1_regression -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 40: using PQexec -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 40: OK: INSERT 0 1 -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ECPGtrans on line 41: action "commit"; connection "ecpg1_regression" -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 43: query: select * from test; with 0 parameter(s) on connection ecpg1_regression -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 43: using PQexec -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 43: correctly got 2 tuples with 3 fields -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 43: RESULT: false offset: -1; array: no -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 43: RESULT: true offset: -1; array: no -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 43: RESULT: 1 offset: -1; array: no -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 43: RESULT: 2 offset: -1; array: no -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 43: RESULT: f offset: -1; array: no -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 43: RESULT: t offset: -1; array: no -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 57: query: drop table test; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: ecpg_execute on line 57: query: insert into test ( name , amount , letter ) values ( 'true' , 2 , 't' ); with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: ecpg_execute on line 57: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 57: OK: DROP TABLE +[NO_PID]: ecpg_process_output on line 57: OK: INSERT 0 1 [NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: ECPGtrans on line 58: action "commit"; connection "ecpg1_regression" [NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 60: query: select * from test; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 60: using PQexec +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_process_output on line 60: correctly got 2 tuples with 3 fields +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_get_data on line 60: RESULT: false offset: -1; array: no +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_get_data on line 60: RESULT: true offset: -1; array: no +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_get_data on line 60: RESULT: 1 offset: -1; array: no +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_get_data on line 60: RESULT: 2 offset: -1; array: no +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_get_data on line 60: RESULT: f offset: -1; array: no +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_get_data on line 60: RESULT: t offset: -1; array: no +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 74: 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 74: using PQexec +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_process_output on line 74: OK: DROP TABLE +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ECPGtrans on line 75: 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/preproc/define.pgc b/src/interfaces/ecpg/test/preproc/define.pgc index 0d07ebfe63..d360da7ece 100644 --- a/src/interfaces/ecpg/test/preproc/define.pgc +++ b/src/interfaces/ecpg/test/preproc/define.pgc @@ -17,15 +17,32 @@ int main(void) { exec sql begin declare section; + exec sql ifdef NAMELEN; typedef char string[NAMELEN]; intarray amount; char name[AMOUNT][NAMELEN]; +exec sql elif AMOUNT; + should not get here; +exec sql else; + should not get here either; +exec sql endif; + +exec sql ifndef NAMELEN; + should not get here; +exec sql elif AMOUNT; + exec sql ifdef NOSUCHNAME; + should not get here; + exec sql else; char letter[AMOUNT][1]; #if 0 int not_used; #endif + exec sql endif; +exec sql elif AMOUNT; + should not get here; exec sql endif; + exec sql end declare section; int i,j;