diff --git a/src/interfaces/ecpg/ChangeLog b/src/interfaces/ecpg/ChangeLog index 06e465ff9cf..939c3bdec41 100644 --- a/src/interfaces/ecpg/ChangeLog +++ b/src/interfaces/ecpg/ChangeLog @@ -1225,6 +1225,12 @@ Wed Mar 6 10:40:28 CET 2002 Sun Mar 10 13:08:22 CET 2002 - Fixed two bugs in define command in lexer. + +Thu Mar 21 08:25:08 CET 2002 + + - Applied patch by Nicolas Bazin for improved + typedef handling. + - Added option '-c' to automatically create C typedef from SQL one. - Set ecpg version to 2.10.0. - Set library version to 3.4.0. diff --git a/src/interfaces/ecpg/preproc/c_keywords.c b/src/interfaces/ecpg/preproc/c_keywords.c index 2dd941f6182..7ce7e376c1b 100644 --- a/src/interfaces/ecpg/preproc/c_keywords.c +++ b/src/interfaces/ecpg/preproc/c_keywords.c @@ -36,6 +36,7 @@ static ScanKeyword ScanKeywords[] = { {"signed", SQL_SIGNED}, {"static", S_STATIC}, {"struct", SQL_STRUCT}, + {"typedef", S_TYPEDEF}, {"union", UNION}, {"unsigned", SQL_UNSIGNED}, {"varchar", VARCHAR}, diff --git a/src/interfaces/ecpg/preproc/ecpg.c b/src/interfaces/ecpg/preproc/ecpg.c index f097089f3b1..968218a3290 100644 --- a/src/interfaces/ecpg/preproc/ecpg.c +++ b/src/interfaces/ecpg/preproc/ecpg.c @@ -1,4 +1,4 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/ecpg.c,v 1.53 2002/01/10 10:42:54 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/ecpg.c,v 1.54 2002/03/21 09:42:50 meskes Exp $ */ /* New main for ecpg, the PostgreSQL embedded SQL precompiler. */ /* (C) Michael Meskes Feb 5th, 1998 */ @@ -17,7 +17,8 @@ extern char *optarg; #include "extern.h" int ret_value = 0, - autocommit = 0; + autocommit = false; + auto_create_c = false; struct _include_path *include_paths = NULL; struct cursor *cur = NULL; struct typedefs *types = NULL; @@ -31,11 +32,11 @@ help(const char *progname) /* printf is a macro some places; don't #ifdef inside its arguments */ #ifdef YYDEBUG printf("Usage:\n" - " %s [-d] [-I DIRECTORY] [-o OUTFILE] [-t] file1 [file2...]\n\n", + " %s [-d] [-I DIRECTORY] [-o OUTFILE] [-t] [-c] [-D symbol] file1 [file2...]\n\n", progname); #else printf("Usage:\n" - " %s [-I DIRECTORY] [-o OUTFILE] [-t] file1 [file2...]\n\n", + " %s [-I DIRECTORY] [-o OUTFILE] [-t] [-c] [-D symbol] file1 [file2...]\n\n", progname); #endif printf("Options:\n"); @@ -45,6 +46,8 @@ help(const char *progname) printf(" -I DIRECTORY search DIRECTORY for include files\n"); printf(" -o OUTFILE write result to OUTFILE\n"); printf(" -t turn on autocommit of transactions\n"); + printf(" -c automatically generate C code from embedded SQL code\n currently this works for EXEC SQL TYPE\n"); + printf(" -D symbol define symbo\n"); printf("\nIf no output file is specified, the name is formed by adding .c\n" "to the input file name, after stripping off .pgc if present.\n"); printf("\nReport bugs to .\n"); @@ -58,6 +61,7 @@ add_include_path(char *path) include_paths = mm_alloc(sizeof(struct _include_path)); include_paths->path = path; include_paths->next = ip; + } static void @@ -107,7 +111,7 @@ main(int argc, char *const argv[]) add_include_path("/usr/local/include"); add_include_path("."); - while ((c = getopt(argc, argv, "vo:I:tD:d")) != -1) + while ((c = getopt(argc, argv, "vco:I:tD:d")) != -1) { switch (c) { @@ -122,13 +126,15 @@ main(int argc, char *const argv[]) add_include_path(optarg); break; case 't': - autocommit = 1; + autocommit = true; break; case 'v': verbose = true; break; + case 'c': + auto_create_c = true; + break; case 'D': - /* XXX not documented */ add_preprocessor_define(optarg); break; case 'd': diff --git a/src/interfaces/ecpg/preproc/extern.h b/src/interfaces/ecpg/preproc/extern.h index 358a141025e..9759db21a2c 100644 --- a/src/interfaces/ecpg/preproc/extern.h +++ b/src/interfaces/ecpg/preproc/extern.h @@ -11,6 +11,7 @@ extern int braces_open, autocommit, + auto_create_c, ret_value, struct_level; extern char *descriptor_index; diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y index 7da47877cf8..f0140bc7a33 100644 --- a/src/interfaces/ecpg/preproc/preproc.y +++ b/src/interfaces/ecpg/preproc/preproc.y @@ -169,6 +169,7 @@ make_name(void) S_DOTPOINT S_EQUAL S_EXTERN S_INC S_LSHIFT S_MEMPOINT S_MEMBER S_MOD S_MUL S_NEQUAL S_OR S_REGISTER S_RSHIFT S_STATIC S_SUB S_VOLATILE + S_TYPEDEF /* I need this and don't know where it is defined inside the backend */ %token TYPECAST @@ -354,12 +355,13 @@ make_name(void) %type stmt ECPGRelease execstring server_name %type connection_object opt_server opt_port c_stuff c_stuff_item %type user_name opt_user char_variable ora_user ident opt_reference -%type quoted_ident_stringvar +%type quoted_ident_stringvar var_type_declarations %type db_prefix server opt_options opt_connection_name c_list %type ECPGSetConnection cpp_line ECPGTypedef c_args ECPGKeywords %type enum_type civar civarind ECPGCursorStmt ECPGDeallocate %type ECPGFree ECPGDeclare ECPGVar opt_at enum_definition -%type struct_type s_struct declaration declarations variable_declarations +%type struct_type s_struct vt_declarations variable_declarations +%type var_declaration type_declaration %type s_union union_type ECPGSetAutocommit on_off %type ECPGAllocateDescr ECPGDeallocateDescr symbol opt_symbol %type ECPGGetDescriptorHeader ECPGColLabel @@ -418,7 +420,7 @@ stmt: AlterDatabaseSetStmt { output_statement($1, 0, connection); } | AlterUserSetStmt { output_statement($1, 0, connection); } | ClosePortalStmt { output_statement($1, 0, connection); } | CommentStmt { output_statement($1, 0, connection); } - | CopyStmt { output_statement($1, 0, connection); } + | CopyStmt { output_statement($1, 0, connection); } | CreateStmt { output_statement($1, 0, connection); } | CreateAsStmt { output_statement($1, 0, connection); } | CreateSchemaStmt { output_statement($1, 0, connection); } @@ -429,7 +431,7 @@ stmt: AlterDatabaseSetStmt { output_statement($1, 0, connection); } | CreateUserStmt { output_statement($1, 0, connection); } | ClusterStmt { output_statement($1, 0, connection); } | DefineStmt { output_statement($1, 0, connection); } - | DropStmt { output_statement($1, 0, connection); } + | DropStmt { output_statement($1, 0, connection); } | DropSchemaStmt { output_statement($1, 0, connection); } | TruncateStmt { output_statement($1, 0, connection); } | DropGroupStmt { output_statement($1, 0, connection); } @@ -437,12 +439,12 @@ stmt: AlterDatabaseSetStmt { output_statement($1, 0, connection); } | DropTrigStmt { output_statement($1, 0, connection); } | DropUserStmt { output_statement($1, 0, connection); } | ExplainStmt { output_statement($1, 0, connection); } - | FetchStmt { output_statement($1, 1, connection); } - | GrantStmt { output_statement($1, 0, connection); } - | IndexStmt { output_statement($1, 0, connection); } + | FetchStmt { output_statement($1, 1, connection); } + | GrantStmt { output_statement($1, 0, connection); } + | IndexStmt { output_statement($1, 0, connection); } | ListenStmt { output_statement($1, 0, connection); } | UnlistenStmt { output_statement($1, 0, connection); } - | LockStmt { output_statement($1, 0, connection); } + | LockStmt { output_statement($1, 0, connection); } | NotifyStmt { output_statement($1, 0, connection); } | ProcedureStmt { output_statement($1, 0, connection); } | ReindexStmt { output_statement($1, 0, connection); } @@ -458,23 +460,23 @@ stmt: AlterDatabaseSetStmt { output_statement($1, 0, connection); } else output_statement($1, 1, connection); } - | RuleStmt { output_statement($1, 0, connection); } + | RuleStmt { output_statement($1, 0, connection); } | TransactionStmt { fprintf(yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", $1); whenever_action(2); free($1); } - | ViewStmt { output_statement($1, 0, connection); } - | LoadStmt { output_statement($1, 0, connection); } + | ViewStmt { output_statement($1, 0, connection); } + | LoadStmt { output_statement($1, 0, connection); } | CreatedbStmt { output_statement($1, 0, connection); } | DropdbStmt { output_statement($1, 0, connection); } | VacuumStmt { output_statement($1, 0, connection); } | AnalyzeStmt { output_statement($1, 0, connection); } | VariableSetStmt { output_statement($1, 0, connection); } | VariableShowStmt { output_statement($1, 0, connection); } - | VariableResetStmt { output_statement($1, 0, connection); } - | ConstraintsSetStmt { output_statement($1, 0, connection); } + | VariableResetStmt { output_statement($1, 0, connection); } + | ConstraintsSetStmt { output_statement($1, 0, connection); } | CheckPointStmt { output_statement($1, 0, connection); } | ECPGAllocateDescr { @@ -605,7 +607,9 @@ stmt: AlterDatabaseSetStmt { output_statement($1, 0, connection); } if (connection) mmerror(PARSE_ERROR, ET_ERROR, "no at option for typedef statement.\n"); - output_simple_statement($1); + fprintf(yyout, "%s", $1); + free($1); + output_line_number(); } | ECPGVar { @@ -3666,7 +3670,7 @@ ECPGDeallocate: SQL_DEALLOCATE SQL_PREPARE ident */ ECPGDeclaration: sql_startdeclare { fputs("/* exec sql begin declare section */", yyout); } - variable_declarations sql_enddeclare + var_type_declarations sql_enddeclare { fprintf(yyout, "%s/* exec sql end declare section */", $3); free($3); @@ -3678,15 +3682,82 @@ sql_startdeclare: ecpgstart BEGIN_TRANS DECLARE SQL_SECTION ';' {}; sql_enddeclare: ecpgstart END_TRANS DECLARE SQL_SECTION ';' {}; -variable_declarations: /*EMPTY*/ { $$ = EMPTY; } - | declarations { $$ = $1; } +var_type_declarations: /*EMPTY*/ { $$ = EMPTY; } + | vt_declarations { $$ = $1; } ; -declarations: declaration { $$ = $1; } - | declarations declaration { $$ = cat2_str($1, $2); } +vt_declarations: var_declaration { $$ = $1; } + | type_declaration { $$ = $1; } + | vt_declarations var_declaration { $$ = cat2_str($1, $2); } + | vt_declarations type_declaration { $$ = cat2_str($1, $2); } ; -declaration: storage_clause storage_modifier +variable_declarations: var_declaration { $$ = $1; } + | variable_declarations var_declaration { $$ = cat2_str($1, $2); } + ; + +type_declaration: S_TYPEDEF + { + /* reset this variable so we see if there was */ + /* an initializer specified */ + initializer = 0; + } + type opt_pointer ECPGColLabel opt_type_array_bounds ';' + { + /* add entry to list */ + struct typedefs *ptr, *this; + int dimension = $6.index1; + int length = $6.index2; + + if (($3.type_enum == ECPGt_struct || + $3.type_enum == ECPGt_union) && + initializer == 1) + { + mmerror(PARSE_ERROR, ET_ERROR, "Initializer not allowed in typedef command"); + + } + else + { + for (ptr = types; ptr != NULL; ptr = ptr->next) + { + if (strcmp($5, ptr->name) == 0) + { + /* re-definition is a bug */ + sprintf(errortext, "Type %s already defined", $5); + mmerror(PARSE_ERROR, ET_ERROR, errortext); + } + } + + adjust_array($3.type_enum, &dimension, &length, $3.type_dimension, $3.type_index, *$4?1:0); + + this = (struct typedefs *) mm_alloc(sizeof(struct typedefs)); + + /* initial definition */ + this->next = types; + this->name = $5; + this->type = (struct this_type *) mm_alloc(sizeof(struct this_type)); + this->type->type_enum = $3.type_enum; + this->type->type_str = mm_strdup($5); + this->type->type_dimension = dimension; /* dimension of array */ + this->type->type_index = length; /* lenght of string */ + this->struct_member_list = ($3.type_enum == ECPGt_struct || $3.type_enum == ECPGt_union) ? + struct_member_list[struct_level] : NULL; + + if ($3.type_enum != ECPGt_varchar && + $3.type_enum != ECPGt_char && + $3.type_enum != ECPGt_unsigned_char && + this->type->type_index >= 0) + mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types"); + + types = this; + } + + fprintf(yyout, "typedef %s %s %s %s;\n", $3.type_str, *$4?"*":"", $5, $6.str); + output_line_number(); + $$ = make_str(""); + }; + +var_declaration: storage_clause storage_modifier { actual_storage[struct_level] = cat2_str(mm_strdup($1), mm_strdup($2)); actual_startline[struct_level] = hashline_number(); @@ -4239,7 +4310,7 @@ ECPGTypedef: TYPE_P if (($5.type_enum == ECPGt_struct || $5.type_enum == ECPGt_union) && initializer == 1) - mmerror(PARSE_ERROR, ET_ERROR, "Initializer not allowed in EXEC SQL VAR command"); + mmerror(PARSE_ERROR, ET_ERROR, "Initializer not allowed in EXEC SQL TYPE command"); else { for (ptr = types; ptr != NULL; ptr = ptr->next) @@ -4276,7 +4347,10 @@ ECPGTypedef: TYPE_P types = this; } - $$ = cat_str(7, make_str("/* exec sql type"), mm_strdup($3), make_str("is"), mm_strdup($5.type_str), mm_strdup($6.str), $7, make_str("*/")); + if (auto_create_c == false) + $$ = cat_str(7, make_str("/* exec sql type"), mm_strdup($3), make_str("is"), mm_strdup($5.type_str), mm_strdup($6.str), $7, make_str("*/")); + else + $$ = cat_str(6, make_str("typedef "), mm_strdup($5.type_str), *$7?make_str("*"):make_str(""), mm_strdup($6.str), mm_strdup($3), make_str(";")); } ; @@ -4504,10 +4578,10 @@ ECPGKeywords: SQL_BREAK { $$ = make_str("break"); } ; /* additional keywords that can be SQL type names (but not ECPGColLabels) */ -ECPGTypeName: SQL_BOOL { $$ = make_str("bool"); } - | SQL_INT { $$ = make_str("int"); } - | SQL_LONG { $$ = make_str("long"); } - | SQL_SHORT { $$ = make_str("short"); } +ECPGTypeName: SQL_BOOL { $$ = make_str("bool"); } + | SQL_INT { $$ = make_str("int"); } + | SQL_LONG { $$ = make_str("long"); } + | SQL_SHORT { $$ = make_str("short"); } | SQL_STRUCT { $$ = make_str("struct"); } | SQL_SIGNED { $$ = make_str("signed"); } | SQL_UNSIGNED { $$ = make_str("unsigned"); } @@ -5006,6 +5080,7 @@ c_anything: IDENT { $$ = $1; } | S_RSHIFT { $$ = make_str(">>"); } | S_STATIC { $$ = make_str("static"); } | S_SUB { $$ = make_str("-="); } + | S_TYPEDEF { $$ = make_str("typedef"); } | SQL_BOOL { $$ = make_str("bool"); } | SQL_ENUM { $$ = make_str("enum"); } | SQL_INT { $$ = make_str("int"); } diff --git a/src/interfaces/ecpg/test/test3.pgc b/src/interfaces/ecpg/test/test3.pgc index 6f0afccc75b..2cdde342003 100644 --- a/src/interfaces/ecpg/test/test3.pgc +++ b/src/interfaces/ecpg/test/test3.pgc @@ -11,9 +11,8 @@ exec sql type str is varchar[10]; int main () { - typedef struct { long born; short age; } birthinfo; - exec sql type birthinfo is struct { long born; short age; }; exec sql begin declare section; + typedef struct { long born; short age; } birthinfo; struct personal_struct { str name; birthinfo birth; } personal;