diff --git a/src/interfaces/ecpg/ChangeLog b/src/interfaces/ecpg/ChangeLog index eac1b96e80..1524849434 100644 --- a/src/interfaces/ecpg/ChangeLog +++ b/src/interfaces/ecpg/ChangeLog @@ -463,3 +463,17 @@ Fri Feb 19 21:40:14 CET 1999 - Fixed bug in libecpg that caused it to start transactions only for the first connection. - Set library version to 2.7.1 + +Son Feb 21 14:10:47 CET 1999 + + - Fixed variable detection in libecpg. + +Mon Feb 22 19:47:45 CET 1999 + + - Added 'at ' option to all commands it is apllicable + to. Due to changing the API of some libecpg functions this + requires me to increase the major version number. + - Synced pgc.l with scan.l. + - Added support for unions. + - Set library version to 3.0.0 + - Set ecpg version to 3.0.0 diff --git a/src/interfaces/ecpg/TODO b/src/interfaces/ecpg/TODO index 82b8a5d8b8..0b4be6dd9f 100644 --- a/src/interfaces/ecpg/TODO +++ b/src/interfaces/ecpg/TODO @@ -11,9 +11,9 @@ DESCRIPTOR statement will be ignored. it would be nice to be able to use :var[:index] as cvariable -'at DB connection' is missing for several commands (is this standard?) +support for dynamic SQL with unknown number of variables with SQLDA structure -support for unions +allocate memory for pointers as C input variables Missing statements: - exec sql allocate diff --git a/src/interfaces/ecpg/include/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h index 4e1d6f9cde..98153747f6 100644 --- a/src/interfaces/ecpg/include/ecpglib.h +++ b/src/interfaces/ecpg/include/ecpglib.h @@ -8,8 +8,8 @@ extern "C" void ECPGdebug(int, FILE *); bool ECPGsetconn(int, const char *); bool ECPGconnect(int, const char *, const char *, const char *, const char *); - bool ECPGdo(int, char *,...); - bool ECPGtrans(int, const char *); + bool ECPGdo(int, const char *, char *,...); + bool ECPGtrans(int, const char *, const char *); bool ECPGdisconnect(int, const char *); bool ECPGprepare(int, char *, char *); bool ECPGdeallocate(int, char *); diff --git a/src/interfaces/ecpg/include/ecpgtype.h b/src/interfaces/ecpg/include/ecpgtype.h index e92220481d..8ca4d697c1 100644 --- a/src/interfaces/ecpg/include/ecpgtype.h +++ b/src/interfaces/ecpg/include/ecpgtype.h @@ -43,10 +43,10 @@ extern "C" ECPGt_varchar, ECPGt_varchar2, ECPGt_array, ECPGt_struct, + ECPGt_char_variable, ECPGt_EOIT, /* End of insert types. */ ECPGt_EORT, /* End of result types. */ - ECPGt_NO_INDICATOR, /* no indicator */ - ECPGt_char_variable + ECPGt_NO_INDICATOR /* no indicator */ }; #define IS_SIMPLE_TYPE(type) ((type) >= ECPGt_char && (type) <= ECPGt_varchar2) diff --git a/src/interfaces/ecpg/lib/Makefile.in b/src/interfaces/ecpg/lib/Makefile.in index 4872e8c0fd..ef2373fa78 100644 --- a/src/interfaces/ecpg/lib/Makefile.in +++ b/src/interfaces/ecpg/lib/Makefile.in @@ -6,13 +6,13 @@ # Copyright (c) 1994, Regents of the University of California # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/Makefile.in,v 1.42 1999/02/21 03:02:35 scrappy Exp $ +# $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/Makefile.in,v 1.43 1999/02/23 12:56:55 scrappy Exp $ # #------------------------------------------------------------------------- NAME= ecpg -SO_MAJOR_VERSION= 2 -SO_MINOR_VERSION= 7.1 +SO_MAJOR_VERSION= 3 +SO_MINOR_VERSION= 0.0 SRCDIR= @top_srcdir@ include $(SRCDIR)/Makefile.global diff --git a/src/interfaces/ecpg/lib/ecpglib.c b/src/interfaces/ecpg/lib/ecpglib.c index 2a7745f2a6..e686edd512 100644 --- a/src/interfaces/ecpg/lib/ecpglib.c +++ b/src/interfaces/ecpg/lib/ecpglib.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -78,6 +79,7 @@ struct statement { int lineno; char *command; + struct connection *connection; struct variable *inlist; struct variable *outlist; }; @@ -104,6 +106,21 @@ register_error(long code, char *fmt,...) sqlca.sqlerrm.sqlerrml = strlen(sqlca.sqlerrm.sqlerrmc); } +static struct connection * +get_connection(const char *connection_name) +{ + struct connection *con = all_connections;; + + if (connection_name == NULL || strcmp(connection_name, "CURRENT") == 0) + return actual_connection; + + for (; con && strcmp(connection_name, con->name) != 0; con = con->next); + if (con) + return con; + else + return NULL; +} + static void ECPGfinish(struct connection * act) { @@ -145,7 +162,6 @@ ecpg_alloc(long size, int lineno) if (!new) { - ECPGfinish(actual_connection); ECPGlog("out of memory\n"); register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); return NULL; @@ -162,7 +178,6 @@ ecpg_strdup(const char *string, int lineno) if (!new) { - ECPGfinish(actual_connection); ECPGlog("out of memory\n"); register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); return NULL; @@ -238,9 +253,26 @@ quote_strings(char *arg, int lineno) return res; } -/* create a list of variables */ +/* + * create a list of variables + * The variables are listed with input variables preceeding outputvariables + * The end of each group is marked by an end marker. + * per variable we list: + * type - as defined in ecpgtype.h + * value - where to store the data + * varcharsize - length of string in case we have a stringvariable, else 0 + * arraysize - 0 for pointer (we don't know the size of the array), + * 1 for simple variable, size for arrays + * offset - offset between ith and (i+1)th entry in an array, + * normally that means sizeof(type) + * ind_type - type of indicator variable + * ind_value - pointer to indicator variable + * ind_varcharsize - empty + * ind_arraysize - arraysize of indicator array + * ind_offset - indicator offset + */ static bool -create_statement(int lineno, struct statement ** stmt, char *query, va_list ap) +create_statement(int lineno, struct connection *connection, struct statement ** stmt, char *query, va_list ap) { struct variable **list = &((*stmt)->inlist); enum ECPGttype type; @@ -249,6 +281,7 @@ create_statement(int lineno, struct statement ** stmt, char *query, va_list ap) return false; (*stmt)->command = query; + (*stmt)->connection = connection; (*stmt)->lineno = lineno; list = &((*stmt)->inlist); @@ -278,7 +311,8 @@ create_statement(int lineno, struct statement ** stmt, char *query, va_list ap) var->ind_arrsize = va_arg(ap, long); var->ind_offset = va_arg(ap, long); var->next = NULL; - + + /* if variable is NULL, the statement hasn't been prepared */ if (var->value == NULL) { ECPGlog("create_statement: invalid statement name\n"); @@ -564,27 +598,27 @@ ECPGexecute(struct statement * stmt) /* Now the request is built. */ - if (actual_connection->committed && !no_auto_trans) + if (stmt->connection->committed && !no_auto_trans) { - if ((results = PQexec(actual_connection->connection, "begin transaction")) == NULL) + if ((results = PQexec(stmt->connection->connection, "begin transaction")) == NULL) { register_error(ECPG_TRANS, "Error starting transaction line %d.", stmt->lineno); return false; } PQclear(results); - actual_connection->committed = false; + stmt->connection->committed = false; } - ECPGlog("ECPGexecute line %d: QUERY: %s\n", stmt->lineno, copiedquery); - results = PQexec(actual_connection->connection, copiedquery); + ECPGlog("ECPGexecute line %d: QUERY: %s on connection %s\n", stmt->lineno, copiedquery, stmt->connection->name); + results = PQexec(stmt->connection->connection, copiedquery); free(copiedquery); if (results == NULL) { ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno, - PQerrorMessage(actual_connection->connection)); + PQerrorMessage(stmt->connection->connection)); register_error(ECPG_PGSQL, "Postgres error: %s line %d.", - PQerrorMessage(actual_connection->connection), stmt->lineno); + PQerrorMessage(stmt->connection->connection), stmt->lineno); } else { @@ -642,6 +676,7 @@ ECPGexecute(struct statement * stmt) status = false; break; } + for (act_tuple = 0; act_tuple < ntuples; act_tuple++) { pval = PQgetvalue(results, act_tuple, act_field); @@ -909,18 +944,18 @@ ECPGexecute(struct statement * stmt) case PGRES_FATAL_ERROR: case PGRES_BAD_RESPONSE: ECPGlog("ECPGexecute line %d: Error: %s", - stmt->lineno, PQerrorMessage(actual_connection->connection)); + stmt->lineno, PQerrorMessage(stmt->connection->connection)); register_error(ECPG_PGSQL, "Error: %s line %d.", - PQerrorMessage(actual_connection->connection), stmt->lineno); + PQerrorMessage(stmt->connection->connection), stmt->lineno); status = false; break; case PGRES_COPY_OUT: ECPGlog("ECPGexecute line %d: Got PGRES_COPY_OUT ... tossing.\n", stmt->lineno); - PQendcopy(actual_connection->connection); + PQendcopy(stmt->connection->connection); break; case PGRES_COPY_IN: ECPGlog("ECPGexecute line %d: Got PGRES_COPY_IN ... tossing.\n", stmt->lineno); - PQendcopy(actual_connection->connection); + PQendcopy(stmt->connection->connection); break; default: ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n", @@ -932,7 +967,7 @@ ECPGexecute(struct statement * stmt) } /* check for asynchronous returns */ - notify = PQnotifies(actual_connection->connection); + notify = PQnotifies(stmt->connection->connection); if (notify) { ECPGlog("ECPGexecute line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received\n", @@ -944,20 +979,27 @@ ECPGexecute(struct statement * stmt) } bool -ECPGdo(int lineno, char *query,...) +ECPGdo(int lineno, const char *connection_name, char *query,...) { va_list args; struct statement *stmt; + struct connection *con = get_connection(connection_name); + if (con == NULL) + { + register_error(ECPG_NO_CONN, "No such connection %s in line %d", connection_name, lineno); + return (false); + } + va_start(args, query); - if (create_statement(lineno, &stmt, query, args) == false) + if (create_statement(lineno, con, &stmt, query, args) == false) return (false); va_end(args); /* are we connected? */ - if (actual_connection == NULL || actual_connection->connection == NULL) + if (con == NULL || con->connection == NULL) { - ECPGlog("ECPGdo: not connected\n"); + ECPGlog("ECPGdo: not connected to %s\n", con->name); register_error(ECPG_NOT_CONN, "Not connected in line %d", lineno); return false; } @@ -967,16 +1009,23 @@ ECPGdo(int lineno, char *query,...) bool -ECPGtrans(int lineno, const char *transaction) +ECPGtrans(int lineno, const char *connection_name, const char *transaction) { PGresult *res; + struct connection *con = get_connection(connection_name); + + if (con == NULL) + { + register_error(ECPG_NO_CONN, "No such connection %s in line %d", connection_name, lineno); + return (false); + } - ECPGlog("ECPGtrans line %d action = %s\n", lineno, transaction); + ECPGlog("ECPGtrans line %d action = %s connection = %s\n", lineno, transaction, con->name); /* if we have no connection we just simulate the command */ - if (actual_connection && actual_connection->connection) + if (con && con->connection) { - if ((res = PQexec(actual_connection->connection, transaction)) == NULL) + if ((res = PQexec(con->connection, transaction)) == NULL) { register_error(ECPG_TRANS, "Error in transaction processing line %d.", lineno); return FALSE; @@ -987,7 +1036,7 @@ ECPGtrans(int lineno, const char *transaction) { struct prepared_statement *this; - actual_connection->committed = true; + con->committed = true; /* deallocate all prepared statements */ for (this = prep_stmts; this != NULL; this = this->next) @@ -1005,11 +1054,8 @@ ECPGtrans(int lineno, const char *transaction) bool ECPGsetconn(int lineno, const char *connection_name) { - struct connection *con = all_connections; + struct connection *con = get_connection(connection_name); - ECPGlog("ECPGsetconn: setting actual connection to %s\n", connection_name); - - for (; con && strcmp(connection_name, con->name) != 0; con = con->next); if (con) { actual_connection = con; @@ -1070,9 +1116,7 @@ ECPGdisconnect(int lineno, const char *connection_name) { struct connection *con; - if (strcmp(connection_name, "CURRENT") == 0) - ECPGfinish(actual_connection); - else if (strcmp(connection_name, "ALL") == 0) + if (strcmp(connection_name, "ALL") == 0) { for (con = all_connections; con;) { @@ -1084,7 +1128,8 @@ ECPGdisconnect(int lineno, const char *connection_name) } else { - for (con = all_connections; con && strcmp(con->name, connection_name) != 0; con = con->next); + con = get_connection(connection_name); + if (con == NULL) { ECPGlog("disconnect: not connected to connection %s\n", connection_name); @@ -1136,6 +1181,21 @@ sqlprint(void) printf("sql error %s\n", sqlca.sqlerrm.sqlerrmc); } +static bool +isvarchar(unsigned char c) +{ + if (isalnum(c)) + return true; + + if (c == '_' || c == '>' || c == '-' || c == '.') + return true; + + if (c >= 128) + return true; + + return(false); +} + static void replace_variables(char *text) { @@ -1150,7 +1210,7 @@ replace_variables(char *text) if (!string && *ptr == ':') { ptr[0] = ptr[1] = ';'; - for (ptr += 2; *ptr && *ptr != ' '; ptr++) + for (ptr += 2; *ptr && isvarchar(*ptr); ptr++) *ptr = ' '; } } @@ -1162,7 +1222,7 @@ ECPGprepare(int lineno, char *name, char *variable) { struct statement *stmt; struct prepared_statement *this; - + /* check if we already have prepared this statement */ for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next); if (this) @@ -1186,6 +1246,7 @@ ECPGprepare(int lineno, char *name, char *variable) /* create statement */ stmt->lineno = lineno; + stmt->connection = NULL; stmt->command = ecpg_strdup(variable, lineno); stmt->inlist = stmt->outlist = NULL; diff --git a/src/interfaces/ecpg/preproc/Makefile b/src/interfaces/ecpg/preproc/Makefile index 87e788c83c..66c8911c5a 100644 --- a/src/interfaces/ecpg/preproc/Makefile +++ b/src/interfaces/ecpg/preproc/Makefile @@ -1,8 +1,8 @@ SRCDIR= ../../.. include $(SRCDIR)/Makefile.global -MAJOR_VERSION=2 -MINOR_VERSION=5 +MAJOR_VERSION=3 +MINOR_VERSION=0 PATCHLEVEL=0 CFLAGS+=-I../include -DMAJOR_VERSION=$(MAJOR_VERSION) \ diff --git a/src/interfaces/ecpg/preproc/c_keywords.c b/src/interfaces/ecpg/preproc/c_keywords.c index 5395b533d3..9a8b94bd25 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", S_SIGNED}, {"static", S_STATIC}, {"struct", S_STRUCT}, + {"union", S_UNION}, {"unsigned", S_UNSIGNED}, {"varchar", S_VARCHAR}, }; diff --git a/src/interfaces/ecpg/preproc/ecpg.c b/src/interfaces/ecpg/preproc/ecpg.c index 2594718cd1..6b37442c83 100644 --- a/src/interfaces/ecpg/preproc/ecpg.c +++ b/src/interfaces/ecpg/preproc/ecpg.c @@ -165,6 +165,7 @@ main(int argc, char *const argv[]) struct arguments *l1, *l2; free(ptr->command); + free(ptr->connection); free(ptr->name); for (l1 = ptr->argsinsert; l1; l1 = l2) { diff --git a/src/interfaces/ecpg/preproc/ecpg_keywords.c b/src/interfaces/ecpg/preproc/ecpg_keywords.c index 52eea065b8..25c1f2b981 100644 --- a/src/interfaces/ecpg/preproc/ecpg_keywords.c +++ b/src/interfaces/ecpg/preproc/ecpg_keywords.c @@ -20,6 +20,7 @@ */ static ScanKeyword ScanKeywords[] = { /* name value */ + {"at", SQL_AT}, {"bool", SQL_BOOL}, {"break", SQL_BREAK}, {"call", SQL_CALL}, diff --git a/src/interfaces/ecpg/preproc/extern.h b/src/interfaces/ecpg/preproc/extern.h index d5bc1fc9ca..94c76a507a 100644 --- a/src/interfaces/ecpg/preproc/extern.h +++ b/src/interfaces/ecpg/preproc/extern.h @@ -6,7 +6,7 @@ extern int braces_open, no_auto_trans, struct_level; -extern char *yytext; +extern char *yytext, errortext[128]; extern int yylineno, yyleng; extern FILE *yyin, diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l index 5ddac659bb..8236969076 100644 --- a/src/interfaces/ecpg/preproc/pgc.l +++ b/src/interfaces/ecpg/preproc/pgc.l @@ -105,7 +105,6 @@ xqstart {quote} xqstop {quote} xqdouble {quote}{quote} xqinside [^\\']* -xqembedded "\\'" xqliteral [\\](.|\n) xqcat {quote}{space}*\n{space}*{quote} @@ -244,22 +243,9 @@ cppline {space}*#.*(\\{space}*\n)*\n* return SCONST; } {xqdouble} | -{xqinside} { - if ((llen+yyleng) > (MAX_PARSE_BUFFER - 1)) - yyerror("ERROR: quoted string parse buffer exceeded"); - memcpy(literal+llen, yytext, yyleng+1); - llen += yyleng; - } -{xqembedded} { - if ((llen+yyleng-1) > (MAX_PARSE_BUFFER - 1)) - yyerror("ERROR: quoted string parse buffer exceeded"); - memcpy(literal+llen, yytext, yyleng+1); - *(literal+llen) = '\''; - llen += yyleng; - } - +{xqinside} | {xqliteral} { - if ((llen+yyleng-1) > (MAX_PARSE_BUFFER - 1)) + if ((llen+yyleng) > (MAX_PARSE_BUFFER - 1)) yyerror("ERROR: quoted string parse buffer exceeded"); memcpy(literal+llen, yytext, yyleng+1); llen += yyleng; diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y index 7bb95d77e8..ae5edf294d 100644 --- a/src/interfaces/ecpg/preproc/preproc.y +++ b/src/interfaces/ecpg/preproc/preproc.y @@ -18,7 +18,8 @@ * Variables containing simple states. */ int struct_level = 0; -static char errortext[128]; +char errortext[128]; +static char *connection = NULL; static int QueryIsRule = 0, ForUpdateNotAllowed = 0; static struct this_type actual_type[STRUCT_DEPTH]; static char *actual_storage[STRUCT_DEPTH]; @@ -489,7 +490,7 @@ output_statement(char * stmt, int mode) { int i, j=strlen(stmt); - fputs("ECPGdo(__LINE__, \"", yyout); + fprintf(yyout, "ECPGdo(__LINE__, %s, \"", connection ? connection : "NULL"); /* do this char by char as we have to filter '\"' */ for (i = 0;i < j; i++) @@ -504,6 +505,8 @@ output_statement(char * stmt, int mode) fputs("ECPGt_EORT);", yyout); whenever_action(mode); free(stmt); + if (connection != NULL) + free(connection); } static struct typedefs * @@ -612,7 +615,7 @@ adjust_array(enum ECPGttype type_enum, int *dimension, int *length, int type_dim } /* special embedded SQL token */ -%token SQL_BOOL SQL_BREAK +%token SQL_AT SQL_BOOL SQL_BREAK %token SQL_CALL SQL_CONNECT SQL_CONNECTION SQL_CONTINUE %token SQL_DEALLOCATE SQL_DISCONNECT SQL_ENUM %token SQL_FOUND SQL_FREE SQL_GO SQL_GOTO @@ -625,8 +628,8 @@ adjust_array(enum ECPGttype type_enum, int *dimension, int *length, int type_dim /* C token */ %token S_ANYTHING S_AUTO S_BOOL S_CHAR S_CONST S_DOUBLE S_ENUM S_EXTERN %token S_FLOAT S_INT S -%token S_LONG S_REGISTER S_SHORT S_SIGNED S_STATIC S_STRUCT -%token S_UNSIGNED S_VARCHAR +%token S_LONG S_REGISTER S_SHORT S_SIGNED S_STATIC S_STRUCT +%token S_UNION S_UNSIGNED S_VARCHAR /* I need this and don't know where it is defined inside the backend */ %token TYPECAST @@ -785,8 +788,9 @@ adjust_array(enum ECPGttype type_enum, int *dimension, int *length, int type_dim %type ECPGSetConnection c_line cpp_line s_enum ECPGTypedef %type enum_type civariableonly ECPGCursorStmt ECPGDeallocate %type ECPGFree ECPGDeclare ECPGVar sql_variable_declarations -%type sql_declaration sql_variable_list sql_variable +%type sql_declaration sql_variable_list sql_variable opt_at %type struct_type s_struct declaration variable_declarations +%type s_struct_or_union sql_struct_or_union %type simple_type varchar_type @@ -803,13 +807,16 @@ prog: statements; statements: /* empty */ | statements statement -statement: ecpgstart stmt SQL_SEMI +statement: ecpgstart opt_at stmt SQL_SEMI { connection = NULL; } + | ecpgstart stmt SQL_SEMI | ECPGDeclaration | c_thing { fprintf(yyout, "%s", $1); free($1); } | cpp_line { fprintf(yyout, "%s", $1); free($1); } | blockstart { fputs($1, yyout); free($1); } | blockend { fputs($1, yyout); free($1); } +opt_at: SQL_AT connection_target { connection = $2; } + stmt: AddAttrStmt { output_statement($1, 0); } | AlterUserStmt { output_statement($1, 0); } | ClosePortalStmt { output_statement($1, 0); } @@ -853,7 +860,7 @@ stmt: AddAttrStmt { output_statement($1, 0); } } | RuleStmt { output_statement($1, 0); } | TransactionStmt { - fprintf(yyout, "ECPGtrans(__LINE__, \"%s\");", $1); + fprintf(yyout, "ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", $1); whenever_action(0); free($1); } @@ -866,6 +873,9 @@ stmt: AddAttrStmt { output_statement($1, 0); } | VariableShowStmt { output_statement($1, 0); } | VariableResetStmt { output_statement($1, 0); } | ECPGConnect { + if (connection) + yyerror("no at option for connect statement.\n"); + fprintf(yyout, "no_auto_trans = %d;\n", no_auto_trans); fprintf(yyout, "ECPGconnect(__LINE__, %s);", $1); whenever_action(0); @@ -876,6 +886,9 @@ stmt: AddAttrStmt { output_statement($1, 0); } free($1); } | ECPGDeallocate { + if (connection) + yyerror("no at option for connect statement.\n"); + fputs($1, yyout); whenever_action(0); free($1); @@ -885,6 +898,9 @@ stmt: AddAttrStmt { output_statement($1, 0); } free($1); } | ECPGDisconnect { + if (connection) + yyerror("no at option for disconnect statement.\n"); + fprintf(yyout, "ECPGdisconnect(__LINE__, \"%s\");", $1); whenever_action(0); free($1); @@ -893,7 +909,7 @@ stmt: AddAttrStmt { output_statement($1, 0); } output_statement($1, 0); } | ECPGFree { - fprintf(yyout, "ECPGdeallocate(__LINE__, \"%s\");", $1); + fprintf(yyout, "ECPGdeallocate(__LINE__, %s, \"%s\");", connection ? connection : "NULL", $1); whenever_action(0); free($1); } @@ -912,7 +928,7 @@ stmt: AddAttrStmt { output_statement($1, 0); } yyerror(errortext); } - fprintf(yyout, "ECPGdo(__LINE__, \"%s\",", ptr->command); + fprintf(yyout, "ECPGdo(__LINE__, %s, \"%s\",", ptr->connection ? ptr->connection : "NULL", ptr->command); /* dump variables to C file*/ dump_variables(ptr->argsinsert, 0); dump_variables(argsinsert, 0); @@ -923,25 +939,40 @@ stmt: AddAttrStmt { output_statement($1, 0); } free($1); } | ECPGPrepare { + if (connection) + yyerror("no at option for set connection statement.\n"); + fprintf(yyout, "ECPGprepare(__LINE__, %s);", $1); whenever_action(0); free($1); } | ECPGRelease { /* output already done */ } | ECPGSetConnection { + if (connection) + yyerror("no at option for set connection statement.\n"); + fprintf(yyout, "ECPGsetconn(__LINE__, %s);", $1); whenever_action(0); free($1); } | ECPGTypedef { + if (connection) + yyerror("no at option for typedef statement.\n"); + fputs($1, yyout); free($1); } | ECPGVar { + if (connection) + yyerror("no at option for var statement.\n"); + fputs($1, yyout); free($1); } | ECPGWhenever { + if (connection) + yyerror("no at option for whenever statement.\n"); + fputs($1, yyout); output_line_number(); free($1); @@ -2727,6 +2758,7 @@ CursorStmt: DECLARE name opt_cursor CURSOR FOR SelectStmt cursor_clause /* initial definition */ this->next = cur; this->name = $2; + this->connection = connection; this->command = cat2_str(cat5_str(make1_str("declare"), mm_strdup($2), $3, make1_str("cursor for"), $6), $7); this->argsinsert = argsinsert; this->argsresult = argsresult; @@ -3103,6 +3135,7 @@ Generic: generic generic: ident { $$ = $1; } | TYPE_P { $$ = make1_str("type"); } + | SQL_AT { $$ = make1_str("at"); } | SQL_BOOL { $$ = make1_str("bool"); } | SQL_BREAK { $$ = make1_str("break"); } | SQL_CALL { $$ = make1_str("call"); } @@ -4306,6 +4339,7 @@ ColId: ident { $$ = $1; } | VALID { $$ = make1_str("valid"); } | VERSION { $$ = make1_str("version"); } | ZONE { $$ = make1_str("zone"); } + | SQL_AT { $$ = make1_str("at"); } | SQL_BOOL { $$ = make1_str("bool"); } | SQL_BREAK { $$ = make1_str("break"); } | SQL_CALL { $$ = make1_str("call"); } @@ -4602,6 +4636,7 @@ ECPGCursorStmt: DECLARE name opt_cursor CURSOR FOR ident cursor_clause /* initial definition */ this->next = cur; this->name = $2; + this->connection = connection; this->command = cat5_str(make1_str("declare"), mm_strdup($2), $3, make1_str("cursor for ;;"), $7); this->argsresult = NULL; @@ -4731,14 +4766,17 @@ struct_type: s_struct '{' variable_declarations '}' $$ = cat4_str($1, make1_str("{"), $3, make1_str("}")); } -s_struct : S_STRUCT opt_symbol +s_struct : s_struct_or_union opt_symbol { struct_member_list[struct_level++] = NULL; if (struct_level >= STRUCT_DEPTH) yyerror("Too many levels in nested structure definition"); - $$ = cat2_str(make1_str("struct"), $2); + $$ = cat2_str($1, $2); } +s_struct_or_union: S_STRUCT { $$ = make1_str("struct"); } + | S_UNION { $$ = make1_str("union"); } + opt_symbol: /* empty */ { $$ = make1_str(""); } | symbol { $$ = $1; } @@ -4940,7 +4978,7 @@ ECPGRelease: TransactionStmt SQL_RELEASE if (strncmp($1, "begin", 5) == 0) yyerror("RELEASE does not make sense when beginning a transaction"); - fprintf(yyout, "ECPGtrans(__LINE__, \"%s\");", $1); + fprintf(yyout, "ECPGtrans(__LINE__, %s, \"%s\");", connection, $1); whenever_action(0); fprintf(yyout, "ECPGdisconnect(\"\");"); whenever_action(0); @@ -5151,7 +5189,7 @@ ctype: CHAR $$.type_index = -1; $$.type_dimension = -1; } - | SQL_STRUCT + | sql_struct_or_union { struct_member_list[struct_level++] = NULL; if (struct_level >= STRUCT_DEPTH) @@ -5159,7 +5197,7 @@ ctype: CHAR } '{' sql_variable_declarations '}' { ECPGfree_struct_member(struct_member_list[struct_level--]); - $$.type_str = cat3_str(make1_str("struct {"), $4, make1_str("}")); + $$.type_str = cat4_str($1, make1_str("{"), $4, make1_str("}")); $$.type_enum = ECPGt_struct; $$.type_index = -1; $$.type_dimension = -1; @@ -5175,6 +5213,9 @@ ctype: CHAR struct_member_list[struct_level] = this->struct_member_list; } +sql_struct_or_union: SQL_STRUCT { $$ = make1_str("struct"); } + | UNION { $$ = make1_str("union"); } + opt_signed: SQL_SIGNED | /* empty */ sql_variable_declarations: /* empty */ @@ -5735,6 +5776,7 @@ c_anything: IDENT { $$ = $1; } | S_SIGNED { $$ = make1_str("signed"); } | S_STATIC { $$ = make1_str("static"); } | S_STRUCT { $$ = make1_str("struct"); } + | S_UNION { $$ = make1_str("union"); } | S_UNSIGNED { $$ = make1_str("unsigned"); } | S_VARCHAR { $$ = make1_str("varchar"); } | S_ANYTHING { $$ = make_name(); } diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c index dd3546b50a..b5144d5d9a 100644 --- a/src/interfaces/ecpg/preproc/type.c +++ b/src/interfaces/ecpg/preproc/type.c @@ -164,7 +164,8 @@ get_type(enum ECPGttype typ) return ("ECPGt_char_variable"); break; default: - abort(); + sprintf(errortext, "illegal variable type %d\n", typ); + yyerror(errortext); } } @@ -357,7 +358,8 @@ ECPGfree_type(struct ECPGtype * typ) if (IS_SIMPLE_TYPE(typ->u.element->typ)) free(typ->u.element); else if (typ->u.element->typ == ECPGt_array) - abort(); /* Array of array, */ + /* Array of array, */ + yyerror("internal error, found multi-dimensional array\n"); else if (typ->u.element->typ == ECPGt_struct) { /* Array of structs. */ @@ -365,7 +367,10 @@ ECPGfree_type(struct ECPGtype * typ) free(typ->u.members); } else - abort(); + { + sprintf(errortext, "illegal variable type %d\n", typ); + yyerror(errortext); + } } else if (typ->typ == ECPGt_struct) { @@ -373,7 +378,10 @@ ECPGfree_type(struct ECPGtype * typ) free(typ->u.members); } else - abort(); + { + sprintf(errortext, "illegal variable type %d\n", typ); + yyerror(errortext); + } } free(typ); } diff --git a/src/interfaces/ecpg/preproc/type.h b/src/interfaces/ecpg/preproc/type.h index f9642d9276..59cf3be900 100644 --- a/src/interfaces/ecpg/preproc/type.h +++ b/src/interfaces/ecpg/preproc/type.h @@ -101,6 +101,7 @@ struct cursor { char *name; char *command; + char *connection; struct arguments *argsinsert; struct arguments *argsresult; struct cursor *next; diff --git a/src/interfaces/ecpg/test/Makefile b/src/interfaces/ecpg/test/Makefile index 45d65fb44c..2c4ba01ee0 100644 --- a/src/interfaces/ecpg/test/Makefile +++ b/src/interfaces/ecpg/test/Makefile @@ -15,4 +15,4 @@ perftest.c:perftest.pgc /usr/local/pgsql/bin/ecpg $? clean: - /bin/rm test1 test2 perftest *.c log + -/bin/rm test1 test2 perftest *.c log diff --git a/src/interfaces/ecpg/test/test1.pgc b/src/interfaces/ecpg/test/test1.pgc index ac2dbdc04e..a1e13bb517 100644 --- a/src/interfaces/ecpg/test/test1.pgc +++ b/src/interfaces/ecpg/test/test1.pgc @@ -4,7 +4,7 @@ exec sql whenever sqlerror sqlprint; exec sql include sqlca; -exec sql define AMOUNT 8; +exec sql define AMOUNT 4; exec sql type intarray is int[AMOUNT]; exec sql type string is char(6); @@ -30,21 +30,31 @@ exec sql end declare section; ECPGdebug(1, dbgs); strcpy(msg, "connect"); - exec sql connect to mm; + exec sql connect to mm as main; + + strcpy(msg, "connect"); + exec sql connect to pm; strcpy(msg, "create"); + exec sql at main create table test(name char(6), amount int, letter char(1)); exec sql create table test(name char(6), amount int, letter char(1)); strcpy(msg, "commit"); + exec sql at main commit; exec sql commit; + strcpy(msg, "set connection"); + exec sql set connection main; + strcpy(msg, "execute insert 1"); - sprintf(command, "insert into test(name, amount, letter) values ('foobar', 1, 'f')"); + sprintf(command, "insert into test(name, amount, letter) values ('db: mm', 1, 'f')"); + exec sql execute immediate :command; + sprintf(command, "insert into test(name, amount, letter) values ('db: mm', 2, 't')"); exec sql execute immediate :command; strcpy(msg, "execute insert 2"); - sprintf(command, "insert into test(name, amount, letter) select name, amount+1, letter from test"); - exec sql execute immediate :command; + sprintf(command, "insert into test(name, amount, letter) values ('db: pm', 1, 'f')"); + exec sql at pm execute immediate :command; strcpy(msg, "execute insert 3"); sprintf(command, "insert into test(name, amount, letter) select name, amount+10, letter from test"); @@ -55,27 +65,35 @@ exec sql end declare section; strcpy(msg, "execute insert 4"); sprintf(command, "insert into test(name, amount, letter) select name, amount+;;, letter from test"); exec sql prepare I from :command; - exec sql execute I using :increment; + exec sql at pm execute I using :increment; printf("Inserted %d tuples via prepared execute\n", sqlca.sqlerrd[2]); strcpy(msg, "commit"); exec sql commit; + exec sql at pm commit; strcpy(msg, "select"); exec sql select name, amount, letter into :name, :amount, :letter from test; + for (i=0, j=sqlca.sqlerrd[2]; i