diff --git a/src/interfaces/ecpg/ChangeLog b/src/interfaces/ecpg/ChangeLog index 52a01f8e9ba..196f7c99e5e 100644 --- a/src/interfaces/ecpg/ChangeLog +++ b/src/interfaces/ecpg/ChangeLog @@ -400,3 +400,60 @@ Tue Feb 2 07:40:52 CET 1999 - Brought preproc.y in sync again with gram.y. - Set ecpg version to 2.4.9 +Wed Feb 3 18:28:46 CET 1999 + + - Started working on PREPARE statement. + - Fixed typo in preproc that cause CREATE statement to not work + anymore. + +Thu Feb 4 19:43:39 CET 1999 + + - Some parts of the PREPARE statement work now. + - Added EXECUTE command + - Added DEALLOCATE PREPARE command + +Fri Feb 5 18:25:07 CET 1999 + + - PREPARE seems to be working okay now. + - Fixed some minor bugs. + - Renamed y.tab.* to preproc.* + +Mon Feb 8 07:57:29 CET 1999 + + - Synced preproc.y with gram.y again. + - Allow ':' as positional variable in prepare statement also. + You can still specify ';;' instead of course. + - Added TYPE statement. + - Set library version to 2.7.0 + +Tue Feb 9 07:07:11 CET 1999 + + - Synced preproc.y with gram.y. + +Tue Feb 9 20:21:44 CET 1999 + + - Added FREE statement. + +Wed Feb 10 07:51:09 CET 1999 + + - Synced keyword.c. + +Sat Feb 13 10:44:43 CET 1999 + + - Added DECLARE STATEMENT for compatibility with Oracle. De facto + this statement does nothing. + - Added VAR statement. + +Son Feb 14 11:36:04 CET 1999 + + - Added type 'enum' to TYPE and VAR statement. + - Allow ecpg keywords as datatypes. + +Thu Feb 18 08:35:35 CET 1999 + + - Make sure indicator for array is array too. + +Fri Feb 19 18:38:45 CET 1999 + + - Finished type aliasing for structures. + - Set ecpg version to 2.5.0 diff --git a/src/interfaces/ecpg/TODO b/src/interfaces/ecpg/TODO index cac4f67056a..82b8a5d8b85 100644 --- a/src/interfaces/ecpg/TODO +++ b/src/interfaces/ecpg/TODO @@ -11,10 +11,10 @@ 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 unions + Missing statements: - - exec sql type - - exec sql prepare - exec sql allocate - - exqc sql free - SQLSTATE - - exec sql whenever sqlwarning diff --git a/src/interfaces/ecpg/include/ecpgerrno.h b/src/interfaces/ecpg/include/ecpgerrno.h index 56cf8d5f570..5fa7e704715 100644 --- a/src/interfaces/ecpg/include/ecpgerrno.h +++ b/src/interfaces/ecpg/include/ecpgerrno.h @@ -28,6 +28,8 @@ #define ECPG_NO_CONN -220 #define ECPG_NOT_CONN -221 +#define ECPG_INVALID_STMT -230 + /* finally the backend error messages, they start at 400 */ #define ECPG_PGSQL -400 #define ECPG_TRANS -401 diff --git a/src/interfaces/ecpg/include/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h index 730753efcda..4e1d6f9cde4 100644 --- a/src/interfaces/ecpg/include/ecpglib.h +++ b/src/interfaces/ecpg/include/ecpglib.h @@ -11,7 +11,10 @@ extern "C" bool ECPGdo(int, char *,...); bool ECPGtrans(int, const char *); bool ECPGdisconnect(int, const char *); - + bool ECPGprepare(int, char *, char *); + bool ECPGdeallocate(int, char *); + char *ECPGprepared_statement(char *); + void ECPGlog(const char *format,...); #ifdef LIBPQ_FE_H diff --git a/src/interfaces/ecpg/include/ecpgtype.h b/src/interfaces/ecpg/include/ecpgtype.h index 945ce0b98d0..e92220481da 100644 --- a/src/interfaces/ecpg/include/ecpgtype.h +++ b/src/interfaces/ecpg/include/ecpgtype.h @@ -45,7 +45,8 @@ extern "C" ECPGt_struct, ECPGt_EOIT, /* End of insert types. */ ECPGt_EORT, /* End of result types. */ - ECPGt_NO_INDICATOR /* no indicator */ + ECPGt_NO_INDICATOR, /* no indicator */ + ECPGt_char_variable }; #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 9173f36c4ee..195ded19680 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.40 1999/01/21 20:01:32 scrappy Exp $ +# $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/Makefile.in,v 1.41 1999/02/20 07:00:53 scrappy Exp $ # #------------------------------------------------------------------------- NAME= ecpg SO_MAJOR_VERSION= 2 -SO_MINOR_VERSION= 6.3 +SO_MINOR_VERSION= 7.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 62769941a7c..01fe37298bb 100644 --- a/src/interfaces/ecpg/lib/ecpglib.c +++ b/src/interfaces/ecpg/lib/ecpglib.c @@ -75,12 +75,19 @@ struct variable struct statement { - int lineno; + int lineno; char *command; struct variable *inlist; struct variable *outlist; }; +struct prepared_statement +{ + char *name; + struct statement *stmt; + struct prepared_statement *next; +} *prep_stmts = NULL; + static int simple_debug = 0; static FILE *debugstream = NULL; static int committed = true; @@ -196,6 +203,41 @@ quote_postgres(char *arg, int lineno) return res; } +/* This function returns a newly malloced string that has the \ + in the strings inside the argument quoted with another \. + */ +static +char * +quote_strings(char *arg, int lineno) +{ + char *res = (char *) ecpg_alloc(2 * strlen(arg) + 1, lineno); + int i, + ri; + bool string = false; + + if (!res) + return (res); + + for (i = 0, ri = 0; arg[i]; i++, ri++) + { + switch (arg[i]) + { + case '\'': + string = string ? false : true; + break; + case '\\': + res[ri++] = '\\'; + default: + ; + } + + res[ri] = arg[i]; + } + res[ri] = '\0'; + + return res; +} + /* create a list of variables */ static bool create_statement(int lineno, struct statement ** stmt, char *query, va_list ap) @@ -236,6 +278,14 @@ 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 (var->value == NULL) + { + ECPGlog("create_statement: invalid statement name\n"); + register_error(ECPG_INVALID_STMT, "Invalid statement name in line %d", lineno); + free(var); + return false; + } for (ptr = *list; ptr && ptr->next; ptr = ptr->next); @@ -251,6 +301,19 @@ create_statement(int lineno, struct statement ** stmt, char *query, va_list ap) return (true); } +static char * +next_insert(char *text) +{ + char *ptr = text; + bool string = false; + + for (; ptr[1] != '\0' && (ptr[0] != ';' || ptr[1] != ';' || string); ptr++) + if (ptr[0] == '\'') + string = string ? false : true; + + return (ptr[1] == '\0') ? NULL : ptr; +} + static bool ECPGexecute(struct statement * stmt) { @@ -379,7 +442,30 @@ ECPGexecute(struct statement * stmt) tobeinserted = mallocedval; } break; + case ECPGt_char_variable: + { + /* set slen to string length if type is char * */ + int slen = (var->varcharsize == 0) ? strlen((char *) var->value) : var->varcharsize; + char *tmp; + if (!(newcopy = ecpg_alloc(slen + 1, stmt->lineno))) + return false; + + strncpy(newcopy, (char *) var->value, slen); + newcopy[slen] = '\0'; + if (!(mallocedval = (char *) ecpg_alloc(2 * strlen(newcopy) + 1, stmt->lineno))) + return false; + + tmp = quote_strings(newcopy, stmt->lineno); + if (!tmp) + return false; + + strcat(mallocedval, tmp); + free(newcopy); + + tobeinserted = mallocedval; + } + break; case ECPGt_varchar: { struct ECPGgeneric_varchar *variable = @@ -428,7 +514,7 @@ ECPGexecute(struct statement * stmt) return false; strcpy(newcopy, copiedquery); - if ((p = strstr(newcopy, ";;")) == NULL) + if ((p = next_insert(newcopy)) == NULL) { /* @@ -449,7 +535,7 @@ ECPGexecute(struct statement * stmt) strcat(newcopy, copiedquery + (p - newcopy) - + 2 /* Length of ;; */ ); + + sizeof(";;") - 1 /* don't count the '\0' */); } /* @@ -470,7 +556,7 @@ ECPGexecute(struct statement * stmt) } /* Check if there are unmatched things left. */ - if (strstr(copiedquery, ";;") != NULL) + if (next_insert(copiedquery) != NULL) { register_error(ECPG_TOO_FEW_ARGUMENTS, "Too few arguments line %d.", stmt->lineno); return false; @@ -898,7 +984,21 @@ ECPGtrans(int lineno, const char *transaction) PQclear(res); } if (strcmp(transaction, "commit") == 0 || strcmp(transaction, "rollback") == 0) + { + struct prepared_statement *this; + committed = 1; + + /* deallocate all prepared statements */ + for (this = prep_stmts; this != NULL; this = this->next) + { + bool b = ECPGdeallocate(lineno, this->name); + + if (!b) + return false; + } + } + return TRUE; } @@ -1033,3 +1133,109 @@ sqlprint(void) sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = '\0'; printf("sql error %s\n", sqlca.sqlerrm.sqlerrmc); } + +static void +replace_variables(char *text) +{ + char *ptr = text; + bool string = false; + + for (; *ptr != '\0'; ptr++) + { + if (*ptr == '\'') + string = string ? false : true; + + if (!string && *ptr == ':') + { + ptr[0] = ptr[1] = ';'; + for (ptr += 2; *ptr && *ptr != ' '; ptr++) + *ptr = ' '; + } + } +} + +/* handle the EXEC SQL PREPARE statement */ +bool +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) + { + bool b = ECPGdeallocate(lineno, name); + + if (!b) + return false; + } + + this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno); + if (!this) + return false; + + stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno); + if (!stmt) + { + free(this); + return false; + } + + /* create statement */ + stmt->lineno = lineno; + stmt->command = ecpg_strdup(variable, lineno); + stmt->inlist = stmt->outlist = NULL; + + /* if we have C variables in our statment replace them with ';;' */ + replace_variables(stmt->command); + + /* add prepared statement to our list */ + this->name = ecpg_strdup(name, lineno); + this->stmt = stmt; + + if (prep_stmts == NULL) + this->next = NULL; + else + this->next = prep_stmts; + + prep_stmts = this; + return true; +} + +/* handle the EXEC SQL DEALLOCATE PREPARE statement */ +bool +ECPGdeallocate(int lineno, char *name) +{ + struct prepared_statement *this, *prev; + + /* check if we really have prepared this statement */ + for (this = prep_stmts, prev = NULL; this != NULL && strcmp(this->name, name) != 0; prev = this, this = this->next); + if (this) + { + /* okay, free all the resources */ + free(this->name); + free(this->stmt->command); + free(this->stmt); + if (prev != NULL) + prev->next = this->next; + else + prep_stmts = this->next; + + return true; + } + ECPGlog("deallocate_prepare: invalid statement name %s\n", name); + register_error(ECPG_INVALID_STMT, "Invalid statement name %s in line %d", name, lineno); + return false; +} + +/* return the prepared statement */ +char * +ECPGprepared_statement(char *name) +{ + struct prepared_statement *this; + + for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next); + return (this) ? this->stmt->command : NULL; +} + diff --git a/src/interfaces/ecpg/lib/typename.c b/src/interfaces/ecpg/lib/typename.c index 0ee2a39cc91..af87b160aa9 100644 --- a/src/interfaces/ecpg/lib/typename.c +++ b/src/interfaces/ecpg/lib/typename.c @@ -8,7 +8,7 @@ ECPGtype_name(enum ECPGttype typ) { switch (typ) { - case ECPGt_char: + case ECPGt_char: return "char"; case ECPGt_unsigned_char: return "unsigned char"; @@ -32,6 +32,8 @@ ECPGtype_name(enum ECPGttype typ) return "bool"; case ECPGt_varchar: return "varchar"; + case ECPGt_char_variable: + return "char"; default: abort(); } diff --git a/src/interfaces/ecpg/preproc/Makefile b/src/interfaces/ecpg/preproc/Makefile index 7fd98c8f06b..87e788c83cc 100644 --- a/src/interfaces/ecpg/preproc/Makefile +++ b/src/interfaces/ecpg/preproc/Makefile @@ -2,20 +2,25 @@ SRCDIR= ../../.. include $(SRCDIR)/Makefile.global MAJOR_VERSION=2 -MINOR_VERSION=4 -PATCHLEVEL=9 +MINOR_VERSION=5 +PATCHLEVEL=0 CFLAGS+=-I../include -DMAJOR_VERSION=$(MAJOR_VERSION) \ -DMINOR_VERSION=$(MINOR_VERSION) -DPATCHLEVEL=$(PATCHLEVEL) \ - -DINCLUDE_PATH=\"$(DESTDIR)$(HEADERDIR)\" + -DINCLUDE_PATH=\"$(DESTDIR)$(HEADERDIR)\" -OBJ=y.tab.o pgc.o type.o ecpg.o ecpg_keywords.o ../../../backend/parser/scansup.o \ +OBJ=preproc.o pgc.o type.o ecpg.o ecpg_keywords.o ../../../backend/parser/scansup.o \ keywords.o c_keywords.o ../lib/typename.o all:: ecpg +preproc.c preproc.h: preproc.y + $(YACC) $(YFLAGS) $< + mv y.tab.c preproc.c + mv y.tab.h preproc.h + clean: - rm -f *.o core a.out ecpg$(X) y.tab.h y.tab.c pgc.c *~ + rm -f *.o core a.out ecpg$(X) *~ install: all $(INSTALL) $(INSTL_EXE_OPTS) ecpg$(X) $(DESTDIR)$(BINDIR) @@ -31,13 +36,10 @@ pgc.c: pgc.l $(LEX) $< mv lex.yy.c pgc.c -y.tab.h y.tab.c: preproc.y - $(YACC) $(YFLAGS) $< - -y.tab.o : y.tab.h ../include/ecpgtype.h keywords.c c_keywords.c ecpg_keywords.c +preproc.o : preproc.h ../include/ecpgtype.h keywords.c c_keywords.c ecpg_keywords.c type.o : ../include/ecpgtype.h -pgc.o : ../include/ecpgtype.h keywords.c c_keywords.c ecpg_keywords.c y.tab.h -keywords.o: ../include/ecpgtype.h y.tab.h -c_keywords.o: ../include/ecpgtype.h y.tab.h -ecpg_keywords.o: ../include/ecpgtype.h y.tab.h +pgc.o : ../include/ecpgtype.h keywords.c c_keywords.c ecpg_keywords.c preproc.h +keywords.o: ../include/ecpgtype.h preproc.h +c_keywords.o: ../include/ecpgtype.h preproc.h +ecpg_keywords.o: ../include/ecpgtype.h preproc.h diff --git a/src/interfaces/ecpg/preproc/c_keywords.c b/src/interfaces/ecpg/preproc/c_keywords.c index 8629b0d133e..5395b533d38 100644 --- a/src/interfaces/ecpg/preproc/c_keywords.c +++ b/src/interfaces/ecpg/preproc/c_keywords.c @@ -9,9 +9,8 @@ #include #include "postgres.h" -#include "type.h" -#include "y.tab.h" #include "extern.h" +#include "preproc.h" /* * List of (keyword-name, keyword-token-value) pairs. diff --git a/src/interfaces/ecpg/preproc/ecpg.c b/src/interfaces/ecpg/preproc/ecpg.c index 11ed36ef65c..2594718cd12 100644 --- a/src/interfaces/ecpg/preproc/ecpg.c +++ b/src/interfaces/ecpg/preproc/ecpg.c @@ -25,6 +25,7 @@ extern char *optarg; struct _include_path *include_paths; int no_auto_trans = 0; struct cursor *cur = NULL; +struct typedefs *types = NULL; static void usage(char *progname) @@ -155,22 +156,22 @@ main(int argc, char *const argv[]) { struct cursor *ptr; struct _defines *defptr; - + struct typedefs *typeptr; + /* remove old cursor definitions if any are still there */ for (ptr = cur; ptr != NULL;) { struct cursor *this = ptr; - struct arguments *l1, - *l2; + struct arguments *l1, *l2; free(ptr->command); free(ptr->name); - for (l1 = argsinsert; l1; l1 = l2) + for (l1 = ptr->argsinsert; l1; l1 = l2) { l2 = l1->next; free(l1); } - for (l1 = argsresult; l1; l1 = l2) + for (l1 = ptr->argsresult; l1; l1 = l2) { l2 = l1->next; free(l1); @@ -189,7 +190,19 @@ main(int argc, char *const argv[]) defptr = defptr->next; free(this); } + + /* and old typedefs */ + for (typeptr = types; typeptr != NULL;) + { + struct typedefs *this = typeptr; + free(typeptr->name); + free(typeptr->type); + ECPGfree_struct_member(typeptr->struct_member_list); + typeptr = typeptr->next; + free(this); + } + /* initialize lex */ lex_init(); diff --git a/src/interfaces/ecpg/preproc/ecpg_keywords.c b/src/interfaces/ecpg/preproc/ecpg_keywords.c index 5a1bfb8ee07..52eea065b83 100644 --- a/src/interfaces/ecpg/preproc/ecpg_keywords.c +++ b/src/interfaces/ecpg/preproc/ecpg_keywords.c @@ -9,9 +9,8 @@ #include #include "postgres.h" -#include "type.h" #include "extern.h" -#include "y.tab.h" +#include "preproc.h" /* * List of (keyword-name, keyword-token-value) pairs. @@ -21,25 +20,38 @@ */ static ScanKeyword ScanKeywords[] = { /* name value */ + {"bool", SQL_BOOL}, {"break", SQL_BREAK}, {"call", SQL_CALL}, {"connect", SQL_CONNECT}, {"connection", SQL_CONNECTION}, {"continue", SQL_CONTINUE}, + {"deallocate", SQL_DEALLOCATE}, {"disconnect", SQL_DISCONNECT}, + {"enum", SQL_ENUM}, {"found", SQL_FOUND}, + {"free", SQL_FREE}, {"go", SQL_GO}, {"goto", SQL_GOTO}, {"identified", SQL_IDENTIFIED}, {"immediate", SQL_IMMEDIATE}, {"indicator", SQL_INDICATOR}, + {"int", SQL_INT}, + {"long", SQL_LONG}, {"open", SQL_OPEN}, + {"prepare", SQL_PREPARE}, + {"reference", SQL_REFERENCE}, {"release", SQL_RELEASE}, {"section", SQL_SECTION}, + {"short", SQL_SHORT}, + {"signed", SQL_SIGNED}, {"sqlerror", SQL_SQLERROR}, {"sqlprint", SQL_SQLPRINT}, {"sqlwarning", SQL_SQLWARNING}, {"stop", SQL_STOP}, + {"struct", SQL_STRUCT}, + {"unsigned", SQL_UNSIGNED}, + {"var", SQL_VAR}, {"whenever", SQL_WHENEVER}, }; diff --git a/src/interfaces/ecpg/preproc/extern.h b/src/interfaces/ecpg/preproc/extern.h index c7bb651a586..d5bc1fc9cab 100644 --- a/src/interfaces/ecpg/preproc/extern.h +++ b/src/interfaces/ecpg/preproc/extern.h @@ -1,63 +1,23 @@ #include "parser/keywords.h" +#include "type.h" #include /* variables */ extern int braces_open, - no_auto_trans; + no_auto_trans, struct_level; extern char *yytext; extern int yylineno, yyleng; extern FILE *yyin, *yyout; -struct _include_path -{ - char *path; - struct _include_path *next; -}; - extern struct _include_path *include_paths; - -struct cursor -{ - char *name; - char *command; - struct arguments *argsinsert; - struct arguments *argsresult; - struct cursor *next; -}; - extern struct cursor *cur; - -struct _defines -{ - char *old; - char *new; - struct _defines *next; -}; - +extern struct typedefs *types; extern struct _defines *defines; - -/* This is a linked list of the variable names and types. */ -struct variable -{ - char *name; - struct ECPGtype *type; - int brace_level; - struct variable *next; -}; - extern struct ECPGtype ecpg_no_indicator; extern struct variable no_indicator; - -struct arguments -{ - struct variable *variable; - struct variable *indicator; - struct arguments *next; -}; - extern struct arguments *argsinsert; extern struct arguments *argsresult; @@ -74,9 +34,10 @@ extern void yyerror(char *); /* return codes */ -#define OK 0 -#define PARSE_ERROR -1 -#define ILLEGAL_OPTION -2 +#define OK 0 +#define PARSE_ERROR -1 +#define ILLEGAL_OPTION -2 +#define INDICATOR_NOT_ARRAY -3 #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 c12504b392f..bcbb3242de9 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.10 1999/02/13 23:22:35 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/keywords.c,v 1.11 1999/02/20 07:01:00 scrappy Exp $ * *------------------------------------------------------------------------- */ @@ -18,7 +18,7 @@ #include "nodes/parsenodes.h" #include "nodes/pg_list.h" #include "type.h" -#include "y.tab.h" +#include "preproc.h" #include "parser/keywords.h" #include "utils/elog.h" @@ -69,9 +69,6 @@ static ScanKeyword ScanKeywords[] = { {"createdb", CREATEDB}, {"createuser", CREATEUSER}, {"cross", CROSS}, - {"current", CURRENT}, /* 6.4 to 6.5 is migration time! CURRENT - * will be removed in 6.5! Use OLD keyword - * in rules. Jan */ {"current_date", CURRENT_DATE}, {"current_time", CURRENT_TIME}, {"current_timestamp", CURRENT_TIMESTAMP}, @@ -96,6 +93,7 @@ static ScanKeyword ScanKeywords[] = { {"end", END_TRANS}, /***S*I***/ {"except", EXCEPT}, + {"execute", EXECUTE}, {"exists", EXISTS}, {"explain", EXPLAIN}, @@ -125,6 +123,7 @@ static ScanKeyword ScanKeywords[] = { {"instead", INSTEAD}, /***S*I***/ {"intersect", INTERSECT}, + {"interval", INTERVAL}, {"into", INTO}, {"is", IS}, @@ -138,6 +137,7 @@ static ScanKeyword ScanKeywords[] = { {"left", LEFT}, {"level", LEVEL}, {"like", LIKE}, + {"limit", LIMIT}, {"listen", LISTEN}, {"load", LOAD}, {"local", LOCAL}, @@ -167,6 +167,7 @@ static ScanKeyword ScanKeywords[] = { {"nullif", NULLIF}, {"numeric", NUMERIC}, {"of", OF}, + {"offset", OFFSET}, {"oids", OIDS}, {"old", CURRENT}, {"on", ON}, diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l index e6ef819a595..5ddac659bbc 100644 --- a/src/interfaces/ecpg/preproc/pgc.l +++ b/src/interfaces/ecpg/preproc/pgc.l @@ -24,9 +24,8 @@ #include "nodes/parsenodes.h" #include "parser/gramparse.h" #include "parser/scansup.h" -#include "type.h" #include "extern.h" -#include "y.tab.h" +#include "preproc.h" #include "utils/builtins.h" /* some versions of lex define this as a macro */ @@ -241,7 +240,7 @@ cppline {space}*#.*(\\{space}*\n)*\n* } {xqstop} { BEGIN(SQL); - yylval.str = strdup(scanstr(literal)); + yylval.str = mm_strdup(scanstr(literal)); return SCONST; } {xqdouble} | @@ -276,7 +275,7 @@ cppline {space}*#.*(\\{space}*\n)*\n* } {xdstop} { BEGIN(SQL); - yylval.str = strdup(literal); + yylval.str = mm_strdup(literal); return CSTRING; } {xdinside} { @@ -292,7 +291,7 @@ cppline {space}*#.*(\\{space}*\n)*\n* } {xdstop} { BEGIN(C); - yylval.str = strdup(literal); + yylval.str = mm_strdup(literal); return CSTRING; } {xdinside} { @@ -316,14 +315,14 @@ cppline {space}*#.*(\\{space}*\n)*\n* } {self} { return yytext[0]; } {operator}/-[\.0-9] { - yylval.str = strdup((char*)yytext); + yylval.str = mm_strdup((char*)yytext); return Op; } {operator} { if (strcmp((char*)yytext,"!=") == 0) - yylval.str = strdup("<>"); /* compatability */ + yylval.str = mm_strdup("<>"); /* compatability */ else - yylval.str = strdup((char*)yytext); + yylval.str = mm_strdup((char*)yytext); return Op; } {param} { @@ -342,7 +341,6 @@ cppline {space}*#.*(\\{space}*\n)*\n* if (isascii((unsigned char)lower_text[i]) && isupper(lower_text[i])) lower_text[i] = tolower(lower_text[i]); -printf("yyt= %s, lt = %s\n", yytext, lower_text); keyword = ScanKeywordLookup((char*)lower_text); if (keyword != NULL) { return keyword->value; @@ -367,7 +365,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text); yb->buffer = YY_CURRENT_BUFFER; yb->lineno = yylineno; - yb->filename = strdup(input_filename); + yb->filename = mm_strdup(input_filename); yb->next = yy_buffer; yy_buffer = yb; @@ -378,7 +376,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text); } if (ptr == NULL) { - yylval.str = strdup((char*)yytext); + yylval.str = mm_strdup((char*)yytext); return IDENT; } } @@ -470,7 +468,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text); return ICONST; } :{identifier}(("->"|\.){identifier})* { - yylval.str = strdup((char*)yytext+1); + yylval.str = mm_strdup((char*)yytext+1); return(CVARIABLE); } {identifier} { @@ -484,7 +482,6 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text); if (isascii((unsigned char)lower_text[i]) && isupper(lower_text[i])) lower_text[i] = tolower(lower_text[i]); -printf("yyt= %s, lt = %s\n", yytext, lower_text); keyword = ScanKeywordLookup((char*)lower_text); if (keyword != NULL) { return keyword->value; @@ -509,7 +506,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text); yb->buffer = YY_CURRENT_BUFFER; yb->lineno = yylineno; - yb->filename = strdup(input_filename); + yb->filename = mm_strdup(input_filename); yb->next = yy_buffer; yy_buffer = yb; @@ -520,19 +517,26 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text); } if (ptr == NULL) { - yylval.str = strdup((char*)yytext); + yylval.str = mm_strdup((char*)yytext); return IDENT; } } } } {space} { /* ignore */ } -";" { BEGIN C; return SQL_SEMI; } +";" { /* + * We may find a ';' inside a structure + * definition in a TYPE or VAR statement. + * This is not a EOL marker. + */ + if (struct_level == 0) + BEGIN C; + return SQL_SEMI; } {other} { return yytext[0]; } {exec}{space}{sql} { BEGIN SQL; return SQL_START; } {ccomment} { /* ignore */ } {cppline} { - yylval.str = strdup((char*)yytext); + yylval.str = mm_strdup((char*)yytext); return(CPP_LINE); } {identifier} { @@ -556,7 +560,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text); yb->buffer = YY_CURRENT_BUFFER; yb->lineno = yylineno; - yb->filename = strdup(input_filename); + yb->filename = mm_strdup(input_filename); yb->next = yy_buffer; yy_buffer = yb; @@ -567,7 +571,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text); } if (ptr == NULL) { - yylval.str = strdup((char*)yytext); + yylval.str = mm_strdup((char*)yytext); return IDENT; } } @@ -585,7 +589,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text); {exec}{space}{sql}{space}{define} {BEGIN(def_ident);} {space} {} {identifier} { - old = strdup(yytext); + old = mm_strdup(yytext); BEGIN(def); llen = 0; *literal = '\0'; @@ -599,7 +603,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text); if (strcmp(old, ptr->old) == 0) { free(ptr->new); - ptr->new = strdup(scanstr(literal)); + ptr->new = mm_strdup(scanstr(literal)); } } if (ptr == NULL) @@ -608,7 +612,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text); /* initial definition */ this->old = old; - this->new = strdup(scanstr(literal)); + this->new = mm_strdup(scanstr(literal)); this->next = defines; defines = this; } @@ -666,7 +670,7 @@ printf("yyt= %s, lt = %s\n", yytext, lower_text); exit(NO_INCLUDE_FILE); } - input_filename = strdup(inc_file); + input_filename = mm_strdup(inc_file); yy_switch_to_buffer(yy_create_buffer(yyin,YY_BUF_SIZE )); yylineno = 0; diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y index 355bf31dda0..7bb95d77e8c 100644 --- a/src/interfaces/ecpg/preproc/preproc.y +++ b/src/interfaces/ecpg/preproc/preproc.y @@ -6,7 +6,6 @@ #include "catalog/catname.h" #include "utils/numeric.h" -#include "type.h" #include "extern.h" #ifdef MULTIBYTE @@ -18,10 +17,10 @@ /* * Variables containing simple states. */ -static int struct_level = 0; +int struct_level = 0; static char errortext[128]; static int QueryIsRule = 0, ForUpdateNotAllowed = 0; -static enum ECPGttype actual_type[STRUCT_DEPTH]; +static struct this_type actual_type[STRUCT_DEPTH]; static char *actual_storage[STRUCT_DEPTH]; /* temporarily store struct members while creating the data structure */ @@ -30,6 +29,8 @@ struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH] = { NULL }; struct ECPGtype ecpg_no_indicator = {ECPGt_NO_INDICATOR, 0L, {NULL}}; struct variable no_indicator = {"no_indicator", &ecpg_no_indicator, 0, NULL}; +struct ECPGtype ecpg_query = {ECPGt_char_variable, 0L, {NULL}}; + /* * Handle the filename and line numbering. */ @@ -505,6 +506,98 @@ output_statement(char * stmt, int mode) free(stmt); } +static struct typedefs * +get_typedef(char *name) +{ + struct typedefs *this; + + for (this = types; this && strcmp(this->name, name); this = this->next); + if (!this) + { + sprintf(errortext, "invalid datatype '%s'", name); + yyerror(errortext); + } + + return(this); +} + +static void +adjust_array(enum ECPGttype type_enum, int *dimension, int *length, int type_dimension, int type_index, bool pointer) +{ + if (type_index >= 0) + { + if (*length >= 0) + yyerror("No multi-dimensional array support"); + + *length = type_index; + } + + if (type_dimension >= 0) + { + if (*dimension >= 0 && *length >= 0) + yyerror("No multi-dimensional array support"); + + if (*dimension >= 0) + *length = *dimension; + + *dimension = type_dimension; + } + + switch (type_enum) + { + case ECPGt_struct: + /* pointer has to get dimension 0 */ + if (pointer) + { + *length = *dimension; + *dimension = 0; + } + + if (*length >= 0) + yyerror("No multi-dimensional array support for structures"); + + break; + case ECPGt_varchar: + /* pointer has to get length 0 */ + if (pointer) + *length=0; + + /* one index is the string length */ + if (*length < 0) + { + *length = *dimension; + *dimension = -1; + } + + break; + case ECPGt_char: + case ECPGt_unsigned_char: + /* pointer has to get length 0 */ + if (pointer) + *length=0; + + /* one index is the string length */ + if (*length < 0) + { + *length = (*dimension < 0) ? 1 : *dimension; + *dimension = -1; + } + + break; + default: + /* a pointer has dimension = 0 */ + if (pointer) { + *length = *dimension; + *dimension = 0; + } + + if (*length >= 0) + yyerror("No multi-dimensional array support for simple data types"); + + break; + } +} + %} %union { @@ -519,12 +612,15 @@ output_statement(char * stmt, int mode) } /* special embedded SQL token */ -%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 -%token SQL_PREPARE SQL_RELEASE -%token SQL_SECTION SQL_SEMI SQL_SQLERROR SQL_SQLPRINT SQL_START -%token SQL_STOP SQL_WHENEVER SQL_SQLWARNING +%token 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 +%token SQL_IDENTIFIED SQL_IMMEDIATE SQL_INDICATOR SQL_INT SQL_LONG +%token SQL_OPEN SQL_PREPARE SQL_RELEASE SQL_REFERENCE +%token SQL_SECTION SQL_SEMI SQL_SHORT SQL_SIGNED SQL_SQLERROR SQL_SQLPRINT +%token SQL_SQLWARNING SQL_START SQL_STOP SQL_STRUCT SQL_UNSIGNED +%token SQL_VAR SQL_WHENEVER /* C token */ %token S_ANYTHING S_AUTO S_BOOL S_CHAR S_CONST S_DOUBLE S_ENUM S_EXTERN @@ -577,9 +673,9 @@ output_statement(char * stmt, int mode) DATABASE, DELIMITERS, DO, EACH, ENCODING, EXPLAIN, EXTEND, FORWARD, FUNCTION, HANDLER, INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL, - LANCOMPILER, LISTEN, UNLISTEN, LOAD, LOCATION, LOCK_P, MAXVALUE, MINVALUE, MOVE, + LANCOMPILER, LIMIT, LISTEN, UNLISTEN, LOAD, LOCATION, LOCK_P, MAXVALUE, MINVALUE, MOVE, NEW, NOCREATEDB, NOCREATEUSER, NONE, NOTHING, NOTIFY, NOTNULL, - OIDS, OPERATOR, PASSWORD, PROCEDURAL, + OFFSET, OIDS, OPERATOR, PASSWORD, PROCEDURAL, RECIPE, RENAME, RESET, RETURNS, ROW, RULE, SERIAL, SEQUENCE, SETOF, SHOW, START, STATEMENT, STDIN, STDOUT, TRUSTED, UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION @@ -654,8 +750,9 @@ output_statement(char * stmt, int mode) %type index_opt_unique IndexStmt set_opt func_return def_rest %type func_args_list func_args opt_with ProcedureStmt def_arg %type def_elem def_list definition def_name def_type DefineStmt -%type opt_instead event event_object OptStmtMulti OptStmtBlock -%type OptStmtList RuleStmt opt_column opt_name oper_argtypes +%type opt_instead event event_object RuleActionList, +%type RuleActionBlock RuleActionMulti +%type RuleStmt opt_column opt_name oper_argtypes %type MathOp RemoveFuncStmt aggr_argtype for_update_clause %type RemoveAggrStmt remove_type RemoveStmt ExtendStmt RecipeStmt %type RemoveOperStmt RenameStmt all_Op user_valid_clause @@ -665,7 +762,7 @@ output_statement(char * stmt, int mode) %type user_createuser_clause user_group_list user_group_clause %type CreateUserStmt AlterUserStmt CreateSeqStmt OptSeqList %type OptSeqElem TriggerForSpec TriggerForOpt TriggerForType -%type DropTrigStmt TriggerOneEvent TriggerEvents +%type DropTrigStmt TriggerOneEvent TriggerEvents RuleActionStmt %type TriggerActionTime CreateTrigStmt DropPLangStmt PLangTrusted %type CreatePLangStmt IntegerOnly TriggerFuncArgs TriggerFuncArg %type ViewStmt LoadStmt CreatedbStmt opt_database1 opt_database2 location @@ -673,27 +770,32 @@ output_statement(char * stmt, int mode) %type GrantStmt privileges operation_commalist operation %type cursor_clause opt_cursor opt_readonly opt_of opt_lmode %type case_expr when_clause_list case_default case_arg when_clause -%type select_w_o_sort +%type select_w_o_sort opt_select_limit select_limit_value, +%type select_offset_value -%type ECPGWhenever ECPGConnect connection_target ECPGOpen open_opts -%type indicator ECPGExecute ecpg_expr dotext +%type ECPGWhenever ECPGConnect connection_target ECPGOpen opt_using +%type indicator ECPGExecute ecpg_expr dotext ECPGPrepare %type storage_clause opt_initializer vartext c_anything blockstart %type blockend variable_list variable var_anything do_anything %type opt_pointer cvariable ECPGDisconnect dis_name %type stmt symbol opt_symbol ECPGRelease execstring server_name -%type connection_object opt_server opt_port c_thing +%type connection_object opt_server opt_port c_thing opt_reference %type user_name opt_user char_variable ora_user ident %type db_prefix server opt_options opt_connection_name -%type ECPGSetConnection c_line cpp_line s_enum -%type enum_type +%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 struct_type s_struct declaration variable_declarations -%type simple_type +%type simple_type varchar_type -%type type +%type type ctype %type action -%type opt_array_bounds nest_array_bounds +%type opt_array_bounds nest_array_bounds opt_type_array_bounds +%type nest_type_array_bounds %% prog: statements; @@ -769,13 +871,29 @@ stmt: AddAttrStmt { output_statement($1, 0); } whenever_action(0); free($1); } + | ECPGCursorStmt { + fputs($1, yyout); + free($1); + } + | ECPGDeallocate { + fputs($1, yyout); + whenever_action(0); + free($1); + } + | ECPGDeclare { + fputs($1, yyout); + free($1); + } | ECPGDisconnect { fprintf(yyout, "ECPGdisconnect(__LINE__, \"%s\");", $1); whenever_action(0); free($1); } | ECPGExecute { - fprintf(yyout, "ECPGdo(__LINE__, %s, ECPGt_EOIT, ECPGt_EORT);", $1); + output_statement($1, 0); + } + | ECPGFree { + fprintf(yyout, "ECPGdeallocate(__LINE__, \"%s\");", $1); whenever_action(0); free($1); } @@ -797,26 +915,39 @@ stmt: AddAttrStmt { output_statement($1, 0); } fprintf(yyout, "ECPGdo(__LINE__, \"%s\",", ptr->command); /* dump variables to C file*/ dump_variables(ptr->argsinsert, 0); + dump_variables(argsinsert, 0); fputs("ECPGt_EOIT, ", yyout); dump_variables(ptr->argsresult, 0); fputs("ECPGt_EORT);", yyout); whenever_action(0); free($1); } + | ECPGPrepare { + fprintf(yyout, "ECPGprepare(__LINE__, %s);", $1); + whenever_action(0); + free($1); + } | ECPGRelease { /* output already done */ } | ECPGSetConnection { fprintf(yyout, "ECPGsetconn(__LINE__, %s);", $1); whenever_action(0); free($1); } + | ECPGTypedef { + fputs($1, yyout); + free($1); + } + | ECPGVar { + fputs($1, yyout); + free($1); + } | ECPGWhenever { fputs($1, yyout); output_line_number(); free($1); } - | ECPGPrepare { - yyerror("PREPARE is not supported yet."); - } + ; + /* * We start with a lot of stuff that's very similar to the backend's parsing @@ -1098,7 +1229,7 @@ copy_delimiter: USING DELIMITERS Sconst { $$ = cat2_str(make1_str("using delim CreateStmt: CREATE OptTemp TABLE relation_name '(' OptTableElementList ')' OptInherit { - $$ = cat5_str(make1_str("create"), $2, make1_str("table"), make3_str(make1_str("("), $6, make1_str(")")), $8); + $$ = cat3_str(cat4_str(make1_str("create"), $2, make1_str("table"), $4), make3_str(make1_str("("), $6, make1_str(")")), $8); } ; @@ -2127,40 +2258,37 @@ opt_column: COLUMN { $$ = make1_str("colmunn"); } RuleStmt: CREATE RULE name AS { QueryIsRule=1; } ON event TO event_object where_clause - DO opt_instead OptStmtList + DO opt_instead RuleActionList { $$ = cat2_str(cat5_str(cat5_str(make1_str("create rule"), $3, make1_str("as on"), $7, make1_str("to")), $9, $10, make1_str("do"), $12), $13); } ; -OptStmtList: NOTHING { $$ = make1_str("nothing"); } - | OptimizableStmt { $$ = $1; } - | '[' OptStmtBlock ']' { $$ = cat3_str(make1_str("["), $2, make1_str("]")); } -/***S*I*D***/ -/* We comment this out because it produces a shift / reduce conflict - * with the select_w_o_sort rule */ -/* | '(' OptStmtBlock ')' { $$ = cat3_str(make1_str("("), $2, make1_str(")")); }*/ +RuleActionList: NOTHING { $$ = make1_str("nothing"); } + | SelectStmt { $$ = $1; } + | RuleActionStmt { $$ = $1; } + | '[' RuleActionBlock ']' { $$ = cat3_str(make1_str("["), $2, make1_str("]")); } + | '(' RuleActionBlock ')' { $$ = cat3_str(make1_str("("), $2, make1_str(")")); } + ; + +RuleActionBlock: RuleActionMulti { $$ = $1; } + | RuleActionStmt { $$ = $1; } ; -OptStmtBlock: OptStmtMulti - { $$ = $1; } - | OptimizableStmt - { $$ = $1; } - ; - -OptStmtMulti: OptStmtMulti OptimizableStmt ';' +RuleActionMulti: RuleActionMulti RuleActionStmt + { $$ = cat2_str($1, $2); } + | RuleActionMulti RuleActionStmt ';' { $$ = cat3_str($1, $2, make1_str(";")); } -/***S*I***/ -/* We comment the next rule because it seems to be redundant - * and produces 16 shift/reduce conflicts with the new SelectStmt rule - * needed for EXCEPT and INTERSECT. So far I did not notice any - * violations by removing the rule! */ -/* | OptStmtMulti OptimizableStmt - { $$ = cat2_str($1, $2); }*/ - | OptimizableStmt ';' + | RuleActionStmt ';' { $$ = cat2_str($1, make1_str(";")); } ; +RuleActionStmt: InsertStmt + | UpdateStmt + | DeleteStmt + | NotifyStmt + ; + event_object: relation_name '.' attr_name { $$ = make3_str($1, make1_str("."), $3); @@ -2588,7 +2716,7 @@ CursorStmt: DECLARE name opt_cursor CURSOR FOR SelectStmt cursor_clause { if (strcmp($2, ptr->name) == 0) { - /* re-definition is a bug*/ + /* re-definition is a bug */ sprintf(errortext, "cursor %s already defined", $2); yyerror(errortext); } @@ -2642,13 +2770,13 @@ opt_of: OF columnList { $$ = make2_str(make1_str("of"), $2); } /* The new 'SelectStmt' rule adapted for the optional use of INTERSECT EXCEPT a nd UNION * accepts the use of '(' and ')' to select an order of set operations. */ -SelectStmt: select_w_o_sort sort_clause for_update_clause +SelectStmt: select_w_o_sort sort_clause for_update_clause opt_select_limit { if (strlen($3) > 0 && ForUpdateNotAllowed != 0) yyerror("SELECT FOR UPDATE is not allowed in this context"); ForUpdateNotAllowed = 0; - $$ = cat3_str($1, $2, $3); + $$ = cat4_str($1, $2, $3, $4); } /***S*I***/ @@ -2736,6 +2864,29 @@ OptUseOp: USING Op { $$ = cat2_str(make1_str("using"), $2); } | /*EMPTY*/ { $$ = make1_str(""); } ; +opt_select_limit: LIMIT select_limit_value ',' select_offset_value + { $$ = cat4_str(make1_str("limit"), $2, make1_str(","), $4); } + | LIMIT select_limit_value OFFSET select_offset_value + { $$ = cat4_str(make1_str("limit"), $2, make1_str("offset"), $4); } + | LIMIT select_limit_value + { $$ = cat2_str(make1_str("limit"), $2);; } + | OFFSET select_offset_value LIMIT select_limit_value + { $$ = cat4_str(make1_str("offset"), $2, make1_str("limit"), $4); } + | OFFSET select_offset_value + { $$ = cat2_str(make1_str("offset"), $2); } + | /* EMPTY */ + { $$ = make1_str(""); } + ; + +select_limit_value: Iconst { $$ = $1; } + | ALL { $$ = make1_str("all"); } + | PARAM { $$ = make_name(); } + ; + +select_offset_value: Iconst { $$ = $1; } + | PARAM { $$ = make_name(); } + ; + /* * jimmy bell-style recursive queries aren't supported in the * current system. @@ -2952,6 +3103,36 @@ Generic: generic generic: ident { $$ = $1; } | TYPE_P { $$ = make1_str("type"); } + | SQL_BOOL { $$ = make1_str("bool"); } + | SQL_BREAK { $$ = make1_str("break"); } + | SQL_CALL { $$ = make1_str("call"); } + | SQL_CONNECT { $$ = make1_str("connect"); } + | SQL_CONNECTION { $$ = make1_str("connection"); } + | SQL_CONTINUE { $$ = make1_str("continue"); } + | SQL_DEALLOCATE { $$ = make1_str("deallocate"); } + | SQL_DISCONNECT { $$ = make1_str("disconnect"); } + | SQL_FOUND { $$ = make1_str("found"); } + | SQL_GO { $$ = make1_str("go"); } + | SQL_GOTO { $$ = make1_str("goto"); } + | SQL_IDENTIFIED { $$ = make1_str("identified"); } + | SQL_IMMEDIATE { $$ = make1_str("immediate"); } + | SQL_INDICATOR { $$ = make1_str("indicator"); } + | SQL_INT { $$ = make1_str("int"); } + | SQL_LONG { $$ = make1_str("long"); } + | SQL_OPEN { $$ = make1_str("open"); } + | SQL_PREPARE { $$ = make1_str("prepare"); } + | SQL_RELEASE { $$ = make1_str("release"); } + | SQL_SECTION { $$ = make1_str("section"); } + | SQL_SHORT { $$ = make1_str("short"); } + | SQL_SIGNED { $$ = make1_str("signed"); } + | SQL_SQLERROR { $$ = make1_str("sqlerror"); } + | SQL_SQLPRINT { $$ = make1_str("sqlprint"); } + | SQL_SQLWARNING { $$ = make1_str("sqlwarning"); } + | SQL_STOP { $$ = make1_str("stop"); } + | SQL_STRUCT { $$ = make1_str("struct"); } + | SQL_UNSIGNED { $$ = make1_str("unsigned"); } + | SQL_VAR { $$ = make1_str("var"); } + | SQL_WHENEVER { $$ = make1_str("whenever"); } ; /* SQL92 numeric data types @@ -3638,7 +3819,7 @@ b_expr: attr opt_indirection $$ = make3_str(make1_str("trim("), $3, make1_str(")")); } | civariableonly - { $$ = make1_str(";;"); } + { $$ = $1; } ; opt_indirection: '[' ecpg_expr ']' opt_indirection @@ -4125,6 +4306,36 @@ ColId: ident { $$ = $1; } | VALID { $$ = make1_str("valid"); } | VERSION { $$ = make1_str("version"); } | ZONE { $$ = make1_str("zone"); } + | SQL_BOOL { $$ = make1_str("bool"); } + | SQL_BREAK { $$ = make1_str("break"); } + | SQL_CALL { $$ = make1_str("call"); } + | SQL_CONNECT { $$ = make1_str("connect"); } + | SQL_CONNECTION { $$ = make1_str("connection"); } + | SQL_CONTINUE { $$ = make1_str("continue"); } + | SQL_DEALLOCATE { $$ = make1_str("deallocate"); } + | SQL_DISCONNECT { $$ = make1_str("disconnect"); } + | SQL_FOUND { $$ = make1_str("found"); } + | SQL_GO { $$ = make1_str("go"); } + | SQL_GOTO { $$ = make1_str("goto"); } + | SQL_IDENTIFIED { $$ = make1_str("identified"); } + | SQL_IMMEDIATE { $$ = make1_str("immediate"); } + | SQL_INDICATOR { $$ = make1_str("indicator"); } + | SQL_INT { $$ = make1_str("int"); } + | SQL_LONG { $$ = make1_str("long"); } + | SQL_OPEN { $$ = make1_str("open"); } + | SQL_PREPARE { $$ = make1_str("prepare"); } + | SQL_RELEASE { $$ = make1_str("release"); } + | SQL_SECTION { $$ = make1_str("section"); } + | SQL_SHORT { $$ = make1_str("short"); } + | SQL_SIGNED { $$ = make1_str("signed"); } + | SQL_SQLERROR { $$ = make1_str("sqlerror"); } + | SQL_SQLPRINT { $$ = make1_str("sqlprint"); } + | SQL_SQLWARNING { $$ = make1_str("sqlwarning"); } + | SQL_STOP { $$ = make1_str("stop"); } + | SQL_STRUCT { $$ = make1_str("struct"); } + | SQL_UNSIGNED { $$ = make1_str("unsigned"); } + | SQL_VAR { $$ = make1_str("var"); } + | SQL_WHENEVER { $$ = make1_str("whenever"); } ; /* Column label * Allowed labels in "AS" clauses. @@ -4197,223 +4408,6 @@ SpecialRuleRelation: CURRENT * and now special embedded SQL stuff */ -/* - * variable declaration inside the exec sql declare block - */ -ECPGDeclaration: sql_startdeclare variable_declarations sql_enddeclare {} - -sql_startdeclare : ecpgstart BEGIN_TRANS DECLARE SQL_SECTION SQL_SEMI { - fputs("/* exec sql begin declare section */\n", yyout); - output_line_number(); - } - -sql_enddeclare: ecpgstart END_TRANS DECLARE SQL_SECTION SQL_SEMI { - fputs("/* exec sql end declare section */\n", yyout); - output_line_number(); -} - -variable_declarations: /* empty */ - | declaration variable_declarations; - -declaration: storage_clause type - { - actual_storage[struct_level] = $1; - actual_type[struct_level] = $2.type_enum; - if ($2.type_enum != ECPGt_varchar && $2.type_enum != ECPGt_struct) - fprintf(yyout, "%s %s", $1, $2.type_str); - free($2.type_str); - } - variable_list ';' { fputc(';', yyout); } - -storage_clause : S_EXTERN { $$ = "extern"; } - | S_STATIC { $$ = "static"; } - | S_SIGNED { $$ = "signed"; } - | S_CONST { $$ = "const"; } - | S_REGISTER { $$ = "register"; } - | S_AUTO { $$ = "auto"; } - | /* empty */ { $$ = ""; } - -type: simple_type - { - $$.type_enum = $1; - $$.type_str = mm_strdup(ECPGtype_name($1)); - } - | struct_type - { - $$.type_enum = ECPGt_struct; - $$.type_str = make1_str(""); - } - | enum_type - { - $$.type_str = $1; - $$.type_enum = ECPGt_int; - } - -enum_type: s_enum '{' c_line '}' - { - $$ = cat4_str($1, make1_str("{"), $3, make1_str("}")); - } - -s_enum: S_ENUM opt_symbol { $$ = cat2_str(make1_str("enum"), $2); } - -struct_type: s_struct '{' variable_declarations '}' - { - ECPGfree_struct_member(struct_member_list[struct_level]); - free(actual_storage[struct_level--]); - fputs("} ", yyout); - } - -s_struct : S_STRUCT opt_symbol - { - struct_member_list[struct_level++] = NULL; - if (struct_level >= STRUCT_DEPTH) - yyerror("Too many levels in nested structure definition"); - fprintf(yyout, "struct %s {", $2); - free($2); - } - -opt_symbol: /* empty */ { $$ = make1_str(""); } - | symbol { $$ = $1; } - -simple_type: S_SHORT { $$ = ECPGt_short; } - | S_UNSIGNED S_SHORT { $$ = ECPGt_unsigned_short; } - | S_INT { $$ = ECPGt_int; } - | S_UNSIGNED S_INT { $$ = ECPGt_unsigned_int; } - | S_LONG { $$ = ECPGt_long; } - | S_UNSIGNED S_LONG { $$ = ECPGt_unsigned_long; } - | S_FLOAT { $$ = ECPGt_float; } - | S_DOUBLE { $$ = ECPGt_double; } - | S_BOOL { $$ = ECPGt_bool; }; - | S_CHAR { $$ = ECPGt_char; } - | S_UNSIGNED S_CHAR { $$ = ECPGt_unsigned_char; } - | S_VARCHAR { $$ = ECPGt_varchar; } - -variable_list: variable - | variable_list ',' - { - if (actual_type[struct_level] != ECPGt_varchar) - fputs(", ", yyout); - else - fputs(";\n ", yyout); - } variable - -variable: opt_pointer symbol opt_array_bounds opt_initializer - { - struct ECPGtype * type; - int dimension = $3.index1; /* dimension of array */ - int length = $3.index2; /* lenght of string */ - char dim[14L]; - - switch (actual_type[struct_level]) - { - case ECPGt_struct: - /* pointer has to get dimension 0 */ - if (strlen($1) > 0) - { - length = dimension; - dimension = 0; - } - - if (length >= 0) - yyerror("No multi-dimensional array support for structures"); - - if (dimension == 1 || dimension < 0) - type = ECPGmake_struct_type(struct_member_list[struct_level]); - else - type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level]), dimension); - - fprintf(yyout, "%s%s%s%s", $1, $2, $3.str, $4); - break; - case ECPGt_varchar: - /* pointer has to get length 0 */ - if (strlen($1) > 0) - length=0; - - /* one index is the string length */ - if (length < 0) - { - length = dimension; - dimension = 1; - } - - if (dimension == 1) - type = ECPGmake_simple_type(actual_type[struct_level], length); - else - type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level], length), dimension); - - switch(dimension) - { - case 0: - strcpy(dim, "[]"); - break; - case 1: - *dim = '\0'; - break; - default: - sprintf(dim, "[%d]", dimension); - break; - } - if (length > 0) - fprintf(yyout, "%s struct varchar_%s { int len; char arr[%d]; } %s%s", actual_storage[struct_level], $2, length, $2, dim); - else - fprintf(yyout, "%s struct varchar_%s { int len; char *arr; } %s%s", actual_storage[struct_level], $2, $2, dim); - break; - case ECPGt_char: - case ECPGt_unsigned_char: - /* pointer has to get length 0 */ - if (strlen($1) > 0) - length=0; - - /* one index is the string length */ - if (length < 0) - { - length = (dimension < 0) ? 1 : dimension; - dimension = 1; - } - - if (dimension == 1) - type = ECPGmake_simple_type(actual_type[struct_level], length); - else - type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level], length), dimension); - - fprintf(yyout, "%s%s%s%s", $1, $2, $3.str, $4); - break; - default: - /* a pointer has dimension = 0 */ - if (strlen($1) > 0) { - length = dimension; - dimension = 0; - } - - if (length >= 0) - yyerror("No multi-dimensional array support for simple data types"); - - if (dimension == 1 || dimension < 0) - type = ECPGmake_simple_type(actual_type[struct_level], 1); - else - type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level], 1), dimension); - - fprintf(yyout, "%s%s%s%s", $1, $2, $3.str, $4); - break; - } - - if (struct_level == 0) - new_variable($2, type); - else - ECPGmake_struct_member($2, type, &(struct_member_list[struct_level - 1])); - - free($1); - free($2); - free($3.str); - free($4); - } - -opt_initializer: /* empty */ { $$ = make1_str(""); } - | '=' vartext { $$ = make2_str(make1_str("="), $2); } - -opt_pointer: /* empty */ { $$ = make1_str(""); } - | '*' { $$ = make1_str("*"); } - /* * the exec sql connect statement: connect to the given database */ @@ -4584,6 +4578,280 @@ opt_options: Op ColId } | /* empty */ { $$ = make1_str(""); } +/* + * Declare a prepared cursor. The syntax is different from the standard + * declare statement, so we create a new rule. + */ +ECPGCursorStmt: DECLARE name opt_cursor CURSOR FOR ident cursor_clause + { + struct cursor *ptr, *this; + struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable)); + + for (ptr = cur; ptr != NULL; ptr = ptr->next) + { + if (strcmp($2, ptr->name) == 0) + { + /* re-definition is a bug */ + sprintf(errortext, "cursor %s already defined", $2); + yyerror(errortext); + } + } + + this = (struct cursor *) mm_alloc(sizeof(struct cursor)); + + /* initial definition */ + this->next = cur; + this->name = $2; + this->command = cat5_str(make1_str("declare"), mm_strdup($2), $3, make1_str("cursor for ;;"), $7); + this->argsresult = NULL; + + thisquery->type = &ecpg_query; + thisquery->brace_level = 0; + thisquery->next = NULL; + thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(\"\")") + strlen($6)); + sprintf(thisquery->name, "ECPGprepared_statement(\"%s\")", $6); + + this->argsinsert = NULL; + add_variable(&(this->argsinsert), thisquery, &no_indicator); + + cur = this; + + $$ = cat3_str(make1_str("/*"), mm_strdup(this->command), make1_str("*/")); + } + ; + +/* + * the exec sql deallocate prepare command to deallocate a previously + * prepared statement + */ +ECPGDeallocate: SQL_DEALLOCATE SQL_PREPARE ident { $$ = make3_str(make1_str("ECPGdeallocate(__LINE__, \""), $3, make1_str("\");")); } + +/* + * variable declaration inside the exec sql declare block + */ +ECPGDeclaration: sql_startdeclare + { + fputs("/* exec sql begin declare section */", yyout); + output_line_number(); + } + variable_declarations sql_enddeclare + { + fprintf(yyout, "%s/* exec sql end declare section */", $3); + free($3); + output_line_number(); + } + +sql_startdeclare : ecpgstart BEGIN_TRANS DECLARE SQL_SECTION SQL_SEMI {} + +sql_enddeclare: ecpgstart END_TRANS DECLARE SQL_SECTION SQL_SEMI {} + +variable_declarations: /* empty */ + { + $$ = make1_str(""); + } + | declaration variable_declarations + { + $$ = cat2_str($1, $2); + } + +declaration: storage_clause + { + actual_storage[struct_level] = mm_strdup($1); + } + type + { + actual_type[struct_level].type_enum = $3.type_enum; + actual_type[struct_level].type_dimension = $3.type_dimension; + actual_type[struct_level].type_index = $3.type_index; + } + variable_list ';' + { + $$ = cat4_str($1, $3.type_str, $5, make1_str(";\n")); + } + +storage_clause : S_EXTERN { $$ = make1_str("extern"); } + | S_STATIC { $$ = make1_str("static"); } + | S_SIGNED { $$ = make1_str("signed"); } + | S_CONST { $$ = make1_str("const"); } + | S_REGISTER { $$ = make1_str("register"); } + | S_AUTO { $$ = make1_str("auto"); } + | /* empty */ { $$ = make1_str(""); } + +type: simple_type + { + $$.type_enum = $1; + $$.type_str = mm_strdup(ECPGtype_name($1)); + $$.type_dimension = -1; + $$.type_index = -1; + } + | varchar_type + { + $$.type_enum = ECPGt_varchar; + $$.type_str = make1_str(""); + $$.type_dimension = -1; + $$.type_index = -1; + } + | struct_type + { + $$.type_enum = ECPGt_struct; + $$.type_str = $1; + $$.type_dimension = -1; + $$.type_index = -1; + } + | enum_type + { + $$.type_str = $1; + $$.type_enum = ECPGt_int; + $$.type_dimension = -1; + $$.type_index = -1; + } + | symbol + { + /* this is for typedef'ed types */ + struct typedefs *this = get_typedef($1); + + $$.type_str = mm_strdup(this->name); + $$.type_enum = this->type->type_enum; + $$.type_dimension = this->type->type_dimension; + $$.type_index = this->type->type_index; + struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list); + } + +enum_type: s_enum '{' c_line '}' + { + $$ = cat4_str($1, make1_str("{"), $3, make1_str("}")); + } + +s_enum: S_ENUM opt_symbol { $$ = cat2_str(make1_str("enum"), $2); } + +struct_type: s_struct '{' variable_declarations '}' + { + ECPGfree_struct_member(struct_member_list[struct_level]); + free(actual_storage[struct_level--]); + $$ = cat4_str($1, make1_str("{"), $3, make1_str("}")); + } + +s_struct : S_STRUCT 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); + } + +opt_symbol: /* empty */ { $$ = make1_str(""); } + | symbol { $$ = $1; } + +simple_type: S_SHORT { $$ = ECPGt_short; } + | S_UNSIGNED S_SHORT { $$ = ECPGt_unsigned_short; } + | S_INT { $$ = ECPGt_int; } + | S_UNSIGNED S_INT { $$ = ECPGt_unsigned_int; } + | S_LONG { $$ = ECPGt_long; } + | S_UNSIGNED S_LONG { $$ = ECPGt_unsigned_long; } + | S_FLOAT { $$ = ECPGt_float; } + | S_DOUBLE { $$ = ECPGt_double; } + | S_BOOL { $$ = ECPGt_bool; }; + | S_CHAR { $$ = ECPGt_char; } + | S_UNSIGNED S_CHAR { $$ = ECPGt_unsigned_char; } + +varchar_type: S_VARCHAR { $$ = ECPGt_varchar; } + +variable_list: variable + { + $$ = $1; + } + | variable_list ',' variable + { + $$ = cat3_str($1, make1_str(","), $3); + } + +variable: opt_pointer symbol opt_array_bounds opt_initializer + { + struct ECPGtype * type; + int dimension = $3.index1; /* dimension of array */ + int length = $3.index2; /* lenght of string */ + char dim[14L], ascii_len[12]; + + adjust_array(actual_type[struct_level].type_enum, &dimension, &length, actual_type[struct_level].type_dimension, actual_type[struct_level].type_index, strlen($1)); + + switch (actual_type[struct_level].type_enum) + { + case ECPGt_struct: + if (dimension < 0) + type = ECPGmake_struct_type(struct_member_list[struct_level]); + else + type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level]), dimension); + + $$ = make4_str($1, mm_strdup($2), $3.str, $4); + break; + case ECPGt_varchar: + if (dimension == -1) + type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length); + else + type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length), dimension); + + switch(dimension) + { + case 0: + strcpy(dim, "[]"); + break; + case -1: + case 1: + *dim = '\0'; + break; + default: + sprintf(dim, "[%d]", dimension); + break; + } + sprintf(ascii_len, "%d", length); + + if (length > 0) + $$ = make4_str(make5_str(mm_strdup(actual_storage[struct_level]), make1_str(" struct varchar_"), mm_strdup($2), make1_str(" { int len; char arr["), mm_strdup(ascii_len)), make1_str("]; } "), mm_strdup($2), mm_strdup(dim)); + else + $$ = make4_str(make3_str(mm_strdup(actual_storage[struct_level]), make1_str(" struct varchar_"), mm_strdup($2)), make1_str(" { int len; char *arr; } "), mm_strdup($2), mm_strdup(dim)); + break; + case ECPGt_char: + case ECPGt_unsigned_char: + if (dimension == -1) + type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length); + else + type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length), dimension); + + $$ = make4_str($1, mm_strdup($2), $3.str, $4); + break; + default: + if (dimension < 0) + type = ECPGmake_simple_type(actual_type[struct_level].type_enum, 1); + else + type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, 1), dimension); + + $$ = make4_str($1, mm_strdup($2), $3.str, $4); + break; + } + + if (struct_level == 0) + new_variable($2, type); + else + ECPGmake_struct_member($2, type, &(struct_member_list[struct_level - 1])); + + free($2); + } + +opt_initializer: /* empty */ { $$ = make1_str(""); } + | '=' vartext { $$ = make2_str(make1_str("="), $2); } + +opt_pointer: /* empty */ { $$ = make1_str(""); } + | '*' { $$ = make1_str("*"); } + +/* + * As long as the prepare statement is not supported by the backend, we will + * try to simulate it here so we get dynamic SQL + */ +ECPGDeclare: DECLARE STATEMENT ident + { + /* this is only supported for compatibility */ + $$ = cat3_str(make1_str("/* declare statement"), $3, make1_str("*/")); + } /* * the exec sql disconnect statement: disconnect from the given database */ @@ -4600,23 +4868,68 @@ connection_object: connection_target { $$ = $1; } /* * execute a given string as sql command */ -ECPGExecute : EXECUTE SQL_IMMEDIATE execstring { $$ = $3; }; +ECPGExecute : EXECUTE SQL_IMMEDIATE execstring + { + struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable)); -execstring: cvariable | + thisquery->type = &ecpg_query; + thisquery->brace_level = 0; + thisquery->next = NULL; + thisquery->name = $3; + + add_variable(&argsinsert, thisquery, &no_indicator); + + $$ = make1_str(";;"); + } + | EXECUTE ident + { + struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable)); + + thisquery->type = &ecpg_query; + thisquery->brace_level = 0; + thisquery->next = NULL; + thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(\"\")") + strlen($2)); + sprintf(thisquery->name, "ECPGprepared_statement(\"%s\")", $2); + + add_variable(&argsinsert, thisquery, &no_indicator); + } opt_using + { + $$ = make1_str(";;"); + } + +execstring: char_variable | CSTRING { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); }; +/* + * the exec sql free command to deallocate a previously + * prepared statement + */ +ECPGFree: SQL_FREE ident { $$ = $2; } + /* * open is an open cursor, at the moment this has to be removed */ -ECPGOpen: SQL_OPEN name open_opts { +ECPGOpen: SQL_OPEN name opt_using { $$ = $2; }; -open_opts: /* empty */ { $$ = make1_str(""); } - | USING cvariable { - yyerror ("open cursor with variables not implemented yet"); +opt_using: /* empty */ { $$ = make1_str(""); } + | USING variablelist { + /* yyerror ("open cursor with variables not implemented yet"); */ + $$ = make1_str(""); } +variablelist: cinputvariable | cinputvariable ',' variablelist + +/* + * As long as the prepare statement is not supported by the backend, we will + * try to simulate it here so we get dynamic SQL + */ +ECPGPrepare: SQL_PREPARE ident FROM char_variable + { + $$ = make4_str(make1_str("\""), $2, make1_str("\", "), $4); + } + /* * for compatibility with ORACLE we will also allow the keyword RELEASE * after a transaction statement to disconnect from the database. @@ -4642,6 +4955,378 @@ ECPGSetConnection: SET SQL_CONNECTION connection_object { $$ = $3; } + +/* + * define a new type for embedded SQL + */ +ECPGTypedef: TYPE_P symbol IS ctype opt_type_array_bounds opt_reference + { + /* add entry to list */ + struct typedefs *ptr, *this; + int dimension = $5.index1; + int length = $5.index2; + + for (ptr = types; ptr != NULL; ptr = ptr->next) + { + if (strcmp($2, ptr->name) == 0) + { + /* re-definition is a bug */ + sprintf(errortext, "type %s already defined", $2); + yyerror(errortext); + } + } + + adjust_array($4.type_enum, &dimension, &length, $4.type_dimension, $4.type_index, strlen($6)); + + this = (struct typedefs *) mm_alloc(sizeof(struct typedefs)); + + /* initial definition */ + this->next = types; + this->name = $2; + this->type = (struct this_type *) mm_alloc(sizeof(struct this_type)); + this->type->type_enum = $4.type_enum; + this->type->type_str = mm_strdup($2); + this->type->type_dimension = dimension; /* dimension of array */ + this->type->type_index = length; /* lenght of string */ + this->struct_member_list = struct_member_list[struct_level]; + + if ($4.type_enum != ECPGt_varchar && + $4.type_enum != ECPGt_char && + $4.type_enum != ECPGt_unsigned_char && + this->type->type_index >= 0) + yyerror("No multi-dimensional array support for simple data types"); + + types = this; + + $$ = cat5_str(cat3_str(make1_str("/* exec sql type"), mm_strdup($2), make1_str("is")), mm_strdup($4.type_str), mm_strdup($5.str), $6, make1_str("*/")); + } + +opt_type_array_bounds: '[' ']' nest_type_array_bounds + { + $$.index1 = 0; + $$.index2 = $3.index1; + $$.str = cat2_str(make1_str("[]"), $3.str); + } + | '(' ')' nest_type_array_bounds + { + $$.index1 = 0; + $$.index2 = $3.index1; + $$.str = cat2_str(make1_str("[]"), $3.str); + } + | '[' Iconst ']' nest_type_array_bounds + { + $$.index1 = atol($2); + $$.index2 = $4.index1; + $$.str = cat4_str(make1_str("["), $2, make1_str("]"), $4.str); + } + | '(' Iconst ')' nest_type_array_bounds + { + $$.index1 = atol($2); + $$.index2 = $4.index1; + $$.str = cat4_str(make1_str("["), $2, make1_str("]"), $4.str); + } + | /* EMPTY */ + { + $$.index1 = -1; + $$.index2 = -1; + $$.str= make1_str(""); + } + ; + +nest_type_array_bounds: '[' ']' nest_type_array_bounds + { + $$.index1 = 0; + $$.index2 = $3.index1; + $$.str = cat2_str(make1_str("[]"), $3.str); + } + | '(' ')' nest_type_array_bounds + { + $$.index1 = 0; + $$.index2 = $3.index1; + $$.str = cat2_str(make1_str("[]"), $3.str); + } + | '[' Iconst ']' nest_type_array_bounds + { + $$.index1 = atol($2); + $$.index2 = $4.index1; + $$.str = cat4_str(make1_str("["), $2, make1_str("]"), $4.str); + } + | '(' Iconst ')' nest_type_array_bounds + { + $$.index1 = atol($2); + $$.index2 = $4.index1; + $$.str = cat4_str(make1_str("["), $2, make1_str("]"), $4.str); + } + | /* EMPTY */ + { + $$.index1 = -1; + $$.index2 = -1; + $$.str= make1_str(""); + } + ; +opt_reference: SQL_REFERENCE { $$ = make1_str("reference"); } + | /* empty */ { $$ = make1_str(""); } + +ctype: CHAR + { + $$.type_str = make1_str("char"); + $$.type_enum = ECPGt_char; + $$.type_index = -1; + $$.type_dimension = -1; + } + | VARCHAR + { + $$.type_str = make1_str("varchar"); + $$.type_enum = ECPGt_varchar; + $$.type_index = -1; + $$.type_dimension = -1; + } + | FLOAT + { + $$.type_str = make1_str("float"); + $$.type_enum = ECPGt_float; + $$.type_index = -1; + $$.type_dimension = -1; + } + | DOUBLE + { + $$.type_str = make1_str("double"); + $$.type_enum = ECPGt_double; + $$.type_index = -1; + $$.type_dimension = -1; + } + | opt_signed SQL_INT + { + $$.type_str = make1_str("int"); + $$.type_enum = ECPGt_int; + $$.type_index = -1; + $$.type_dimension = -1; + } + | SQL_ENUM + { + $$.type_str = make1_str("int"); + $$.type_enum = ECPGt_int; + $$.type_index = -1; + $$.type_dimension = -1; + } + | opt_signed SQL_SHORT + { + $$.type_str = make1_str("short"); + $$.type_enum = ECPGt_short; + $$.type_index = -1; + $$.type_dimension = -1; + } + | opt_signed SQL_LONG + { + $$.type_str = make1_str("long"); + $$.type_enum = ECPGt_long; + $$.type_index = -1; + $$.type_dimension = -1; + } + | SQL_BOOL + { + $$.type_str = make1_str("bool"); + $$.type_enum = ECPGt_bool; + $$.type_index = -1; + $$.type_dimension = -1; + } + | SQL_UNSIGNED SQL_INT + { + $$.type_str = make1_str("unsigned int"); + $$.type_enum = ECPGt_unsigned_int; + $$.type_index = -1; + $$.type_dimension = -1; + } + | SQL_UNSIGNED SQL_SHORT + { + $$.type_str = make1_str("unsigned short"); + $$.type_enum = ECPGt_unsigned_short; + $$.type_index = -1; + $$.type_dimension = -1; + } + | SQL_UNSIGNED SQL_LONG + { + $$.type_str = make1_str("unsigned long"); + $$.type_enum = ECPGt_unsigned_long; + $$.type_index = -1; + $$.type_dimension = -1; + } + | SQL_STRUCT + { + struct_member_list[struct_level++] = NULL; + if (struct_level >= STRUCT_DEPTH) + yyerror("Too many levels in nested structure definition"); + } '{' sql_variable_declarations '}' + { + ECPGfree_struct_member(struct_member_list[struct_level--]); + $$.type_str = cat3_str(make1_str("struct {"), $4, make1_str("}")); + $$.type_enum = ECPGt_struct; + $$.type_index = -1; + $$.type_dimension = -1; + } + | symbol + { + struct typedefs *this = get_typedef($1); + + $$.type_str = mm_strdup($1); + $$.type_enum = this->type->type_enum; + $$.type_dimension = this->type->type_dimension; + $$.type_index = this->type->type_index; + struct_member_list[struct_level] = this->struct_member_list; + } + +opt_signed: SQL_SIGNED | /* empty */ + +sql_variable_declarations: /* empty */ + { + $$ = make1_str(""); + } + | sql_declaration sql_variable_declarations + { + $$ = cat2_str($1, $2); + } + ; + +sql_declaration: ctype + { + actual_type[struct_level].type_enum = $1.type_enum; + actual_type[struct_level].type_dimension = $1.type_dimension; + actual_type[struct_level].type_index = $1.type_index; + } + sql_variable_list SQL_SEMI + { + $$ = cat3_str($1.type_str, $3, make1_str(";")); + } + +sql_variable_list: sql_variable + { + $$ = $1; + } + | sql_variable_list ',' sql_variable + { + $$ = make3_str($1, make1_str(","), $3); + } + +sql_variable: opt_pointer symbol opt_array_bounds + { + int dimension = $3.index1; + int length = $3.index2; + struct ECPGtype * type; + char dim[14L]; + + adjust_array(actual_type[struct_level].type_enum, &dimension, &length, actual_type[struct_level].type_dimension, actual_type[struct_level].type_index, strlen($1)); + + switch (actual_type[struct_level].type_enum) + { + case ECPGt_struct: + if (dimension < 0) + type = ECPGmake_struct_type(struct_member_list[struct_level]); + else + type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level]), dimension); + + break; + case ECPGt_varchar: + if (dimension == -1) + type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length); + else + type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length), dimension); + + switch(dimension) + { + case 0: + strcpy(dim, "[]"); + break; + case -1: + case 1: + *dim = '\0'; + break; + default: + sprintf(dim, "[%d]", dimension); + break; + } + + break; + case ECPGt_char: + case ECPGt_unsigned_char: + if (dimension == -1) + type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length); + else + type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length), dimension); + + break; + default: + if (length >= 0) + yyerror("No multi-dimensional array support for simple data types"); + + if (dimension < 0) + type = ECPGmake_simple_type(actual_type[struct_level].type_enum, 1); + else + type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, 1), dimension); + + break; + } + + if (struct_level == 0) + new_variable($2, type); + else + ECPGmake_struct_member($2, type, &(struct_member_list[struct_level - 1])); + + $$ = cat3_str($1, $2, $3.str); + } + +/* + * define the type of one variable for embedded SQL + */ +ECPGVar: SQL_VAR symbol IS ctype opt_type_array_bounds opt_reference + { + struct variable *p = find_variable($2); + int dimension = $5.index1; + int length = $5.index2; + struct ECPGtype * type; + + adjust_array($4.type_enum, &dimension, &length, $4.type_dimension, $4.type_index, strlen($6)); + + switch ($4.type_enum) + { + case ECPGt_struct: + if (dimension < 0) + type = ECPGmake_struct_type(struct_member_list[struct_level]); + else + type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level]), dimension); + break; + case ECPGt_varchar: + if (dimension == -1) + type = ECPGmake_simple_type($4.type_enum, length); + else + type = ECPGmake_array_type(ECPGmake_simple_type($4.type_enum, length), dimension); + + break; + case ECPGt_char: + case ECPGt_unsigned_char: + if (dimension == -1) + type = ECPGmake_simple_type($4.type_enum, length); + else + type = ECPGmake_array_type(ECPGmake_simple_type($4.type_enum, length), dimension); + + break; + default: + if (length >= 0) + yyerror("No multi-dimensional array support for simple data types"); + + if (dimension < 0) + type = ECPGmake_simple_type($4.type_enum, 1); + else + type = ECPGmake_array_type(ECPGmake_simple_type($4.type_enum, 1), dimension); + + break; + } + + ECPGfree_type(p->type); + p->type = type; + + $$ = cat5_str(cat3_str(make1_str("/* exec sql var"), mm_strdup($2), make1_str("is")), mm_strdup($4.type_str), mm_strdup($5.str), $6, make1_str("*/")); + } + /* * whenever statement: decide what to do in case of error/no data found * according to SQL standards we lack: SQLSTATE, CONSTRAINT and SQLEXCEPTION @@ -4703,14 +5388,6 @@ action : SQL_CONTINUE { $$.str = cat2_str(make1_str("call"), mm_strdup($$.command)); } -/* - * As long as the prepare statement in not supported by the backend, we will - * try to simulate it here so we get dynamic SQL - */ -ECPGPrepare: SQL_PREPARE name FROM name - { - } - /* some other stuff for ecpg */ ecpg_expr: attr opt_indirection { @@ -4987,7 +5664,7 @@ ecpg_expr: attr opt_indirection | NOT ecpg_expr { $$ = cat2_str(make1_str("not"), $2); } | civariableonly - { $$ = make1_str(";;"); } + { $$ = $1; } ; into_list : coutputvariable | into_list ',' coutputvariable; @@ -5010,6 +5687,7 @@ cinputvariable : cvariable indicator { civariableonly : cvariable { add_variable(&argsinsert, find_variable($1), &no_indicator); + $$ = make1_str(";;"); } cvariable: CVARIABLE { $$ = $1; } diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c index 52cc8f8087a..dd3546b50ad 100644 --- a/src/interfaces/ecpg/preproc/type.c +++ b/src/interfaces/ecpg/preproc/type.c @@ -2,7 +2,6 @@ #include #include -#include "type.h" #include "extern.h" /* malloc + error check */ @@ -36,8 +35,8 @@ mm_strdup(const char *string) } /* duplicate memberlist */ -static struct ECPGstruct_member * -struct_member_dup(struct ECPGstruct_member * rm) +struct ECPGstruct_member * +ECPGstruct_member_dup(struct ECPGstruct_member * rm) { struct ECPGstruct_member *new = NULL; @@ -71,7 +70,8 @@ void ECPGmake_struct_member(char *name, struct ECPGtype * type, struct ECPGstruct_member ** start) { struct ECPGstruct_member *ptr, - *ne = (struct ECPGstruct_member *) mm_alloc(sizeof(struct ECPGstruct_member)); + *ne = + (struct ECPGstruct_member *) mm_alloc(sizeof(struct ECPGstruct_member)); ne->name = strdup(name); ne->typ = type; @@ -112,7 +112,7 @@ ECPGmake_struct_type(struct ECPGstruct_member * rm) { struct ECPGtype *ne = ECPGmake_simple_type(ECPGt_struct, 1); - ne->u.members = struct_member_dup(rm); + ne->u.members = ECPGstruct_member_dup(rm); return ne; } @@ -160,6 +160,9 @@ get_type(enum ECPGttype typ) case ECPGt_NO_INDICATOR: /* no indicator */ return ("ECPGt_NO_INDICATOR"); break; + case ECPGt_char_variable: /* string that should not be quoted */ + return ("ECPGt_char_variable"); + break; default: abort(); } @@ -202,23 +205,30 @@ ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * typ, const char *in { ECPGdump_a_simple(o, name, typ->u.element->typ, typ->u.element->size, typ->size, NULL, prefix); - if (ind_typ == &ecpg_no_indicator) + if (ind_typ->typ == ECPGt_NO_INDICATOR) ECPGdump_a_simple(o, ind_name, ind_typ->typ, ind_typ->size, -1, NULL, ind_prefix); else + { + if (ind_typ->typ != ECPGt_array) + { + fprintf(stderr, "Indicator for an array has to be array too.\n"); + exit(INDICATOR_NOT_ARRAY); + } ECPGdump_a_simple(o, ind_name, ind_typ->u.element->typ, ind_typ->u.element->size, ind_typ->size, NULL, prefix); + } } else if (typ->u.element->typ == ECPGt_array) { - yyerror("No nested arrays allowed (except strings)"); /* Array of array, */ + yyerror("No nested arrays allowed (except strings)"); /* array of array */ } else if (typ->u.element->typ == ECPGt_struct) { - /* Array of structs. */ + /* Array of structs */ ECPGdump_a_struct(o, name, ind_name, typ->size, typ->u.element, ind_typ->u.element, NULL, prefix, ind_prefix); } else - yyerror("Internal error: unknown datatype, pleqase inform pgsql-bugs@postgresql.org"); + yyerror("Internal error: unknown datatype, please inform pgsql-bugs@postgresql.org"); break; case ECPGt_struct: ECPGdump_a_struct(o, name, ind_name, 1, typ, ind_typ, NULL, prefix, ind_prefix); @@ -260,6 +270,7 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ, break; case ECPGt_char: case ECPGt_unsigned_char: + case ECPGt_char_variable: sprintf(offset, "%ld*sizeof(char)", varcharsize); break; default: diff --git a/src/interfaces/ecpg/preproc/type.h b/src/interfaces/ecpg/preproc/type.h index f5c5941c130..f9642d92766 100644 --- a/src/interfaces/ecpg/preproc/type.h +++ b/src/interfaces/ecpg/preproc/type.h @@ -30,6 +30,7 @@ struct ECPGtype *ECPGmake_simple_type(enum ECPGttype, long); struct ECPGtype *ECPGmake_varchar_type(enum ECPGttype, long); struct ECPGtype *ECPGmake_array_type(struct ECPGtype *, long); struct ECPGtype *ECPGmake_struct_type(struct ECPGstruct_member *); +struct ECPGstruct_member * ECPGstruct_member_dup(struct ECPGstruct_member *); /* Frees a type. */ void ECPGfree_struct_member(struct ECPGstruct_member *); @@ -84,6 +85,54 @@ struct index struct this_type { - enum ECPGttype type_enum; - char *type_str; + enum ECPGttype type_enum; + char *type_str; + int type_dimension; + int type_index; +}; + +struct _include_path +{ + char *path; + struct _include_path *next; +}; + +struct cursor +{ + char *name; + char *command; + struct arguments *argsinsert; + struct arguments *argsresult; + struct cursor *next; +}; + +struct typedefs +{ + char *name; + struct this_type *type; + struct ECPGstruct_member *struct_member_list; + struct typedefs *next; +}; + +struct _defines +{ + char *old; + char *new; + struct _defines *next; +}; + +/* This is a linked list of the variable names and types. */ +struct variable +{ + char *name; + struct ECPGtype *type; + int brace_level; + struct variable *next; +}; + +struct arguments +{ + struct variable *variable; + struct variable *indicator; + struct arguments *next; }; diff --git a/src/interfaces/ecpg/test/test1.pgc b/src/interfaces/ecpg/test/test1.pgc index fde066a668e..ac2dbdc04e1 100644 --- a/src/interfaces/ecpg/test/test1.pgc +++ b/src/interfaces/ecpg/test/test1.pgc @@ -4,16 +4,25 @@ exec sql whenever sqlerror sqlprint; exec sql include sqlca; -exec sql define AMOUNT 5; +exec sql define AMOUNT 8; + +exec sql type intarray is int[AMOUNT]; +exec sql type string is char(6); + +typedef int intarray[AMOUNT]; int main () { exec sql begin declare section; - int amount[AMOUNT]; - char name[AMOUNT][8]; + intarray amount; + int increment=100; + char name[AMOUNT][6]; + char letter[AMOUNT][1]; + char command[128]; exec sql end declare section; - char msg[128], command[128]; + exec sql var name is string(AMOUNT); + char msg[128]; FILE *dbgs; int i,j; @@ -24,30 +33,40 @@ exec sql end declare section; exec sql connect to mm; strcpy(msg, "create"); - exec sql create table test(name char(8), amount int); + exec sql create table test(name char(6), amount int, letter char(1)); + + strcpy(msg, "commit"); + exec sql commit; strcpy(msg, "execute insert 1"); - sprintf(command, "insert into test(name, amount) values ('foobar', 1)"); + sprintf(command, "insert into test(name, amount, letter) values ('foobar', 1, 'f')"); exec sql execute immediate :command; - strcpy(msg, "excute insert 2"); - sprintf(command, "insert into test(name, amount) select name, amount+1 from test"); + 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; - strcpy(msg, "excute insert 3"); - sprintf(command, "insert into test(name, amount) select name, amount+10 from test"); + strcpy(msg, "execute insert 3"); + sprintf(command, "insert into test(name, amount, letter) select name, amount+10, letter from test"); exec sql execute immediate :command; printf("Inserted %d tuples via execute immediate\n", sqlca.sqlerrd[2]); + 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; + + printf("Inserted %d tuples via prepared execute\n", sqlca.sqlerrd[2]); + strcpy(msg, "commit"); exec sql commit; strcpy(msg, "select"); - exec sql select name, amount into :name, :amount from test; + exec sql select name, amount, letter into :name, :amount, :letter from test; for (i=0, j=sqlca.sqlerrd[2]; i