From ef567413d2f7a75c31b79a48c79ff628fec3e5fa Mon Sep 17 00:00:00 2001 From: "Marc G. Fournier" Date: Mon, 18 May 1998 16:05:05 +0000 Subject: [PATCH] From: Michael Meskes + + Wed May 6 16:09:45 CEST 1998 + + - Some more cleanups in the library. + + Thu May 7 12:34:28 CEST 1998 + + - Made CONNECT and DISCONNECT statement more SQL3 compliant. + - Changed the API for the ECPGconnect function to be able to handle + hostnames and ports + + Fri May 8 13:54:45 CEST 1998 + - More changes to the parser. The connect statement now allows + ORACLE style logins. + - db-name is accepted in two ways: + - [@][:] + - esql:postgresql://[:][/] + + Mon May 11 10:28:37 CEST 1998 + + - Added '? options' to connect call. + - Also allow USING as keyword for the password + + Thu May 14 15:09:58 CEST 1998 + + - Changed preproc.y and pgc.l according to the parser changes in the + backend. + + Fri May 15 09:55:21 CEST 1998 + + - Added connection_name handling + + + Mon May 18 10:33:58 CEST 1998 + + - Fixed some more bugs + - Set version to 2.3.1 + - Set library version to 2.2 --- src/interfaces/ecpg/ChangeLog | 39 ++ src/interfaces/ecpg/include/ecpgerrno.h | 36 +- src/interfaces/ecpg/include/ecpglib.h | 6 +- src/interfaces/ecpg/lib/Makefile.in | 2 +- src/interfaces/ecpg/lib/ecpglib.c | 234 ++++++++-- src/interfaces/ecpg/preproc/Makefile | 2 +- src/interfaces/ecpg/preproc/ecpg_keywords.c | 4 +- src/interfaces/ecpg/preproc/extern.h | 10 +- src/interfaces/ecpg/preproc/keywords.c | 4 +- src/interfaces/ecpg/preproc/pgc.l | 12 +- src/interfaces/ecpg/preproc/preproc.y | 457 ++++++++++++-------- src/interfaces/ecpg/preproc/type.c | 43 +- src/interfaces/ecpg/test/perftest.pgc | 2 +- src/interfaces/ecpg/test/test1.pgc | 2 +- src/interfaces/ecpg/test/test2.pgc | 8 +- 15 files changed, 571 insertions(+), 290 deletions(-) diff --git a/src/interfaces/ecpg/ChangeLog b/src/interfaces/ecpg/ChangeLog index 5828ef8a81..c7acdf023d 100644 --- a/src/interfaces/ecpg/ChangeLog +++ b/src/interfaces/ecpg/ChangeLog @@ -189,3 +189,42 @@ Wed May 6 11:42:48 CEST 1998 an example) - Set version to 2.3.0 - Set library version to 2.1 + +Wed May 6 16:09:45 CEST 1998 + + - Some more cleanups in the library. + +Thu May 7 12:34:28 CEST 1998 + + - Made CONNECT and DISCONNECT statement more SQL3 compliant. + - Changed the API for the ECPGconnect function to be able to handle + hostnames and ports + +Fri May 8 13:54:45 CEST 1998 + - More changes to the parser. The connect statement now allows + ORACLE style logins. + - db-name is accepted in two ways: + - [@][:] + - esql:postgresql://[:][/] + +Mon May 11 10:28:37 CEST 1998 + + - Added '? options' to connect call. + - Also allow USING as keyword for the password + +Thu May 14 15:09:58 CEST 1998 + + - Changed preproc.y and pgc.l according to the parser changes in the + backend. + +Fri May 15 09:55:21 CEST 1998 + + - Added connection_name handling + + +Mon May 18 10:33:58 CEST 1998 + + - Fixed some more bugs + - Set version to 2.3.1 + - Set library version to 2.2 + diff --git a/src/interfaces/ecpg/include/ecpgerrno.h b/src/interfaces/ecpg/include/ecpgerrno.h index 1be718216d..cddc7e6a68 100644 --- a/src/interfaces/ecpg/include/ecpgerrno.h +++ b/src/interfaces/ecpg/include/ecpgerrno.h @@ -1,22 +1,32 @@ #ifndef _ECPG_ERROR_H #define _ECPG_ERROR_H +#include + /* This is a list of all error codes the embedded SQL program can return */ #define ECPG_NO_ERROR 0 #define ECPG_NOT_FOUND 100 -#define ECPG_PGSQL -1 -#define ECPG_UNSUPPORTED -2 -#define ECPG_TOO_MANY_ARGUMENTS -3 -#define ECPG_TOO_FEW_ARGUMENTS -4 -#define ECPG_TRANS -5 -#define ECPG_TOO_MANY_MATCHES -6 -#define ECPG_INT_FORMAT -7 -#define ECPG_UINT_FORMAT -8 -#define ECPG_FLOAT_FORMAT -9 -#define ECPG_CONVERT_BOOL -10 -#define ECPG_EMPTY -11 -#define ECPG_CONNECT -12 -#define ECPG_DISCONNECT -13 +/* system error codes returned by ecpglib get the correct number, + * but are made negative + */ +#define ECPG_OUT_OF_MEMORY -ENOMEM + +/* first we have a set of ecpg messages, they start at 200 */ +#define ECPG_UNSUPPORTED -200 +#define ECPG_TOO_MANY_ARGUMENTS -201 +#define ECPG_TOO_FEW_ARGUMENTS -202 +#define ECPG_TOO_MANY_MATCHES -203 +#define ECPG_INT_FORMAT -204 +#define ECPG_UINT_FORMAT -205 +#define ECPG_FLOAT_FORMAT -206 +#define ECPG_CONVERT_BOOL -207 +#define ECPG_EMPTY -208 +#define ECPG_NO_CONN -209 + +/* finally the backend error messages, they start at 300 */ +#define ECPG_PGSQL -300 +#define ECPG_TRANS -301 +#define ECPG_CONNECT -302 #endif /* !_ECPG_ERROR_H */ diff --git a/src/interfaces/ecpg/include/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h index 9a5c2732d8..c0603b2e7f 100644 --- a/src/interfaces/ecpg/include/ecpglib.h +++ b/src/interfaces/ecpg/include/ecpglib.h @@ -5,11 +5,11 @@ extern "C" { #endif void ECPGdebug(int, FILE *); -bool ECPGconnect(const char *); +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 ECPGfinish(void); -bool ECPGdisconnect(const char *); +bool ECPGdisconnect(int, const char *); void ECPGlog(const char *format,...); diff --git a/src/interfaces/ecpg/lib/Makefile.in b/src/interfaces/ecpg/lib/Makefile.in index 0fb1d6f026..c402afc844 100644 --- a/src/interfaces/ecpg/lib/Makefile.in +++ b/src/interfaces/ecpg/lib/Makefile.in @@ -4,7 +4,7 @@ include $(SRCDIR)/Makefile.global PQ_INCLUDE=-I$(SRCDIR)/interfaces/libpq SO_MAJOR_VERSION=2 -SO_MINOR_VERSION=1 +SO_MINOR_VERSION=2 PORTNAME=@PORTNAME@ diff --git a/src/interfaces/ecpg/lib/ecpglib.c b/src/interfaces/ecpg/lib/ecpglib.c index 4f40a2e102..445776ec82 100644 --- a/src/interfaces/ecpg/lib/ecpglib.c +++ b/src/interfaces/ecpg/lib/ecpglib.c @@ -26,7 +26,13 @@ extern int no_auto_trans; -static PGconn *simple_connection = NULL; +static struct connection +{ + char *name; + PGconn *connection; + struct connection *next; +} *all_connections = NULL, *actual_connection = NULL; + static int simple_debug = 0; static FILE *debugstream = NULL; static int committed = true; @@ -54,6 +60,9 @@ quote_postgres(char *arg) int i, ri; + if (!res) + return(res); + for (i = 0, ri = 0; arg[i]; i++, ri++) { switch (arg[i]) @@ -73,6 +82,40 @@ quote_postgres(char *arg) } +static void +ECPGfinish(struct connection *act) +{ + if (act != NULL) + { + ECPGlog("ECPGfinish: finishing %s.\n", act->name); + PQfinish(act->connection); + /* remove act from the list */ + if (act == all_connections) + { + all_connections = act->next; + free(act->name); + free(act); + } + else + { + struct connection *con; + + for (con = all_connections; con->next && con->next != act; con = con->next); + if (con->next) + { + con->next = act->next; + free(act->name); + free(act); + } + } + + if (actual_connection == act) + actual_connection = all_connections; + } + else + ECPGlog("ECPGfinish: called an extra time.\n"); +} + bool ECPGdo(int lineno, char *query,...) { @@ -195,14 +238,40 @@ ECPGdo(int lineno, char *query,...) { /* set slen to string length if type is char * */ int slen = (varcharsize == 0) ? strlen((char *) value) : varcharsize; + char * tmp; newcopy = (char *) malloc(slen + 1); + if (!newcopy) + { + ECPGfinish(actual_connection); + ECPGlog("out of memory\n"); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + return false; + } + strncpy(newcopy, (char *) value, slen); newcopy[slen] = '\0'; mallocedval = (char *) malloc(2 * strlen(newcopy) + 3); + if (!mallocedval) + { + ECPGfinish(actual_connection); + ECPGlog("out of memory\n"); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + return false; + } + strcpy(mallocedval, "'"); - strcat(mallocedval, quote_postgres(newcopy)); + tmp = quote_postgres(newcopy); + if (!tmp) + { + ECPGfinish(actual_connection); + ECPGlog("out of memory\n"); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + return false; + } + + strcat(mallocedval, tmp); strcat(mallocedval, "'"); free(newcopy); @@ -215,14 +284,40 @@ ECPGdo(int lineno, char *query,...) { struct ECPGgeneric_varchar *var = (struct ECPGgeneric_varchar *) value; + char *tmp; newcopy = (char *) malloc(var->len + 1); + if (!newcopy) + { + ECPGfinish(actual_connection); + ECPGlog("out of memory\n"); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + return false; + } + strncpy(newcopy, var->arr, var->len); newcopy[var->len] = '\0'; mallocedval = (char *) malloc(2 * strlen(newcopy) + 3); + if (!mallocedval) + { + ECPGfinish(actual_connection); + ECPGlog("out of memory\n"); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + return false; + } + strcpy(mallocedval, "'"); - strcat(mallocedval, quote_postgres(newcopy)); + tmp = quote_postgres(newcopy); + if (!tmp) + { + ECPGfinish(actual_connection); + ECPGlog("out of memory\n"); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + return false; + } + + strcat(mallocedval, tmp); strcat(mallocedval, "'"); free(newcopy); @@ -249,6 +344,14 @@ ECPGdo(int lineno, char *query,...) newcopy = (char *) malloc(strlen(copiedquery) + strlen(tobeinserted) + 1); + if (!newcopy) + { + ECPGfinish(actual_connection); + ECPGlog("out of memory\n"); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + return false; + } + strcpy(newcopy, copiedquery); if ((p = strstr(newcopy, ";;")) == NULL) { @@ -301,7 +404,7 @@ ECPGdo(int lineno, char *query,...) if (committed && !no_auto_trans) { - if ((results = PQexec(simple_connection, "begin transaction")) == NULL) + if ((results = PQexec(actual_connection->connection, "begin transaction")) == NULL) { register_error(ECPG_TRANS, "Error starting transaction line %d.", lineno); return false; @@ -311,15 +414,15 @@ ECPGdo(int lineno, char *query,...) } ECPGlog("ECPGdo line %d: QUERY: %s\n", lineno, copiedquery); - results = PQexec(simple_connection, copiedquery); + results = PQexec(actual_connection->connection, copiedquery); free(copiedquery); if (results == NULL) { ECPGlog("ECPGdo line %d: error: %s", lineno, - PQerrorMessage(simple_connection)); + PQerrorMessage(actual_connection->connection)); register_error(ECPG_PGSQL, "Postgres error: %s line %d.", - PQerrorMessage(simple_connection), lineno); + PQerrorMessage(actual_connection->connection), lineno); } else { @@ -644,9 +747,9 @@ ECPGdo(int lineno, char *query,...) case PGRES_FATAL_ERROR: case PGRES_BAD_RESPONSE: ECPGlog("ECPGdo line %d: Error: %s", - lineno, PQerrorMessage(simple_connection)); + lineno, PQerrorMessage(actual_connection->connection)); register_error(ECPG_PGSQL, "Error: %s line %d.", - PQerrorMessage(simple_connection), lineno); + PQerrorMessage(actual_connection->connection), lineno); status = false; break; case PGRES_COPY_OUT: @@ -667,7 +770,7 @@ ECPGdo(int lineno, char *query,...) } /* check for asynchronous returns */ - notify = PQnotifies(simple_connection); + notify = PQnotifies(actual_connection->connection); if (notify) { ECPGlog("ECPGdo line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received\n", @@ -686,7 +789,7 @@ ECPGtrans(int lineno, const char * transaction) PGresult *res; ECPGlog("ECPGtrans line %d action = %s\n", lineno, transaction); - if ((res = PQexec(simple_connection, transaction)) == NULL) + if ((res = PQexec(actual_connection->connection, transaction)) == NULL) { register_error(ECPG_TRANS, "Error in transaction processing line %d.", lineno); return (FALSE); @@ -698,59 +801,101 @@ ECPGtrans(int lineno, const char * transaction) } bool -ECPGsetdb(PGconn *newcon) +ECPGsetconn(int lineno, const char *connection_name) { - ECPGfinish(); - simple_connection = newcon; - return true; + struct connection *con = all_connections; + + for (; con && strcmp(connection_name, con->name) == 0; con=con->next); + if (con) + { + actual_connection = con; + return true; + } + else + { + register_error(ECPG_NO_CONN, "No such connection %s in line %d", connection_name, lineno); + return false; + } } bool -ECPGconnect(const char *dbname) +ECPGconnect(int lineno, const char *dbname, const char *user, const char *passwd, const char * connection_name) { - char *name = strdup(dbname); - - ECPGlog("ECPGconnect: opening database %s\n", name); + struct connection *this = malloc(sizeof(struct connection)); - sqlca.sqlcode = 0; + if (!this) + { + ECPGlog("out of memory\n"); + register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno); + return false; + } + + if (dbname == NULL && connection_name == NULL) + connection_name = "DEFAULT"; + + /* add connection to our list */ + if (connection_name != NULL) + this->name = strdup(connection_name); + else + this->name = strdup(dbname); + + if (all_connections == NULL) + this->next = NULL; + else + this->next = all_connections; - ECPGsetdb(PQsetdb(NULL, NULL, NULL, NULL, name)); + actual_connection = all_connections = this; + + ECPGlog("ECPGconnect: opening database %s %s%s\n", dbname ? dbname : "NULL", user ? "for user ": "", user ? user : ""); - free(name); - name = NULL; + sqlca.sqlcode = 0; - if (PQstatus(simple_connection) == CONNECTION_BAD) + this->connection = PQsetdbLogin(NULL, NULL, NULL, NULL, dbname, user, passwd); + + if (PQstatus(this->connection) == CONNECTION_BAD) { - ECPGfinish(); - ECPGlog("connect: could not open database %s\n", dbname); - register_error(ECPG_CONNECT, "connect: could not open database %s.", dbname); + ECPGfinish(this); + ECPGlog("connect: could not open database %s %s%s in line %d\n", dbname ? dbname : "NULL", user ? "for user ": "", user ? user : "", lineno); + + register_error(ECPG_CONNECT, "connect: could not open database %s.", dbname ? dbname : "NULL"); return false; } + return true; } bool -ECPGdisconnect(const char *dbname) +ECPGdisconnect(int lineno, const char *connection_name) { - if (strlen(dbname) > 0 && strcmp(PQdb(simple_connection), dbname) != 0) + struct connection *con; + + if (strcmp(connection_name, "CURRENT") == 0) + ECPGfinish(actual_connection); + else if (strcmp(connection_name, "ALL") == 0) { - ECPGlog("disconnect: not connected to database %s\n", dbname); - register_error(ECPG_DISCONNECT, "disconnect: not connected to database %s.", dbname); - return false; + for (con = all_connections; con;) + { + struct connection *f = con; + + con = con->next; + ECPGfinish(f); + } } - return ECPGfinish(); -} - -bool -ECPGfinish(void) -{ - if (simple_connection != NULL) + else { - ECPGlog("ECPGfinish: finishing.\n"); - PQfinish(simple_connection); + for (con = all_connections; con && strcmp(con->name, connection_name);con = con->next); + if (con == NULL) + { + ECPGlog("disconnect: not connected to connection %s\n", connection_name); + register_error(ECPG_NO_CONN, "No such connection %s in line %d", connection_name, lineno); + return false; + } + else + { + ECPGfinish(con); + } } - else - ECPGlog("ECPGfinish: called an extra time.\n"); + return true; } @@ -771,6 +916,9 @@ ECPGlog(const char *format,...) { char *f = (char *) malloc(strlen(format) + 100); + if (!f) + return; + sprintf(f, "[%d]: %s", getpid(), format); va_start(ap, format); diff --git a/src/interfaces/ecpg/preproc/Makefile b/src/interfaces/ecpg/preproc/Makefile index 5cb3861c22..1410087832 100644 --- a/src/interfaces/ecpg/preproc/Makefile +++ b/src/interfaces/ecpg/preproc/Makefile @@ -3,7 +3,7 @@ include $(SRCDIR)/Makefile.global MAJOR_VERSION=2 MINOR_VERSION=3 -PATCHLEVEL=0 +PATCHLEVEL=1 CFLAGS+=-I../include -DMAJOR_VERSION=$(MAJOR_VERSION) \ -DMINOR_VERSION=$(MINOR_VERSION) -DPATCHLEVEL=$(PATCHLEVEL) \ diff --git a/src/interfaces/ecpg/preproc/ecpg_keywords.c b/src/interfaces/ecpg/preproc/ecpg_keywords.c index 3badb93997..bc724c54b3 100644 --- a/src/interfaces/ecpg/preproc/ecpg_keywords.c +++ b/src/interfaces/ecpg/preproc/ecpg_keywords.c @@ -24,12 +24,14 @@ static ScanKeyword ScanKeywords[] = { {"break", SQL_BREAK}, {"call", SQL_CALL}, {"connect", SQL_CONNECT}, + {"connection", SQL_CONNECTION}, {"continue", SQL_CONTINUE}, {"disconnect", SQL_DISCONNECT}, {"found", SQL_FOUND}, {"go", SQL_GO}, {"goto", SQL_GOTO}, - {"immediate", SQL_IMMEDIATE}, + {"identified", SQL_IDENTIFIED}, + {"immediate", SQL_IMMEDIATE}, {"indicator", SQL_INDICATOR}, {"open", SQL_OPEN}, {"release", SQL_RELEASE}, diff --git a/src/interfaces/ecpg/preproc/extern.h b/src/interfaces/ecpg/preproc/extern.h index 6faa36be88..e18bb33219 100644 --- a/src/interfaces/ecpg/preproc/extern.h +++ b/src/interfaces/ecpg/preproc/extern.h @@ -1,4 +1,5 @@ #include "parser/keywords.h" +#include /* variables */ @@ -47,7 +48,8 @@ extern void yyerror(char *); /* return codes */ #define OK 0 -#define NO_INCLUDE_FILE 1 -#define PARSE_ERROR 2 -#define OUT_OF_MEMORY 3 -#define ILLEGAL_OPTION 4 +#define PARSE_ERROR -1 +#define ILLEGAL_OPTION -2 + +#define NO_INCLUDE_FILE ENOENT +#define OUT_OF_MEMORY ENOMEM diff --git a/src/interfaces/ecpg/preproc/keywords.c b/src/interfaces/ecpg/preproc/keywords.c index 4d8722f69d..9f953cb98f 100644 --- a/src/interfaces/ecpg/preproc/keywords.c +++ b/src/interfaces/ecpg/preproc/keywords.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/keywords.c,v 1.1 1998/04/21 13:23:06 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/keywords.c,v 1.2 1998/05/18 16:05:00 scrappy Exp $ * *------------------------------------------------------------------------- */ @@ -189,6 +189,8 @@ static ScanKeyword ScanKeywords[] = { {"substring", SUBSTRING}, {"table", TABLE}, {"time", TIME}, + {"timezone_hour", TIMEZONE_HOUR}, + {"timezone_minute", TIMEZONE_MINUTE}, {"to", TO}, {"trailing", TRAILING}, {"transaction", TRANSACTION}, diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l index 146938bf7d..c9f0cd459b 100644 --- a/src/interfaces/ecpg/preproc/pgc.l +++ b/src/interfaces/ecpg/preproc/pgc.l @@ -139,12 +139,13 @@ self [,()\[\].$\:\+\-\*\/\<\>\=\|] op_and_self [\~\!\@\#\%\^\&\|\`\?\$\:\+\-\*\/\<\>\=] operator {op_and_self}+ -xminteger {integer}/- -xmreal {real}/{space}*-{digit} xmstop - -integer -?{digit}+ -real -?{digit}+\.{digit}+([Ee][-+]?{digit}+)? +integer [\-]?{digit}+ +/* +real [\-]?{digit}+\.{digit}+([Ee][-+]?{digit}+)? +*/ +real [\-]?(((({digit}*\.{digit}+)|({digit}+\.{digit}*))([Ee][-+]?{digit}+)?)|({digit}+[Ee][-+]?{digit}+)) param \${integer} @@ -309,7 +310,8 @@ before_comment); {typecast} { return TYPECAST; } -{self}/-[\.0-9] { +{self}/{space}*-[\.0-9] { + BEGIN(xm); return (yytext[0]); } {self} { return (yytext[0]); } diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y index b4fa4f2e38..5e99e7190d 100644 --- a/src/interfaces/ecpg/preproc/preproc.y +++ b/src/interfaces/ecpg/preproc/preproc.y @@ -70,11 +70,13 @@ whenever_action(int mode) { if (mode == 1 && when_nf.code != W_NOTHING) { + output_line_number(); fprintf(yyout, "\nif (sqlca.sqlcode == ECPG_NOT_FOUND) "); print_action(&when_nf); } if (when_error.code != W_NOTHING) { + output_line_number(); fprintf(yyout, "\nif (sqlca.sqlcode < 0) "); print_action(&when_error); } @@ -112,7 +114,7 @@ static struct variable * find_variable(char * name); static struct variable * find_struct_member(char *name, char *str, struct ECPGstruct_member *members) { - char *next = strpbrk(++str, ".-"), c = '\0'; + char *next = strchr(++str, '.'), c = '\0'; if (next != NULL) { @@ -129,6 +131,8 @@ find_struct_member(char *name, char *str, struct ECPGstruct_member *members) /* found the end */ switch (members->typ->typ) { + case ECPGt_array: + return(new_variable(name, ECPGmake_array_type(members->typ->u.element, members->typ->size))); case ECPGt_struct: return(new_variable(name, ECPGmake_struct_type(members->typ->u.members))); default: @@ -138,8 +142,12 @@ find_struct_member(char *name, char *str, struct ECPGstruct_member *members) else { *next = c; - if (c == '-') next++; - return(find_struct_member(name, next, members->typ->u.members)); + if (c == '-') + { + next++; + return(find_struct_member(name, next, members->typ->u.element->u.members)); + } + else return(find_struct_member(name, next, members->typ->u.members)); } } } @@ -159,9 +167,12 @@ find_struct(char * name, char *next) /* restore the name, we will need it later on */ *next = c; - if (*next == '-') next++; - - return (find_struct_member(name, next, p->type->u.members)); + if (c == '-') + { + next++; + return (find_struct_member(name, next, p->type->u.element->u.members)); + } + else return (find_struct_member(name, next, p->type->u.members)); } static struct variable * @@ -178,12 +189,20 @@ find_simple(char * name) return(NULL); } +/* Note that this function will end the program in case of an unknown */ +/* variable */ static struct variable * find_variable(char * name) { char * next; - struct variable * p = - ((next = strpbrk(name, ".-")) != NULL) ? find_struct(name, next) : find_simple(name); + struct variable * p; + + if ((next = strchr(name, '.')) != NULL) + p = find_struct(name, next); + else if ((next = strstr(name, "->")) != NULL) + p = find_struct(name, next); + else + p = find_simple(name); if (p == NULL) { @@ -231,7 +250,6 @@ struct arguments { struct arguments * next; }; - static struct arguments * argsinsert = NULL; static struct arguments * argsresult = NULL; @@ -495,8 +513,9 @@ output_statement(char * stmt, int mode) } /* special embedded SQL token */ -%token SQL_BREAK SQL_CALL SQL_CONNECT SQL_CONTINUE SQL_DISCONNECT SQL_FOUND SQL_GO SQL_GOTO -%token SQL_IMMEDIATE SQL_INDICATOR SQL_OPEN SQL_RELEASE +%token SQL_BREAK SQL_CALL SQL_CONNECT SQL_CONNECTION SQL_CONTINUE +%token SQL_DISCONNECT SQL_FOUND SQL_GO SQL_GOTO +%token SQL_IDENTIFIED SQL_IMMEDIATE SQL_INDICATOR SQL_OPEN SQL_RELEASE %token SQL_SECTION SQL_SEMI SQL_SQLERROR SQL_SQLPRINT SQL_START %token SQL_STOP SQL_WHENEVER @@ -527,8 +546,9 @@ output_statement(char * stmt, int mode) PARTIAL, POSITION, PRECISION, PRIMARY, PRIVILEGES, PROCEDURE, PUBLIC, REFERENCES, REVOKE, RIGHT, ROLLBACK, SECOND_P, SELECT, SET, SUBSTRING, - TABLE, TIME, TIMESTAMP, TO, TRAILING, TRANSACTION, TRIM, - UNION, UNIQUE, UPDATE, USING, + TABLE, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE, + TO, TRAILING, TRANSACTION, TRIM, + UNION, UNIQUE, UPDATE, USER, USING, VALUES, VARCHAR, VARYING, VIEW, WHERE, WITH, WORK, YEAR_P, ZONE @@ -559,7 +579,7 @@ output_statement(char * stmt, int mode) * * Todd A. Brandys */ -%token USER, PASSWORD, CREATEDB, NOCREATEDB, CREATEUSER, NOCREATEUSER, VALID, UNTIL +%token PASSWORD, CREATEDB, NOCREATEDB, CREATEUSER, NOCREATEUSER, VALID, UNTIL /* Special keywords, not in the query language - see the "lex" file */ %token IDENT SCONST Op CSTRING CVARIABLE @@ -620,7 +640,7 @@ output_statement(char * stmt, int mode) %type sortby OptUseOp opt_inh_star relation_name_list name_list %type group_clause groupby_list groupby having_clause from_clause %type from_list from_val join_expr join_outer join_spec join_list -%type join_using where_clause relation_expr +%type join_using where_clause relation_expr row_op sub_type %type opt_column_list insert_rest InsertStmt %type columnList DeleteStmt LockStmt UpdateStmt CursorStmt %type NotifyStmt columnElem copy_dirn OptimizableStmt @@ -650,12 +670,16 @@ output_statement(char * stmt, int mode) %type DestroydbStmt ClusterStmt grantee RevokeStmt %type GrantStmt privileges operation_commalist operation -%type ECPGWhenever ECPGConnect db_name ECPGOpen open_opts +%type ECPGWhenever ECPGConnect connection_target ECPGOpen open_opts %type indicator ECPGExecute c_expr variable_list dotext %type storage_clause opt_initializer vartext c_anything blockstart %type blockend variable_list variable var_anything sql_anything %type opt_pointer ecpg_ident cvariable ECPGDisconnect dis_name -%type stmt symbol opt_symbol ECPGRelease execstring +%type stmt symbol opt_symbol ECPGRelease execstring server_name +%type connection_object opt_server opt_port +%type user_name opt_user char_variable ora_user ident +%type db_prefix server opt_options opt_connection_name +%type ECPGSetConnection %type simple_type type struct_type @@ -721,12 +745,12 @@ stmt: AddAttrStmt { output_statement($1, 0); } | VariableShowStmt { output_statement($1, 0); } | VariableResetStmt { output_statement($1, 0); } | ECPGConnect { - fprintf(yyout, "ECPGconnect(\"%s\");", $1); + fprintf(yyout, "ECPGconnect(__LINE__, %s);", $1); whenever_action(0); free($1); } | ECPGDisconnect { - fprintf(yyout, "ECPGdisconnect(\"%s\");", $1); + fprintf(yyout, "ECPGdisconnect(__LINE__, \"%s\");", $1); whenever_action(0); free($1); } @@ -737,6 +761,11 @@ stmt: AddAttrStmt { output_statement($1, 0); } } | ECPGOpen { output_statement($1, 0); } | ECPGRelease { /* output already done */ } + | ECPGSetConnection { + fprintf(yyout, "ECPGsetcon(__LINE__, %s);", $1); + whenever_action(0); + free($1); + } | ECPGWhenever { fputs($1, yyout); output_line_number(); @@ -828,7 +857,7 @@ user_group_clause: IN GROUP user_group_list { $$ = cat2_str(make1_str("in group | /*EMPTY*/ { $$ = make1_str(""); } ; -user_valid_clause: VALID UNTIL SCONST { $$ = cat2_str(make1_str("valid until"), $3);; } +user_valid_clause: VALID UNTIL Sconst { $$ = cat2_str(make1_str("valid until"), $3);; } | /*EMPTY*/ { $$ = make1_str(""); } ; @@ -853,6 +882,7 @@ VariableSetStmt: SET ColId TO var_value { $$ = cat2_str(make1_str("set time zone"), $4); } + ; var_value: Sconst { $$ = $1; } @@ -1136,7 +1166,9 @@ default_expr: AexprConst $$ = "current_timestamp"; } | CURRENT_USER - { $$ = make1_str("current user"); } + { $$ = make1_str("current_user"); } + | USER + { $$ = make1_str("user"); } ; /* ConstraintElem specifies constraint syntax which is not embedded into @@ -1459,7 +1491,7 @@ TriggerFuncArg: Iconst $$ = make_name(); } | Sconst { $$ = $1; } - | ecpg_ident { $$ = $1; } + | ident { $$ = $1; } ; DropTrigStmt: DROP TRIGGER name ON relation_name @@ -2434,8 +2466,10 @@ groupby: ColId having_clause: HAVING a_expr { +#if FALSE yyerror("HAVING clause not yet implemented"); -/* $$ = cat2_str(make1_str("having"), $2); use this line instead to enable HAVING */ +#endif + $$ = cat2_str(make1_str("having"), $2); } | /*EMPTY*/ { $$ = make1_str(""); } ; @@ -2610,7 +2644,7 @@ Generic: generic } ; -generic: ecpg_ident { $$ = $1; } +generic: ident { $$ = $1; } | TYPE_P { $$ = make1_str("type"); } ; @@ -2724,7 +2758,7 @@ opt_decimal: '(' Iconst ',' Iconst ')' Character: character '(' Iconst ')' { if (strncasecmp($1, "char", strlen("char")) && strncasecmp($1, "varchar", strlen("varchar"))) - yyerror("parse error"); + yyerror("internal parsing error; unrecognized character type"); if (atol($3) < 1) { sprintf(errortext, "length for '%s' type must be at least 1",$1); yyerror(errortext); @@ -2749,10 +2783,9 @@ Character: character '(' Iconst ')' character: CHARACTER opt_varying opt_charset opt_collate { - if (strlen($4) > 0) { - sprintf(errortext, "COLLATE %s not yet implemented",$4); - yyerror(errortext); - } + if (strlen($4) > 0) + fprintf(stderr, "COLLATE %s not yet implemented",$4); + $$ = cat4_str(make1_str("character"), $2, $3, $4); } | CHAR opt_varying { $$ = cat2_str(make1_str("char"), $2); } @@ -2809,6 +2842,7 @@ opt_interval: datetime { $$ = $1; } | DAY_P TO MINUTE_P { $$ = make1_str("day to minute"); } | DAY_P TO SECOND_P { $$ = make1_str("day to second"); } | HOUR_P TO MINUTE_P { $$ = make1_str("hour to minute"); } + | MINUTE_P TO SECOND_P { $$ = make1_str("minute to second"); } | HOUR_P TO SECOND_P { $$ = make1_str("hour to second"); } | /*EMPTY*/ { $$ = make1_str(""); } ; @@ -2831,6 +2865,11 @@ a_expr_or_null: a_expr /* Expressions using row descriptors * Define row_descriptor to allow yacc to break the reduce/reduce conflict * with singleton expressions. + * Eliminated lots of code by defining row_op and sub_type clauses. + * However, can not consolidate EXPR_LINK case with others subselects + * due to shift/reduce conflict with the non-subselect clause (the parser + * would have to look ahead more than one token to resolve the conflict). + * - thomas 1998-05-09 */ row_expr: '(' row_descriptor ')' IN '(' SubSelect ')' { @@ -2840,134 +2879,18 @@ row_expr: '(' row_descriptor ')' IN '(' SubSelect ')' { $$ = make5_str(make1_str("("), $2, make1_str(") not in ("), $7, make1_str(")")); } - | '(' row_descriptor ')' Op '(' SubSelect ')' - { - $$ = make3_str(make5_str(make1_str("("), $2, make1_str(")"), $4, make1_str("(")), $6, make1_str(")")); - } - | '(' row_descriptor ')' '+' '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")+("), $6, make1_str(")")); - } - | '(' row_descriptor ')' '-' '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")-("), $6, make1_str(")")); - } - | '(' row_descriptor ')' '/' '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")/("), $6, make1_str(")")); - } - | '(' row_descriptor ')' '*' '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")*("), $6, make1_str(")")); - } - | '(' row_descriptor ')' '<' '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")<("), $6, make1_str(")")); - } - | '(' row_descriptor ')' '>' '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")>("), $6, make1_str(")")); - } - | '(' row_descriptor ')' '=' '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")=("), $6, make1_str(")")); - } - | '(' row_descriptor ')' Op ANY '(' SubSelect ')' - { - $$ = cat3_str(make3_str(make1_str("("), $2, make1_str(")")), $4, make3_str(make1_str("any("), $7, make1_str(")"))); - } - | '(' row_descriptor ')' '+' ANY '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")+any("), $7, make1_str(")")); - } - | '(' row_descriptor ')' '-' ANY '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")-any("), $7, make1_str(")")); - } - | '(' row_descriptor ')' '/' ANY '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")/any("), $7, make1_str(")")); - } - | '(' row_descriptor ')' '*' ANY '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")*any("), $7, make1_str(")")); - } - | '(' row_descriptor ')' '<' ANY '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")' ANY '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")>any("), $7, make1_str(")")); - } - | '(' row_descriptor ')' '=' ANY '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")=any("), $7, make1_str(")")); - } - | '(' row_descriptor ')' Op ALL '(' SubSelect ')' - { - $$ = cat3_str(make3_str(make1_str("("), $2, make1_str(")")), $4, make3_str(make1_str("all("), $7, make1_str(")"))); - } - | '(' row_descriptor ')' '+' ALL '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")+all("), $7, make1_str(")")); - } - | '(' row_descriptor ')' '-' ALL '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")-all("), $7, make1_str(")")); - } - | '(' row_descriptor ')' '/' ALL '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")/all("), $7, make1_str(")")); - } - | '(' row_descriptor ')' '*' ALL '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")*all("), $7, make1_str(")")); - } - | '(' row_descriptor ')' '<' ALL '(' SubSelect ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")' ALL '(' SubSelect ')' + | '(' row_descriptor ')' row_op sub_type '(' SubSelect ')' { - $$ = make5_str(make1_str("("), $2, make1_str(")>all("), $7, make1_str(")")); + $$ = make4_str(make5_str(make1_str("("), $2, make1_str(")"), $4, $5), make1_str("("), $7, make1_str(")")); } - | '(' row_descriptor ')' '=' ALL '(' SubSelect ')' + | '(' row_descriptor ')' row_op '(' SubSelect ')' { - $$ = make5_str(make1_str("("), $2, make1_str(")=all("), $7, make1_str(")")); + $$ = make3_str(make5_str(make1_str("("), $2, make1_str(")"), $4, make1_str("(")), $6, make1_str(")")); } - | '(' row_descriptor ')' Op '(' row_descriptor ')' + | '(' row_descriptor ')' row_op '(' row_descriptor ')' { $$ = cat3_str(make3_str(make1_str("("), $2, make1_str(")")), $4, make3_str(make1_str("("), $6, make1_str(")"))); } - | '(' row_descriptor ')' '+' '(' row_descriptor ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")+("), $6, make1_str(")")); - } - | '(' row_descriptor ')' '-' '(' row_descriptor ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")-("), $6, make1_str(")")); - } - | '(' row_descriptor ')' '/' '(' row_descriptor ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")/("), $6, make1_str(")")); - } - | '(' row_descriptor ')' '*' '(' row_descriptor ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")*("), $6, make1_str(")")); - } - | '(' row_descriptor ')' '<' '(' row_descriptor ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")<("), $6, make1_str(")")); - } - | '(' row_descriptor ')' '>' '(' row_descriptor ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")>("), $6, make1_str(")")); - } - | '(' row_descriptor ')' '=' '(' row_descriptor ')' - { - $$ = make5_str(make1_str("("), $2, make1_str(")=("), $6, make1_str(")")); - } ; row_descriptor: row_list ',' a_expr @@ -2976,6 +2899,21 @@ row_descriptor: row_list ',' a_expr } ; +row_op: Op { $$ = $1; } + | '<' { $$ = "<"; } + | '=' { $$ = "="; } + | '>' { $$ = ">"; } + | '+' { $$ = "+"; } + | '-' { $$ = "-"; } + | '*' { $$ = "*"; } + | '/' { $$ = "/"; } + ; + +sub_type: ANY { $$ = make1_str("ANY"); } + | ALL { $$ = make1_str("ALL"); } + ; + + row_list: row_list ',' a_expr { $$ = cat3_str($1, make1_str(","), $3); @@ -3089,6 +3027,11 @@ a_expr: attr opt_indirection { $$ = make1_str("current_user"); } + | USER + { + $$ = make1_str("user"); + } + | EXISTS '(' SubSelect ')' { $$ = make3_str(make1_str("exists("), $3, make1_str(")")); @@ -3358,6 +3301,10 @@ b_expr: attr opt_indirection { $$ = make1_str("current_user"); } + | USER + { + $$ = make1_str("user"); + } | POSITION '(' position_list ')' { $$ = make3_str(make1_str("position ("), $3, make1_str(")")); @@ -3417,12 +3364,9 @@ extract_list: extract_arg FROM a_expr { $$ = make1_str(";;"); } ; -/* Add in TIMEZONE_HOUR and TIMEZONE_MINUTE for SQL92 compliance - * for next release. Just set up extract_arg for now... - * - thomas 1998-04-08 - */ -extract_arg: datetime - { $$ = $1; } +extract_arg: datetime { $$ = $1; } + | TIMEZONE_HOUR { $$ = make1_str("timezone_hour"); } + | TIMEZONE_MINUTE { $$ = make1_str("timezone_minute"); } ; position_list: position_expr IN position_expr @@ -3660,9 +3604,9 @@ relation_name: SpecialRuleRelation ; database_name: ColId { $$ = $1; }; -access_method: ecpg_ident { $$ = $1; }; +access_method: ident { $$ = $1; }; attr_name: ColId { $$ = $1; }; -class: ecpg_ident { $$ = $1; }; +class: ident { $$ = $1; }; index_name: ColId { $$ = $1; }; /* Functions @@ -3673,7 +3617,7 @@ name: ColId { $$ = $1; }; func_name: ColId { $$ = $1; }; file_name: Sconst { $$ = $1; }; -recipe_name: ecpg_ident { $$ = $1; }; +recipe_name: ident { $$ = $1; }; /* Constants * Include TRUE/FALSE for SQL3 support. - thomas 1997-10-24 @@ -3723,8 +3667,9 @@ Sconst: SCONST { strcpy($$+1, $1); $$[strlen($1)+2]='\0'; $$[strlen($1)+1]='\''; + free($1); } -UserId: ecpg_ident { $$ = $1;}; +UserId: ident { $$ = $1;}; /* Column and type identifier * Does not include explicit datetime types @@ -3746,7 +3691,7 @@ TypeId: ColId * list due to shift/reduce conflicts in yacc. If so, move * down to the ColLabel entity. - thomas 1997-11-06 */ -ColId: ecpg_ident { $$ = $1; } +ColId: ident { $$ = $1; } | datetime { $$ = $1; } | ACTION { $$ = make1_str("action"); } | CACHE { $$ = make1_str("cache"); } @@ -3773,9 +3718,10 @@ ColId: ecpg_ident { $$ = $1; } | START { $$ = make1_str("start"); } | STATEMENT { $$ = make1_str("statement"); } | TIME { $$ = make1_str("time"); } + | TIMEZONE_HOUR { $$ = make1_str("timezone_hour"); } + | TIMEZONE_MINUTE { $$ = make1_str("timezone_minute"); } | TRIGGER { $$ = make1_str("trigger"); } | TYPE_P { $$ = make1_str("type"); } - | USER { $$ = make1_str("user"); } | VALID { $$ = make1_str("valid"); } | VERSION { $$ = make1_str("version"); } | ZONE { $$ = make1_str("zone"); } @@ -4011,8 +3957,8 @@ variable: opt_pointer symbol opt_array_bounds opt_initializer if (struct_level == 0) new_variable($2, type); else - ECPGmake_struct_member($2, type, &(struct_member_list[struct_level-1])); - + ECPGmake_struct_member($2, type, &(struct_member_list[struct_level - 1]))->typ; + free($1); free($2); free($3.str); @@ -4028,10 +3974,114 @@ opt_pointer: /* empty */ { $$ = make1_str(""); } /* * the exec sql connect statement: connect to the given database */ -ECPGConnect: SQL_CONNECT db_name { $$ = $2; } +ECPGConnect: SQL_CONNECT TO connection_target opt_connection_name opt_user + { + $$ = make5_str($3, make1_str(","), $5, make1_str(","), $4); + } + | SQL_CONNECT TO DEFAULT + { + $$ = make1_str("NULL,NULL,NULL,\"DEFAULT\""); + } + /* also allow ORACLE syntax */ + | SQL_CONNECT ora_user + { + $$ = make3_str(make1_str("NULL,"), $2, make1_str(",NULL")); + } + +connection_target: database_name opt_server opt_port + { + /* old style: dbname[@server][:port] */ + if (strlen($2) > 0 && *($2) != '@') + { + sprintf(errortext, "parse error at or near '%s'", $2); + yyerror(errortext); + } -db_name: database_name { $$ = $1; } - | cvariable { /* check if we have a char variable */ + $$ = make5_str(make1_str("\""), $1, $2, $3, make1_str("\"")); + } + | db_prefix server opt_port '/' database_name opt_options + { + /* new style: esql:postgresql://server[:port][/dbname] */ + if (strncmp($2, "://", 3) != 0) + { + sprintf(errortext, "parse error at or near '%s'", $2); + yyerror(errortext); + } + + $$ = make4_str(make5_str(make1_str("\""), $1, $2, $3, make1_str("/")), $5, $6, make1_str("\"")); + } + | char_variable + { + $$ = $1; + } + +db_prefix: ident cvariable + { + if (strcmp($2, "postgresql") != 0 && strcmp($2, "postgres") != 0) + { + sprintf(errortext, "parse error at or near '%s'", $2); + yyerror(errortext); + } + + if (strcmp($1, "esql") != 0 && strcmp($1, "ecpg") != 0 && strcmp($1, "sql") != 0 && strcmp($1, "isql") != 0 && strcmp($1, "proc") != 0) + { + sprintf(errortext, "Illegal connection type %s", $1); + yyerror(errortext); + } + + $$ = make3_str($1, make1_str(":"), $2); + } + +server: Op server_name + { + if (strcmp($1, "@") != 0 && strcmp($1, "://") != 0) + { + sprintf(errortext, "parse error at or near '%s'", $1); + yyerror(errortext); + } + + $$ = make2_str($1, $2); + } + +opt_server: server { $$ = $1; } + | /* empty */ { $$ = make1_str(""); } + +server_name: ColId { $$ = $1; } + | ColId '.' server_name { $$ = make3_str($1, make1_str("."), $3); } + +opt_port: ':' Iconst { $$ = make2_str(make1_str(":"), $2); } + | /* empty */ { $$ = make1_str(""); } + +opt_connection_name: AS connection_target { $$ = $2; } + | /* empty */ { $$ = make1_str("NULL"); } + +opt_user: USER ora_user { $$ = $2; } + | /* empty */ { $$ = make1_str("NULL,NULL"); } + +ora_user: user_name + { + $$ = make2_str($1, make1_str(",NULL")); + } + | user_name '/' ColId + { + $$ = make3_str($1, make1_str(","), $3); + } + | user_name SQL_IDENTIFIED BY user_name + { + $$ = make3_str($1, make1_str(","), $4); + } + | user_name USING user_name + { + $$ = make3_str($1, make1_str(","), $3); + } + +user_name: UserId { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); } + | char_variable { $$ = $1; } + | CSTRING { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); } + | SCONST { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); } + +char_variable: cvariable + { /* check if we have a char variable */ struct variable *p = find_variable($1); enum ECPGttype typ = p->type->typ; @@ -4039,33 +4089,48 @@ db_name: database_name { $$ = $1; } if (typ == ECPGt_array) typ = p->type->u.element->typ; - if (typ != ECPGt_char && typ != ECPGt_unsigned_char) - yyerror("invalid datatype"); - $$ = $1; - } + switch (typ) + { + case ECPGt_char: + case ECPGt_unsigned_char: + $$ = $1; + break; + case ECPGt_varchar: + $$ = make2_str($1, make1_str(".arr")); + break; + default: + yyerror("invalid datatype"); + break; + } + } + +opt_options: Op ColId + { + if (strlen($1) == 0) + yyerror("parse error"); + + if (strcmp($1, "?") != 0) + { + sprintf(errortext, "parse error at or near %s", $1); + yyerror(errortext); + } + + $$ = make2_str(make1_str("?"), $2); + } + | /* empty */ { $$ = make1_str(""); } /* * the exec sql disconnect statement: disconnect from the given database */ ECPGDisconnect: SQL_DISCONNECT dis_name { $$ = $2; } -dis_name: database_name { $$ = $1; } - | cvariable { /* check if we have a char variable */ - struct variable *p = find_variable($1); - enum ECPGttype typ = p->type->typ; +dis_name: connection_object { $$ = $1; } + | CURRENT { $$ = make1_str("CURRENT"); } + | ALL { $$ = make1_str("ALL"); } + | /* empty */ { $$ = make1_str("CURRENT"); } - /* if array see what's inside */ - if (typ == ECPGt_array) - typ = p->type->u.element->typ; - - if (typ != ECPGt_char && typ != ECPGt_unsigned_char) - yyerror("invalid datatype"); - $$ = $1; - } - | CURRENT { $$ = make1_str(""); } - | DEFAULT { $$ = make1_str(""); } - | ALL { $$ = make1_str(""); } - | /* empty */ { $$ = make1_str(""); } +connection_object: connection_target { $$ = $1; } + | DEFAULT { $$ = make1_str("DEFAULT"); } /* * execute a given string as sql command @@ -4118,6 +4183,14 @@ ECPGRelease: TransactionStmt SQL_RELEASE free($1); } +/* + * set the actual connection, this needs a differnet handling as the other + * set commands + */ +ECPGSetConnection: SET SQL_CONNECTION connection_object + { + $$ = $3; + } /* * whenever statement: decide what to do in case of error/no data found * according to SQL standards we miss: SQLSTATE, CONSTRAINT, SQLEXCEPTION @@ -4163,7 +4236,7 @@ action : SQL_CONTINUE { | DO name '(' dotext ')' { $$.code = W_DO; $$.command = make4_str($2, make1_str("("), $4, make1_str(")")); - $$.str = cat2_str(make1_str("do"), $$.command); + $$.str = cat2_str(make1_str("do"), strdup($$.command)); } | DO SQL_BREAK { $$.code = W_BREAK; @@ -4173,7 +4246,7 @@ action : SQL_CONTINUE { | SQL_CALL name '(' dotext ')' { $$.code = W_DO; $$.command = make4_str($2, make1_str("("), $4, make1_str(")")); - $$.str = cat2_str(make1_str("call"), $$.command); + $$.str = cat2_str(make1_str("call"), strdup($$.command)); } /* some other stuff for ecpg */ @@ -4478,14 +4551,16 @@ civariableonly : cvariable name { add_variable(&argsinsert, find_variable($1), &no_indicator); } -cvariable: CVARIABLE { $$ = $1; } +cvariable: CVARIABLE { $$ = make1_str($1); } indicator: /* empty */ { $$ = NULL; } | cvariable { check_indicator((find_variable($1))->type); $$ = $1; } | SQL_INDICATOR cvariable { check_indicator((find_variable($2))->type); $$ = $2; } | SQL_INDICATOR name { check_indicator((find_variable($2))->type); $$ = $2; } -ecpg_ident: IDENT { $$ = $1; } +ident: IDENT { $$ = make1_str($1); } + +ecpg_ident: ident { $$ = $1; } | CSTRING { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); } /* * C stuff diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c index cbc7446909..ce0e1b0cd2 100644 --- a/src/interfaces/ecpg/preproc/type.c +++ b/src/interfaces/ecpg/preproc/type.c @@ -20,21 +20,6 @@ mm_alloc(size_t size) return (ptr); } -/* realloc + error check */ -void * -mm_realloc(void *ptr, size_t size) -{ - ptr = realloc(ptr, size); - - if (ptr == NULL) - { - fprintf(stderr, "Out of memory\n"); - exit(OUT_OF_MEMORY); - } - - return (ptr); -} - /* duplicate memberlist */ static struct ECPGstruct_member * struct_member_dup(struct ECPGstruct_member * rm) @@ -43,7 +28,22 @@ struct_member_dup(struct ECPGstruct_member * rm) while (rm) { - ECPGmake_struct_member(rm->name, rm->typ, &new); + struct ECPGtype *type; + + switch(rm->typ->typ) + { + case ECPGt_struct: + type = ECPGmake_struct_type(rm->typ->u.members); + break; + case ECPGt_array: + type = ECPGmake_array_type(ECPGmake_simple_type(rm->typ->u.element->typ, rm->typ->u.element->size), rm->typ->size); + break; + default: + type = ECPGmake_simple_type(rm->typ->typ, rm->typ->size); + break; + } + + ECPGmake_struct_member(rm->name, type, &new); rm = rm->next; } @@ -165,11 +165,11 @@ static const char *get_type(enum ECPGttype typ) size is the maxsize in case it is a varchar. Otherwise it is the size of the variable (required to do array fetches of structs). */ -void +static void ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ, long varcharsize, long arrsiz, const char *siz, const char *prefix); -void +static void ECPGdump_a_struct(FILE *o, const char *name, const char *ind_name, long arrsiz, struct ECPGtype * typ, struct ECPGtype * ind_typ, const char *offset, const char *prefix, const char * ind_prefix); @@ -223,7 +223,7 @@ ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * typ, const char *in /* If siz is NULL, then the offset is 0, if not use siz as a string, it represents the offset needed if we are in an array of structs. */ -void +static void ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ, long varcharsize, long arrsize, @@ -272,7 +272,7 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ, /* Penetrate a struct and dump the contents. */ -void +static void ECPGdump_a_struct(FILE *o, const char *name, const char * ind_name, long arrsiz, struct ECPGtype * typ, struct ECPGtype * ind_typ, const char *offsetarg, const char *prefix, const char *ind_prefix) { @@ -319,6 +319,7 @@ ECPGfree_struct_member(struct ECPGstruct_member * rm) rm = rm->next; free(p->name); + free(p->typ); free(p); } } @@ -337,7 +338,7 @@ ECPGfree_type(struct ECPGtype * typ) else if (typ->u.element->typ == ECPGt_struct) { /* Array of structs. */ - ECPGfree_struct_member(typ->u.members); + ECPGfree_struct_member(typ->u.element->u.members); free(typ->u.members); } else diff --git a/src/interfaces/ecpg/test/perftest.pgc b/src/interfaces/ecpg/test/perftest.pgc index f56d814dd9..6ec604d47f 100644 --- a/src/interfaces/ecpg/test/perftest.pgc +++ b/src/interfaces/ecpg/test/perftest.pgc @@ -28,7 +28,7 @@ exec sql begin declare section; exec sql end declare section; struct timeval tvs, tve; - exec sql connect mm; + exec sql connect to mm; exec sql create table perftest1(number int4, ascii char(16)); diff --git a/src/interfaces/ecpg/test/test1.pgc b/src/interfaces/ecpg/test/test1.pgc index 3363af07b1..c485a487fc 100644 --- a/src/interfaces/ecpg/test/test1.pgc +++ b/src/interfaces/ecpg/test/test1.pgc @@ -19,7 +19,7 @@ exec sql end declare section; ECPGdebug(1, dbgs); strcpy(msg, "connect"); - exec sql connect mm; + exec sql connect to mm; strcpy(msg, "create"); exec sql create table test(name char(8), amount int); diff --git a/src/interfaces/ecpg/test/test2.pgc b/src/interfaces/ecpg/test/test2.pgc index ed0599cdea..3b7ff3f9d5 100644 --- a/src/interfaces/ecpg/test/test2.pgc +++ b/src/interfaces/ecpg/test/test2.pgc @@ -11,9 +11,9 @@ exec sql begin declare section; short age; } birth; } personal; - struct personal_indicator { short name; - struct birth_indicator { short born; - int age; + struct personal_indicator { short ind_name; + struct birth_indicator { short ind_born; + int ind_age; } ind_birth; } ind_personal; long ind_married; @@ -26,7 +26,7 @@ exec sql end declare section; ECPGdebug(1, dbgs); strcpy(msg, "connect"); - exec sql connect mm; + exec sql connect to mm; strcpy(msg, "create"); exec sql create table meskes(name char(8), born integer, age smallint, married char(8)); -- 2.40.0