diff --git a/src/interfaces/ecpg/preproc/ecpg.addons b/src/interfaces/ecpg/preproc/ecpg.addons index ca3efad..219203f 100644 --- a/src/interfaces/ecpg/preproc/ecpg.addons +++ b/src/interfaces/ecpg/preproc/ecpg.addons @@ -1,22 +1,6 @@ /* src/interfaces/ecpg/preproc/ecpg.addons */ ECPG: stmtClosePortalStmt block - { - if (INFORMIX_MODE) - { - if (pg_strcasecmp($1+strlen("close "), "database") == 0) - { - if (connection) - mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in CLOSE DATABASE statement"); - - fprintf(base_yyout, "{ ECPGdisconnect(__LINE__, \"CURRENT\");"); - whenever_action(2); - free($1); - break; - } - } - - output_statement($1, 0, ECPGst_normal); - } + { $$ = $1;} ECPG: stmtDeallocateStmt block { output_deallocate_prepare_statement($1); @@ -25,7 +9,11 @@ ECPG: stmtDeclareCursorStmt block { output_simple_statement($1); } ECPG: stmtDiscardStmt block ECPG: stmtFetchStmt block - { output_statement($1, 1, ECPGst_normal); } + { + /* g_cursor_name is set in the fetch_args block and freed in the output_cursor_statement */ + output_cursor_statement(ECPGcst_fetch, g_cursor_name, NULL, $1, 1, ECPGst_normal); + g_cursor_name = NULL; + } ECPG: stmtDeleteStmt block ECPG: stmtInsertStmt block ECPG: stmtSelectStmt block @@ -63,6 +51,10 @@ ECPG: stmtViewStmt rule whenever_action(2); free($1); } + | ECPGDeclareStmt + { + output_declare_statement($1); + } | ECPGCursorStmt { output_simple_statement($1); @@ -132,7 +124,14 @@ ECPG: stmtViewStmt rule if ((ptr = add_additional_variables($1, true)) != NULL) { connection = ptr->connection ? mm_strdup(ptr->connection) : NULL; - output_statement(mm_strdup(ptr->command), 0, ECPGst_normal); + if (ptr->prepared_name) + { + output_cursor_statement(ECPGcst_open, $1, mm_strdup(ptr->prepared_name), mm_strdup(ptr->command), 0, ECPGst_normal); + } + else + { + output_cursor_statement(ECPGcst_open, $1, NULL, mm_strdup(ptr->command), 0, ECPGst_normal); + } ptr->opened = true; } } @@ -204,6 +203,7 @@ ECPG: var_valueNumericOnly addon } ECPG: fetch_argscursor_name addon add_additional_variables($1, false); + g_cursor_name = mm_strdup($1); if ($1[0] == ':') { free($1); @@ -211,6 +211,7 @@ ECPG: fetch_argscursor_name addon } ECPG: fetch_argsfrom_incursor_name addon add_additional_variables($2, false); + g_cursor_name = mm_strdup($2); if ($2[0] == ':') { free($2); @@ -222,6 +223,7 @@ ECPG: fetch_argsFIRST_Popt_from_incursor_name addon ECPG: fetch_argsLAST_Popt_from_incursor_name addon ECPG: fetch_argsALLopt_from_incursor_name addon add_additional_variables($3, false); + g_cursor_name = mm_strdup($3); if ($3[0] == ':') { free($3); @@ -229,6 +231,7 @@ ECPG: fetch_argsALLopt_from_incursor_name addon } ECPG: fetch_argsSignedIconstopt_from_incursor_name addon add_additional_variables($3, false); + g_cursor_name = mm_strdup($3); if ($3[0] == ':') { free($3); @@ -242,6 +245,7 @@ ECPG: fetch_argsSignedIconstopt_from_incursor_name addon ECPG: fetch_argsFORWARDALLopt_from_incursor_name addon ECPG: fetch_argsBACKWARDALLopt_from_incursor_name addon add_additional_variables($4, false); + g_cursor_name = mm_strdup($4); if ($4[0] == ':') { free($4); @@ -252,6 +256,7 @@ ECPG: fetch_argsRELATIVE_PSignedIconstopt_from_incursor_name addon ECPG: fetch_argsFORWARDSignedIconstopt_from_incursor_name addon ECPG: fetch_argsBACKWARDSignedIconstopt_from_incursor_name addon add_additional_variables($4, false); + g_cursor_name = mm_strdup($4); if ($4[0] == ':') { free($4); @@ -304,6 +309,7 @@ ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectSt } this = (struct cursor *) mm_alloc(sizeof(struct cursor)); + memset(this, 0 ,sizeof(struct cursor)); this->next = cur; this->name = $2; @@ -336,8 +342,31 @@ ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectSt } ECPG: ClosePortalStmtCLOSEcursor_name block { + char *stmt = NULL; + char *cursor_name = NULL; char *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : $2; - $$ = cat2_str(mm_strdup("close"), cursor_marker); + + cursor_name = mm_strdup($2); + + stmt = cat2_str(mm_strdup("close"), cursor_marker); + + if (INFORMIX_MODE) + { + if (pg_strcasecmp(stmt+strlen("close "), "database") == 0) + { + if (connection) + mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in CLOSE DATABASE statement"); + + fprintf(base_yyout, "{ ECPGdisconnect(__LINE__, \"CURRENT\");"); + whenever_action(2); + free(stmt); + break; + } + } + + output_cursor_statement(ECPGcst_close, cursor_name, NULL, stmt, 0, ECPGst_normal); + + $$ = EMPTY; } ECPG: opt_hold block { @@ -416,48 +445,56 @@ ECPG: FetchStmtMOVEfetch_args rule { char *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3; add_additional_variables($3, false); + g_cursor_name = mm_strdup($3); $$ = cat_str(2, mm_strdup("fetch forward"), cursor_marker); } | FETCH FORWARD from_in cursor_name opt_ecpg_fetch_into { char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4; add_additional_variables($4, false); + g_cursor_name = mm_strdup($4); $$ = cat_str(2, mm_strdup("fetch forward from"), cursor_marker); } | FETCH BACKWARD cursor_name opt_ecpg_fetch_into { char *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3; add_additional_variables($3, false); + g_cursor_name = mm_strdup($3); $$ = cat_str(2, mm_strdup("fetch backward"), cursor_marker); } | FETCH BACKWARD from_in cursor_name opt_ecpg_fetch_into { char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4; add_additional_variables($4, false); + g_cursor_name = mm_strdup($4); $$ = cat_str(2, mm_strdup("fetch backward from"), cursor_marker); } | MOVE FORWARD cursor_name { char *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3; add_additional_variables($3, false); + g_cursor_name = mm_strdup($3); $$ = cat_str(2, mm_strdup("move forward"), cursor_marker); } | MOVE FORWARD from_in cursor_name { char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4; add_additional_variables($4, false); + g_cursor_name = mm_strdup($4); $$ = cat_str(2, mm_strdup("move forward from"), cursor_marker); } | MOVE BACKWARD cursor_name { char *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3; add_additional_variables($3, false); + g_cursor_name = mm_strdup($3); $$ = cat_str(2, mm_strdup("move backward"), cursor_marker); } | MOVE BACKWARD from_in cursor_name { char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4; add_additional_variables($4, false); + g_cursor_name = mm_strdup($4); $$ = cat_str(2, mm_strdup("move backward from"), cursor_marker); } ECPG: limit_clauseLIMITselect_limit_value','select_offset_value block diff --git a/src/interfaces/ecpg/preproc/ecpg.c b/src/interfaces/ecpg/preproc/ecpg.c index aaecbf81..c2396db 100644 --- a/src/interfaces/ecpg/preproc/ecpg.c +++ b/src/interfaces/ecpg/preproc/ecpg.c @@ -28,6 +28,7 @@ struct _include_path *include_paths = NULL; struct cursor *cur = NULL; struct typedefs *types = NULL; struct _defines *defines = NULL; +struct declared_name_st *g_declared_list = NULL; static void help(const char *progname) @@ -111,6 +112,48 @@ add_preprocessor_define(char *define) defines->next = pd; } +static void +free_argument(struct arguments *arg) +{ + if (arg == NULL) + return; + + free_argument(arg->next); + + /* + * Don't free variables in it because the original codes don't free it either + * variables are static structures instead of allocating + */ + free(arg); +} + +static void +free_cursor(struct cursor *c) +{ + if (c == NULL) + return; + + free_cursor(c->next); + free_argument(c->argsinsert); + free_argument(c->argsresult); + + free(c->name); + free(c->function); + free(c->command); + free(c->prepared_name); + free(c); +} + +static void +free_declared_stmt(struct declared_name_st *st) +{ + if (st == NULL) + return; + + free_declared_stmt(st->next); + free(st); +} + #define ECPG_GETOPT_LONG_REGRESSION 1 int main(int argc, char *const argv[]) @@ -343,29 +386,18 @@ main(int argc, char *const argv[]) struct typedefs *typeptr; /* remove old cursor definitions if any are still there */ - for (ptr = cur; ptr != NULL;) + if (cur) { - struct cursor *this = ptr; - struct arguments *l1, - *l2; - - free(ptr->command); - free(ptr->connection); - free(ptr->name); - for (l1 = ptr->argsinsert; l1; l1 = l2) - { - l2 = l1->next; - free(l1); - } - for (l1 = ptr->argsresult; l1; l1 = l2) - { - l2 = l1->next; - free(l1); - } - ptr = ptr->next; - free(this); + free_cursor(cur); + cur = NULL; + } + + /* remove old declared statements if any are still there */ + if (g_declared_list) + { + free_declared_stmt(g_declared_list); + g_declared_list = NULL; } - cur = NULL; /* remove non-pertinent old defines as well */ while (defines && !defines->pertinent) @@ -479,6 +511,18 @@ main(int argc, char *const argv[]) free(input_filename); } + + if(g_declared_list) + { + free_declared_stmt(g_declared_list); + g_declared_list = NULL; + } + + if(cur) + { + free_cursor(cur); + cur = NULL; + } } return ret_value; } diff --git a/src/interfaces/ecpg/preproc/ecpg.header b/src/interfaces/ecpg/preproc/ecpg.header index 2562366..0cebf56 100644 --- a/src/interfaces/ecpg/preproc/ecpg.header +++ b/src/interfaces/ecpg/preproc/ecpg.header @@ -38,6 +38,7 @@ char *current_function; int ecpg_internal_var = 0; char *connection = NULL; char *input_filename = NULL; +char *g_cursor_name = NULL; static int FoundInto = 0; static int initializer = 0; diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer index 1c10879..af15b88 100644 --- a/src/interfaces/ecpg/preproc/ecpg.trailer +++ b/src/interfaces/ecpg/preproc/ecpg.trailer @@ -284,6 +284,38 @@ prepared_name: name ; /* + * Declare Statement + */ +ECPGDeclareStmt: DECLARE prepared_name STATEMENT + { + struct declared_name_st *ptr = NULL; + + /* Check whether the declared name has been defined or not */ + for (ptr = g_declared_list; ptr != NULL; ptr = ptr->next) + { + if (strcmp($2, ptr->name) == 0) + { + /* re-definition is a bug */ + mmerror(PARSE_ERROR, ET_ERROR, "declared name %s is already defined", ptr->name); + } + } + + /* Add a new declared name into the g_declared_list */ + ptr = NULL; + ptr = (struct declared_name_st *) mm_alloc(sizeof(struct declared_name_st)); + if (ptr) + { + /* initial definition */ + ptr->name = $2; + ptr->next = g_declared_list; + g_declared_list = ptr; + } + + $$ = mm_strdup($2); + } +; + +/* * Declare a prepared cursor. The syntax is different from the standard * declare statement, so we create a new rule. */ @@ -309,6 +341,7 @@ ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared } this = (struct cursor *) mm_alloc(sizeof(struct cursor)); + memset(this, 0 ,sizeof(struct cursor)); /* initial definition */ this->next = cur; @@ -318,6 +351,7 @@ ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared this->command = cat_str(6, mm_strdup("declare"), cursor_marker, $3, mm_strdup("cursor"), $5, mm_strdup("for $1")); this->argsresult = NULL; this->argsresult_oos = NULL; + this->prepared_name = $7; thisquery->type = &ecpg_query; thisquery->brace_level = 0; diff --git a/src/interfaces/ecpg/preproc/ecpg.type b/src/interfaces/ecpg/preproc/ecpg.type index ac6aa00..978be84 100644 --- a/src/interfaces/ecpg/preproc/ecpg.type +++ b/src/interfaces/ecpg/preproc/ecpg.type @@ -9,6 +9,7 @@ %type ECPGDeallocateDescr %type ECPGDeclaration %type ECPGDeclare +%type ECPGDeclareStmt %type ECPGDescribe %type ECPGDisconnect %type ECPGExecuteImmediateStmt diff --git a/src/interfaces/ecpg/preproc/extern.h b/src/interfaces/ecpg/preproc/extern.h index 0ce3a6e..dcfdc06 100644 --- a/src/interfaces/ecpg/preproc/extern.h +++ b/src/interfaces/ecpg/preproc/extern.h @@ -50,6 +50,7 @@ extern struct _include_path *include_paths; extern struct cursor *cur; extern struct typedefs *types; extern struct _defines *defines; +extern struct declared_name_st *g_declared_list; extern struct ECPGtype ecpg_no_indicator; extern struct variable no_indicator; extern struct arguments *argsinsert; @@ -72,6 +73,8 @@ extern void output_statement(char *, int, enum ECPG_statement_type); extern void output_prepare_statement(char *, char *); extern void output_deallocate_prepare_statement(char *); extern void output_simple_statement(char *); +extern void output_declare_statement(char *); +extern void output_cursor_statement(int , char *, char *, char *, int , enum ECPG_statement_type); extern char *hashline_number(void); extern int base_yyparse(void); extern int base_yylex(void); diff --git a/src/interfaces/ecpg/preproc/output.c b/src/interfaces/ecpg/preproc/output.c index 59d5d30..cac8bc5 100644 --- a/src/interfaces/ecpg/preproc/output.c +++ b/src/interfaces/ecpg/preproc/output.c @@ -5,6 +5,7 @@ #include "extern.h" static void output_escaped_str(char *cmd, bool quoted); +static void output_cursor_name(char *str); void output_line_number(void) @@ -195,7 +196,16 @@ static void output_escaped_str(char *str, bool quoted) { int i = 0; - int len = strlen(str); + int len = 0; + + if (str == NULL) + { + fputs("NULL", base_yyout); + + return; + } + + len = strlen(str); if (quoted && str[0] == '"' && str[len - 1] == '"') /* do not escape quotes * at beginning and end @@ -244,3 +254,148 @@ output_escaped_str(char *str, bool quoted) if (quoted && str[0] == '"' && str[len] == '"') fputs("\"", base_yyout); } + +/* + * This is a tool function used by the output_cursor_statement function to print + * cursor name after the string such as "ECPGopen(","ECPGfetch(","ECPGclose(". + * This function filters escaped sequences such as \t, \n, \r to print cursor name cleanly + * / +static void +output_cursor_name(char *str) +{ + int i = 0; + int len = 0; + + if (str == NULL) + { + fputs("NULL", base_yyout); + + return; + } + + len = strlen(str); + fputs("\"", base_yyout); + if (str[0] == '\"' && str[len - 1] == '\"') + { + i = 1; + len--; + fputs("\\\"", base_yyout); + + /* output this char by char as we have to filter " and \n */ + for (; i < len; i++) + { + if (str[i] == '"') + fputs("\\\"", base_yyout); + else if (str[i] == '\n') + fputs("\\\n", base_yyout); + else if (str[i] == '\\') + { + int j = i; + + /* + * check whether this is a continuation line if it is, do not + * output anything because newlines are escaped anyway + */ + + /* accept blanks after the '\' as some other compilers do too */ + do + { + j++; + } while (str[j] == ' ' || str[j] == '\t'); + + if ((str[j] != '\n') && (str[j] != '\r' || str[j + 1] != '\n')) /* not followed by a + * newline */ + fputs("\\\\",base_yyout); + } + else if (str[i] == '\r' && str[i + 1] == '\n') + { + fputs("\\\r\n", base_yyout); + i++; + } + else + fputc(str[i], base_yyout); + } + + fputs("\\\"", base_yyout); + } + else + fputs(str, base_yyout); + + fputs("\"", base_yyout); +} + +/* + * Transform the EXEC SQL DECLARE STATEMENT into ECPGdeclare function + */ +void +output_declare_statement(char *name) +{ + /* connection is set in "at:" token in ecpg.trailer file */ + fprintf(base_yyout, "{ ECPGdeclare(__LINE__, %s, ", connection ? connection : "NULL"); + output_escaped_str(name, true); + fputs(");", base_yyout); + + whenever_action(2); + free(name); + if (connection != NULL) + free(connection); +} + +/* + * Transform the EXEC SQL CURSOR STATEMENT such as OPEN/FETCH/CLOSE cursor into + * ECPGopen/ECPGfetch/ECPGclose function + */ +void +output_cursor_statement(int cursor_stmt, char *cursor_name, char *prepared_name, char *stmt, int whenever_mode, enum ECPG_statement_type st) +{ + switch(cursor_stmt) + { + case ECPGcst_open: + fprintf(base_yyout, "{ ECPGopen("); + output_cursor_name(cursor_name); + fprintf(base_yyout, ", "); + output_escaped_str(prepared_name, true); + fprintf(base_yyout, ", __LINE__, %d, %d, %s, %d, ", + compat, force_indicator, connection ? connection : "NULL", questionmarks); + break; + case ECPGcst_fetch: + fprintf(base_yyout, "{ ECPGfetch("); + output_cursor_name(cursor_name); + fprintf(base_yyout, ", __LINE__, %d, %d, %s, %d, ", + compat, force_indicator, connection ? connection : "NULL", questionmarks); + break; + case ECPGcst_close: + fprintf(base_yyout, "{ ECPGclose("); + output_cursor_name(cursor_name); + fprintf(base_yyout, ", __LINE__, %d, %d, %s, %d, ", + compat, force_indicator, connection ? connection : "NULL", questionmarks); + break; + } + + if (st == ECPGst_execute || st == ECPGst_exec_immediate) + fprintf(base_yyout, "%s, %s, ", ecpg_statement_type_name[st], stmt); + else + { + if (st == ECPGst_prepnormal && auto_prepare) + fputs("ECPGst_prepnormal, \"", base_yyout); + else + fputs("ECPGst_normal, \"", base_yyout); + + output_escaped_str(stmt, false); + fputs("\", ", base_yyout); + } + + /* dump variables to C file */ + dump_variables(argsinsert, 1); + fputs("ECPGt_EOIT, ", base_yyout); + dump_variables(argsresult, 1); + fputs("ECPGt_EORT);", base_yyout); + reset_variables(); + + whenever_action(whenever_mode | 2); + free(cursor_name); + free(prepared_name); + free(stmt); + if (connection != NULL) + free(connection); +} diff --git a/src/interfaces/ecpg/preproc/type.h b/src/interfaces/ecpg/preproc/type.h index cd0d1da..1b6ddfb 100644 --- a/src/interfaces/ecpg/preproc/type.h +++ b/src/interfaces/ecpg/preproc/type.h @@ -129,6 +129,7 @@ struct cursor char *command; char *connection; bool opened; + char *prepared_name; struct arguments *argsinsert; struct arguments *argsinsert_oos; struct arguments *argsresult; @@ -136,6 +137,13 @@ struct cursor struct cursor *next; }; +/* structure to store declared name */ +struct declared_name_st +{ + char *name; /* declared name */ + struct declared_name_st *next; +}; + struct typedefs { char *name;