From 64e35e1468e1b4d24c980e63e568cf1f54c7057b Mon Sep 17 00:00:00 2001 From: "Marc G. Fournier" <scrappy@hub.org> Date: Tue, 21 Apr 1998 13:23:24 +0000 Subject: [PATCH] Upgrade ECPG to 2.0 Michael Meskes <meskes@topsystem.de> --- src/interfaces/ecpg/ChangeLog | 40 + src/interfaces/ecpg/TODO | 32 +- src/interfaces/ecpg/include/Makefile | 12 +- src/interfaces/ecpg/include/ecpglib.h | 5 - src/interfaces/ecpg/include/ecpgtype.h | 3 +- src/interfaces/ecpg/lib/Makefile.in | 5 +- src/interfaces/ecpg/lib/ecpglib.c | 143 +- src/interfaces/ecpg/preproc/Makefile | 28 +- src/interfaces/ecpg/preproc/c_keywords.c | 63 + src/interfaces/ecpg/preproc/ecpg.c | 27 +- src/interfaces/ecpg/preproc/ecpg_keywords.c | 60 + src/interfaces/ecpg/preproc/extern.h | 12 + src/interfaces/ecpg/preproc/keywords.c | 242 ++ src/interfaces/ecpg/preproc/pgc.l | 565 ++- src/interfaces/ecpg/preproc/preproc.y | 4242 +++++++++++++++++-- src/interfaces/ecpg/preproc/type.c | 88 +- src/interfaces/ecpg/preproc/type.h | 5 +- src/interfaces/ecpg/test/Makefile | 10 +- src/interfaces/ecpg/test/perftest.pgc | 17 +- src/interfaces/ecpg/test/test2.pgc | 40 +- 20 files changed, 5000 insertions(+), 639 deletions(-) create mode 100644 src/interfaces/ecpg/preproc/c_keywords.c create mode 100644 src/interfaces/ecpg/preproc/ecpg_keywords.c create mode 100644 src/interfaces/ecpg/preproc/keywords.c diff --git a/src/interfaces/ecpg/ChangeLog b/src/interfaces/ecpg/ChangeLog index f20a717dbc4..e06e4ffa73d 100644 --- a/src/interfaces/ecpg/ChangeLog +++ b/src/interfaces/ecpg/ChangeLog @@ -86,3 +86,43 @@ Fri Mar 13 13:35:13 CET 1998 Mon Mar 16 15:09:10 CET 1998 - fixed parser to print correct filename and line number + +Wed Mar 18 14:28:49 CET 1998 + + - started working on indicator variables + +Mon Mar 23 13:49:15 CET 1998 + + - fixed some bugs in indicator variable handling + - completely rewrote parser for fetch and insert statements + - indicator variables are also allowed in insert statements now + +Mon Mar 23 16:09:05 CET 1998 + + - fixed whenever command goto to only allow valid lables + +Thu Mar 26 13:33:02 MEZ 1998 + + - some minor bugfixes + +Mon Apr 20 13:06:09 CEST 1998 + + - database name no longer has to entered as string constant, i.e. + just remove the '...' around the name + +Mon Apr 20 14:38:45 CEST 1998 + + - both test cases compile cleanly + +Mon Apr 20 16:13:25 CEST 1998 + + - Phew! Finally finished parser rewriting. + +Mon Apr 20 16:39:23 CEST 1998 + + - Cursor is opened when the open command is issued, not at declare time. + +Tue Apr 21 12:53:49 CEST 1998 + + - Set indicator to amount of data really written (truncation). + diff --git a/src/interfaces/ecpg/TODO b/src/interfaces/ecpg/TODO index 4c7c15f40f0..c74c2732746 100644 --- a/src/interfaces/ecpg/TODO +++ b/src/interfaces/ecpg/TODO @@ -1,15 +1,6 @@ This list is still from Linus. MM The variables should be static. - -Preprocessor cannot do syntax checking on your SQL statements Whatever you -write is copied more or less exactly to the PostgreSQL and you will not be -able to locate your errors until run-time. - -No restriction to strings only The PQ interface, and most of all the PQexec -function, that is used by the ecpg relies on that the request is built up as -a string. In some cases, like when the data contains the null character, -this will be a serious problem. There should be different error numbers for the different errors instead of just -1 for them all. @@ -21,12 +12,6 @@ ecpg it is done for compatibility reasons only. For them to improve speed would require a lot more insight in the postgres internal mechanisms than I possess. -Oracle has indicator variables that tell if a value is null or if it is -empty. This largely simplifies array operations and provides for a way to -hack around some design flaws in the handling of VARCHAR2 (like that an -empty string isn't distinguishable from a null value). I am not sure if this -is an Oracle extension or part of the ANSI standard. - As well as complex types like records and arrays, typedefs would be a good thing to take care of. @@ -43,8 +28,6 @@ Now comes my list (MM): The return code is alway -1 in case of an error. You cannot see which error occured by examining the return code. -The cursor is opened when the declare statement is issued. - ecpg does not understand enum datatypes. There is no exec sql prepare statement. @@ -59,7 +42,16 @@ There is no way yet to fill a complete array with one call except arrays of ecpg cannot use pointer variables except [unsigned] char * -List all commands as sqlcommand, not just S_SYMBOL or even better rewrite -pareser to be equivalent to backendĀ“s parser. +give back the number of tuples affected via sqlca + +exec sql disconnect {current|default|all|connectionname|connection_hostvar}; + oder <disconnect statement> ::= + DISCONNECT <disconnect object> + + <disconnect object> ::= + <connection object> + | ALL + | CURRENT + commit release|commit work release auch disconnect -Set standard include paths. +It is not neccessary to check for sql not found after all commands. diff --git a/src/interfaces/ecpg/include/Makefile b/src/interfaces/ecpg/include/Makefile index bfb6840b68c..caa13a8d916 100644 --- a/src/interfaces/ecpg/include/Makefile +++ b/src/interfaces/ecpg/include/Makefile @@ -6,13 +6,13 @@ all clean:: @echo Nothing to be done. install:: - $(INSTALL) $(INSTLOPTS) ecpglib.h $(HEADERDIR) - $(INSTALL) $(INSTLOPTS) ecpgtype.h $(HEADERDIR) - $(INSTALL) $(INSTLOPTS) sqlca.h $(HEADERDIR) + $(INSTALL) $(INSTLOPTS) ecpglib.h $(DESTDIR)$(HEADERDIR) + $(INSTALL) $(INSTLOPTS) ecpgtype.h $(DESTDIR)$(HEADERDIR) + $(INSTALL) $(INSTLOPTS) sqlca.h $(DESTDIR)$(HEADERDIR) uninstall:: - rm -f $(HEADERDIR)/ecpglib.h - rm -f $(HEADERDIR)/ecpgtype.h - rm -f $(HEADERDIR)/sqlca.h + rm -f $(DESTDIR)$(HEADERDIR)/ecpglib.h + rm -f $(DESTDIR)$(HEADERDIR)/ecpgtype.h + rm -f $(DESTDIR)$(HEADERDIR)/sqlca.h dep depend: diff --git a/src/interfaces/ecpg/include/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h index 6dd2f92aebf..b828763fa36 100644 --- a/src/interfaces/ecpg/include/ecpglib.h +++ b/src/interfaces/ecpg/include/ecpglib.h @@ -13,11 +13,6 @@ bool ECPGstatus(void); void ECPGlog(const char *format,...); -/* These functions are only kept for compatibility reasons. */ -/* Use ECPGtrans instead. */ -bool ECPGcommit(int); -bool ECPGrollback(int); - #ifdef LIBPQ_FE_H bool ECPGsetdb(PGconn *); diff --git a/src/interfaces/ecpg/include/ecpgtype.h b/src/interfaces/ecpg/include/ecpgtype.h index 30b1f557138..54442e6e3dd 100644 --- a/src/interfaces/ecpg/include/ecpgtype.h +++ b/src/interfaces/ecpg/include/ecpgtype.h @@ -43,7 +43,8 @@ enum ECPGttype ECPGt_array, ECPGt_record, ECPGt_EOIT, /* End of insert types. */ - ECPGt_EORT /* End of result types. */ + ECPGt_EORT, /* End of result types. */ + ECPGt_NO_INDICATOR /* no indicator */ }; #define IS_SIMPLE_TYPE(type) ((type) >= ECPGt_char && (type) <= ECPGt_varchar2) diff --git a/src/interfaces/ecpg/lib/Makefile.in b/src/interfaces/ecpg/lib/Makefile.in index f4039edf99c..926b3b54696 100644 --- a/src/interfaces/ecpg/lib/Makefile.in +++ b/src/interfaces/ecpg/lib/Makefile.in @@ -3,8 +3,8 @@ include $(SRCDIR)/Makefile.global PQ_INCLUDE=-I$(SRCDIR)/interfaces/libpq -SO_MAJOR_VERSION=1 -SO_MINOR_VERSION=1 +SO_MAJOR_VERSION=2 +SO_MINOR_VERSION=0 PORTNAME=@PORTNAME@ @@ -16,6 +16,7 @@ endif shlib := install-shlib-dep := ifeq ($(PORTNAME), linux) + LINUX_ELF=@LINUX_ELF@ ifdef LINUX_ELF install-shlib-dep := install-shlib shlib := libecpg.so.$(SO_MAJOR_VERSION).$(SO_MINOR_VERSION) diff --git a/src/interfaces/ecpg/lib/ecpglib.c b/src/interfaces/ecpg/lib/ecpglib.c index 87b2350bdb0..815f4bff0bc 100644 --- a/src/interfaces/ecpg/lib/ecpglib.c +++ b/src/interfaces/ecpg/lib/ecpglib.c @@ -96,10 +96,11 @@ ECPGdo(int lineno, char *query,...) */ while (type != ECPGt_EOIT) { - void *value = NULL; - long varcharsize; - long size; - long arrsize; + void *value = NULL, *ind_value; + long varcharsize, ind_varcharsize; + long size, ind_size; + long arrsize, ind_arrsize; + enum ECPGttype ind_type; char *newcopy; char *mallocedval = NULL; @@ -117,9 +118,40 @@ ECPGdo(int lineno, char *query,...) varcharsize = va_arg(ap, long); size = va_arg(ap, long); arrsize = va_arg(ap, long); - - switch (type) + ind_type = va_arg(ap, enum ECPGttype); + ind_value = va_arg(ap, void *); + ind_varcharsize = va_arg(ap, long); + ind_size = va_arg(ap, long); + ind_arrsize = va_arg(ap, long); + + buff[0] = '\0'; + + /* check for null value and set input buffer accordingly */ + switch (ind_type) { + case ECPGt_short: + case ECPGt_unsigned_short: + if (*(short *) ind_value < 0) + strcpy(buff, "null"); + break; + case ECPGt_int: + case ECPGt_unsigned_int: + if (*(int *) ind_value < 0) + strcpy(buff, "null"); + break; + case ECPGt_long: + case ECPGt_unsigned_long: + if (*(long *) ind_value < 0L) + strcpy(buff, "null"); + break; + default: + break; + } + + if (*buff == '\0') + { + switch (type) + { case ECPGt_short: case ECPGt_int: sprintf(buff, "%d", *(int *) value); @@ -205,7 +237,10 @@ ECPGdo(int lineno, char *query,...) ECPGtype_name(type), lineno); return false; break; + } } + else + tobeinserted = buff; /* * Now tobeinserted points to an area that is to be inserted at @@ -266,7 +301,7 @@ ECPGdo(int lineno, char *query,...) if (committed) { - if ((results = PQexec(simple_connection, "begin")) == NULL) + if ((results = PQexec(simple_connection, "begin transaction")) == NULL) { register_error(-1, "Error starting transaction line %d.", lineno); return false; @@ -324,10 +359,11 @@ ECPGdo(int lineno, char *query,...) for (x = 0; x < m && status; x++) { - void *value = NULL; - long varcharsize; - long size; - long arrsize; + void *value = NULL, *ind_value; + long varcharsize, ind_varcharsize; + long size, ind_size; + long arrsize, ind_arrsize; + enum ECPGttype ind_type; char *pval = PQgetvalue(results, 0, x); @@ -339,14 +375,38 @@ ECPGdo(int lineno, char *query,...) ECPGlog("ECPGdo line %d: RESULT: %s\n", lineno, pval ? pval : ""); - /* No the pval is a pointer to the value. */ + /* Now the pval is a pointer to the value. */ /* We will have to decode the value */ type = va_arg(ap, enum ECPGttype); value = va_arg(ap, void *); varcharsize = va_arg(ap, long); size = va_arg(ap, long); arrsize = va_arg(ap, long); - + ind_type = va_arg(ap, enum ECPGttype); + ind_value = va_arg(ap, void *); + ind_varcharsize = va_arg(ap, long); + ind_size = va_arg(ap, long); + ind_arrsize = va_arg(ap, long); + + /* check for null value and set indicator accordingly */ + switch (ind_type) + { + case ECPGt_short: + case ECPGt_unsigned_short: + *(short *) ind_value = -PQgetisnull(results, 0, x); + break; + case ECPGt_int: + case ECPGt_unsigned_int: + *(int *) ind_value = -PQgetisnull(results, 0, x); + break; + case ECPGt_long: + case ECPGt_unsigned_long: + *(long *) ind_value = -PQgetisnull(results, 0, x); + break; + default: + break; + } + switch (type) { long res; @@ -486,7 +546,30 @@ ECPGdo(int lineno, char *query,...) ((char *) value)[strlen(pval)] = '\0'; } else + { strncpy((char *) value, pval, varcharsize); + if (varcharsize < strlen(pval)) + { + /* truncation */ + switch (ind_type) + { + case ECPGt_short: + case ECPGt_unsigned_short: + *(short *) ind_value = varcharsize; + break; + case ECPGt_int: + case ECPGt_unsigned_int: + *(int *) ind_value = varcharsize; + break; + case ECPGt_long: + case ECPGt_unsigned_long: + *(long *) ind_value = varcharsize; + break; + default: + break; + } + } + } } break; @@ -498,7 +581,28 @@ ECPGdo(int lineno, char *query,...) strncpy(var->arr, pval, varcharsize); var->len = strlen(pval); if (var->len > varcharsize) + { + /* truncation */ + switch (ind_type) + { + case ECPGt_short: + case ECPGt_unsigned_short: + *(short *) ind_value = varcharsize; + break; + case ECPGt_int: + case ECPGt_unsigned_int: + *(int *) ind_value = varcharsize; + break; + case ECPGt_long: + case ECPGt_unsigned_long: + *(long *) ind_value = varcharsize; + break; + default: + break; + } + var->len = varcharsize; + } } break; @@ -587,19 +691,6 @@ ECPGtrans(int lineno, const char * transaction) return (TRUE); } -/* include these for compatibility */ -bool -ECPGcommit(int lineno) -{ - return(ECPGtrans(lineno, "end")); -} - -bool -ECPGrollback(int lineno) -{ - return(ECPGtrans(lineno, "abort")); -} - bool ECPGsetdb(PGconn *newcon) { diff --git a/src/interfaces/ecpg/preproc/Makefile b/src/interfaces/ecpg/preproc/Makefile index 04d6ccb597b..73596c05fdd 100644 --- a/src/interfaces/ecpg/preproc/Makefile +++ b/src/interfaces/ecpg/preproc/Makefile @@ -1,13 +1,16 @@ SRCDIR= ../../.. include $(SRCDIR)/Makefile.global -MAJOR_VERSION=1 -MINOR_VERSION=1 +MAJOR_VERSION=2 +MINOR_VERSION=0 PATCHLEVEL=0 CFLAGS+=-I../include -DMAJOR_VERSION=$(MAJOR_VERSION) \ -DMINOR_VERSION=$(MINOR_VERSION) -DPATCHLEVEL=$(PATCHLEVEL) \ - -DINCLUDE_PATH=\"$(HEADERDIR)\" + -DINCLUDE_PATH=\"$(DESTDIR)$(HEADERDIR)\" + +OBJ=y.tab.o pgc.o type.o ecpg.o ecpg_keywords.o ../../../backend/parser/scansup.o \ + keywords.o c_keywords.o ../lib/typename.o all:: ecpg @@ -15,21 +18,22 @@ clean: rm -f *.o core a.out ecpg y.tab.h y.tab.c pgc.c *~ install: all - $(INSTALL) $(INSTL_EXE_OPTS) ecpg $(BINDIR) + $(INSTALL) $(INSTL_EXE_OPTS) ecpg $(DESTDIR)$(BINDIR) uninstall: - rm -f $(BINDIR)/ecpg - -dep depend: - $(CC) -MM $(CFLAGS) *.c > depend + rm -f $(DESTDIR)$(BINDIR)/ecpg # Rule that really do something. -ecpg: y.tab.o pgc.o type.o ecpg.o ../lib/typename.o - $(CC) -o ecpg y.tab.o pgc.o type.o ecpg.o ../lib/typename.o $(LEXLIB) $(LDFLAGS) +ecpg: $(OBJ) + $(CC) -o ecpg $(OBJ) $(LEXLIB) y.tab.h y.tab.c: preproc.y $(YACC) $(YFLAGS) $< -y.tab.o : y.tab.h ../include/ecpgtype.h +y.tab.o : y.tab.h ../include/ecpgtype.h keywords.c c_keywords.c ecpg_keywords.c type.o : ../include/ecpgtype.h -pgc.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 + diff --git a/src/interfaces/ecpg/preproc/c_keywords.c b/src/interfaces/ecpg/preproc/c_keywords.c new file mode 100644 index 00000000000..d50de7bd6e7 --- /dev/null +++ b/src/interfaces/ecpg/preproc/c_keywords.c @@ -0,0 +1,63 @@ +/*------------------------------------------------------------------------- + * + * keywords.c-- + * lexical token lookup for reserved words in postgres embedded SQL + * + *------------------------------------------------------------------------- + */ +#include <ctype.h> +#include <string.h> + +#include "postgres.h" +#include "type.h" +#include "y.tab.h" +#include "extern.h" + +/* + * List of (keyword-name, keyword-token-value) pairs. + * + * !!WARNING!!: This list must be sorted, because binary + * search is used to locate entries. + */ +static ScanKeyword ScanKeywords[] = { + /* name value */ + {"auto", S_AUTO}, + {"bool", S_BOOL}, + {"char", S_CHAR}, + {"const", S_CONST}, + {"double", S_DOUBLE}, + {"extern", S_EXTERN}, + {"float", S_FLOAT}, + {"int", S_INT}, + {"long", S_LONG}, + {"register", S_REGISTER}, + {"short", S_SHORT}, + {"signed", S_SIGNED}, + {"static", S_STATIC}, + {"struct", S_STRUCT}, + {"unsigned", S_UNSIGNED}, + {"varchar", S_VARCHAR}, +}; + +ScanKeyword * +ScanCKeywordLookup(char *text) +{ + ScanKeyword *low = &ScanKeywords[0]; + ScanKeyword *high = endof(ScanKeywords) - 1; + ScanKeyword *middle; + int difference; + + while (low <= high) + { + middle = low + (high - low) / 2; + difference = strcmp(middle->name, text); + if (difference == 0) + return (middle); + else if (difference < 0) + low = middle + 1; + else + high = middle - 1; + } + + return (NULL); +} diff --git a/src/interfaces/ecpg/preproc/ecpg.c b/src/interfaces/ecpg/preproc/ecpg.c index a97b74e748b..e156468f2b8 100644 --- a/src/interfaces/ecpg/preproc/ecpg.c +++ b/src/interfaces/ecpg/preproc/ecpg.c @@ -91,7 +91,7 @@ main(int argc, char *const argv[]) /* after the options there must not be anything but filenames */ for (fnr = optind; fnr < argc; fnr++) { - char *ptr2ext; + char *output_filename = NULL, *ptr2ext; input_filename = mm_alloc(strlen(argv[fnr]) + 5); @@ -113,7 +113,7 @@ main(int argc, char *const argv[]) if (out_option == 0)/* calculate the output name */ { - char *output_filename = strdup(input_filename); + output_filename = strdup(input_filename); ptr2ext = strrchr(output_filename, '.'); /* make extension = .c */ @@ -128,7 +128,6 @@ main(int argc, char *const argv[]) free(input_filename); continue; } - free(output_filename); } yyin = fopen(input_filename, "r"); @@ -136,9 +135,25 @@ main(int argc, char *const argv[]) perror(argv[fnr]); else { + struct cursor *ptr; + /* initialize lex */ lex_init(); - + + /* initialize cursor list */ + for (ptr = cur; ptr != NULL;) + { + struct cursor *c; + + free(ptr->name); + free(ptr->command); + c = ptr; + ptr = ptr->next; + free(c); + } + + cur = NULL; + /* we need two includes */ fprintf(yyout, "/* Processed by ecpg (%d.%d.%d) */\n/*These two include files are added by the preprocessor */\n#include <ecpgtype.h>\n#include <ecpglib.h>\n", MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL); @@ -150,6 +165,10 @@ main(int argc, char *const argv[]) if (out_option == 0) fclose(yyout); } + + if (output_filename) + free(output_filename); + free(input_filename); } } diff --git a/src/interfaces/ecpg/preproc/ecpg_keywords.c b/src/interfaces/ecpg/preproc/ecpg_keywords.c new file mode 100644 index 00000000000..6fea8a46bb3 --- /dev/null +++ b/src/interfaces/ecpg/preproc/ecpg_keywords.c @@ -0,0 +1,60 @@ +/*------------------------------------------------------------------------- + * + * keywords.c-- + * lexical token lookup for reserved words in postgres embedded SQL + * + *------------------------------------------------------------------------- + */ +#include <ctype.h> +#include <string.h> + +#include "postgres.h" +#include "type.h" +#include "y.tab.h" +#include "extern.h" + +/* + * List of (keyword-name, keyword-token-value) pairs. + * + * !!WARNING!!: This list must be sorted, because binary + * search is used to locate entries. + */ +static ScanKeyword ScanKeywords[] = { + /* name value */ + {"connect", SQL_CONNECT}, + {"continue", SQL_CONTINUE}, + {"found", SQL_FOUND}, + {"go", SQL_GO}, + {"goto", SQL_GOTO}, + {"immediate", SQL_IMMEDIATE}, + {"indicator", SQL_INDICATOR}, + {"open", SQL_OPEN}, + {"section", SQL_SECTION}, + {"sqlerror", SQL_SQLERROR}, + {"sqlprint", SQL_SQLPRINT}, + {"stop", SQL_STOP}, + {"whenever", SQL_WHENEVER}, +}; + +ScanKeyword * +ScanECPGKeywordLookup(char *text) +{ + ScanKeyword *low = &ScanKeywords[0]; + ScanKeyword *high = endof(ScanKeywords) - 1; + ScanKeyword *middle; + int difference; + + while (low <= high) + { + middle = low + (high - low) / 2; + difference = strcmp(middle->name, text); + if (difference == 0) + return (middle); + else if (difference < 0) + low = middle + 1; + else + high = middle - 1; + } + + return (NULL); +} diff --git a/src/interfaces/ecpg/preproc/extern.h b/src/interfaces/ecpg/preproc/extern.h index 53da42cfb12..96c7bc0ed30 100644 --- a/src/interfaces/ecpg/preproc/extern.h +++ b/src/interfaces/ecpg/preproc/extern.h @@ -1,3 +1,5 @@ +#include "parser/keywords.h" + /* variables */ extern int debugging, @@ -14,9 +16,19 @@ struct _include_path { char * path; extern struct _include_path *include_paths; +struct cursor { char *name; + char *command; + struct cursor *next; + }; + +extern struct cursor *cur; + /* functions */ extern void lex_init(void); extern char *input_filename; extern int yyparse(void); extern void *mm_alloc(size_t), *mm_realloc(void *, size_t); +ScanKeyword * ScanECPGKeywordLookup(char *); +ScanKeyword * ScanCKeywordLookup(char *); +extern void yyerror(char *); diff --git a/src/interfaces/ecpg/preproc/keywords.c b/src/interfaces/ecpg/preproc/keywords.c new file mode 100644 index 00000000000..4d8722f69df --- /dev/null +++ b/src/interfaces/ecpg/preproc/keywords.c @@ -0,0 +1,242 @@ +/*------------------------------------------------------------------------- + * + * keywords.c-- + * lexical token lookup for reserved words in postgres SQL + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/keywords.c,v 1.1 1998/04/21 13:23:06 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <ctype.h> +#include <string.h> + +#include "postgres.h" +#include "nodes/parsenodes.h" +#include "nodes/pg_list.h" +#include "type.h" +#include "y.tab.h" +#include "parser/keywords.h" +#include "utils/elog.h" + +/* + * List of (keyword-name, keyword-token-value) pairs. + * + * !!WARNING!!: This list must be sorted, because binary + * search is used to locate entries. + */ +static ScanKeyword ScanKeywords[] = { + /* name value */ + {"abort", ABORT_TRANS}, + {"action", ACTION}, + {"add", ADD}, + {"after", AFTER}, + {"aggregate", AGGREGATE}, + {"all", ALL}, + {"alter", ALTER}, + {"analyze", ANALYZE}, + {"and", AND}, + {"any", ANY}, + {"archive", ARCHIVE}, + {"as", AS}, + {"asc", ASC}, + {"backward", BACKWARD}, + {"before", BEFORE}, + {"begin", BEGIN_TRANS}, + {"between", BETWEEN}, + {"binary", BINARY}, + {"both", BOTH}, + {"by", BY}, + {"cache", CACHE}, + {"cascade", CASCADE}, + {"cast", CAST}, + {"char", CHAR}, + {"character", CHARACTER}, + {"check", CHECK}, + {"close", CLOSE}, + {"cluster", CLUSTER}, + {"collate", COLLATE}, + {"column", COLUMN}, + {"commit", COMMIT}, + {"constraint", CONSTRAINT}, + {"copy", COPY}, + {"create", CREATE}, + {"createdb", CREATEDB}, + {"createuser", CREATEUSER}, + {"cross", CROSS}, + {"current", CURRENT}, + {"current_date", CURRENT_DATE}, + {"current_time", CURRENT_TIME}, + {"current_timestamp", CURRENT_TIMESTAMP}, + {"current_user", CURRENT_USER}, + {"cursor", CURSOR}, + {"cycle", CYCLE}, + {"database", DATABASE}, + {"day", DAY_P}, + {"decimal", DECIMAL}, + {"declare", DECLARE}, + {"default", DEFAULT}, + {"delete", DELETE}, + {"delimiters", DELIMITERS}, + {"desc", DESC}, + {"distinct", DISTINCT}, + {"do", DO}, + {"double", DOUBLE}, + {"drop", DROP}, + {"each", EACH}, + {"end", END_TRANS}, + {"execute", EXECUTE}, + {"exists", EXISTS}, + {"explain", EXPLAIN}, + {"extend", EXTEND}, + {"extract", EXTRACT}, + {"false", FALSE_P}, + {"fetch", FETCH}, + {"float", FLOAT}, + {"for", FOR}, + {"foreign", FOREIGN}, + {"forward", FORWARD}, + {"from", FROM}, + {"full", FULL}, + {"function", FUNCTION}, + {"grant", GRANT}, + {"group", GROUP}, + {"handler", HANDLER}, + {"having", HAVING}, + {"hour", HOUR_P}, + {"in", IN}, + {"increment", INCREMENT}, + {"index", INDEX}, + {"inherits", INHERITS}, + {"inner", INNER_P}, + {"insert", INSERT}, + {"instead", INSTEAD}, + {"interval", INTERVAL}, + {"into", INTO}, + {"is", IS}, + {"isnull", ISNULL}, + {"join", JOIN}, + {"key", KEY}, + {"lancompiler", LANCOMPILER}, + {"language", LANGUAGE}, + {"leading", LEADING}, + {"left", LEFT}, + {"like", LIKE}, + {"listen", LISTEN}, + {"load", LOAD}, + {"local", LOCAL}, + {"location", LOCATION}, + {"lock", LOCK_P}, + {"match", MATCH}, + {"maxvalue", MAXVALUE}, + {"minute", MINUTE_P}, + {"minvalue", MINVALUE}, + {"month", MONTH_P}, + {"move", MOVE}, + {"national", NATIONAL}, + {"natural", NATURAL}, + {"nchar", NCHAR}, + {"new", NEW}, + {"no", NO}, + {"nocreatedb", NOCREATEDB}, + {"nocreateuser", NOCREATEUSER}, + {"none", NONE}, + {"not", NOT}, + {"nothing", NOTHING}, + {"notify", NOTIFY}, + {"notnull", NOTNULL}, + {"null", NULL_P}, + {"numeric", NUMERIC}, + {"oids", OIDS}, + {"on", ON}, + {"operator", OPERATOR}, + {"option", OPTION}, + {"or", OR}, + {"order", ORDER}, + {"outer", OUTER_P}, + {"partial", PARTIAL}, + {"password", PASSWORD}, + {"position", POSITION}, + {"precision", PRECISION}, + {"primary", PRIMARY}, + {"privileges", PRIVILEGES}, + {"procedural", PROCEDURAL}, + {"procedure", PROCEDURE}, + {"public", PUBLIC}, + {"recipe", RECIPE}, + {"references", REFERENCES}, + {"rename", RENAME}, + {"reset", RESET}, + {"returns", RETURNS}, + {"revoke", REVOKE}, + {"right", RIGHT}, + {"rollback", ROLLBACK}, + {"row", ROW}, + {"rule", RULE}, + {"second", SECOND_P}, + {"select", SELECT}, + {"sequence", SEQUENCE}, + {"set", SET}, + {"setof", SETOF}, + {"show", SHOW}, + {"start", START}, + {"statement", STATEMENT}, + {"stdin", STDIN}, + {"stdout", STDOUT}, + {"substring", SUBSTRING}, + {"table", TABLE}, + {"time", TIME}, + {"to", TO}, + {"trailing", TRAILING}, + {"transaction", TRANSACTION}, + {"trigger", TRIGGER}, + {"trim", TRIM}, + {"true", TRUE_P}, + {"trusted", TRUSTED}, + {"type", TYPE_P}, + {"union", UNION}, + {"unique", UNIQUE}, + {"until", UNTIL}, + {"update", UPDATE}, + {"user", USER}, + {"using", USING}, + {"vacuum", VACUUM}, + {"valid", VALID}, + {"values", VALUES}, + {"varchar", VARCHAR}, + {"varying", VARYING}, + {"verbose", VERBOSE}, + {"version", VERSION}, + {"view", VIEW}, + {"where", WHERE}, + {"with", WITH}, + {"work", WORK}, + {"year", YEAR_P}, + {"zone", ZONE}, +}; + +ScanKeyword * +ScanKeywordLookup(char *text) +{ + ScanKeyword *low = &ScanKeywords[0]; + ScanKeyword *high = endof(ScanKeywords) - 1; + ScanKeyword *middle; + int difference; + + while (low <= high) + { + middle = low + (high - low) / 2; + difference = strcmp(middle->name, text); + if (difference == 0) + return (middle); + else if (difference < 0) + low = middle + 1; + else + high = middle - 1; + } + + return (NULL); +} diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l index 8fc341400ea..669da63a068 100644 --- a/src/interfaces/ecpg/preproc/pgc.l +++ b/src/interfaces/ecpg/preproc/pgc.l @@ -1,6 +1,8 @@ -/* Copyright comment! */ +/* This is a modified version of src/backend/parser/scan.l */ %{ #include "config.h" + +#include <ctype.h> #include <sys/types.h> #include <limits.h> #if defined(HAVE_STRING_H) @@ -8,101 +10,428 @@ #else #include <strings.h> #endif +#include <errno.h> +#include "postgres.h" +#include "miscadmin.h" +#include "nodes/pg_list.h" +#include "nodes/parsenodes.h" +#include "parser/gramparse.h" +#include "parser/scansup.h" #include "type.h" #include "y.tab.h" +#include "utils/builtins.h" #include "extern.h" +/* some versions of lex define this as a macro */ +#if defined(yywrap) +#undef yywrap +#endif /* yywrap */ + +int debugging = 0; +extern YYSTYPE yylval; +int llen; +char literal[MAX_PARSE_BUFFER]; + struct _yy_buffer { YY_BUFFER_STATE buffer; long lineno; char * filename; struct _yy_buffer * next; } *yy_buffer = NULL; -#define dbg(arg) if (debugging) fprintf(stderr, "DEBUG, %d: %s\n", yylineno, #arg); %} %option yylineno %s C SQL incl -ccomment \/\*([^*]|\*[^/]|\*\*[^/])*\*\/ -ws ([ \t\n][ \t\n]*|{ccomment})* -letter [A-Za-z_] -digit [0-9] -length {digit}+ -symbol {letter}({letter}|{digit})* -label ({letter}|{digit})* -string '[^']*' - -abort [aA][bB][oO][rR][tT] -begin [bB][eE][gG][iI][nN] -commit [cC][oO][mM][mM][iI][tT] -connect [cC][oO][nN][nN][eE][cC][tT] -continue [cC][oO][nN][tT][iI][nN][uU][eE] -declare [dD][eE][cC][lL][aA][rR][eE] -do [dD][oO] -end [eE][nN][dD] -exec [eE][xX][eE][cC] -execute [eE][xX][eE][cC][uU][tT][eE] -fetch [fF][eE][tT][cC][hH] -found [fF][oO][uU][nN][dD] -from [fF][rR][oO][mM] -go [gG][oO] -goto [gG][oO][tT][oO] -immediate [iI][mM][mM][eE][dD][iI][aA][tT][eE] +/* OK, here is a short description of lex/flex rules behavior. + * The longest pattern which matches an input string is always chosen. + * For equal-length patterns, the first occurring in the rules list is chosen. + * INITIAL is the starting condition, to which all non-conditional rules apply. + * When in an exclusive condition, only those rules defined for that condition apply. + * + * Exclusive states change parsing rules while the state is active. + * There are exclusive states for quoted strings, extended comments, + * and to eliminate parsing troubles for numeric strings. + * Exclusive states: + * <xb> binary numeric string - thomas 1997-11-16 + * <xc> extended C-style comments - tgl 1997-07-12 + * <xd> delimited identifiers (double-quoted identifiers) - tgl 1997-10-27 + * <xh> hexadecimal numeric string - thomas 1997-11-16 + * <xm> numeric strings with embedded minus sign - tgl 1997-09-05 + * <xq> quoted strings - tgl 1997-07-30 + * + * The "extended comment" syntax closely resembles allowable operator syntax. + * So, when in condition <xc>, only strings which would terminate the + * "extended comment" trigger any action other than "ignore". + * Be sure to match _any_ candidate comment, including those with appended + * operator-like symbols. - thomas 1997-07-14 + */ + +%x xb +%x xc +%x xd +%x xh +%x xm +%x xq + +/* Binary number + */ +xbstart [bB]{quote} +xbstop {quote} +xbinside [^']* +xbcat {quote}{space}*\n{space}*{quote} + +/* Hexadecimal number + */ +xhstart [xX]{quote} +xhstop {quote} +xhinside [^']* +xhcat {quote}{space}*\n{space}*{quote} + +/* Extended quote + * xqdouble implements SQL92 embedded quote + * xqcat allows strings to cross input lines + */ +quote ' +xqstart {quote} +xqstop {quote} +xqdouble {quote}{quote} +xqinside [^\\']* +xqembedded "\\'" +xqliteral [\\](.|\n) +xqcat {quote}{space}*\n{space}*{quote} + +/* Delimited quote + * Allows embedded spaces and other special characters into identifiers. + */ +dquote \" +xdstart {dquote} +xdstop {dquote} +xdinside [^"]* + +/* Comments + * Ignored by the scanner and parser. + */ +xcline [\/][\*].*[\*][\/]{space}*\n* +xcstart [\/][\*]{op_and_self}* +xcstop {op_and_self}*[\*][\/]({space}*|\n) +xcinside [^*]* +xcstar [^/] + +digit [0-9] +number [-+.0-9Ee] +letter [\200-\377_A-Za-z] +letter_or_digit [\200-\377_A-Za-z0-9] + +identifier {letter}{letter_or_digit}* + +typecast "::" + +self [,()\[\].$\:\+\-\*\/\<\>\=\|] +op_and_self [\~\!\@\#\%\^\&\|\`\?\$\:\+\-\*\/\<\>\=] +operator {op_and_self}+ + +xminteger {integer}/- +xmreal {real}/{space}*-{digit} +xmstop - + +integer -?{digit}+ +real -?{digit}+\.{digit}+([Ee][-+]?{digit}+)? + +param \${integer} + +comment ("--"|"//").*\n + +space [ \t\n\f] +other . + +/* some stuff needed for ecpg */ +ccomment \/\*([^*]|\*[^/]|\*\*[^/])*\*\/ +exec [eE][xX][eE][cC] include [iI][nN][cC][lL][uU][dD][eE] -in [iI][nN] -into [iI][nN][tT][oO] -not [nN][oO][tT] -open [oO][pP][eE][nN] -release [rR][eE][lL][eE][aA][sS][eE] -rollback [rR][oO][lL][lL][bB][aA][cC][kK] -section [sS][eE][cC][tT][iI][oO][nN] -sql [sS][qQ][lL] -sqlerror [sS][qQ][lL][eE][rR][rR][oO][rR] -sqlprint [sS][qQ][lL][pP][rR][iI][nN][tT] -stop [sS][tT][oO][pP] -transaction [tT][rR][aA][nN][sS][aA][cC][tT][iI][oO][nN] -to [tT][oO] -varchar [vV][aA][rR][cC][hH][aA][rR] -varchar2 [vV][aA][rR][cC][hH][aA][rR]2 -whenever [wW][hH][eE][nN][eE][vV][eE][rR] -work [wW][oO][rR][kK] -vacuum [vV][aA][cC][uU][uU][mM] +sql [sS][qQ][lL] + +/* DO NOT PUT ANY COMMENTS IN THE FOLLOWING SECTION. + * AT&T lex does not properly handle C-style comments in this second lex block. + * So, put comments here. tgl - 1997-09-08 + * + * Quoted strings must allow some special characters such as single-quote + * and newline. + * Embedded single-quotes are implemented both in the SQL/92-standard + * style of two adjacent single quotes "''" and in the Postgres/Java style + * of escaped-quote "\'". + * Other embedded escaped characters are matched explicitly and the leading + * backslash is dropped from the string. - thomas 1997-09-24 + */ + %% -<C>{exec}{ws}{sql} { BEGIN SQL; dbg(SQL_START); return SQL_START; } -<SQL>";" { BEGIN C; dbg(SQL_SEMI); return SQL_SEMI; } -<SQL>{abort} { dbg(SQL_ABORT); return SQL_ABORT; } -<SQL>{begin} { dbg(SQL_BEGIN); return SQL_BEGIN; } -<SQL>{end} { dbg(SQL_END); return SQL_END; } -<SQL>{declare} { dbg(SQL_DECLARE); return SQL_DECLARE; } -<SQL>{execute} { dbg(SQL_EXECUTE); return SQL_EXECUTE; } -<SQL>{immediate} { dbg(SQL_IMMEDIATE); return SQL_IMMEDIATE; } -<SQL>{section} { dbg(SQL_SECTION); return SQL_SECTION; } -<SQL>{connect} { dbg(SQL_CONNECT); return SQL_CONNECT; } -<SQL>{open} { dbg(SQL_OPEN); return SQL_OPEN; } -<SQL>{commit} { dbg(SQL_COMMIT); return SQL_COMMIT; } -<SQL>{release} { dbg(SQL_RELEASE); return SQL_RELEASE; } -<SQL>{work} { dbg(SQL_WORK); return SQL_WORK; } -<SQL>{fetch} { dbg(SQL_FETCH); return SQL_FETCH; } -<SQL>{rollback} { dbg(SQL_ROLLBACK); return SQL_ROLLBACK; } -<SQL>{whenever} { dbg(SQL_WHENEVER); return SQL_WHENEVER; } -<SQL>{sqlerror} { dbg(SQL_SQLERROR); return SQL_SQLERROR; } -<SQL>{sqlprint} { dbg(SQL_SQLPRINT); return SQL_SQLPRINT; } -<SQL>{not}{ws}{found} { dbg(SQL_NOT_FOUND); return SQL_NOT_FOUND; } -<SQL>{continue} { dbg(SQL_CONTINUE); return SQL_CONTINUE; } -<SQL>{into} { dbg(SQL_INTO); return SQL_INTO; } -<SQL>{in} { dbg(SQL_IN); return SQL_IN; } -<SQL>{goto} { dbg(SQL_GOTO); return SQL_GOTO; } -<SQL>{go}{ws}{to} { dbg(SQL_GOTO); return SQL_GOTO; } -<SQL>{stop} { dbg(SQL_STOP); return SQL_STOP; } -<SQL>{do} { dbg(SQL_DO); return SQL_DO; } -<SQL>{from} { dbg(SQL_FROM); return SQL_FROM; } -<SQL>{transaction} { dbg(SQL_TRANSACTION); return SQL_TRANSACTION; } -<SQL>{vacuum} { dbg(SQL_VACUUM); return SQL_VACUUM; } - - -<C>{exec}{ws}{sql}{ws}{include} { BEGIN(incl); } -<incl>{ws} /* eat the whitespace */ +<SQL>{comment} { /* ignore */ } + +<SQL>{xcline} { /* ignore */ } + +<xc>{xcstar} | +<SQL>{xcstart} { BEGIN(xc); } + +<xc>{xcstop} { BEGIN(SQL); } + +<xc>{xcinside} { /* ignore */ } + +<SQL>{xbstart} { + BEGIN(xb); + llen = 0; + *literal = '\0'; + } +<xb>{xbstop} { + char* endptr; + + BEGIN(SQL); + errno = 0; + yylval.ival = strtol((char *)literal,&endptr,2); + if (*endptr != '\0' || errno == ERANGE) + yyerror("ERROR: Bad binary integer input!"); + return (ICONST); + } +<xh>{xhinside} | +<xb>{xbinside} { + if ((llen+yyleng) > (MAX_PARSE_BUFFER - 1)) + yyerror("ERROR: quoted string parse buffer exceeded"); + memcpy(literal+llen, yytext, yyleng+1); + llen += yyleng; + } +<xh>{xhcat} | +<xb>{xbcat} { + } + +<SQL>{xhstart} { + BEGIN(xh); + llen = 0; + *literal = '\0'; + } +<xh>{xhstop} { + char* endptr; + + BEGIN(SQL); + errno = 0; + yylval.ival = strtol((char *)literal,&endptr,16); + if (*endptr != '\0' || errno == ERANGE) + yyerror("ERROR: Bad hexadecimal integer input"); + return (ICONST); + } + +<SQL>{xqstart} { + BEGIN(xq); + llen = 0; + *literal = '\0'; + } +<xq>{xqstop} { + BEGIN(SQL); + yylval.str = strdup(scanstr(literal)); + return (SCONST); + } +<xq>{xqdouble} | +<xq>{xqinside} { + if ((llen+yyleng) > (MAX_PARSE_BUFFER - 1)) + yyerror("ERROR: quoted string parse buffer exceeded"); + memcpy(literal+llen, yytext, yyleng+1); + llen += yyleng; + } +<xq>{xqembedded} { + if ((llen+yyleng-1) > (MAX_PARSE_BUFFER - 1)) + yyerror("ERROR: quoted string parse buffer exceeded"); + memcpy(literal+llen, yytext, yyleng+1); + *(literal+llen) = '\''; + llen += yyleng; + } + +<xq>{xqliteral} { + if ((llen+yyleng-1) > (MAX_PARSE_BUFFER - 1)) + yyerror("ERROR: quoted string parse buffer exceeded"); + memcpy(literal+llen, yytext, yyleng+1); + llen += yyleng; + } +<xq>{xqcat} { + } + + +<SQL>{xdstart} { + BEGIN(xd); + llen = 0; + *literal = '\0'; + } +<xd>{xdstop} { + BEGIN(SQL); + yylval.str = strdup(literal); + return (IDENT); + } +<xd>{xdinside} { + if ((llen+yyleng) > (MAX_PARSE_BUFFER - 1)) + yyerror("ERROR: quoted string parse buffer exceeded"); + memcpy(literal+llen, yytext, yyleng+1); + llen += yyleng; + } + + +<xm>{space}* { /* ignore */ } +<xm>{xmstop} { + BEGIN(SQL); + return (yytext[0]); + } + + +<SQL>{typecast} { return TYPECAST; } + +<SQL>{self}/-[\.0-9] { + return (yytext[0]); + } +<SQL>{self} { return (yytext[0]); } +<SQL>{operator}/-[\.0-9] { + yylval.str = strdup((char*)yytext); + return (Op); + } +<SQL>{operator} { + if (strcmp((char*)yytext,"!=") == 0) + yylval.str = strdup("<>"); /* compatability */ + else + yylval.str = strdup((char*)yytext); + return (Op); + } +<SQL>{param} { + yylval.ival = atoi((char*)&yytext[1]); + return (PARAM); + } + +<SQL>{identifier}/{space}*-{number} { + int i; + ScanKeyword *keyword; + + BEGIN(xm); + for(i = 0; yytext[i]; i++) + if (isupper(yytext[i])) + yytext[i] = tolower(yytext[i]); + + keyword = ScanKeywordLookup((char*)yytext); + if (keyword != NULL) { + return (keyword->value); + } + else + { + keyword = ScanECPGKeywordLookup((char*)yytext); + if (keyword != NULL) { + return (keyword->value); + } + else + { + yylval.str = strdup((char*)yytext); + return (IDENT); + } + } + } +{integer}/{space}*-{number} { + char* endptr; + + BEGIN(xm); + errno = 0; + yylval.ival = strtol((char *)yytext,&endptr,10); + if (*endptr != '\0' || errno == ERANGE) + { + errno = 0; + yylval.dval = strtod(((char *)yytext),&endptr); + if (*endptr != '\0' || errno == ERANGE) + yyerror("ERROR: Bad integer input"); + yyerror("WARNING: Integer input is out of range; promoted to float"); + return (FCONST); + } + return (ICONST); + } +{real}/{space}*-{number} { + char* endptr; + + BEGIN(xm); + errno = 0; + yylval.dval = strtod(((char *)yytext),&endptr); + if (*endptr != '\0' || errno == ERANGE) + yyerror("ERROR: Bad float8 input"); + return (FCONST); + } +{integer} { + char* endptr; + + errno = 0; + yylval.ival = strtol((char *)yytext,&endptr,10); + if (*endptr != '\0' || errno == ERANGE) + { + errno = 0; + yylval.dval = strtod(((char *)yytext),&endptr); + if (*endptr != '\0' || errno == ERANGE) + yyerror("ERROR: Bad integer input"); + yyerror("WARNING: Integer input is out of range; promoted to float"); + return (FCONST); + } + return (ICONST); + } +{real} { + char* endptr; + + errno = 0; + yylval.dval = strtod((char *)yytext,&endptr); + if (*endptr != '\0' || errno == ERANGE) + yyerror("ERROR: Bad float input"); + return (FCONST); + } + +<SQL>{identifier} { + int i; + ScanKeyword *keyword; + + for(i = 0; yytext[i]; i++) + if (isupper(yytext[i])) + yytext[i] = tolower(yytext[i]); + + keyword = ScanKeywordLookup((char*)yytext); + if (keyword != NULL) { + return (keyword->value); + } + else + { + keyword = ScanECPGKeywordLookup((char*)yytext); + if (keyword != NULL) { + return (keyword->value); + } + else + { + yylval.str = strdup((char*)yytext); + return (IDENT); + } + } + } +<SQL>{space} { /* ignore */ } +<SQL>";" { BEGIN C; return SQL_SEMI; } +<SQL>{other} { return (yytext[0]); } + +<C>{exec}{space}{sql} { BEGIN SQL; return SQL_START; } +<C>{identifier} { + ScanKeyword *keyword; + + keyword = ScanCKeywordLookup((char*)yytext); + if (keyword != NULL) { + return (keyword->value); + } + else + { + yylval.str = strdup((char*)yytext); + return (IDENT); + } + } +<C>";" { return(';'); } +<C>{space} { ECHO; } +\{ { return('{'); } +\} { return('}'); } +\[ { return('['); } +\] { return(']'); } +\= { return('='); } +<C>{other} { return (S_ANYTHING); } +<C>{exec}{space}{sql}{space}{include} { BEGIN(incl); } +<incl>{space} /* eat the whitespace */ <incl>[^ \t\n]+ { /* got the include file name */ struct _yy_buffer *yb; struct _include_path *ip; @@ -125,7 +454,7 @@ vacuum [vV][aA][cC][uU][uU][mM] { if (strlen(ip->path) + strlen(yytext) + 3 > PATH_MAX) { - fprintf(stderr, "Path %s/%s is too long, skipping.\n", ip->path, yytext); + fprintf(stderr, "Error: Path %s/%s is too long in line %d, skipping.\n", ip->path, yytext, yylineno); continue; } sprintf (inc_file, "%s/%s", ip->path, yytext); @@ -142,7 +471,7 @@ vacuum [vV][aA][cC][uU][uU][mM] } if (!yyin) { - fprintf(stderr, "Cannot open include file %s\n", yytext); + fprintf(stderr, "Error: Cannot open include file %s in line %d\n", yytext, yylineno); exit(1); } @@ -153,79 +482,6 @@ vacuum [vV][aA][cC][uU][uU][mM] BEGIN C; } <incl>";" { BEGIN C; } -{length} { dbg(S_LENGTH); return S_LENGTH; } - -{varchar} { dbg(S_VARCHAR); return S_VARCHAR; } -{varchar2} { dbg(S_VARCHAR2); return S_VARCHAR2; } -long { dbg(S_LONG); return S_LONG; } -short { dbg(S_SHORT); return S_SHORT; } -int { dbg(S_INT); return S_INT; } -char { dbg(S_CHAR); return S_CHAR; } -float { dbg(S_FLOAT); return S_FLOAT; } -double { dbg(S_DOUBLE); return S_DOUBLE; } -bool { dbg(S_BOOL); return S_BOOL; } - -static { dbg(S_STATIC); return S_STATIC; } -signed { dbg(S_SIGNED); return S_SIGNED; } -extern { dbg(S_EXTERN); return S_EXTERN; } -auto { dbg(S_AUTO); return S_AUTO; } -const { dbg(S_CONST); return S_CONST; } -register { dbg(S_REGISTER); return S_REGISTER; } - -struct { dbg(S_STRUCT); return S_STRUCT; } - -{string} { dbg(SQL_STRING); return SQL_STRING; } -<SQL>{ws} ; -{symbol} { dbg(S_SYMBOL); return S_SYMBOL; } -{label} { dbg(S_LABEL); return S_LABEL; } - -<SQL>"!<" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"!>" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"!^" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"!|" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"!~" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"!~*" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"#<" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"#<=" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"#<>" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"#=" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"#>" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"#>=" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"&&" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"&<" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"&>" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"<<" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"<=" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"<===>" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"<>" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"<?>" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"===>" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"===`" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"=|=" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>">=" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>">>" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"@@" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"|/" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"||/" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"~*" { dbg(S_SYMBOL); return S_SYMBOL; } -<SQL>"~=" { dbg(S_SYMBOL); return S_SYMBOL; } - -"[" { dbg([); return '['; } -"]" { dbg(]); return ']'; } -";" { dbg(;); return ';'; } -"=" { dbg(=); return '='; } -"," { dbg(komma); return ','; } -\( { dbg(braceopen); return '('; } -\) { dbg(braceclose); return ')'; } -\{ { dbg(blockstart); return '{'; } -\} { dbg(blockend); return '}'; } -\* { dbg(*); return('*'); } - -<SQL>":" { dbg(:); return ':'; } -<SQL>"::" { dbg(SQL_CONV); return SQL_CONV; } - -{ws} { ECHO; } -. { dbg(.); return S_ANYTHING; } <<EOF>> { if (yy_buffer == NULL) yyterminate(); else @@ -260,4 +516,3 @@ int yywrap(void) { return 1; } - diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y index d3a2fc6d9ba..6eb4a88acd5 100644 --- a/src/interfaces/ecpg/preproc/preproc.y +++ b/src/interfaces/ecpg/preproc/preproc.y @@ -3,23 +3,25 @@ #include <stdio.h> #include <string.h> #include <stdlib.h> +#include "catalog/catname.h" #include "type.h" #include "extern.h" -static void yyerror(char *); - /* * Variables containing simple states. */ -int debugging = 0; static int struct_level = 0; -static char *do_str = NULL; +static char *do_str = NULL, errortext[128]; static int do_length = 0; +static int QueryIsRule = 0; /* temporarily store record members while creating the data structure */ struct ECPGrecord_member *record_member_list[128] = { NULL }; +/* keep a list of cursors */ +struct cursor *cur = NULL; + /* * Handle the filename and line numbering. */ @@ -44,9 +46,9 @@ print_action(struct when *w) { case W_SQLPRINT: fprintf(yyout, "sqlprint();"); break; - case W_GOTO: fprintf(yyout, "goto %s;", w->str); + case W_GOTO: fprintf(yyout, "goto %s;", w->command); break; - case W_DO: fprintf(yyout, "%s;", w->str); + case W_DO: fprintf(yyout, "%s;", w->command); break; case W_STOP: fprintf(yyout, "exit (1);"); break; @@ -72,7 +74,7 @@ whenever_action() } /* - * Handling of the variables. + * Handling of variables. */ /* @@ -95,6 +97,7 @@ static struct variable * find_variable(char * name) { struct variable * p; + char * errorstring = (char *) mm_alloc(strlen(name) + 100); for (p = allvariables; p; p = p->next) { @@ -102,13 +105,10 @@ find_variable(char * name) return p; } - { - char * errorstring = (char *) malloc(strlen(name) + 100); - - sprintf(errorstring, "The variable :%s is not declared.", name); + sprintf(errorstring, "The variable :%s is not declared", name); + yyerror(errorstring); + free (errorstring); - yyerror(errorstring); - } return NULL; } @@ -116,7 +116,7 @@ find_variable(char * name) static void new_variable(const char * name, struct ECPGtype * type) { - struct variable * p = (struct variable*) malloc(sizeof(struct variable)); + struct variable * p = (struct variable*) mm_alloc(sizeof(struct variable)); p->name = strdup(name); p->type = type; @@ -159,6 +159,7 @@ remove_variables(int brace_level) */ struct arguments { struct variable * variable; + struct variable * indicator; struct arguments * next; }; @@ -166,6 +167,9 @@ struct arguments { static struct arguments * argsinsert = NULL; static struct arguments * argsresult = NULL; +static struct ECPGtype ecpg_no_indicator = {ECPGt_NO_INDICATOR, 0L, {NULL}}; +static struct variable no_indicator = {"no_indicator", &ecpg_no_indicator, 0, NULL}; + static void reset_variables(void) { @@ -176,10 +180,11 @@ reset_variables(void) /* Add a variable to a request. */ static void -add_variable(struct arguments ** list, struct variable * var) +add_variable(struct arguments ** list, struct variable * var, struct variable * ind) { - struct arguments * p = (struct arguments *)malloc(sizeof(struct arguments)); + struct arguments * p = (struct arguments *)mm_alloc(sizeof(struct arguments)); p->variable = var; + p->indicator = ind; p->next = *list; *list = p; } @@ -203,118 +208,3502 @@ dump_variables(struct arguments * list) dump_variables(list->next); - /* Then the current element. */ - ECPGdump_a_type(yyout, list->variable->name, list->variable->type, NULL); + /* Then the current element and its indicator */ + ECPGdump_a_type(yyout, list->variable->name, list->variable->type, list->indicator->name, list->indicator->type, NULL, NULL); /* Then release the list element. */ free(list); } + +static void +check_indicator(struct ECPGtype *var) +{ + /* make sure this is a valid indicator variable */ + switch (var->typ) + { + struct ECPGrecord_member *p; + + case ECPGt_short: + case ECPGt_int: + case ECPGt_long: + case ECPGt_unsigned_short: + case ECPGt_unsigned_int: + case ECPGt_unsigned_long: + break; + + case ECPGt_record: + for (p = var->u.members; p; p = p->next) + check_indicator(p->typ); + break; + + case ECPGt_array: + check_indicator(var->u.element); + break; + default: + yyerror ("indicator variable must be integer type"); + break; + } +} + +static char * +cat2_str(const char *str1, const char *str2) +{ + char * res_str = (char *)mm_alloc(strlen(str1) + strlen(str2) + 1); + + strcpy(res_str, str1); + strcat(res_str, str2); + return(res_str); +} + +static char * +make2_str(const char *str1, const char *str2) +{ + char * res_str = (char *)mm_alloc(strlen(str1) + strlen(str2) + 2); + + strcpy(res_str, str1); + strcat(res_str, " "); + strcat(res_str, str2); + return(res_str); +} + +static char * +cat3_str(const char *str1, const char *str2, const char * str3) +{ + char * res_str = (char *)mm_alloc(strlen(str1) + strlen(str2) + strlen(str3) + 1); + + strcpy(res_str, str1); + strcat(res_str, str2); + strcat(res_str, str3); + return(res_str); +} + +static char * +make3_str(const char *str1, const char *str2, const char * str3) +{ + char * res_str = (char *)mm_alloc(strlen(str1) + strlen(str2) + strlen(str3) + 3); + + strcpy(res_str, str1); + strcat(res_str, " "); + strcat(res_str, str2); + strcat(res_str, " "); + strcat(res_str, str3); + return(res_str); +} + +static char * +cat4_str(const char *str1, const char *str2, const char *str3, const char *str4) +{ + char * res_str = (char *)mm_alloc(strlen(str1) + strlen(str2) + strlen(str3) + strlen(str4) + 1); + + strcpy(res_str, str1); + strcat(res_str, str2); + strcat(res_str, str3); + strcat(res_str, str4); + return(res_str); +} + +static char * +make4_str(const char *str1, const char *str2, const char *str3, const char *str4) +{ + char * res_str = (char *)mm_alloc(strlen(str1) + strlen(str2) + strlen(str3) + strlen(str4) + 4); + + strcpy(res_str, str1); + strcat(res_str, " "); + strcat(res_str, str2); + strcat(res_str, " "); + strcat(res_str, str3); + strcat(res_str, " "); + strcat(res_str, str4); + return(res_str); +} + +static char * +cat5_str(const char *str1, const char *str2, const char *str3, const char *str4, const char *str5) +{ + char * res_str = (char *)mm_alloc(strlen(str1) + strlen(str2) + strlen(str3) + strlen(str4) + strlen(str5) + 1); + + strcpy(res_str, str1); + strcat(res_str, str2); + strcat(res_str, str3); + strcat(res_str, str4); + strcat(res_str, str5); + return(res_str); +} + +static char * +make5_str(const char *str1, const char *str2, const char *str3, const char *str4, const char *str5) +{ + char * res_str = (char *)mm_alloc(strlen(str1) + strlen(str2) + strlen(str3) + strlen(str4) + strlen(str5) + 5); + + strcpy(res_str, str1); + strcat(res_str, " "); + strcat(res_str, str2); + strcat(res_str, " "); + strcat(res_str, str3); + strcat(res_str, " "); + strcat(res_str, str4); + strcat(res_str, " "); + strcat(res_str, str5); + return(res_str); +} + +static char * +make_name(void) +{ + char * name = (char *)mm_alloc(yyleng + 1); + + strncpy(name, yytext, yyleng); + name[yyleng] = '\0'; + return(name); +} + +static void +output_statement(const char * stmt) +{ + fprintf(yyout, "ECPGdo(__LINE__, \"%s\", ", stmt); + + /* dump variables to C file*/ + dump_variables(argsinsert); + fputs("ECPGt_EOIT, ", yyout); + dump_variables(argsresult); + fputs("ECPGt_EORT);", yyout); + whenever_action(); +} %} %union { - int tagname; - struct ECPGtemp_type type; - char * symbolname; - long indexsize; - enum ECPGttype type_enum; - struct when action; -} - -%token <tagname> SQL_START SQL_SEMI SQL_STRING SQL_INTO SQL_IN -%token <tagname> SQL_BEGIN SQL_END SQL_DECLARE SQL_SECTION SQL_INCLUDE -%token <tagname> SQL_CONNECT SQL_OPEN SQL_EXECUTE SQL_IMMEDIATE -%token <tagname> SQL_COMMIT SQL_ROLLBACK SQL_RELEASE SQL_WORK SQL_WHENEVER -%token <tagname> SQL_SQLERROR SQL_NOT_FOUND SQL_CONTINUE SQL_FROM SQL_FETCH -%token <tagname> SQL_DO SQL_GOTO SQL_SQLPRINT SQL_STOP SQL_CONV -%token <tagname> SQL_ABORT SQL_TRANSACTION SQL_VACUUM - -%token <tagname> S_SYMBOL S_LENGTH S_ANYTHING S_LABEL -%token <tagname> S_VARCHAR S_VARCHAR2 -%token <tagname> S_EXTERN S_STATIC S_AUTO S_CONST S_REGISTER S_STRUCT -%token <tagname> S_UNSIGNED S_SIGNED -%token <tagname> S_LONG S_SHORT S_INT S_CHAR S_FLOAT S_DOUBLE S_BOOL -%token <tagname> '[' ']' ';' ',' '{' '}' '=' '*' '(' ')' - -%type <type> type type_detailed varchar_type simple_type struct_type string_type -/* % type <type> array_type pointer_type */ -%type <symbolname> symbol label transactionstmt -%type <tagname> maybe_storage_clause varchar_tag db_name cursor -%type <type_enum> simple_tag char_tag -%type <indexsize> index length -%type <action> action -%type <tagname> canything sqlanything both_anything vartext sqlcommand -%type <tagname> transbegin, transend, transabort -%% -prog : statements; - -statements : /* empty */ - | statements statement; - -statement : sqlconnect - | sqldeclaration - | sqlexecute - | sqlfetch - | sqlinclude - | sqlopen - | sqlstatement - | sqltransaction - | sqlwhenever - | blockstart - | blockend - | cthing; - -sqldeclaration : sql_startdeclare - variable_declarations - sql_enddeclare; - -sql_startdeclare : SQL_START SQL_BEGIN SQL_DECLARE SQL_SECTION SQL_SEMI { - fprintf(yyout, "/* exec sql begin declare section */\n"); - output_line_number(); + double dval; + int ival; + char * str; + struct ECPGtemp_type type; + struct when action; + int tagname; + enum ECPGttype type_enum; } -sql_enddeclare : SQL_START SQL_END SQL_DECLARE SQL_SECTION SQL_SEMI { - fprintf(yyout,"/* exec sql end declare section */\n"); +/* special embedded SQL token */ +%token SQL_CONNECT SQL_CONTINUE SQL_FOUND SQL_GO SQL_GOTO +%token SQL_IMMEDIATE SQL_INDICATOR SQL_OPEN +%token SQL_SECTION SQL_SEMI SQL_SQLERROR SQL_SQLPRINT SQL_START +%token SQL_STOP SQL_WHENEVER + +/* C token */ +%token S_ANYTHING S_AUTO S_BOOL S_CHAR S_CONST S_DOUBLE S_EXTERN +%token S_FLOAT S_INT +%token S_LONG S_REGISTER S_SHORT S_SIGNED S_STATIC S_STRUCT S_UNSIGNED +%token S_VARCHAR + +/* I need this and don't know where it is defined inside the backend */ +%token TYPECAST + +/* Keywords (in SQL92 reserved words) */ +%token ACTION, ADD, ALL, ALTER, AND, ANY AS, ASC, + BEGIN_TRANS, BETWEEN, BOTH, BY, + CASCADE, CAST, CHAR, CHARACTER, CHECK, CLOSE, COLLATE, COLUMN, COMMIT, + CONSTRAINT, CREATE, CROSS, CURRENT, CURRENT_DATE, CURRENT_TIME, + CURRENT_TIMESTAMP, CURRENT_USER, CURSOR, + DAY_P, DECIMAL, DECLARE, DEFAULT, DELETE, DESC, DISTINCT, DOUBLE, DROP, + END_TRANS, EXECUTE, EXISTS, EXTRACT, + FETCH, FLOAT, FOR, FOREIGN, FROM, FULL, + GRANT, GROUP, HAVING, HOUR_P, + IN, INNER_P, INSERT, INTERVAL, INTO, IS, + JOIN, KEY, LANGUAGE, LEADING, LEFT, LIKE, LOCAL, + MATCH, MINUTE_P, MONTH_P, + NATIONAL, NATURAL, NCHAR, NO, NOT, NOTIFY, NULL_P, NUMERIC, + ON, OPTION, OR, ORDER, OUTER_P, + 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, + VALUES, VARCHAR, VARYING, VIEW, + WHERE, WITH, WORK, YEAR_P, ZONE + +/* Keywords (in SQL3 reserved words) */ +%token FALSE_P, TRIGGER, TRUE_P + +/* Keywords (in SQL92 non-reserved words) */ +%token TYPE_P + +/* Keywords for Postgres support (not in SQL92 reserved words) */ +%token ABORT_TRANS, AFTER, AGGREGATE, ANALYZE, + BACKWARD, BEFORE, BINARY, CACHE, CLUSTER, COPY, CYCLE, + DATABASE, DELIMITERS, DO, EACH, EXPLAIN, EXTEND, + FORWARD, FUNCTION, HANDLER, + INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL, + LANCOMPILER, LISTEN, LOAD, LOCK_P, LOCATION, MAXVALUE, MINVALUE, MOVE, + NEW, NONE, NOTHING, NOTNULL, OIDS, OPERATOR, PROCEDURAL, + RECIPE, RENAME, RESET, RETURNS, ROW, RULE, + SEQUENCE, SETOF, SHOW, START, STATEMENT, STDIN, STDOUT, TRUSTED, + VACUUM, VERBOSE, VERSION + +/* Keywords (obsolete; retain through next version for parser - thomas 1997-12-0 4) */ +%token ARCHIVE + +/* + * Tokens for pg_passwd support. The CREATEDB and CREATEUSER tokens should go a way + * when some sort of pg_privileges relation is introduced. + * + * Todd A. Brandys + */ +%token USER, PASSWORD, CREATEDB, NOCREATEDB, CREATEUSER, NOCREATEUSER, VALID, UNTIL + +/* Special keywords, not in the query language - see the "lex" file */ +%token <str> IDENT SCONST Op +%token <ival> ICONST PARAM +%token <dval> FCONST + +/* these are not real. they are here so that they get generated as #define's*/ +%token OP + +/* precedence */ +%left OR +%left AND +%right NOT +%right '=' +%nonassoc '<' '>' +%nonassoc LIKE +%nonassoc BETWEEN +%nonassoc IN +%nonassoc Op /* multi-character ops and user-defined operators */ +%nonassoc NOTNULL +%nonassoc ISNULL +%nonassoc IS +%left '+' '-' +%left '*' '/' +%left '|' /* this is the relation union op, not logical or */ +/* Unary Operators */ +%right ':' +%left ';' /* end of statement or natural log */ +%right UMINUS +%left '.' +%left '[' ']' +%nonassoc TYPECAST +%nonassoc REDUCE +%left UNION + +%type <str> Iconst Sconst TransactionStmt CreateStmt UserId +%type <str> CreateAsElement OptCreateAs CreateAsList CreateAsStmt +%type <str> OptArchiveType OptInherit key_reference key_action +%type <str> key_match constraint_expr ColLabel SpecialRuleRelation +%type <str> ColId default_expr ColQualifier columnDef ColQualList +%type <str> ColConstraint ColConstraintElem default_list +%type <str> OptTableElementList OptTableElement TableConstraint +%type <str> ConstraintElem key_actions constraint_list TypeId +%type <str> res_target_list res_target_el res_target_list2 +%type <str> res_target_el2 opt_id relation_name database_name +%type <str> access_method attr_name class index_name name func_name +%type <str> file_name recipe_name AexprConst ParamNo NumConst TypeId +%type <str> in_expr_nodes not_in_expr_nodes a_expr b_expr +%type <str> opt_indirection expr_list extract_list extract_arg +%type <str> position_list position_expr substr_list substr_from +%type <str> trim_list in_expr substr_for not_in_expr attr attrs +%type <str> Typename Array Generic Numeric generic opt_float opt_numeric +%type <str> opt_decimal Character character opt_varying opt_charset +%type <str> opt_collate Datetime datetime opt_timezone opt_interval +%type <str> numeric a_expr_or_null row_expr row_descriptor row_list +%type <str> SelectStmt union_clause select_list SubSelect result +%type <str> opt_table opt_union opt_unique sort_clause sortby_list +%type <str> sortby OptUseOp opt_inh_star relation_name_list name_list +%type <str> group_clause groupby_list groupby having_clause from_clause +%type <str> from_list from_val join_expr join_outer join_spec join_list +%type <str> join_using where_clause relation_expr opt_array_bounds +%type <str> nest_array_bounds opt_column_list insert_rest InsertStmt +%type <str> columnList DeleteStmt LockStmt UpdateStmt CursorStmt +%type <str> NotifyStmt columnElem copy_dirn OptimizableStmt +%type <str> copy_delimiter ListenStmt CopyStmt copy_file_name opt_binary +%type <str> opt_with_copy FetchStmt opt_direction fetch_how_many opt_portal_name +%type <str> ClosePortalStmt DestroyStmt VacuumStmt opt_verbose +%type <str> opt_analyze opt_va_list va_list ExplainStmt index_params +%type <str> index_list func_index index_elem opt_type opt_class access_method_clause +%type <str> index_opt_unique IndexStmt set_opt func_return def_rest +%type <str> func_args_list func_args opt_with ProcedureStmt def_arg +%type <str> def_elem def_list definition def_name def_type DefineStmt +%type <str> opt_instead event event_object OptStmtMulti OptStmtBlock +%type <str> OptStmtList RuleStmt opt_column opt_name oper_argtypes +%type <str> MathOp RemoveOperStmt RemoveFuncStmt aggr_argtype +%type <str> RemoveAggrStmt remove_type RemoveStmt ExtendStmt RecipeStmt +%type <str> RemoveOperStmt RenameStmt all_Op user_valid_clause +%type <str> VariableSetStmt var_value zone_value VariableShowStmt +%type <str> VariableResetStmt AddAttrStmt alter_clause DropUserStmt +%type <str> user_passwd_clause user_createdb_clause +%type <str> user_createuser_clause user_group_list user_group_clause +%type <str> CreateUserStmt AlterUserStmt CreateSeqStmt OptSeqList +%type <str> OptSeqElem TriggerForSpec TriggerForOpt TriggerForType +%type <str> TriggerFuncArgs DropTrigStmt TriggerOneEvent TriggerEvents +%type <str> TriggerActionTime CreateTrigStmt DropPLangStmt PLangTrusted +%type <str> CreatePLangStmt IntegerOnly TriggerFuncArgs TriggerFuncArg +%type <str> ViewStmt LoadStmt CreatedbStmt opt_database location +%type <str> DestroydbStmt ClusterStmt grantee RevokeStmt +%type <str> GrantStmt privileges operation_commalist operation + +%type <str> ECPGWhenever ECPGConnect db_name ECPGOpen open_opts +%type <str> indicator ECPGExecute c_expr +%type <str> stmt symbol + +%type <action> action + +%% +prog: statements; + +statements: /* empty */ + | statements statement + +statement: ecpgstart stmt SQL_SEMI + | ECPGDeclaration + | c_anything + | blockstart + | blockend + +stmt: AddAttrStmt { output_statement($1); } + | AlterUserStmt { output_statement($1); } + | ClosePortalStmt { output_statement($1); } + | CopyStmt { output_statement($1); } + | CreateStmt { output_statement($1); } + | CreateAsStmt { output_statement($1); } + | CreateSeqStmt { output_statement($1); } + | CreatePLangStmt { output_statement($1); } + | CreateTrigStmt { output_statement($1); } + | CreateUserStmt { output_statement($1); } + | ClusterStmt { output_statement($1); } + | DefineStmt { output_statement($1); } + | DestroyStmt { output_statement($1); } + | DropPLangStmt { output_statement($1); } + | DropTrigStmt { output_statement($1); } + | DropUserStmt { output_statement($1); } + | ExtendStmt { output_statement($1); } + | ExplainStmt { output_statement($1); } + | FetchStmt { output_statement($1); } + | GrantStmt { output_statement($1); } + | IndexStmt { output_statement($1); } + | ListenStmt { output_statement($1); } + | LockStmt { output_statement($1); } + | ProcedureStmt { output_statement($1); } + | RecipeStmt { output_statement($1); } + | RemoveAggrStmt { output_statement($1); } + | RemoveOperStmt { output_statement($1); } + | RemoveFuncStmt { output_statement($1); } + | RemoveStmt { output_statement($1); } + | RenameStmt { output_statement($1); } + | RevokeStmt { output_statement($1); } + | OptimizableStmt { /* already written out */ } + | RuleStmt { output_statement($1); } + | TransactionStmt { + fprintf(yyout, "ECPGtrans(__LINE__, \"%s\");", $1); + whenever_action(); + } + | ViewStmt { output_statement($1); } + | LoadStmt { output_statement($1); } + | CreatedbStmt { output_statement($1); } + | DestroydbStmt { output_statement($1); } + | VacuumStmt { output_statement($1); } + | VariableSetStmt { output_statement($1); } + | VariableShowStmt { output_statement($1); } + | VariableResetStmt { output_statement($1); } + | ECPGConnect { + fprintf(yyout, "ECPGconnect(\"%s\");", $1); + whenever_action(); + } +/* | ECPGDisconnect */ + | ECPGExecute { + fprintf(yyout, "ECPGdo(__LINE__, %s, ECPGt_EOIT, ECPGt_EORT);", $1); + whenever_action(); + } + | ECPGOpen { output_statement($1); } + | ECPGWhenever { + fputs($1, yyout); + output_line_number(); + } + +/* + * We start with a lot of stuff that's very similar to the backend's parsing + */ + +/***************************************************************************** + * + * Create a new Postgres DBMS user + * + * + *****************************************************************************/ + +CreateUserStmt: CREATE USER UserId user_passwd_clause user_createdb_clause + user_createuser_clause user_group_clause user_valid_clause + { + $$ = make3_str(make5_str("create user", $3, $4, $5, $6), $7, $8); + } + ; + +/***************************************************************************** + * + * Alter a postresql DBMS user + * + * + *****************************************************************************/ + +AlterUserStmt: ALTER USER UserId user_passwd_clause user_createdb_clause + user_createuser_clause user_group_clause user_valid_clause + { + $$ = make3_str(make5_str("alter user", $3, $4, $5, $6), $7, $8); + } + ; + +/***************************************************************************** + * + * Drop a postresql DBMS user + * + * + *****************************************************************************/ + +DropUserStmt: DROP USER UserId + { + $$ = make2_str("drop user", $3); + } + ; + +user_passwd_clause: WITH PASSWORD UserId { $$ = make2_str("with password", $3); } + | /*EMPTY*/ { $$ = ""; } + ; + +user_createdb_clause: CREATEDB + { + $$ = "createdb"; + } + | NOCREATEDB + { + $$ = "nocreatedb"; + } + | /*EMPTY*/ { $$ = ""; } + ; + +user_createuser_clause: CREATEUSER + { + $$ = "createuser"; + } + | NOCREATEUSER + { + $$ = "nocreateuser"; + } + | /*EMPTY*/ { $$ = NULL; } + ; + +user_group_list: user_group_list ',' UserId + { + $$ = make3_str($1, ",", $3); + } + | UserId + { + $$ = $1; + } + ; + +user_group_clause: IN GROUP user_group_list { $$ = make2_str("in group", $3); } + | /*EMPTY*/ { $$ = ""; } + ; + +user_valid_clause: VALID UNTIL SCONST { $$ = make2_str("valid until", $3);; } + | /*EMPTY*/ { $$ = ""; } + ; + +/***************************************************************************** + * + * Set PG internal variable + * SET name TO 'var_value' + * Include SQL92 syntax (thomas 1997-10-22): + * SET TIME ZONE 'var_value' + * + *****************************************************************************/ + +VariableSetStmt: SET ColId TO var_value + { + $$ = make4_str("set", $2, "to", $4); + } + | SET ColId '=' var_value + { + $$ = make4_str("set", $2, "=", $4); + } + | SET TIME ZONE zone_value + { + $$ = make2_str("set time zone", $4); + } + ; + +var_value: Sconst { $$ = $1; } + | DEFAULT { $$ = "default"; } + ; + +zone_value: Sconst { $$ = $1; } + | DEFAULT { $$ = "default"; } + | LOCAL { $$ = "local"; } + ; + +VariableShowStmt: SHOW ColId + { + $$ = make2_str("show", $2); + } + | SHOW TIME ZONE + { + $$ = "show time zone"; + } + ; + +VariableResetStmt: RESET ColId + { + $$ = make2_str("reset", $2); + } + | RESET TIME ZONE + { + $$ = "reset time zone"; + } + ; + + +/***************************************************************************** + * + * QUERY : + * addattr ( attr1 = type1 .. attrn = typen ) to <relname> [*] + * + *****************************************************************************/ + +AddAttrStmt: ALTER TABLE relation_name opt_inh_star alter_clause + { + $$ = make4_str("alter table", $3, $4, $5); + } + ; + +alter_clause: ADD opt_column columnDef + { + $$ = make3_str("add", $2, $3); + } + | ADD '(' OptTableElementList ')' + { + $$ = cat3_str("add(", $3, ")"); + } + | DROP opt_column ColId + { yyerror("ALTER TABLE/DROP COLUMN not yet implemented"); } + | ALTER opt_column ColId SET DEFAULT default_expr + { yyerror("ALTER TABLE/ALTER COLUMN/SET DEFAULT not yet implemented"); } + | ALTER opt_column ColId DROP DEFAULT + { yyerror("ALTER TABLE/ALTER COLUMN/DROP DEFAULT not yet implemented"); } + | ADD ConstraintElem + { yyerror("ALTER TABLE/ADD CONSTRAINT not yet implemented"); } + ; + +/***************************************************************************** + * + * QUERY : + * close <optname> + * + *****************************************************************************/ + +ClosePortalStmt: CLOSE opt_id + { + $$ = make2_str("close", $2); + } + ; + + +/***************************************************************************** + * + * QUERY : + * COPY [BINARY] <relname> FROM/TO + * [USING DELIMITERS <delimiter>] + * + *****************************************************************************/ + +CopyStmt: COPY opt_binary relation_name opt_with_copy copy_dirn copy_file_name copy_delimiter + { + $$ = make3_str(make5_str("copy", $2, $3, $4, $5), $6, $7); + } + ; + +copy_dirn: TO + { $$ = "to"; } + | FROM + { $$ = "from"; } + ; + +/* + * copy_file_name NULL indicates stdio is used. Whether stdin or stdout is + * used depends on the direction. (It really doesn't make sense to copy from + * stdout. We silently correct the "typo". - AY 9/94 + */ +copy_file_name: Sconst { $$ = $1; } + | STDIN { $$ = "stdin"; } + | STDOUT { $$ = "stdout"; } + ; + +opt_binary: BINARY { $$ = "binary"; } + | /*EMPTY*/ { $$ = ""; } + ; + +opt_with_copy: WITH OIDS { $$ = "with oids"; } + | /*EMPTY*/ { $$ = ""; } + ; + +/* + * the default copy delimiter is tab but the user can configure it + */ +copy_delimiter: USING DELIMITERS Sconst { $$ = make2_str("using delimiters", $3); } + | /*EMPTY*/ { $$ = ""; } + ; + + + +/***************************************************************************** + * + * QUERY : + * CREATE relname + * + *****************************************************************************/ + +CreateStmt: CREATE TABLE relation_name '(' OptTableElementList ')' + OptInherit OptArchiveType + { + $$ = make5_str("create table", $3, cat3_str("(", $5, ")"), $7, $8); + } + ; + +OptTableElementList: OptTableElementList ',' OptTableElement + { + $$ = make3_str($1, ",", $3); + } + | OptTableElement + { + $$ = $1; + } + | /*EMPTY*/ { $$ = ""; } + ; + +OptTableElement: columnDef { $$ = $1; } + | TableConstraint { $$ = $1; } + ; + +columnDef: ColId Typename ColQualifier + { + $$ = make3_str($1, $2, $3); + } + ; + +ColQualifier: ColQualList { $$ = $1; } + | /*EMPTY*/ { $$ = ""; } + ; + +ColQualList: ColQualList ColConstraint { $$ = make2_str($1,$2); } + | ColConstraint { $$ = $1; } + ; + +ColConstraint: + CONSTRAINT name ColConstraintElem + { + $$ = make3_str("constraint", $2, $3); + } + | ColConstraintElem + { $$ = $1; } + ; + +ColConstraintElem: CHECK '(' constraint_expr ')' + { + $$ = cat3_str("check(", $3, ")"); + } + | DEFAULT default_expr + { + $$ = make2_str("default", $2); + } + | NOT NULL_P + { + $$ = "not null"; + } + | UNIQUE + { + $$ = "unique"; + } + | PRIMARY KEY + { + $$ = "primary key"; + } + | REFERENCES ColId opt_column_list key_match key_actions + { + fprintf(stderr, "CREATE TABLE/FOREIGN KEY clause ignored; not yet implemented"); + $$ = ""; + } + ; + +default_list: default_list ',' default_expr + { + $$ = make3_str($1, ",", $3); + } + | default_expr + { + $$ = $1; + } + ; + +default_expr: AexprConst + { $$ = $1; } + | NULL_P + { $$ = "null"; } + | '-' default_expr %prec UMINUS + { $$ = make2_str("-", $2); } + | default_expr '+' default_expr + { $$ = make3_str($1, "+", $3); } + | default_expr '-' default_expr + { $$ = make3_str($1, "-", $3); } + | default_expr '/' default_expr + { $$ = make3_str($1, "/", $3); } + | default_expr '*' default_expr + { $$ = make3_str($1, "*", $3); } + | default_expr '=' default_expr + { yyerror("boolean expressions not supported in DEFAULT"); } + | default_expr '<' default_expr + { yyerror("boolean expressions not supported in DEFAULT"); } + | default_expr '>' default_expr + { yyerror("boolean expressions not supported in DEFAULT"); } +/* not possible in embedded sql + | ':' default_expr + { $$ = make2_str(":", $2); } +*/ + | ';' default_expr + { $$ = make2_str(";", $2); } + | '|' default_expr + { $$ = make2_str("|", $2); } + | default_expr TYPECAST Typename + { $$ = make3_str($1, "::", $3); } + | CAST '(' default_expr AS Typename ')' + { + $$ = make3_str(cat2_str("cast(", $3) , "as", cat2_str($5, ")")); + } + | '(' default_expr ')' + { $$ = cat3_str("(", $2, ")"); } + | func_name '(' ')' + { $$ = make2_str($1, "()"); } + | func_name '(' default_list ')' + { $$ = make2_str($1, cat3_str("(", $3, ")")); } + | default_expr Op default_expr + { + if (!strcmp("<=", $2) || !strcmp(">=", $2)) + yyerror("boolean expressions not supported in DEFAULT"); + $$ = make3_str($1, $2, $3); + } + | Op default_expr + { $$ = make2_str($1, $2); } + | default_expr Op + { $$ = make2_str($1, $2); } + /* XXX - thomas 1997-10-07 v6.2 function-specific code to be changed */ + | CURRENT_DATE + { $$ = "current_date"; } + | CURRENT_TIME + { $$ = "current_time"; } + | CURRENT_TIME '(' Iconst ')' + { + if ($3 != 0) + fprintf(stderr, "CURRENT_TIME(%s) precision not implemented; zero used instead",$3); + $$ = "current_time"; + } + | CURRENT_TIMESTAMP + { $$ = "current_timestamp"; } + | CURRENT_TIMESTAMP '(' Iconst ')' + { + if ($3 != 0) + fprintf(stderr, "CURRENT_TIMESTAMP(%s) precision not implemented; zero used instead",$3); + $$ = "current_timestamp"; + } + | CURRENT_USER + { $$ = "current user"; } + ; + +/* ConstraintElem specifies constraint syntax which is not embedded into + * a column definition. ColConstraintElem specifies the embedded form. + * - thomas 1997-12-03 + */ +TableConstraint: CONSTRAINT name ConstraintElem + { + $$ = make3_str("constraint", $2, $3); + } + | ConstraintElem + { $$ = $1; } + ; + +ConstraintElem: CHECK '(' constraint_expr ')' + { + $$ = cat3_str("check(", $3, ")"); + } + | UNIQUE '(' columnList ')' + { + $$ = cat3_str("unique(", $3, ")"); + } + | PRIMARY KEY '(' columnList ')' + { + $$ = cat3_str("primary key(", $4, ")"); + } + | FOREIGN KEY '(' columnList ')' REFERENCES ColId opt_column_list key_match key_actions + { + fprintf(stderr, "CREATE TABLE/FOREIGN KEY clause ignored; not yet implemented"); + $$ = ""; + } + ; + +constraint_list: constraint_list ',' constraint_expr + { + $$ = make3_str($1, ",", $3); + } + | constraint_expr + { + $$ = $1; + } + ; + +constraint_expr: AexprConst + { $$ = $1; } + | NULL_P + { $$ = "null"; } + | ColId + { + $$ = $1; + } + | '-' constraint_expr %prec UMINUS + { $$ = make2_str("-", $2); } + | constraint_expr '+' constraint_expr + { $$ = make3_str($1, "+", $3); } + | constraint_expr '-' constraint_expr + { $$ = make3_str($1, "-", $3); } + | constraint_expr '/' constraint_expr + { $$ = make3_str($1, "/", $3); } + | constraint_expr '*' constraint_expr + { $$ = make3_str($1, "*", $3); } + | constraint_expr '=' constraint_expr + { $$ = make3_str($1, "=", $3); } + | constraint_expr '<' constraint_expr + { $$ = make3_str($1, "<", $3); } + | constraint_expr '>' constraint_expr + { $$ = make3_str($1, ">", $3); } +/* this one doesn't work with embedded sql anyway + | ':' constraint_expr + { $$ = make2_str(":", $2); } +*/ + | ';' constraint_expr + { $$ = make2_str(";", $2); } + | '|' constraint_expr + { $$ = make2_str("|", $2); } + | constraint_expr TYPECAST Typename + { + $$ = make3_str($1, "::", $3); + } + | CAST '(' constraint_expr AS Typename ')' + { + $$ = make3_str(cat2_str("cast(", $3), "as", cat2_str($5, ")")); + } + | '(' constraint_expr ')' + { $$ = cat3_str("(", $2, ")"); } + | func_name '(' ')' + { + { $$ = make2_str($1, "()"); } + } + | func_name '(' constraint_list ')' + { + $$ = make2_str($1, cat3_str("(", $3, +")")); + } + | constraint_expr Op constraint_expr + { $$ = make3_str($1, $2, $3); } + | constraint_expr LIKE constraint_expr + { $$ = make3_str($1, "like", $3); } + | constraint_expr AND constraint_expr + { $$ = make3_str($1, "and", $3); } + | constraint_expr OR constraint_expr + { $$ = make3_str($1, "or", $3); } + | NOT constraint_expr + { $$ = make2_str("not", $2); } + | Op constraint_expr + { $$ = make2_str($1, $2); } + | constraint_expr Op + { $$ = make2_str($1, $2); } + | constraint_expr ISNULL + { $$ = make2_str($1, "isnull"); } + | constraint_expr IS NULL_P + { $$ = make2_str($1, "is null"); } + | constraint_expr NOTNULL + { $$ = make2_str($1, "notnull"); } + | constraint_expr IS NOT NULL_P + { $$ = make2_str($1, "is not null"); } + | constraint_expr IS TRUE_P + { $$ = make2_str($1, "is true"); } + | constraint_expr IS FALSE_P + { $$ = make2_str($1, "is false"); } + | constraint_expr IS NOT TRUE_P + { $$ = make2_str($1, "is not true"); } + | constraint_expr IS NOT FALSE_P + { $$ = make2_str($1, "is not false"); } + ; + +key_match: MATCH FULL { $$ = "match full"; } + | MATCH PARTIAL { $$ = "match partial"; } + | /*EMPTY*/ { $$ = ""; } + ; + +key_actions: key_action key_action { $$ = make2_str($1, $2); } + | key_action { $$ = $1; } + | /*EMPTY*/ { $$ = ""; } + ; + +key_action: ON DELETE key_reference { $$ = make2_str("on delete", $3); } + | ON UPDATE key_reference { $$ = make2_str("on update", $3); } + ; + +key_reference: NO ACTION { $$ = "no action"; } + | CASCADE { $$ = "cascade"; } + | SET DEFAULT { $$ = "set default"; } + | SET NULL_P { $$ = "set null"; } + ; + +OptInherit: INHERITS '(' relation_name_list ')' { $$ = cat3_str("inherits (", $3, ")"); } + | /*EMPTY*/ { $$ = ""; } + ; + +/* + * "ARCHIVE" keyword was removed in 6.3, but we keep it for now + * so people can upgrade with old pg_dump scripts. - momjian 1997-11-20(?) + */ +OptArchiveType: ARCHIVE '=' NONE { $$ = "archive = none"; } + | /*EMPTY*/ { $$ = ""; } + ; + +CreateAsStmt: CREATE TABLE relation_name OptCreateAs AS SubSelect + { + $$ = make5_str("create table", $3, $4, "as", $6); + } + ; + +OptCreateAs: '(' CreateAsList ')' { $$ = cat3_str("(", $2, ")"); } + | /*EMPTY*/ { $$ = ""; } + ; + +CreateAsList: CreateAsList ',' CreateAsElement { $$ = make3_str($1, ",", $3); } + | CreateAsElement { $$ = $1; } + ; + +CreateAsElement: ColId { $$ = $1; } + ; + +/***************************************************************************** + * + * QUERY : + * CREATE SEQUENCE seqname + * + *****************************************************************************/ + +CreateSeqStmt: CREATE SEQUENCE relation_name OptSeqList + { + $$ = make3_str("create sequence", $3, $4); + } + ; + +OptSeqList: OptSeqList OptSeqElem + { $$ = make2_str($1, $2); } + | { $$ = ""; } + ; + +OptSeqElem: CACHE IntegerOnly + { + $$ = make2_str("cache", $2); + } + | CYCLE + { + $$ = "cycle"; + } + | INCREMENT IntegerOnly + { + $$ = make2_str("increment", $2); + } + | MAXVALUE IntegerOnly + { + $$ = make2_str("maxvalue", $2); + } + | MINVALUE IntegerOnly + { + $$ = make2_str("minvalue", $2); + } + | START IntegerOnly + { + $$ = make2_str("start", $2); + } + ; + +IntegerOnly: Iconst + { + $$ = $1; + } + | '-' Iconst + { + $$ = make2_str("-", $2); + } + ; + +/***************************************************************************** + * + * QUERIES : + * CREATE PROCEDURAL LANGUAGE ... + * DROP PROCEDURAL LANGUAGE ... + * + *****************************************************************************/ + +CreatePLangStmt: CREATE PLangTrusted PROCEDURAL LANGUAGE Sconst + HANDLER def_name LANCOMPILER Sconst + { + $$ = make4_str(make5_str("create", $2, "precedural language", $5, "handler"), $7, "langcompiler", $9); + } + ; + +PLangTrusted: TRUSTED { $$ = "trusted"; } + | { $$ = ""; } + +DropPLangStmt: DROP PROCEDURAL LANGUAGE Sconst + { + $$ = make2_str("drop procedural language", $4); + } + ; + +/***************************************************************************** + * + * QUERIES : + * CREATE TRIGGER ... + * DROP TRIGGER ... + * + *****************************************************************************/ + +CreateTrigStmt: CREATE TRIGGER name TriggerActionTime TriggerEvents ON + relation_name TriggerForSpec EXECUTE PROCEDURE + name '(' TriggerFuncArgs ')' + { + $$ = make2_str(make5_str(make5_str("create trigger", $3, $4, $5, "on"), $7, $8, "execute procedure", $11), cat3_str("(", $13, ")")); + } + ; + +TriggerActionTime: BEFORE { $$ = "before"; } + | AFTER { $$ = "after"; } + ; + +TriggerEvents: TriggerOneEvent + { + $$ = $1; + } + | TriggerOneEvent OR TriggerOneEvent + { + $$ = make3_str($1, "or", $3); + } + | TriggerOneEvent OR TriggerOneEvent OR TriggerOneEvent + { + $$ = make5_str($1, "or", $3, "or", $5); + } + ; + +TriggerOneEvent: INSERT { $$ = "insert"; } + | DELETE { $$ = "delete"; } + | UPDATE { $$ = "update"; } + ; + +TriggerForSpec: FOR TriggerForOpt TriggerForType + { + $$ = make3_str("for", $2, $3); + } + ; + +TriggerForOpt: EACH { $$ = "each"; } + | /*EMPTY*/ { $$ = ""; } + ; + +TriggerForType: ROW { $$ = "row"; } + | STATEMENT { $$ = "statement"; } + ; + +TriggerFuncArgs: TriggerFuncArg + { $$ = $1 } + | TriggerFuncArgs ',' TriggerFuncArg + { $$ = make3_str($1, ",", $3); } + | /*EMPTY*/ + { $$ = ""; } + ; + +TriggerFuncArg: Iconst + { + $$ = $1; + } + | FCONST + { + $$ = make_name(); + } + | Sconst { $$ = $1; } + | IDENT { $$ = $1; } + ; + +DropTrigStmt: DROP TRIGGER name ON relation_name + { + $$ = make4_str("drop trigger", $3, "on", $5); + } + ; + +/***************************************************************************** + * + * QUERY : + * define (type,operator,aggregate) + * + *****************************************************************************/ + +DefineStmt: CREATE def_type def_rest + { + $$ = make3_str("create", $2, $3); + } + ; + +def_rest: def_name definition + { + $$ = make2_str($1, $2); + } + ; + +def_type: OPERATOR { $$ = "operator"; } + | TYPE_P { $$ = "type"; } + | AGGREGATE { $$ = "aggregate"; } + ; + +def_name: PROCEDURE { $$ = "procedure"; } + | JOIN { $$ = "join"; } + | ColId { $$ = $1; } + | MathOp { $$ = $1; } + | Op { $$ = $1; } + ; + +definition: '(' def_list ')' { $$ = cat3_str("(", $2, ")"); } + ; + +def_list: def_elem { $$ = $1; } + | def_list ',' def_elem { $$ = make3_str($1, ",", $3); } + ; + +def_elem: def_name '=' def_arg { + $$ = make3_str($1, "=", $3); + } + | def_name + { + $$ = $1; + } + | DEFAULT '=' def_arg + { + $$ = make2_str("default =", $3); + } + ; + +def_arg: ColId { $$ = $1; } + | all_Op { $$ = $1; } + | NumConst { $$ = $1; /* already a Value */ } + | Sconst { $$ = $1; } + | SETOF ColId + { + $$ = make2_str("setof", $2); + } + ; + +/***************************************************************************** + * + * QUERY: + * destroy <relname1> [, <relname2> .. <relnameN> ] + * + *****************************************************************************/ + +DestroyStmt: DROP TABLE relation_name_list + { + $$ = make2_str("drop table", $3); + } + | DROP SEQUENCE relation_name_list + { + $$ = make2_str("drop sequence", $3); + } + ; + + + +/***************************************************************************** + * + * QUERY: + * fetch/move [forward | backward] [number | all ] [ in <portalname> ] + * + *****************************************************************************/ + +FetchStmt: FETCH opt_direction fetch_how_many opt_portal_name INTO into_list + { + $$ = make4_str("fetch", $2, $3, $4); + } + | MOVE opt_direction fetch_how_many opt_portal_name + { + $$ = make4_str("fetch", $2, $3, $4); + } + ; + +opt_direction: FORWARD { $$ = "forward"; } + | BACKWARD { $$ = "backward"; } + | /*EMPTY*/ { $$ = ""; /* default */ } + ; + +fetch_how_many: Iconst + { $$ = $1; + if (atol($1) <= 0) yyerror("Please specify nonnegative count for fetch"); } + | ALL { $$ = "all"; } + | /*EMPTY*/ { $$ = ""; /*default*/ } + ; + +opt_portal_name: IN name { $$ = make2_str("in", $2); } + | name { $$ = make2_str("in", $1); } + | /*EMPTY*/ { $$ = ""; } + ; + + +/***************************************************************************** + * + * QUERY: + * GRANT [privileges] ON [relation_name_list] TO [GROUP] grantee + * + *****************************************************************************/ + +GrantStmt: GRANT privileges ON relation_name_list TO grantee opt_with_grant + { + $$ = make2_str(make5_str("grant", $2, "on", $4, "to"), $6); + } + ; + +privileges: ALL PRIVILEGES + { + $$ = "all privileges"; + } + | ALL + { + $$ = "all"; + } + | operation_commalist + { + $$ = $1; + } + ; + +operation_commalist: operation + { + $$ = $1; + } + | operation_commalist ',' operation + { + $$ = make3_str($1, ",", $3); + } + ; + +operation: SELECT + { + $$ = "select"; + } + | INSERT + { + $$ = "insert"; + } + | UPDATE + { + $$ = "update"; + } + | DELETE + { + $$ = "delete"; + } + | RULE + { + $$ = "rule"; + } + ; + +grantee: PUBLIC + { + $$ = "public"; + } + | GROUP ColId + { + $$ = make2_str("group", $2); + } + | ColId + { + $$ = $1; + } + ; + +opt_with_grant: WITH GRANT OPTION + { + yyerror("WITH GRANT OPTION is not supported. Only relation owners can set privileges"); + } + | /*EMPTY*/ + ; + + +/***************************************************************************** + * + * QUERY: + * REVOKE [privileges] ON [relation_name] FROM [user] + * + *****************************************************************************/ + +RevokeStmt: REVOKE privileges ON relation_name_list FROM grantee + { + $$ = make2_str(make5_str("revoke", $2, "on", $4, "from"), $6); + } + ; + + + +/***************************************************************************** + * + * QUERY: + * create index <indexname> on <relname> + * using <access> "(" (<col> with <op>)+ ")" [with + * <target_list>] + * + * [where <qual>] is not supported anymore + *****************************************************************************/ + +IndexStmt: CREATE index_opt_unique INDEX index_name ON relation_name + access_method_clause '(' index_params ')' opt_with + { + /* should check that access_method is valid, + etc ... but doesn't */ + $$ = make5_str(make5_str("create", $2, "index", $4, "on"), $6, $7, cat3_str("(", $9, ")"), $11); + } + ; + +index_opt_unique: UNIQUE { $$ = "unique"; } + | /*EMPTY*/ { $$ = ""; } + ; + +access_method_clause: USING access_method { $$ = make2_str("using", $2); } + | /*EMPTY*/ { $$ = ""; } + ; + +index_params: index_list { $$ = $1; } + | func_index { $$ = $1; } + ; + +index_list: index_list ',' index_elem { $$ = make3_str($1, ",", $3); } + | index_elem { $$ = $1; } + ; + +func_index: func_name '(' name_list ')' opt_type opt_class + { + $$ = make4_str($1, cat3_str("(", $3, ")"), $5, $6); + } + ; + +index_elem: attr_name opt_type opt_class + { + $$ = make3_str($1, $3, $3); + } + ; + +opt_type: ':' Typename { $$ = make2_str(":", $2); } + | FOR Typename { $$ = make2_str("for", $2); } + | /*EMPTY*/ { $$ = ""; } + ; + +/* opt_class "WITH class" conflicts with preceeding opt_type + * for Typename of "TIMESTAMP WITH TIME ZONE" + * So, remove "WITH class" from the syntax. OK?? + * - thomas 1997-10-12 + * | WITH class { $$ = $2; } + */ +opt_class: class { $$ = $1; } + | USING class { $$ = make2_str("using", $2); } + | /*EMPTY*/ { $$ = ""; } + ; + +/***************************************************************************** + * + * QUERY: + * extend index <indexname> [where <qual>] + * + *****************************************************************************/ + +ExtendStmt: EXTEND INDEX index_name where_clause + { + $$ = make3_str("extend index", $3, $4); + } + ; + + +/***************************************************************************** + * + * QUERY: + * execute recipe <recipeName> + * + *****************************************************************************/ + +RecipeStmt: EXECUTE RECIPE recipe_name + { + $$ = make2_str("execute recipe", $3); + } + ; + +/***************************************************************************** + * + * QUERY: + * define function <fname> + * (language = <lang>, returntype = <typename> + * [, arch_pct = <percentage | pre-defined>] + * [, disk_pct = <percentage | pre-defined>] + * [, byte_pct = <percentage | pre-defined>] + * [, perbyte_cpu = <int | pre-defined>] + * [, percall_cpu = <int | pre-defined>] + * [, iscachable]) + * [arg is (<type-1> { , <type-n>})] + * as <filename or code in language as appropriate> + * + *****************************************************************************/ + +ProcedureStmt: CREATE FUNCTION func_name func_args + RETURNS func_return opt_with AS Sconst LANGUAGE Sconst + { + $$ = make2_str(make5_str(make5_str("create function", $3, $4, "returns", $6), $7, "as", $9, "language"), $11); + } + +opt_with: WITH definition { $$ = make2_str("with", $2); } + | /*EMPTY*/ { $$ = ""; } + ; + +func_args: '(' func_args_list ')' { $$ = cat3_str("(", $2, ")"); } + | '(' ')' { $$ = "()"; } + ; + +func_args_list: TypeId { $$ = $1; } + | func_args_list ',' TypeId + { $$ = make3_str($1, ",", $3); } + ; + +func_return: set_opt TypeId + { + $$ = make2_str($1, $2); + } + ; + +set_opt: SETOF { $$ = "setof"; } + | /*EMPTY*/ { $$ = ""; } + ; + + +/***************************************************************************** + * + * QUERY: + * + * remove function <funcname> + * (REMOVE FUNCTION "funcname" (arg1, arg2, ...)) + * remove aggregate <aggname> + * (REMOVE AGGREGATE "aggname" "aggtype") + * remove operator <opname> + * (REMOVE OPERATOR "opname" (leftoperand_typ rightoperand_typ)) + * remove type <typename> + * (REMOVE TYPE "typename") + * remove rule <rulename> + * (REMOVE RULE "rulename") + * + *****************************************************************************/ + +RemoveStmt: DROP remove_type name + { + $$ = make3_str("drop", $2, $3);; + } + ; + +remove_type: TYPE_P { $$ = "type"; } + | INDEX { $$ = "index"; } + | RULE { $$ = "rule"; } + | VIEW { $$ = "view"; } + ; + + +RemoveAggrStmt: DROP AGGREGATE name aggr_argtype + { + $$ = make3_str("drop aggregate", $3, $4); + } + ; + +aggr_argtype: name { $$ = $1; } + | '*' { $$ = "*"; } + ; + + +RemoveFuncStmt: DROP FUNCTION func_name func_args + { + $$ = make3_str("drop function", $3, $4); + } + ; + + +RemoveOperStmt: DROP OPERATOR all_Op '(' oper_argtypes ')' + { + $$ = make3_str("drop operator", $3, cat3_str("(", $5, ")")); + } + ; + +all_Op: Op | MathOp; + +MathOp: '+' { $$ = "+"; } + | '-' { $$ = "-"; } + | '*' { $$ = "*"; } + | '/' { $$ = "/"; } + | '<' { $$ = "<"; } + | '>' { $$ = ">"; } + | '=' { $$ = "="; } + ; + +oper_argtypes: name + { + yyerror("parser: argument type missing (use NONE for unary operators)"); + } + | name ',' name + { $$ = make3_str($1, ",", $3); } + | NONE ',' name /* left unary */ + { $$ = make2_str("none,", $3); } + | name ',' NONE /* right unary */ + { $$ = make2_str($1, ", none"); } + ; + + +/***************************************************************************** + * + * QUERY: + * rename <attrname1> in <relname> [*] to <attrname2> + * rename <relname1> to <relname2> + * + *****************************************************************************/ + +RenameStmt: ALTER TABLE relation_name opt_inh_star + RENAME opt_column opt_name TO name + { + $$ = make4_str(make5_str("alter table", $3, $4, "rename", $6), $7, "to", $9); + } + ; + +opt_name: name { $$ = $1; } + | /*EMPTY*/ { $$ = ""; } + ; + +opt_column: COLUMN { $$ = "colmunn"; } + | /*EMPTY*/ { $$ = ""; } + ; + + +/***************************************************************************** + * + * QUERY: Define Rewrite Rule , Define Tuple Rule + * Define Rule <old rules > + * + * only rewrite rule is supported -- ay 9/94 + * + *****************************************************************************/ + +RuleStmt: CREATE RULE name AS + { QueryIsRule=1; } + ON event TO event_object where_clause + DO opt_instead OptStmtList + { + $$ = make2_str(make5_str(make5_str("create rule", $3, "as on", $7, "to"), $9, $10, "do", $12), $13); + } + ; + +OptStmtList: NOTHING { $$ = "nothing"; } + | OptimizableStmt { $$ = $1; } + | '[' OptStmtBlock ']' { $$ = make3_str("[", $2, "]"); } + ; + +OptStmtBlock: OptStmtMulti + { $$ = $1; } + | OptimizableStmt + { $$ = $1; } + ; + +OptStmtMulti: OptStmtMulti OptimizableStmt ';' + { $$ = make3_str($1, $2, ";"); } + | OptStmtMulti OptimizableStmt + { $$ = make2_str($1, $2); } + | OptimizableStmt ';' + { $$ = $1; } + ; + +event_object: relation_name '.' attr_name + { + $$ = make3_str($1, ",", $3); + } + | relation_name + { + $$ = $1; + } + ; + +/* change me to select, update, etc. some day */ +event: SELECT { $$ = "select"; } + | UPDATE { $$ = "update"; } + | DELETE { $$ = "delete"; } + | INSERT { $$ = "insert"; } + ; + +opt_instead: INSTEAD { $$ = "instead"; } + | /*EMPTY*/ { $$ = ""; } + ; + + +/***************************************************************************** + * + * QUERY: + * NOTIFY <relation_name> can appear both in rule bodies and + * as a query-level command + * + *****************************************************************************/ + +NotifyStmt: NOTIFY relation_name + { + $$ = make2_str("notify", $2); + } + ; + +ListenStmt: LISTEN relation_name + { + $$ = make2_str("listen", $2); + } +; + +/***************************************************************************** + * + * Transactions: + * + * abort transaction + * (ABORT) + * begin transaction + * (BEGIN) + * end transaction + * (END) + * + *****************************************************************************/ +TransactionStmt: ABORT_TRANS TRANSACTION { $$ = "rollback"; } + | BEGIN_TRANS TRANSACTION { $$ = "begin transaction"; } + | BEGIN_TRANS WORK { $$ = "begin transaction"; } + | COMMIT WORK { $$ = "commit"; } + | END_TRANS TRANSACTION { $$ = "commit"; } + | ROLLBACK WORK { $$ = "rollback"; } + | ABORT_TRANS { $$ = "rollback"; } + | COMMIT { $$ = "commit"; } + | ROLLBACK { $$ = "rollback"; } + +/***************************************************************************** + * + * QUERY: + * define view <viewname> '('target-list ')' [where <quals> ] + * + *****************************************************************************/ + +ViewStmt: CREATE VIEW name AS SelectStmt + { + $$ = make4_str("create view", $3, "as", $5); + } + ; + + +/***************************************************************************** + * + * QUERY: + * load "filename" + * + *****************************************************************************/ + +LoadStmt: LOAD file_name + { + $$ = make2_str("load", $2); + } + ; + + +/***************************************************************************** + * + * QUERY: + * createdb dbname + * + *****************************************************************************/ + +CreatedbStmt: CREATE DATABASE database_name opt_database + { + $$ = make3_str("create database", $3, $4); + } + ; + +opt_database: WITH LOCATION '=' location { $$ = make2_str("with location =", $4); } + | /*EMPTY*/ { $$ = ""; } + ; + +location: Sconst { $$ = $1; } + | DEFAULT { $$ = "default"; } + | /*EMPTY*/ { $$ = ""; } + ; + +/***************************************************************************** + * + * QUERY: + * destroydb dbname + * + *****************************************************************************/ + +DestroydbStmt: DROP DATABASE database_name + { + $$ = make2_str("drop database", $3); + } + ; + + +/***************************************************************************** + * + * QUERY: + * cluster <index_name> on <relation_name> + * + *****************************************************************************/ + +ClusterStmt: CLUSTER index_name ON relation_name + { + $$ = make4_str("cluster", $2, "on", $4); + } + ; + + +/***************************************************************************** + * + * QUERY: + * vacuum + * + *****************************************************************************/ + +VacuumStmt: VACUUM opt_verbose opt_analyze + { + $$ = make3_str("vacuum", $2, $3); + } + | VACUUM opt_verbose opt_analyze relation_name opt_va_list + { + if ( strlen($5) > 0 && strlen($4) == 0 ) + yyerror("parser: syntax error at or near \"(\""); + $$ = make5_str("vacuum", $2, $3, $4, $5); + } + ; + +opt_verbose: VERBOSE { $$ = "verbose"; } + | /*EMPTY*/ { $$ = ""; } + ; + +opt_analyze: ANALYZE { $$ = "analyse"; } + | /*EMPTY*/ { $$ = ""; } + ; + +opt_va_list: '(' va_list ')' { $$ = cat3_str("(", $2, ")"); } + | /*EMPTY*/ { $$ = ""; } + ; + +va_list: name + { $$=$1; } + | va_list ',' name + { $$=make3_str($1, ",", $3); } + ; + + +/***************************************************************************** + * + * QUERY: + * EXPLAIN query + * + *****************************************************************************/ + +ExplainStmt: EXPLAIN opt_verbose OptimizableStmt + { + $$ = make3_str("explain", $2, $3); + } + ; + + +/***************************************************************************** + * * + * Optimizable Stmts: * + * * + * one of the five queries processed by the planner * + * * + * [ultimately] produces query-trees as specified * + * in the query-spec document in ~postgres/ref * + * * + *****************************************************************************/ + +OptimizableStmt: SelectStmt { output_statement($1); } + | CursorStmt { fputs($1, yyout); output_line_number(); } + | UpdateStmt { output_statement($1); } + | InsertStmt { output_statement($1); } + | NotifyStmt { output_statement($1); } + | DeleteStmt { output_statement($1); } + ; + + +/***************************************************************************** + * + * QUERY: + * INSERT STATEMENTS + * + *****************************************************************************/ + +InsertStmt: INSERT INTO relation_name opt_column_list insert_rest + { + $$ = make4_str("insert into", $3, $4, $5); + } + ; + +insert_rest: VALUES '(' res_target_list2 ')' + { + $$ = cat3_str("values(", $3, ")"); + } + | SELECT opt_unique res_target_list2 + from_clause where_clause + group_clause having_clause + union_clause + { + $$ = make4_str(make5_str("select", $2, $3, $4, $5), $6, $7, $8); + } + ; + +opt_column_list: '(' columnList ')' { $$ = cat3_str("(", $2, ")"); } + | /*EMPTY*/ { $$ = ""; } + ; + +columnList: + columnList ',' columnElem + { $$ = make3_str($1, ",", $3); } + | columnElem + { $$ = $1; } + ; + +columnElem: ColId opt_indirection + { + $$ = make2_str($1, $2); + } + ; + + +/***************************************************************************** + * + * QUERY: + * DELETE STATEMENTS + * + *****************************************************************************/ + +DeleteStmt: DELETE FROM relation_name + where_clause + { + $$ = make3_str("delete from", $3, $4); + } + ; + +/* + * Total hack to just lock a table inside a transaction. + * Is it worth making this a separate command, with + * its own node type and file. I don't think so. bjm 1998/1/22 + */ +LockStmt: LOCK_P opt_table relation_name + { + $$ = make3_str("lock", $2, $3); + } + ; + + +/***************************************************************************** + * + * QUERY: + * UpdateStmt (UPDATE) + * + *****************************************************************************/ + +UpdateStmt: UPDATE relation_name + SET res_target_list + from_clause + where_clause + { + $$ = make2_str(make5_str("update", $2, "set", $4, $5), $6); + } + ; + + +/***************************************************************************** + * + * QUERY: + * CURSOR STATEMENTS + * + *****************************************************************************/ +CursorStmt: DECLARE name opt_binary CURSOR FOR + SELECT opt_unique res_target_list2 + from_clause where_clause + group_clause having_clause + union_clause sort_clause + { + struct cursor *ptr, *this = (struct cursor *) mm_alloc(sizeof(struct cursor)); + + this->name = $2; + this->command = make4_str(make5_str(make5_str("declare", $2, $3, "cursor for select", $7), $8, $9, $10, $11), $12, $13, $14); + this->next = NULL; + + for (ptr = cur; ptr != NULL; ptr = ptr->next) + { + if (strcmp(this->name, ptr->name) == 0) + { + /* re-definition */ + free(ptr->command); + ptr->command = this->command; + break; + } + } + + if (ptr == NULL) + { + /* initial definition */ + this->next = cur; + cur = this; + } + + $$ = make5_str("/* declare cursor\"", $2, "\"statement has been moved to location of open cursor \"", $2, "\"statement. */"); + } + ; + + + +/***************************************************************************** + * + * QUERY: + * SELECT STATEMENTS + * + *****************************************************************************/ + +SelectStmt: SELECT opt_unique res_target_list2 + result from_clause where_clause + group_clause having_clause + union_clause sort_clause + { + $$ = make2_str(make5_str(make5_str("select", $2, $3, $4, $5), $6, $7, $8, $9), $10); + } + ; + +union_clause: UNION opt_union select_list + { + $$ = make3_str("union", $2, $3); + } + | /*EMPTY*/ + { $$ = ""; } + ; + +select_list: select_list UNION opt_union SubSelect + { + $$ = make4_str($1, "union", $3, $4); + } + | SubSelect + { $$ = $1; } + ; + +SubSelect: SELECT opt_unique res_target_list2 + from_clause where_clause + group_clause having_clause + { + $$ = make3_str(make5_str("select", $2, $3, $4, $5), $6, $7); + } + ; + +result: INTO opt_table relation_name { $$= make3_str("into", $2, $3); } + | INTO into_list { $$ = ""; } + | /*EMPTY*/ { $$ = ""; } + ; + +opt_table: TABLE { $$ = "table"; } + | /*EMPTY*/ { $$ = ""; } + ; + +opt_union: ALL { $$ = "all"; } + | /*EMPTY*/ { $$ = ""; } + ; + +opt_unique: DISTINCT { $$ = "distinct"; } + | DISTINCT ON ColId { $$ = make2_str("distinct on", $3); } + | ALL { $$ = "all"; } + | /*EMPTY*/ { $$ = ""; } + ; + +sort_clause: ORDER BY sortby_list { $$ = make2_str("order by", $3); } + | /*EMPTY*/ { $$ = ""; } + ; + +sortby_list: sortby { $$ = $1; } + | sortby_list ',' sortby { $$ = make3_str($1, ",", $3); } + ; + +sortby: ColId OptUseOp + { + $$ = make2_str($1, $2); + } + | ColId '.' ColId OptUseOp + { + $$ = make4_str($1, ".", $3, $4); + } + | Iconst OptUseOp + { + $$ = make2_str($1, $2); + } + ; + +OptUseOp: USING Op { $$ = make2_str("using", $2); } + | USING '<' { $$ = "using <"; } + | USING '>' { $$ = "using >"; } + | ASC { $$ = "asc"; } + | DESC { $$ = "desc"; } + | /*EMPTY*/ { $$ = ""; } + ; + +/* + * jimmy bell-style recursive queries aren't supported in the + * current system. + * + * ...however, recursive addattr and rename supported. make special + * cases for these. + */ +opt_inh_star: '*' { $$ = "*"; } + | /*EMPTY*/ { $$ = ""; } + ; + +relation_name_list: name_list { $$ = $1; }; + +name_list: name + { $$ = $1; } + | name_list ',' name + { $$ = make3_str($1, ",", $3); } + ; + +group_clause: GROUP BY groupby_list { $$ = make2_str("groub by", $3); } + | /*EMPTY*/ { $$ = ""; } + ; + +groupby_list: groupby { $$ = $1; } + | groupby_list ',' groupby { $$ = make3_str($1, ",", $3); } + ; + +groupby: ColId + { + $$ = $1; + } + | ColId '.' ColId + { + $$ = make3_str($1, ",", $3); + } + | Iconst + { + $$ = $1; + } + ; + +having_clause: HAVING a_expr + { + yyerror("HAVING clause not yet implemented"); +/* $$ = make2_str("having", $2); use this line instead to enable HAVING */ + } + | /*EMPTY*/ { $$ = ""; } + ; + + +/***************************************************************************** + * + * clauses common to all Optimizable Stmts: + * from_clause - + * where_clause - + * + *****************************************************************************/ + +from_clause: FROM '(' relation_expr join_expr JOIN relation_expr join_spec ')' + { + yyerror("JOIN not yet implemented"); + } + | FROM from_list { $$ = make2_str("from", $2); } + | /*EMPTY*/ { $$ = ""; } + ; + +from_list: from_list ',' from_val + { $$ = make3_str($1, ",", $3); } + | from_val CROSS JOIN from_val + { yyerror("CROSS JOIN not yet implemented"); } + | from_val + { $$ = $1; } + ; + +from_val: relation_expr AS ColLabel + { + $$ = make3_str($1, "as", $3); + } + | relation_expr ColId + { + $$ = make2_str($1, $2); + } + | relation_expr + { + $$ = $1; + } + ; + +join_expr: NATURAL join_expr { $$ = make2_str("natural", $2); } + | FULL join_outer + { yyerror("FULL OUTER JOIN not yet implemented"); } + | LEFT join_outer + { yyerror("LEFT OUTER JOIN not yet implemented"); } + | RIGHT join_outer + { yyerror("RIGHT OUTER JOIN not yet implemented"); } + | OUTER_P + { yyerror("OUTER JOIN not yet implemented"); } + | INNER_P + { yyerror("INNER JOIN not yet implemented"); } + | UNION + { yyerror("UNION JOIN not yet implemented"); } + | /*EMPTY*/ + { yyerror("INNER JOIN not yet implemented"); } + ; + +join_outer: OUTER_P { $$ = "outer"; } + | /*EMPTY*/ { $$ = ""; /* no qualifiers */ } + ; + +join_spec: ON '(' a_expr ')' { $$ = cat3_str("on (", $3, ")"); } + | USING '(' join_list ')' { $$ = cat3_str("using (", $3, ")"); } + | /*EMPTY*/ { $$ = ""; /* no qualifiers */ } + ; + +join_list: join_using { $$ = $1; } + | join_list ',' join_using { $$ = make3_str($1, ",", $3); } + ; + +join_using: ColId + { + $$ = $1; + } + | ColId '.' ColId + { + $$ = make3_str($1, ".", $3); + } + | Iconst + { + $$ = $1;; + } + ; + +where_clause: WHERE a_expr { $$ = make2_str("where", $2); } + | /*EMPTY*/ { $$ = ""; /* no qualifiers */ } + ; + +relation_expr: relation_name + { + /* normal relations */ + $$ = $1; + } + | relation_name '*' %prec '=' + { + /* inheritance query */ + $$ = make2_str($1, "*"); + } + +opt_array_bounds: '[' ']' nest_array_bounds + { $$ = make2_str("[]", $3); } + | '[' Iconst ']' nest_array_bounds + { $$ = make4_str("[", $2, "]", $4); } + | /* EMPTY */ + { $$ = ""; } + ; + +nest_array_bounds: '[' ']' nest_array_bounds + { $$ = make2_str("[]", $3); } + | '[' Iconst ']' nest_array_bounds + { $$ = make4_str("[", $2, "]", $4); } + | /*EMPTY*/ + { $$ = ""; } + ; + + +/***************************************************************************** + * + * Type syntax + * SQL92 introduces a large amount of type-specific syntax. + * Define individual clauses to handle these cases, and use + * the generic case to handle regular type-extensible Postgres syntax. + * - thomas 1997-10-10 + * + *****************************************************************************/ + +Typename: Array opt_array_bounds + { + $$ = make2_str($1, $2); + } + | Character { $$ = $1; } + | SETOF Array + { + $$ = make2_str("setof", $2); + } + ; + +Array: Generic + | Datetime { $$ = $1; } + | Numeric { $$ = $1; } + ; + +Generic: generic + { + $$ = $1; + } + ; + +generic: IDENT { $$ = $1; } + | TYPE_P { $$ = "type"; } + ; + +/* SQL92 numeric data types + * Check FLOAT() precision limits assuming IEEE floating types. + * Provide rudimentary DECIMAL() and NUMERIC() implementations + * by checking parameters and making sure they match what is possible with INTEGER. + * - thomas 1997-09-18 + */ +Numeric: FLOAT opt_float + { + $$ = make2_str("float", $2); + } + | DOUBLE PRECISION + { + $$ = "double precision"; + } + | DECIMAL opt_decimal + { + $$ = make2_str("decimal", $2); + } + | NUMERIC opt_numeric + { + $$ = make2_str("numeric", $2); + } + ; + +numeric: FLOAT + { $$ = "float"; } + | DOUBLE PRECISION + { $$ = "double precision"; } + | DECIMAL + { $$ = "decimal"; } + | NUMERIC + { $$ = "numeric"; } + ; + +opt_float: '(' Iconst ')' + { + if (atol($2) < 1) + yyerror("precision for FLOAT must be at least 1"); + else if (atol($2) >= 16) + yyerror("precision for FLOAT must be less than 16"); + $$ = cat3_str("(", $2, ")"); + } + | /*EMPTY*/ + { + $$ = ""; + } + ; + +opt_numeric: '(' Iconst ',' Iconst ')' + { + if (atol($2) != 9) { + sprintf(errortext, "NUMERIC precision %s must be 9", $2); + yyerror(errortext); + } + if (atol($4) != 0) { + sprintf(errortext, "NUMERIC scale %s must be zero", $4); + yyerror(errortext); + } + $$ = make3_str(cat2_str("(", $2), ",", cat2_str($4, ")")); + } + | '(' Iconst ')' + { + if (atol($2) != 9) { + sprintf("NUMERIC precision %s must be 9",$2); + yyerror(errortext); + } + $$ = cat3_str("(", $2, ")"); + } + | /*EMPTY*/ + { + $$ = ""; + } + ; + +opt_decimal: '(' Iconst ',' Iconst ')' + { + if (atol($2) != 9) { + sprintf(errortext, "DECIMAL precision %s exceeds implementation limit of 9", $2); + yyerror(errortext); + } + if (atol($4) != 0) { + sprintf(errortext, "DECIMAL scale %s must be zero",$4); + yyerror(errortext); + } + $$ = make3_str(cat2_str("(", $2), ",", cat2_str($4, ")")); + } + | '(' Iconst ')' + { + if (atol($2) != 9) { + sprintf(errortext, "DECIMAL precision %s exceeds implementation limit of 9",$2); + yyerror(errortext); + } + $$ = cat3_str("(", $2, ")"); + } + | /*EMPTY*/ + { + $$ = ""; + } + ; + +/* SQL92 character data types + * The following implements CHAR() and VARCHAR(). + * We do it here instead of the 'Generic' production + * because we don't want to allow arrays of VARCHAR(). + * I haven't thought about whether that will work or not. + * - ay 6/95 + */ +Character: character '(' Iconst ')' + { + if (strncasecmp($1, "char", strlen("char")) && strncasecmp($1, "varchar", strlen("varchar"))) + yyerror("parse error"); + if (atol($3) < 1) { + sprintf(errortext, "length for '%s' type must be at least 1",$1); + yyerror(errortext); + } + else if (atol($3) > 4096) { + /* we can store a char() of length up to the size + * of a page (8KB) - page headers and friends but + * just to be safe here... - ay 6/95 + * XXX note this hardcoded limit - thomas 1997-07-13 + */ + sprintf(errortext, "length for type '%s' cannot exceed 4096",$1); + yyerror(errortext); + } + + $$ = make2_str($1, cat3_str("(", $3, ")")); + } + | character + { + $$ = $1; + } + ; + +character: CHARACTER opt_varying opt_charset opt_collate + { + if (strlen($4) > 0) { + sprintf(errortext, "COLLATE %s not yet implemented",$4); + yyerror(errortext); + } + $$ = make4_str("character", $2, $3, $4); + } + | CHAR opt_varying { $$ = make2_str("char", $2); } + | VARCHAR { $$ = "varchar"; } + | NATIONAL CHARACTER opt_varying { $$ = make2_str("national character", $3); } + | NCHAR opt_varying { $$ = make2_str("nchar", $2); } + ; + +opt_varying: VARYING { $$ = "varying"; } + | /*EMPTY*/ { $$ = ""; } + ; + +opt_charset: CHARACTER SET ColId { $$ = make2_str("character set", $3); } + | /*EMPTY*/ { $$ = ""; } + ; + +opt_collate: COLLATE ColId { $$ = make2_str("collate", $2); } + | /*EMPTY*/ { $$ = ""; } + ; + +Datetime: datetime + { + $$ = $1; + } + | TIMESTAMP opt_timezone + { + $$ = make2_str("timestamp", $2); + } + | TIME + { + $$ = "time"; + } + | INTERVAL opt_interval + { + $$ = make2_str("interval", $2); + } + ; + +datetime: YEAR_P { $$ = "year"; } + | MONTH_P { $$ = "month"; } + | DAY_P { $$ = "day"; } + | HOUR_P { $$ = "hour"; } + | MINUTE_P { $$ = "minute"; } + | SECOND_P { $$ = "second"; } + ; + +opt_timezone: WITH TIME ZONE { $$ = "with time zone"; } + | /*EMPTY*/ { $$ = ""; } + ; + +opt_interval: datetime { $$ = $1; } + | YEAR_P TO MONTH_P { $$ = "year to #month"; } + | DAY_P TO HOUR_P { $$ = "day to hour"; } + | DAY_P TO MINUTE_P { $$ = "day to minute"; } + | DAY_P TO SECOND_P { $$ = "day to second"; } + | HOUR_P TO MINUTE_P { $$ = "hour to minute"; } + | HOUR_P TO SECOND_P { $$ = "hour to second"; } + | /*EMPTY*/ { $$ = ""; } + ; + + +/***************************************************************************** + * + * expression grammar, still needs some cleanup + * + *****************************************************************************/ + +a_expr_or_null: a_expr + { $$ = $1; } + | NULL_P + { + $$ = "null"; + } + ; + +/* Expressions using row descriptors + * Define row_descriptor to allow yacc to break the reduce/reduce conflict + * with singleton expressions. + */ +row_expr: '(' row_descriptor ')' IN '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ") in (", $6, ")"); + } + | '(' row_descriptor ')' NOT IN '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ") not in (", $7, ")"); + } + | '(' row_descriptor ')' Op '(' SubSelect ')' + { + $$ = cat3_str(cat5_str("(", $2, ")", $4, "("), $6, ")"); + } + | '(' row_descriptor ')' '+' '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")+(", $6, ")"); + } + | '(' row_descriptor ')' '-' '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")-(", $6, ")"); + } + | '(' row_descriptor ')' '/' '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")/(", $6, ")"); + } + | '(' row_descriptor ')' '*' '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")*(", $6, ")"); + } + | '(' row_descriptor ')' '<' '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")<(", $6, ")"); + } + | '(' row_descriptor ')' '>' '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")>(", $6, ")"); + } + | '(' row_descriptor ')' '=' '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")=(", $6, ")"); + } + | '(' row_descriptor ')' Op ANY '(' SubSelect ')' + { + $$ = make3_str(cat3_str("(", $2, ")"), $4, cat3_str("any(", $7, ")")); + } + | '(' row_descriptor ')' '+' ANY '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")+any(", $7, ")"); + } + | '(' row_descriptor ')' '-' ANY '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")-any(", $7, ")"); + } + | '(' row_descriptor ')' '/' ANY '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")/any(", $7, ")"); + } + | '(' row_descriptor ')' '*' ANY '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")*any(", $7, ")"); + } + | '(' row_descriptor ')' '<' ANY '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")<any(", $7, ")"); + } + | '(' row_descriptor ')' '>' ANY '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")>any(", $7, ")"); + } + | '(' row_descriptor ')' '=' ANY '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")=any(", $7, ")"); + } + | '(' row_descriptor ')' Op ALL '(' SubSelect ')' + { + $$ = make3_str(cat3_str("(", $2, ")"), $4, cat3_str("all(", $7, ")")); + } + | '(' row_descriptor ')' '+' ALL '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")+all(", $7, ")"); + } + | '(' row_descriptor ')' '-' ALL '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")-all(", $7, ")"); + } + | '(' row_descriptor ')' '/' ALL '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")/all(", $7, ")"); + } + | '(' row_descriptor ')' '*' ALL '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")*all(", $7, ")"); + } + | '(' row_descriptor ')' '<' ALL '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")<all(", $7, ")"); + } + | '(' row_descriptor ')' '>' ALL '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")>all(", $7, ")"); + } + | '(' row_descriptor ')' '=' ALL '(' SubSelect ')' + { + $$ = cat5_str("(", $2, ")=all(", $7, ")"); + } + | '(' row_descriptor ')' Op '(' row_descriptor ')' + { + $$ = make3_str(cat3_str("(", $2, ")"), $4, cat3_str("(", $6, ")")); + } + | '(' row_descriptor ')' '+' '(' row_descriptor ')' + { + $$ = cat5_str("(", $2, ")+(", $6, ")"); + } + | '(' row_descriptor ')' '-' '(' row_descriptor ')' + { + $$ = cat5_str("(", $2, ")-(", $6, ")"); + } + | '(' row_descriptor ')' '/' '(' row_descriptor ')' + { + $$ = cat5_str("(", $2, ")/(", $6, ")"); + } + | '(' row_descriptor ')' '*' '(' row_descriptor ')' + { + $$ = cat5_str("(", $2, ")*(", $6, ")"); + } + | '(' row_descriptor ')' '<' '(' row_descriptor ')' + { + $$ = cat5_str("(", $2, ")<(", $6, ")"); + } + | '(' row_descriptor ')' '>' '(' row_descriptor ')' + { + $$ = cat5_str("(", $2, ")>(", $6, ")"); + } + | '(' row_descriptor ')' '=' '(' row_descriptor ')' + { + $$ = cat5_str("(", $2, ")=(", $6, ")"); + } + ; + +row_descriptor: row_list ',' a_expr + { + $$ = make3_str($1, ",", $3); + } + ; + +row_list: row_list ',' a_expr + { + $$ = make3_str($1, ",", $3); + } + | a_expr + { + $$ = $1; + } + ; + +/* + * This is the heart of the expression syntax. + * Note that the BETWEEN clause looks similar to a boolean expression + * and so we must define b_expr which is almost the same as a_expr + * but without the boolean expressions. + * All operations are allowed in a BETWEEN clause if surrounded by parens. + */ + +a_expr: attr opt_indirection + { + $$ = make2_str($1, $2); + } + | row_expr + { $$ = $1; } + | AexprConst + { $$ = $1; } + | ColId + { + $$ = $1; + } + | '-' a_expr %prec UMINUS + { $$ = make2_str("-", $2); } + | a_expr '+' a_expr + { $$ = make3_str($1, "+", $3); } + | a_expr '-' a_expr + { $$ = make3_str($1, "-", $3); } + | a_expr '/' a_expr + { $$ = make3_str($1, "/", $3); } + | a_expr '*' a_expr + { $$ = make3_str($1, "*", $3); } + | a_expr '<' a_expr + { $$ = make3_str($1, "<", $3); } + | a_expr '>' a_expr + { $$ = make3_str($1, ">", $3); } + | a_expr '=' a_expr + { $$ = make3_str($1, "=", $3); } +/* not possible in embedded sql | ':' a_expr + { $$ = make2_str(":", $2); } +*/ + | ';' a_expr + { $$ = make2_str(";", $2); } + | '|' a_expr + { $$ = make2_str("|", $2); } + | a_expr TYPECAST Typename + { + $$ = make3_str($1, "::", $3); + } + | CAST '(' a_expr AS Typename ')' + { + $$ = make3_str(cat2_str("cast(", $3), "as", cat2_str($5, ")")); + } + | '(' a_expr_or_null ')' + { $$ = cat3_str("(", $2, ")"); } + | a_expr Op a_expr + { $$ = make3_str($1, $2, $3); } + | a_expr LIKE a_expr + { $$ = make3_str($1, "like", $3); } + | a_expr NOT LIKE a_expr + { $$ = make3_str($1, "not like", $4); } + | Op a_expr + { $$ = make2_str($1, $2); } + | a_expr Op + { $$ = make2_str($1, $2); } + | func_name '(' '*' ')' + { + $$ = make2_str($1, "(*)"); + } + | func_name '(' ')' + { + $$ = make2_str($1, "()"); + } + | func_name '(' expr_list ')' + { + $$ = cat4_str($1, "(", $3, ")"); + } + | CURRENT_DATE + { + $$ = "current_date"; + } + | CURRENT_TIME + { + $$ = "current_time"; + } + | CURRENT_TIME '(' Iconst ')' + { + if (atol($3) != 0) + fprintf(stderr,"CURRENT_TIME(%s) precision not implemented; zero used instead", $3); + $$ = "current_time"; + } + | CURRENT_TIMESTAMP + { + $$ = "current_timestamp"; + } + | CURRENT_TIMESTAMP '(' Iconst ')' + { + if (atol($3) != 0) + fprintf(stderr,"CURRENT_TIMESTAMP(%s) precision not implemented; zero used instead",$3); + $$ = "current_timestamp"; + } + | CURRENT_USER + { + $$ = "current_user"; + } + | EXISTS '(' SubSelect ')' + { + $$ = cat3_str("exists(", $3, ")"); + } + | EXTRACT '(' extract_list ')' + { + $$ = cat3_str("extract(", $3, ")"); + } + | POSITION '(' position_list ')' + { + $$ = cat3_str("position(", $3, ")"); + } + | SUBSTRING '(' substr_list ')' + { + $$ = cat3_str("substring(", $3, ")"); + } + /* various trim expressions are defined in SQL92 - thomas 1997-07-19 */ + | TRIM '(' BOTH trim_list ')' + { + $$ = cat3_str("trim(both", $4, ")"); + } + | TRIM '(' LEADING trim_list ')' + { + $$ = cat3_str("trim(leading", $4, ")"); + } + | TRIM '(' TRAILING trim_list ')' + { + $$ = cat3_str("trim(trailing", $4, ")"); + } + | TRIM '(' trim_list ')' + { + $$ = cat3_str("trim(", $3, ")"); + } + | a_expr ISNULL + { $$ = make2_str($1, "isnull"); } + | a_expr IS NULL_P + { $$ = make2_str($1, "is null"); } + | a_expr NOTNULL + { $$ = make2_str($1, "notnull"); } + | a_expr IS NOT NULL_P + { $$ = make2_str($1, "is not null"); } + /* IS TRUE, IS FALSE, etc used to be function calls + * but let's make them expressions to allow the optimizer + * a chance to eliminate them if a_expr is a constant string. + * - thomas 1997-12-22 + */ + | a_expr IS TRUE_P + { + { $$ = make2_str($1, "is true"); } + } + | a_expr IS NOT FALSE_P + { + { $$ = make2_str($1, "is not false"); } + } + | a_expr IS FALSE_P + { + { $$ = make2_str($1, "is false"); } + } + | a_expr IS NOT TRUE_P + { + { $$ = make2_str($1, "is not true"); } + } + | a_expr BETWEEN b_expr AND b_expr + { + $$ = make5_str($1, "between", $3, "and", $5); + } + | a_expr NOT BETWEEN b_expr AND b_expr + { + $$ = make5_str($1, "not between", $4, "and", $6); + } + | a_expr IN '(' in_expr ')' + { + $$ = cat4_str($1, "in (", $4, ")"); + } + | a_expr NOT IN '(' not_in_expr ')' + { + $$ = cat4_str($1, "not in (", $5, ")"); + } + | a_expr Op '(' SubSelect ')' + { + $$ = make3_str($1, $2, cat3_str("(", $4, ")")); + } + | a_expr '+' '(' SubSelect ')' + { + $$ = cat4_str($1, "+(", $4, ")"); + } + | a_expr '-' '(' SubSelect ')' + { + $$ = cat4_str($1, "-(", $4, ")"); + } + | a_expr '/' '(' SubSelect ')' + { + $$ = cat4_str($1, "/(", $4, ")"); + } + | a_expr '*' '(' SubSelect ')' + { + $$ = cat4_str($1, "*(", $4, ")"); + } + | a_expr '<' '(' SubSelect ')' + { + $$ = cat4_str($1, "<(", $4, ")"); + } + | a_expr '>' '(' SubSelect ')' + { + $$ = cat4_str($1, ">(", $4, ")"); + } + | a_expr '=' '(' SubSelect ')' + { + $$ = cat4_str($1, "=(", $4, ")"); + } + | a_expr Op ANY '(' SubSelect ')' + { + $$ = make3_str($1, $2, cat3_str("any(", $5, ")")); + } + | a_expr '+' ANY '(' SubSelect ')' + { + $$ = cat4_str($1, "+any(", $5, ")"); + } + | a_expr '-' ANY '(' SubSelect ')' + { + $$ = cat4_str($1, "-any(", $5, ")"); + } + | a_expr '/' ANY '(' SubSelect ')' + { + $$ = cat4_str($1, "/any(", $5, ")"); + } + | a_expr '*' ANY '(' SubSelect ')' + { + $$ = cat4_str($1, "*any(", $5, ")"); + } + | a_expr '<' ANY '(' SubSelect ')' + { + $$ = cat4_str($1, "<any(", $5, ")"); + } + | a_expr '>' ANY '(' SubSelect ')' + { + $$ = cat4_str($1, ">any(", $5, ")"); + } + | a_expr '=' ANY '(' SubSelect ')' + { + $$ = cat4_str($1, "=any(", $5, ")"); + } + | a_expr Op ALL '(' SubSelect ')' + { + $$ = make3_str($1, $2, cat3_str("all (", $5, ")")); + } + | a_expr '+' ALL '(' SubSelect ')' + { + $$ = cat4_str($1, "+all(", $5, ")"); + } + | a_expr '-' ALL '(' SubSelect ')' + { + $$ = cat4_str($1, "-all(", $5, ")"); + } + | a_expr '/' ALL '(' SubSelect ')' + { + $$ = cat4_str($1, "/all(", $5, ")"); + } + | a_expr '*' ALL '(' SubSelect ')' + { + $$ = cat4_str($1, "*all(", $5, ")"); + } + | a_expr '<' ALL '(' SubSelect ')' + { + $$ = cat4_str($1, "<all(", $5, ")"); + } + | a_expr '>' ALL '(' SubSelect ')' + { + $$ = cat4_str($1, ">all(", $5, ")"); + } + | a_expr '=' ALL '(' SubSelect ')' + { + $$ = cat4_str($1, "=all(", $5, ")"); + } + | a_expr AND a_expr + { $$ = make3_str($1, "and", $3); } + | a_expr OR a_expr + { $$ = make3_str($1, "or", $3); } + | NOT a_expr + { $$ = make2_str("not", $2); } + | cinputvariable + { $$ = ";;"; } + ; + +/* + * b_expr is a subset of the complete expression syntax + * defined by a_expr. b_expr is used in BETWEEN clauses + * to eliminate parser ambiguities stemming from the AND keyword. + */ + +b_expr: attr opt_indirection + { + $$ = make2_str($1, $2); + } + | AexprConst + { $$ = $1; } + | ColId + { + $$ = $1; + } + | '-' b_expr %prec UMINUS + { $$ = make2_str("-", $2); } + | b_expr '+' b_expr + { $$ = make3_str($1, "+", $3); } + | b_expr '-' b_expr + { $$ = make3_str($1, "-", $3); } + | b_expr '/' b_expr + { $$ = make3_str($1, "/", $3); } + | b_expr '*' b_expr + { $$ = make3_str($1, "*", $3); } +/* not possible in embedded sql | ':' b_expr + { $$ = make2_str(":", $2); } +*/ + | ';' b_expr + { $$ = make2_str(";", $2); } + | '|' b_expr + { $$ = make2_str("|", $2); } + | b_expr TYPECAST Typename + { + $$ = make3_str($1, "::", $3); + } + | CAST '(' b_expr AS Typename ')' + { + $$ = make3_str(cat2_str("cast(", $3), "as", cat2_str($5, ")")); + } + | '(' a_expr ')' + { $$ = cat3_str("(", $2, ")"); } + | b_expr Op b_expr + { $$ = make3_str($1, $2, $3); } + | Op b_expr + { $$ = make2_str($1, $2); } + | b_expr Op + { $$ = make2_str($1, $2); } + | func_name '(' ')' + { + $$ = make2_str($1, "()"); + } + | func_name '(' expr_list ')' + { + $$ = cat4_str($1, "(", $3, ")"); + } + | CURRENT_DATE + { + $$ = "current_date"; + } + | CURRENT_TIME + { + $$ = "current_time"; + } + | CURRENT_TIME '(' Iconst ')' + { + if ($3 != 0) + fprintf(stderr,"CURRENT_TIME(%s) precision not implemented; zero used instead", $3); + $$ = "current_time"; + } + | CURRENT_TIMESTAMP + { + $$ = "current_timestamp"; + } + | CURRENT_TIMESTAMP '(' Iconst ')' + { + if (atol($3) != 0) + fprintf(stderr,"CURRENT_TIMESTAMP(%s) precision not implemented; zero used instead",$3); + $$ = "current_timestamp"; + } + | CURRENT_USER + { + $$ = "current_user"; + } + | POSITION '(' position_list ')' + { + $$ = cat3_str("position (", $3, ")"); + } + | SUBSTRING '(' substr_list ')' + { + $$ = cat3_str("substring (", $3, ")"); + } + /* various trim expressions are defined in SQL92 - thomas 1997-07-19 */ + | TRIM '(' BOTH trim_list ')' + { + $$ = cat3_str("trim(both", $4, ")"); + } + | TRIM '(' LEADING trim_list ')' + { + $$ = cat3_str("trim(leading", $4, ")"); + } + | TRIM '(' TRAILING trim_list ')' + { + $$ = cat3_str("trim(trailing", $4, ")"); + } + | TRIM '(' trim_list ')' + { + $$ = cat3_str("trim(", $3, ")"); + } + | civariableonly + { $$ = ";;"; } + ; + +opt_indirection: '[' c_expr ']' opt_indirection + { + $$ = make4_str("[", $2, "]", $4); + } + | '[' c_expr ':' c_expr ']' opt_indirection + { + $$ = make2_str(make5_str("[", $2, ":", $4, "]"), $6); + } + | /* EMPTY */ + { $$ = ""; } + ; + +expr_list: a_expr_or_null + { $$ = $1; } + | expr_list ',' a_expr_or_null + { $$ = make3_str($1, ",", $3); } + | expr_list USING a_expr + { $$ = make3_str($1, "using", $3); } + ; + +extract_list: extract_arg FROM a_expr + { + $$ = make3_str($1, "from", $3); + } + | /* EMPTY */ + { $$ = ""; } + | cinputvariable + { $$ = ";;"; } + ; + +/* 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; } + ; + +position_list: position_expr IN position_expr + { $$ = make3_str($1, "in", $3); } + | /* EMPTY */ + { $$ = ""; } + ; + +position_expr: attr opt_indirection + { + $$ = make2_str($1, $2); + } + | AexprConst + { $$ = $1; } + | '-' position_expr %prec UMINUS + { $$ = make2_str("-", $2); } + | position_expr '+' position_expr + { $$ = make3_str($1, "+", $3); } + | position_expr '-' position_expr + { $$ = make3_str($1, "-", $3); } + | position_expr '/' position_expr + { $$ = make3_str($1, "/", $3); } + | position_expr '*' position_expr + { $$ = make3_str($1, "*", $3); } + | '|' position_expr + { $$ = make2_str("|", $2); } + | position_expr TYPECAST Typename + { + $$ = make3_str($1, "::", $3); + } + | CAST '(' position_expr AS Typename ')' + { + $$ = make3_str(cat2_str("cast(", $3), "as", cat2_str($5, ")")); + } + | '(' position_expr ')' + { $$ = cat3_str("(", $2, ")"); } + | position_expr Op position_expr + { $$ = make3_str($1, $2, $3); } + | Op position_expr + { $$ = make2_str($1, $2); } + | position_expr Op + { $$ = make2_str($1, $2); } + | ColId + { + $$ = $1; + } + | func_name '(' ')' + { + $$ = make2_str($1, "()"); + } + | func_name '(' expr_list ')' + { + $$ = cat4_str($1, "(", $3, ")"); + } + | POSITION '(' position_list ')' + { + $$ = cat3_str("position(", $3, ")"); + } + | SUBSTRING '(' substr_list ')' + { + $$ = cat3_str("substring(", $3, ")"); + } + /* various trim expressions are defined in SQL92 - thomas 1997-07-19 */ + | TRIM '(' BOTH trim_list ')' + { + $$ = cat3_str("trim(both", $4, ")"); + } + | TRIM '(' LEADING trim_list ')' + { + $$ = cat3_str("trim(leading", $4, ")"); + } + | TRIM '(' TRAILING trim_list ')' + { + $$ = cat3_str("trim(trailing", $4, ")"); + } + | TRIM '(' trim_list ')' + { + $$ = cat3_str("trim(", $3, ")"); + } + ; + +substr_list: expr_list substr_from substr_for + { + $$ = make3_str($1, $2, $3); + } + | /* EMPTY */ + { $$ = ""; } + ; + +substr_from: FROM expr_list + { $$ = make2_str("from", $2); } + | /* EMPTY */ + { + $$ = ""; + } + ; + +substr_for: FOR expr_list + { $$ = make2_str("for", $2); } + | /* EMPTY */ + { $$ = ""; } + ; + +trim_list: a_expr FROM expr_list + { $$ = make3_str($1, "from", $3); } + | FROM expr_list + { $$ = make2_str("from", $2); } + | expr_list + { $$ = $1; } + ; + +in_expr: SubSelect + { + $$ = $1; + } + | in_expr_nodes + { $$ = $1; } + ; + +in_expr_nodes: AexprConst + { $$ = $1; } + | in_expr_nodes ',' AexprConst + { $$ = make3_str($1, ",", $3);} + ; + +not_in_expr: SubSelect + { + $$ = $1; + } + | not_in_expr_nodes + { $$ = $1; } + ; + +not_in_expr_nodes: AexprConst + { $$ = $1; } + | not_in_expr_nodes ',' AexprConst + { $$ = make3_str($1, ",", $3);} + ; + +attr: relation_name '.' attrs + { + $$ = make3_str($1, ".", $3); + } + | ParamNo '.' attrs + { + $$ = make3_str($1, ".", $3); + } + ; + +attrs: attr_name + { $$ = $1; } + | attrs '.' attr_name + { $$ = make3_str($1, ".", $3); } + | attrs '.' '*' + { $$ = make2_str($1, ".*"); } + ; + + +/***************************************************************************** + * + * target lists + * + *****************************************************************************/ + +res_target_list: res_target_list ',' res_target_el + { $$ = make3_str($1, ",",$3); } + | res_target_el + { $$ = $1; } + | '*' { $$ = "*"; } + ; + +res_target_el: ColId opt_indirection '=' a_expr_or_null + { + $$ = make4_str($1, $2, "=", $4); + } + | attr opt_indirection + { + $$ = make2_str($1, $2); + } + | relation_name '.' '*' + { + $$ = make2_str($1, ".*"); + } + ; + +/* +** target list for select. +** should get rid of the other but is still needed by the defunct select into +** and update (uses a subset) +*/ +res_target_list2: res_target_list2 ',' res_target_el2 + { $$ = make3_str($1, ",", $3); } + | res_target_el2 + { $$ = $1; } + ; + +/* AS is not optional because shift/red conflict with unary ops */ +res_target_el2: a_expr_or_null AS ColLabel + { + $$ = make3_str($1, "as", $3); + } + | a_expr_or_null + { + $$ = $1; + } + | relation_name '.' '*' + { + $$ = make2_str($1, ".*"); + } + | '*' + { + $$ = "*"; + } + ; + +opt_id: ColId { $$ = $1; } + | /* EMPTY */ { $$ = ""; } + ; + +relation_name: SpecialRuleRelation + { + $$ = $1; + } + | ColId + { + /* disallow refs to variable system tables */ + if (strcmp(LogRelationName, $1) == 0 + || strcmp(VariableRelationName, $1) == 0) { + sprintf(errortext, "%s cannot be accessed by users",$1); + yyerror(errortext); + } + else + $$ = $1; + } + ; + +database_name: ColId { $$ = $1; }; +access_method: IDENT { $$ = $1; }; +attr_name: ColId { $$ = $1; }; +class: IDENT { $$ = $1; }; +index_name: ColId { $$ = $1; }; + +/* Functions + * Include date/time keywords as SQL92 extension. + * Include TYPE as a SQL92 unreserved keyword. - thomas 1997-10-05 + */ +name: ColId { $$ = $1; }; +func_name: ColId { $$ = $1; }; + +file_name: Sconst { $$ = $1; }; +recipe_name: IDENT { $$ = $1; }; + +/* Constants + * Include TRUE/FALSE for SQL3 support. - thomas 1997-10-24 + */ +AexprConst: Iconst + { + $$ = $1; + } + | FCONST + { + $$ = make_name(); + } + | Sconst + { + $$ = $1; + } + | Typename Sconst + { + $$ = make2_str($1, $2); + } + | ParamNo + { $$ = $1; } + | TRUE_P + { + $$ = "true"; + } + | FALSE_P + { + $$ = "false"; + } + ; + +ParamNo: PARAM + { + $$ = make_name(); + } + ; + +NumConst: Iconst { $$ = $1; } + | FCONST { $$ = make_name(); } + ; + +Iconst: ICONST { $$ = make_name();}; +Sconst: SCONST { + $$ = (char *)mm_alloc(strlen($1) + 3); + $$[0]='\''; + strcpy($$+1, $1); + $$[strlen($1)+2]='\0'; + $$[strlen($1)+1]='\''; + } +UserId: IDENT { $$ = $1;}; + +/* Column and type identifier + * Does not include explicit datetime types + * since these must be decoupled in Typename syntax. + * Use ColId for most identifiers. - thomas 1997-10-21 + */ +TypeId: ColId + { $$ = $1; } + | numeric + { $$ = $1; } + | character + { $$ = $1; } + ; +/* Column identifier + * Include date/time keywords as SQL92 extension. + * Include TYPE as a SQL92 unreserved keyword. - thomas 1997-10-05 + * Add other keywords. Note that as the syntax expands, + * some of these keywords will have to be removed from this + * list due to shift/reduce conflicts in yacc. If so, move + * down to the ColLabel entity. - thomas 1997-11-06 + */ +ColId: IDENT { $$ = $1; } + | datetime { $$ = $1; } + | ACTION { $$ = "action"; } + | CACHE { $$ = "cache"; } + | CYCLE { $$ = "cycle"; } + | DATABASE { $$ = "database"; } + | DELIMITERS { $$ = "delimiters"; } + | DOUBLE { $$ = "double"; } + | EACH { $$ = "each"; } + | FUNCTION { $$ = "function"; } + | INCREMENT { $$ = "increment"; } + | INDEX { $$ = "index"; } + | KEY { $$ = "key"; } + | LANGUAGE { $$ = "language"; } + | LOCATION { $$ = "location"; } + | MATCH { $$ = "match"; } + | MAXVALUE { $$ = "maxvalue"; } + | MINVALUE { $$ = "minvalue"; } + | OPERATOR { $$ = "operator"; } + | OPTION { $$ = "option"; } + | PASSWORD { $$ = "password"; } + | PRIVILEGES { $$ = "privileges"; } + | RECIPE { $$ = "recipe"; } + | ROW { $$ = "row"; } + | START { $$ = "start"; } + | STATEMENT { $$ = "statement"; } + | TIME { $$ = "time"; } + | TRIGGER { $$ = "trigger"; } + | TYPE_P { $$ = "type"; } + | USER { $$ = "user"; } + | VALID { $$ = "valid"; } + | VERSION { $$ = "version"; } + | ZONE { $$ = "zone"; } + ; + +/* Column label + * Allowed labels in "AS" clauses. + * Include TRUE/FALSE SQL3 reserved words for Postgres backward + * compatibility. Cannot allow this for column names since the + * syntax would not distinguish between the constant value and + * a column name. - thomas 1997-10-24 + * Add other keywords to this list. Note that they appear here + * rather than in ColId if there was a shift/reduce conflict + * when used as a full identifier. - thomas 1997-11-06 + */ +ColLabel: ColId { $$ = $1; } + | ARCHIVE { $$ = "archive"; } + | CLUSTER { $$ = "cluster"; } + | CONSTRAINT { $$ = "constraint"; } + | CROSS { $$ = "cross"; } + | FOREIGN { $$ = "foreign"; } + | GROUP { $$ = "group"; } + | LOAD { $$ = "load"; } + | ORDER { $$ = "order"; } + | POSITION { $$ = "position"; } + | PRECISION { $$ = "precision"; } + | TABLE { $$ = "table"; } + | TRANSACTION { $$ = "transaction"; } + | TRUE_P { $$ = "true"; } + | FALSE_P { $$ = "false"; } + ; + +SpecialRuleRelation: CURRENT + { + if (QueryIsRule) + $$ = "current"; + else + yyerror("CURRENT used in non-rule query"); + } + | NEW + { + if (QueryIsRule) + $$ = "new"; + else + yyerror("NEW used in non-rule query"); + } + ; + +/* + * 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 */ - | variable_declarations variable_declaration; + | variable_declarations variable_declaration; /* Here is where we can enter support for typedef. */ -variable_declaration : type initializer ';' { +variable_declaration: type initializer ';' { /* don't worry about our list when we're working on a struct */ if (struct_level == 0) { - new_variable($<type>1.name, $<type>1.typ); - free((void *)$<type>1.name); + new_variable($<type>1.name, $<type>1.typ); + free((void *)$<type>1.name); } - fprintf(yyout, ";"); + fputs(";", yyout); } initializer : /*empty */ - | '=' {fwrite(yytext, yyleng, 1, yyout);} vartext; - -vartext : /* empty */ {} - | vartext both_anything { - if (do_length == 0) - fwrite(yytext, yyleng, 1, yyout); - else - { - if (strlen(do_str) + yyleng + 1 >= do_length) - do_str = mm_realloc(do_str, do_length += yyleng); - - strcat(do_str, yytext); - } -} - -symbol : S_SYMBOL { - char * name = (char *)malloc(yyleng + 1); - - strncpy(name, yytext, yyleng); - name[yyleng] = '\0'; - - $<symbolname>$ = name; -} + | '=' {fwrite(yytext, yyleng, 1, yyout);} vartext; type : maybe_storage_clause type_detailed { $<type>$ = $<type>2; }; type_detailed : varchar_type { $<type>$ = $<type>1; } @@ -325,65 +3714,64 @@ type_detailed : varchar_type { $<type>$ = $<type>1; } | struct_type {$<type>$ = $<type>1; }; varchar_type : varchar_tag symbol index { - if ($<indexsize>3 > 0L) - fprintf(yyout, "struct varchar_%s { int len; char arr[%ld]; } %s", $<symbolname>2, $<indexsize>3, $<symbolname>2); + if ($<ival>3 > 0L) + fprintf(yyout, "struct varchar_%s { int len; char arr[%d]; } %s", $2, $<ival>3, $2); else - fprintf(yyout, "struct varchar_%s { int len; char arr[]; } %s", $<symbolname>2, $<symbolname>2); + fprintf(yyout, "struct varchar_%s { int len; char arr[]; } %s", $2, $2); if (struct_level == 0) { - $<type>$.name = $<symbolname>2; - $<type>$.typ = ECPGmake_varchar_type(ECPGt_varchar, $<indexsize>3); + $<type>$.name = $2; + $<type>$.typ = ECPGmake_varchar_type(ECPGt_varchar, $<ival>3); } else - ECPGmake_record_member($<symbolname>2, ECPGmake_varchar_type(ECPGt_varchar, $<indexsize>3), &(record_member_list[struct_level-1])); + ECPGmake_record_member($2, ECPGmake_varchar_type(ECPGt_varchar, $<ival>3), &(record_member_list[struct_level-1])); } -varchar_tag : S_VARCHAR { $<tagname>$ = $<tagname>1; } - | S_VARCHAR2 { $<tagname>$ = $<tagname>1; }; +varchar_tag: S_VARCHAR /*| S_VARCHAR2 */; simple_type : simple_tag symbol { - fprintf(yyout, "%s %s", ECPGtype_name($<type_enum>1), $<symbolname>2); + fprintf(yyout, "%s %s", ECPGtype_name($<type_enum>1), $2); if (struct_level == 0) { - $<type>$.name = $<symbolname>2; + $<type>$.name = $2; $<type>$.typ = ECPGmake_simple_type($<type_enum>1, 1); } else - ECPGmake_record_member($<symbolname>2, ECPGmake_simple_type($<type_enum>1, 1), &(record_member_list[struct_level-1])); + ECPGmake_record_member($2, ECPGmake_simple_type($<type_enum>1, 1), &(record_member_list[struct_level-1])); } string_type : char_tag symbol index { - if ($<indexsize>3 > 0L) - fprintf(yyout, "%s %s [%ld]", ECPGtype_name($<type_enum>1), $<symbolname>2, $<indexsize>3); + if ($<ival>3 > 0L) + fprintf(yyout, "%s %s [%d]", ECPGtype_name($<type_enum>1), $2, $<ival>3); else - fprintf(yyout, "%s %s []", ECPGtype_name($<type_enum>1), $<symbolname>2); + fprintf(yyout, "%s %s []", ECPGtype_name($<type_enum>1), $2); if (struct_level == 0) { - $<type>$.name = $<symbolname>2; - $<type>$.typ = ECPGmake_simple_type($<type_enum>1, $<indexsize>3); + $<type>$.name = $2; + $<type>$.typ = ECPGmake_simple_type($<type_enum>1, $<ival>3); } else - ECPGmake_record_member($<symbolname>2, ECPGmake_simple_type($<type_enum>1, $<indexsize>3), &(record_member_list[struct_level-1])); + ECPGmake_record_member($2, ECPGmake_simple_type($<type_enum>1, $<ival>3), &(record_member_list[struct_level-1])); } | char_tag '*' symbol { - fprintf(yyout, "%s *%s", ECPGtype_name($<type_enum>1), $<symbolname>3); + fprintf(yyout, "%s *%s", ECPGtype_name($<type_enum>1), $3); if (struct_level == 0) { - $<type>$.name = $<symbolname>3; + $<type>$.name = $3; $<type>$.typ = ECPGmake_simple_type($<type_enum>1, 0); } else - ECPGmake_record_member($<symbolname>3, ECPGmake_simple_type($<type_enum>1, 0), &(record_member_list[struct_level-1])); + ECPGmake_record_member($3, ECPGmake_simple_type($<type_enum>1, 0), &(record_member_list[struct_level-1])); } | char_tag symbol { - fprintf(yyout, "%s %s", ECPGtype_name($<type_enum>1), $<symbolname>2); + fprintf(yyout, "%s %s", ECPGtype_name($<type_enum>1), $2); if (struct_level == 0) { - $<type>$.name = $<symbolname>2; + $<type>$.name = $2; $<type>$.typ = ECPGmake_simple_type($<type_enum>1, 1); } else - ECPGmake_record_member($<symbolname>2, ECPGmake_simple_type($<type_enum>1, 1), &(record_member_list[struct_level-1])); + ECPGmake_record_member($2, ECPGmake_simple_type($<type_enum>1, 1), &(record_member_list[struct_level-1])); } char_tag : S_CHAR { $<type_enum>$ = ECPGt_char; } @@ -391,46 +3779,46 @@ char_tag : S_CHAR { $<type_enum>$ = ECPGt_char; } /* array_type : simple_tag symbol index { - if ($<indexsize>3 > 0) - fprintf(yyout, "%s %s [%ld]", ECPGtype_name($<type_enum>1), $<symbolname>2, $<indexsize>3); + if ($<ival>3 > 0) + fprintf(yyout, "%s %s [%ld]", ECPGtype_name($<type_enum>1), $2, $<ival>3); else - fprintf(yyout, "%s %s []", ECPGtype_name($<type_enum>1), $<symbolname>2); + fprintf(yyout, "%s %s []", ECPGtype_name($<type_enum>1), $2); if (struct_level == 0) { - $<type>$.name = $<symbolname>2; - $<type>$.typ = ECPGmake_array_type(ECPGmake_simple_type($<type_enum>1), $<indexsize>3); + $<type>$.name = $2; + $<type>$.typ = ECPGmake_array_type(ECPGmake_simple_type($<type_enum>1), $<ival>3); } else - ECPGmake_record_member($<symbolname>2, ECPGmake_array_type(ECPGmake_simple_type($<type_enum>1), $<indexsize>3), &(record_member_list[struct_level-1])); + ECPGmake_record_member($2, ECPGmake_array_type(ECPGmake_simple_type($<type_enum>1), $<ival>3), &(record_member_list[struct_level-1])); } pointer_type : simple_tag '*' symbol { - fprintf(yyout, "%s * %s", ECPGtype_name($<type_enum>1), $<symbolname>3); + fprintf(yyout, "%s * %s", ECPGtype_name($<type_enum>1), $3); if (struct_level == 0) { - $<type>$.name = $<symbolname>3; + $<type>$.name = $3; $<type>$.typ = ECPGmake_array_type(ECPGmake_simple_type($<type_enum>1), 0); } else - ECPGmake_record_member($<symbolname>3, ECPGmake_array_type(ECPGmake_simple_type($<type_enum>1), 0), &(record_member_list[struct_level-1])); + ECPGmake_record_member($3, ECPGmake_array_type(ECPGmake_simple_type($<type_enum>1), 0), &(record_member_list[struct_level-1])); } */ s_struct : S_STRUCT symbol { struct_level++; - fprintf(yyout, "struct %s {", $<symbolname>2); + fprintf(yyout, "struct %s {", $2); } struct_type : s_struct '{' variable_declarations '}' symbol { struct_level--; if (struct_level == 0) { - $<type>$.name = $<symbolname>5; + $<type>$.name = $5; $<type>$.typ = ECPGmake_record_type(record_member_list[struct_level]); } else - ECPGmake_record_member($<symbolname>5, ECPGmake_record_type(record_member_list[struct_level]), &(record_member_list[struct_level-1])); - fprintf(yyout, "} %s", $<symbolname>5); + ECPGmake_record_member($5, ECPGmake_record_type(record_member_list[struct_level]), &(record_member_list[struct_level-1])); + fprintf(yyout, "} %s", $5); record_member_list[struct_level] = NULL; } @@ -452,24 +3840,17 @@ maybe_storage_clause : S_EXTERN { fwrite(yytext, yyleng, 1, yyout); } | S_AUTO { fwrite(yytext, yyleng, 1, yyout); } | /* empty */ { }; -index : '[' length ']' { $<indexsize>$ = $<indexsize>2; } - | '[' ']' { $<indexsize>$ = 0L; } - -length : S_LENGTH { $<indexsize>$ = atol(yytext); } - -sqlinclude : SQL_START SQL_INCLUDE { fprintf(yyout, "#include \""); } - filename SQL_SEMI { fprintf(yyout, ".h\""); output_line_number(); }; +index : '[' Iconst ']' { $<ival>$ = atol($2); } + | '[' ']' { $<ival>$ = 0L; } -filename : cthing - | filename cthing; - -sqlconnect : SQL_START SQL_CONNECT { fprintf(yyout, "ECPGconnect("); } - db_name - SQL_SEMI { fprintf(yyout, ");"); whenever_action();} +/* + * the exec sql connect statement: connect to the given database + */ +ECPGConnect : SQL_CONNECT db_name { $$ = $2; } -db_name : SQL_STRING { fprintf(yyout, "\""); fwrite(yytext + 1, yyleng - 2, 1, yyout); fprintf(yyout, "\""); } - | ':' symbol { /* check if we have a char variable */ - struct variable *p = find_variable($<symbolname>2); +db_name : database_name { $$ = $1; } + | ':' name { /* check if we have a char variable */ + struct variable *p = find_variable($2); enum ECPGttype typ = p->type->typ; /* if array see what's inside */ @@ -478,213 +3859,436 @@ db_name : SQL_STRING { fprintf(yyout, "\""); fwrite(yytext + 1, yyleng - 2, 1, y if (typ != ECPGt_char && typ != ECPGt_unsigned_char) yyerror("invalid datatype"); - - fprintf(yyout, "%s", $<symbolname>2); + $$ = $2; } -/* Open is an open cursor. Removed. */ -sqlopen : SQL_START SQL_OPEN sqlgarbage SQL_SEMI { output_line_number(); }; - -sqlgarbage : /* Empty */ - | sqlgarbage sqlanything; - - -sqltransaction : SQL_START transactionstmt SQL_SEMI { - fprintf(yyout, "ECPGtrans(__LINE__, \"%s\");", $<symbolname>2); - whenever_action(); -} +/* + * execute a given string as sql command + */ +ECPGExecute : EXECUTE SQL_IMMEDIATE ':' name { $$ = $4; }; +/* + * open is an open cursor, at the moment this has to be removed + */ +ECPGOpen: SQL_OPEN name open_opts { + struct cursor *ptr; -transactionstmt: transbegin + for (ptr = cur; ptr != NULL; ptr=ptr->next) + { + if (strcmp(ptr->name, $2) == 0) { - $<symbolname>$="begin"; - } - | transend - { - $<symbolname>$="end"; - } - | transabort - { - $<symbolname>$="abort"; + $$ = ptr->command; + break; } + } -transabort: SQL_ABORT SQL_TRANSACTION | SQL_ROLLBACK SQL_WORK - | SQL_ABORT | SQL_ROLLBACK; - -transend: SQL_END SQL_TRANSACTION | SQL_COMMIT | SQL_COMMIT SQL_RELEASE - | SQL_COMMIT SQL_WORK SQL_RELEASE; - -transbegin: SQL_BEGIN SQL_TRANSACTION | SQL_BEGIN SQL_WORK; - -sqlexecute : SQL_START SQL_EXECUTE SQL_IMMEDIATE ':' symbol SQL_SEMI { - fprintf(yyout, "ECPGdo(__LINE__, %s, ECPGt_EOIT, ECPGt_EORT );", $5); - whenever_action(); + if (ptr == NULL) + { + sprintf(errortext, "unknown cursor %s opened", $2); + yyerror(errortext); + } }; -sqlwhenever : SQL_START SQL_WHENEVER SQL_SQLERROR { - fprintf(yyout, "/* exec sql whenever sqlerror "); -} action SQL_SEMI{ - when_error.code = $<action>5.code; - when_error.str = $<action>5.str; - fprintf(yyout, "; */\n"); +open_opts: /* empty */ { $$ = ""; } + | USING ':' name { + yyerror ("open cursor with variables not implemented yet"); + } + +/* + * whenever statement: decide what to do in case of error/no dat + */ +ECPGWhenever: SQL_WHENEVER SQL_SQLERROR action { + when_error.code = $<action>3.code; + when_error.command = $<action>3.command; + $$ = make3_str("/* exec sql whenever sqlerror ", $3.str, "; */\n"); } - | SQL_START SQL_WHENEVER SQL_NOT_FOUND { - fprintf(yyout, "/* exec sql whenever not found "); -} action SQL_SEMI{ - when_nf.code = $<action>5.code; - when_nf.str=$<action>5.str; - fprintf(yyout, "; */\n"); + | SQL_WHENEVER NOT SQL_FOUND action { + when_nf.code = $<action>4.code; + when_nf.command = $<action>4.command; + $$ = make3_str("/* exec sql whenever not found ", $4.str, "; */\n"); } action : SQL_CONTINUE { $<action>$.code = W_NOTHING; - $<action>$.str = NULL; - fprintf(yyout, "continue"); + $<action>$.command = NULL; + $<action>$.str = "continue"; } | SQL_SQLPRINT { $<action>$.code = W_SQLPRINT; - $<action>$.str = NULL; - fprintf(yyout, "sqlprint"); + $<action>$.command = NULL; + $<action>$.str = "sqlprint"; } | SQL_STOP { $<action>$.code = W_STOP; - $<action>$.str = NULL; - fprintf(yyout, "stop"); + $<action>$.command = NULL; + $<action>$.str = "stop"; } - | SQL_GOTO label { - $<action>$.code = W_GOTO; - $<action>$.str = $<symbolname>2; - fprintf(yyout, "goto %s", $<symbolname>2); + | SQL_GOTO name { + $<action>$.code = W_GOTO; + $<action>$.command = $2; + $<action>$.str = make2_str("goto ", $2); } - | SQL_GOTO symbol { + | SQL_GO TO name { $<action>$.code = W_GOTO; - $<action>$.str = $<symbolname>2; - fprintf(yyout, "goto %s", $<symbolname>2); + $<action>$.command = $3; + $<action>$.str = make2_str("goto ", $3); } - | SQL_DO symbol '(' { - do_str = (char *) mm_alloc(do_length = strlen($<symbolname>2) + 4); - sprintf(do_str, "%s (", $<symbolname>2); -} vartext ')' { + | DO name '(' { + do_str = (char *) mm_alloc(do_length = strlen($2) + 4); + sprintf(do_str, "%s (", $2); +} dotext ')' { do_str[strlen(do_str)+1]='\0'; do_str[strlen(do_str)]=')'; $<action>$.code = W_DO; - $<action>$.str = do_str; - fprintf(yyout, "do %s", do_str); + $<action>$.command = do_str; + $<action>$.str = make2_str("do ", do_str); do_str = NULL; do_length = 0; } -label : S_LABEL { - char * name = (char *)malloc(yyleng + 1); - - strncpy(name, yytext, yyleng); - name[yyleng] = '\0'; - - $<symbolname>$ = name; -} - -sqlfetch: SQL_START SQL_FETCH { - reset_variables(); - fprintf(yyout, "ECPGdo(__LINE__, \"fetch in "); -} cursor { - fwrite(yytext, yyleng, 1, yyout); - fwrite(" ", 1, 1, yyout); -} SQL_INTO into_list SQL_SEMI { - /* Dump */ - fprintf(yyout, "\", "); - dump_variables(argsinsert); - fprintf(yyout, "ECPGt_EOIT, "); - dump_variables(argsresult); - fprintf(yyout, "ECPGt_EORT );"); - whenever_action(); -} - -cursor: SQL_IN S_SYMBOL | S_SYMBOL; - -sqlstatement : SQL_START { /* Reset stack */ - reset_variables(); - fprintf(yyout, "ECPGdo(__LINE__, \""); -} sqlcommand { - fwrite(yytext, yyleng, 1, yyout); - fwrite(" ", 1, 1, yyout); -} sqlstatement_words SQL_SEMI { - /* Dump */ - fprintf(yyout, "\", "); - dump_variables(argsinsert); - fprintf(yyout, "ECPGt_EOIT, "); - dump_variables(argsresult); - fprintf(yyout, "ECPGt_EORT );"); - whenever_action(); -} - -/* FIXME: instead of S_SYMBOL we should list all possible commands */ -sqlcommand : S_SYMBOL | SQL_DECLARE | SQL_VACUUM; - -sqlstatement_words : /* empty */ - | sqlstatement_words sqlstatement_word; - -sqlstatement_word : ':' symbol - { - add_variable(&argsinsert, find_variable($2)); - fprintf(yyout, " ;; "); - } - | SQL_INTO into_list SQL_FROM { - fwrite(yytext, yyleng, 1, yyout); - fwrite(" ", 1, 1, yyout); - } - | sqlanything - { - fwrite(yytext, yyleng, 1, yyout); - fwrite(" ", 1, 1, yyout); - } - | SQL_INTO sqlanything - { - fprintf(yyout, " into "); - fwrite(yytext, yyleng, 1, yyout); - fwrite(" ", 1, 1, yyout); - }; - -into_list : ':' symbol { - add_variable(&argsresult, find_variable($2)); -} - | into_list ',' ':' symbol{ - add_variable(&argsresult, find_variable($4)); -}; +/* some other stuff for ecpg */ + +c_expr: attr opt_indirection + { + $$ = make2_str($1, $2); + } + | row_expr + { $$ = $1; } + | AexprConst + { $$ = $1; } + | ColId + { + $$ = $1; + } + | '-' c_expr %prec UMINUS + { $$ = make2_str("-", $2); } + | a_expr '+' c_expr + { $$ = make3_str($1, "+", $3); } + | a_expr '-' c_expr + { $$ = make3_str($1, "-", $3); } + | a_expr '/' c_expr + { $$ = make3_str($1, "/", $3); } + | a_expr '*' c_expr + { $$ = make3_str($1, "*", $3); } + | a_expr '<' c_expr + { $$ = make3_str($1, "<", $3); } + | a_expr '>' c_expr + { $$ = make3_str($1, ">", $3); } + | a_expr '=' c_expr + { $$ = make3_str($1, "=", $3); } + /* | ':' c_expr + { $$ = make2_str(":", $2); }*/ + | ';' c_expr + { $$ = make2_str(";", $2); } + | '|' c_expr + { $$ = make2_str("|", $2); } + | a_expr TYPECAST Typename + { + $$ = make3_str($1, "::", $3); + } + | CAST '(' a_expr AS Typename ')' + { + $$ = make3_str(cat2_str("cast(", $3), "as", cat2_str($5, ")")); + } + | '(' a_expr_or_null ')' + { $$ = cat3_str("(", $2, ")"); } + | a_expr Op c_expr + { $$ = make3_str($1, $2, $3); } + | a_expr LIKE c_expr + { $$ = make3_str($1, "like", $3); } + | a_expr NOT LIKE c_expr + { $$ = make3_str($1, "not like", $4); } + | Op c_expr + { $$ = make2_str($1, $2); } + | a_expr Op + { $$ = make2_str($1, $2); } + | func_name '(' '*' ')' + { + $$ = make2_str($1, "(*)"); + } + | func_name '(' ')' + { + $$ = make2_str($1, "()"); + } + | func_name '(' expr_list ')' + { + $$ = cat4_str($1, "(", $3, ")"); + } + | CURRENT_DATE + { + $$ = "current_date"; + } + | CURRENT_TIME + { + $$ = "current_time"; + } + | CURRENT_TIME '(' Iconst ')' + { + if (atol($3) != 0) + fprintf(stderr,"CURRENT_TIME(%s) precision not implemented; zero used instead", $3); + $$ = "current_time"; + } + | CURRENT_TIMESTAMP + { + $$ = "current_timestamp"; + } + | CURRENT_TIMESTAMP '(' Iconst ')' + { + if (atol($3) != 0) + fprintf(stderr,"CURRENT_TIMESTAMP(%s) precision not implemented; zero used instead",$3); + $$ = "current_timestamp"; + } + | CURRENT_USER + { + $$ = "current_user"; + } + | EXISTS '(' SubSelect ')' + { + $$ = cat3_str("exists(", $3, ")"); + } + | EXTRACT '(' extract_list ')' + { + $$ = cat3_str("extract(", $3, ")"); + } + | POSITION '(' position_list ')' + { + $$ = cat3_str("position(", $3, ")"); + } + | SUBSTRING '(' substr_list ')' + { + $$ = cat3_str("substring(", $3, ")"); + } + /* various trim expressions are defined in SQL92 - thomas 1997-07-19 */ + | TRIM '(' BOTH trim_list ')' + { + $$ = cat3_str("trim(both", $4, ")"); + } + | TRIM '(' LEADING trim_list ')' + { + $$ = cat3_str("trim(leading", $4, ")"); + } + | TRIM '(' TRAILING trim_list ')' + { + $$ = cat3_str("trim(trailing", $4, ")"); + } + | TRIM '(' trim_list ')' + { + $$ = cat3_str("trim(", $3, ")"); + } + | a_expr ISNULL + { $$ = make2_str($1, "isnull"); } + | a_expr IS NULL_P + { $$ = make2_str($1, "is null"); } + | a_expr NOTNULL + { $$ = make2_str($1, "notnull"); } + | a_expr IS NOT NULL_P + { $$ = make2_str($1, "is not null"); } + /* IS TRUE, IS FALSE, etc used to be function calls + * but let's make them expressions to allow the optimizer + * a chance to eliminate them if a_expr is a constant string. + * - thomas 1997-12-22 + */ + | a_expr IS TRUE_P + { + { $$ = make2_str($1, "is true"); } + } + | a_expr IS NOT FALSE_P + { + { $$ = make2_str($1, "is not false"); } + } + | a_expr IS FALSE_P + { + { $$ = make2_str($1, "is false"); } + } + | a_expr IS NOT TRUE_P + { + { $$ = make2_str($1, "is not true"); } + } + | a_expr BETWEEN b_expr AND b_expr + { + $$ = make5_str($1, "between", $3, "and", $5); + } + | a_expr NOT BETWEEN b_expr AND b_expr + { + $$ = make5_str($1, "not between", $4, "and", $6); + } + | a_expr IN '(' in_expr ')' + { + $$ = cat4_str($1, "in (", $4, ")"); + } + | a_expr NOT IN '(' not_in_expr ')' + { + $$ = cat4_str($1, "not in (", $5, ")"); + } + | a_expr Op '(' SubSelect ')' + { + $$ = make3_str($1, $2, cat3_str("(", $4, +")")); + } + | a_expr '+' '(' SubSelect ')' + { + $$ = cat4_str($1, "+(", $4, ")"); + } + | a_expr '-' '(' SubSelect ')' + { + $$ = cat4_str($1, "-(", $4, ")"); + } + | a_expr '/' '(' SubSelect ')' + { + $$ = cat4_str($1, "/(", $4, ")"); + } + | a_expr '*' '(' SubSelect ')' + { + $$ = cat4_str($1, "*(", $4, ")"); + } + | a_expr '<' '(' SubSelect ')' + { + $$ = cat4_str($1, "<(", $4, ")"); + } + | a_expr '>' '(' SubSelect ')' + { + $$ = cat4_str($1, ">(", $4, ")"); + } + | a_expr '=' '(' SubSelect ')' + { + $$ = cat4_str($1, "=(", $4, ")"); + } + | a_expr Op ANY '(' SubSelect ')' + { + $$ = make3_str($1, $2, cat3_str("any (", $5, ")")); + } + | a_expr '+' ANY '(' SubSelect ')' + { + $$ = cat4_str($1, "+any(", $5, ")"); + } + | a_expr '-' ANY '(' SubSelect ')' + { + $$ = cat4_str($1, "-any(", $5, ")"); + } + | a_expr '/' ANY '(' SubSelect ')' + { + $$ = cat4_str($1, "/any(", $5, ")"); + } + | a_expr '*' ANY '(' SubSelect ')' + { + $$ = cat4_str($1, "*any(", $5, ")"); + } + | a_expr '<' ANY '(' SubSelect ')' + { + $$ = cat4_str($1, "<any(", $5, ")"); + } + | a_expr '>' ANY '(' SubSelect ')' + { + $$ = cat4_str($1, ">any(", $5, ")"); + } + | a_expr '=' ANY '(' SubSelect ')' + { + $$ = cat4_str($1, "=any(", $5, ")"); + } + | a_expr Op ALL '(' SubSelect ')' + { + $$ = cat3_str($1, $2, cat3_str("all (", $5, ")")); + } + | a_expr '+' ALL '(' SubSelect ')' + { + $$ = cat4_str($1, "+all(", $5, ")"); + } + | a_expr '-' ALL '(' SubSelect ')' + { + $$ = cat4_str($1, "-all(", $5, ")"); + } + | a_expr '/' ALL '(' SubSelect ')' + { + $$ = cat4_str($1, "/all(", $5, ")"); + } + | a_expr '*' ALL '(' SubSelect ')' + { + $$ = cat4_str($1, "*all(", $5, ")"); + } + | a_expr '<' ALL '(' SubSelect ')' + { + $$ = cat4_str($1, "<all(", $5, ")"); + } + | a_expr '>' ALL '(' SubSelect ')' + { + $$ = cat4_str($1, ">all(", $5, ")"); + } + | a_expr '=' ALL '(' SubSelect ')' + { + $$ = cat4_str($1, "=all(", $5, ")"); + } + | a_expr AND c_expr + { $$ = make3_str($1, "and", $3); } + | a_expr OR c_expr + { $$ = make3_str($1, "or", $3); } + | NOT c_expr + { $$ = make2_str("not", $2); } + | civariableonly + { $$ = ";;"; } + ; + +into_list : coutputvariable | into_list ',' coutputvariable; + +ecpgstart: SQL_START { reset_variables();} -cthing : canything { - fwrite(yytext, yyleng, 1, yyout); +dotext: /* empty */ + | dotext sql_anything { + if (strlen(do_str) + yyleng + 1 >= do_length) + do_str = mm_realloc(do_str, do_length += yyleng); + + strcat(do_str, yytext); +} + +vartext: both_anything { fwrite(yytext, yyleng, 1, yyout); } + | vartext both_anything { fwrite(yytext, yyleng, 1, yyout); } + +coutputvariable : ':' name indicator { + add_variable(&argsresult, find_variable($2), ($3 == NULL) ? &no_indicator : find_variable($3)); +} + +cinputvariable : ':' name indicator { + add_variable(&argsinsert, find_variable($2), ($3 == NULL) ? &no_indicator : find_variable($3)); +} + +civariableonly : ':' name { + add_variable(&argsinsert, find_variable($2), &no_indicator); } -canything : both_anything - | SQL_INTO - | ';'; +indicator: /* empty */ { $$ = NULL; } + | ':' name { check_indicator((find_variable($2))->type); $$ = $2; } + | SQL_INDICATOR ':' name { check_indicator((find_variable($3))->type); $$ = $3; } + | SQL_INDICATOR name { check_indicator((find_variable($2))->type); $$ = $2; } + +/* + * C stuff + */ -sqlanything : both_anything; +symbol: IDENT { $$ = $1; } -both_anything : S_LENGTH | S_VARCHAR | S_VARCHAR2 - | S_LONG | S_SHORT | S_INT | S_CHAR | S_FLOAT | S_DOUBLE | S_BOOL - | SQL_OPEN | SQL_CONNECT - | SQL_STRING | SQL_CONV - | SQL_BEGIN | SQL_END - | SQL_DECLARE | SQL_SECTION | SQL_FETCH | SQL_FROM - | SQL_INCLUDE | SQL_IN - | S_SYMBOL | S_LABEL - | S_STATIC | S_EXTERN | S_AUTO | S_CONST | S_REGISTER | S_STRUCT - | '[' | ']' | ',' | '=' | '*' | '(' | ')' - | S_ANYTHING; +c_anything: both_anything { fwrite(yytext, yyleng, 1, yyout); } + | ';' { fputc(';', yyout); } + +sql_anything: IDENT {} | ICONST {} | FCONST {} + +both_anything: IDENT {} | ICONST {} | FCONST {} + | S_AUTO | S_BOOL | S_CHAR | S_CONST | S_DOUBLE | S_EXTERN | S_FLOAT + | S_INT | S_LONG | S_REGISTER | S_SHORT | S_SIGNED | S_STATIC + | S_STRUCT | S_UNSIGNED | S_VARCHAR | S_ANYTHING + | '[' | ']' | '(' | ')' | '=' blockstart : '{' { braces_open++; - fwrite(yytext, yyleng, 1, yyout); + fputc('{', yyout); } blockend : '}' { remove_variables(braces_open--); - fwrite(yytext, yyleng, 1, yyout); + fputc('}', yyout); } + %% -static void yyerror(char * error) +void yyerror(char * error) { fprintf(stderr, "%s in line %d\n", error, yylineno); exit(1); diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c index 074e8b74933..2b43fa9976a 100644 --- a/src/interfaces/ecpg/preproc/type.c +++ b/src/interfaces/ecpg/preproc/type.c @@ -123,22 +123,27 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ, long varcharsize, long arrsiz, const char *siz, const char *prefix); void -ECPGdump_a_record(FILE *o, const char *name, long arrsiz, - struct ECPGtype * typ, const char *offset, const char *prefix); +ECPGdump_a_record(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); void -ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * typ, const char *prefix) +ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * typ, const char *ind_name, struct ECPGtype * ind_typ, const char *prefix, const char *ind_prefix) { if (IS_SIMPLE_TYPE(typ->typ)) { ECPGdump_a_simple(o, name, typ->typ, typ->size, 0, 0, prefix); + ECPGdump_a_simple(o, ind_name, ind_typ->typ, ind_typ->size, 0, 0, ind_prefix); } else if (typ->typ == ECPGt_array) { if (IS_SIMPLE_TYPE(typ->u.element->typ)) + { ECPGdump_a_simple(o, name, typ->u.element->typ, typ->u.element->size, typ->size, 0, prefix); + ECPGdump_a_simple(o, ind_name, ind_typ->u.element->typ, + ind_typ->u.element->size, ind_typ->size, 0, prefix); + } else if (typ->u.element->typ == ECPGt_array) { abort(); /* Array of array, */ @@ -146,7 +151,7 @@ ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * typ, const char *pr else if (typ->u.element->typ == ECPGt_record) { /* Array of records. */ - ECPGdump_a_record(o, name, typ->size, typ->u.element, 0, prefix); + ECPGdump_a_record(o, name, ind_name, typ->size, typ->u.element, ind_typ->u.element, 0, prefix, ind_prefix); } else { @@ -155,7 +160,7 @@ ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * typ, const char *pr } else if (typ->typ == ECPGt_record) { - ECPGdump_a_record(o, name, 0, typ, 0, prefix); + ECPGdump_a_record(o, name, ind_name, 0, typ, ind_typ, 0, prefix, ind_prefix); } else { @@ -171,7 +176,8 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ, long varcharsize, long arrsiz, const char *siz, - const char *prefix) + const char *prefix + ) { switch (typ) { @@ -241,15 +247,19 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ, varcharsize, arrsiz, siz); break; + case ECPGt_NO_INDICATOR: /* no indicator */ + fprintf(o, "\n\tECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, "); + break; default: abort(); } + } /* Penetrate a record and dump the contents. */ void -ECPGdump_a_record(FILE *o, const char *name, long arrsiz, struct ECPGtype * typ, const char *offsetarg, const char *prefix) +ECPGdump_a_record(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) { /* @@ -257,9 +267,9 @@ ECPGdump_a_record(FILE *o, const char *name, long arrsiz, struct ECPGtype * typ, * then we are in a record in a record and the offset is used as * offset. */ - struct ECPGrecord_member *p; + struct ECPGrecord_member *p, *ind_p; char obuf[BUFSIZ]; - char pbuf[BUFSIZ]; + char pbuf[BUFSIZ], ind_pbuf[BUFSIZ]; const char *offset; if (offsetarg == NULL) @@ -274,63 +284,13 @@ ECPGdump_a_record(FILE *o, const char *name, long arrsiz, struct ECPGtype * typ, sprintf(pbuf, "%s%s.", prefix ? prefix : "", name); prefix = pbuf; + + sprintf(ind_pbuf, "%s%s.", ind_prefix ? ind_prefix : "", ind_name); + ind_prefix = ind_pbuf; - for (p = typ->u.members; p; p = p->next) + for (p = typ->u.members, ind_p = ind_typ->u.members; p; p = p->next, ind_p = ind_p->next) { -#if 0 - if (IS_SIMPLE_TYPE(p->typ->typ)) - { - sprintf(buf, "%s.%s", name, p->name); - ECPGdump_a_simple(o, buf, p->typ->typ, p->typ->size, - arrsiz, offset); - } - else if (p->typ->typ == ECPGt_array) - { - int i; - - for (i = 0; i < p->typ->size; i++) - { - if (IS_SIMPLE_TYPE(p->typ->u.element->typ)) - { - /* sprintf(buf, "%s.%s[%d]", name, p->name, i); */ - sprintf(buf, "%s.%s", name, p->name); - ECPGdump_a_simple(o, buf, p->typ->u.element->typ, p->typ->u.element->size, - p->typ->u.element->size, offset); - } - else if (p->typ->u.element->typ == ECPGt_array) - { - /* Array within an array. NOT implemented. */ - abort(); - } - else if (p->typ->u.element->typ == ECPGt_record) - { - - /* - * Record within array within record. NOT implemented - * yet. - */ - abort(); - } - else - { - /* Unknown type */ - abort(); - } - } - } - else if (p->typ->typ == ECPGt_record) - { - /* Record within a record */ - sprintf(buf, "%s.%s", name, p->name); - ECPGdump_a_record(o, buf, arrsiz, p->typ, offset); - } - else - { - /* Unknown type */ - abort(); - } -#endif - ECPGdump_a_type(o, p->name, p->typ, prefix); + ECPGdump_a_type(o, p->name, p->typ, ind_p->name, ind_p->typ, prefix, ind_prefix); } } diff --git a/src/interfaces/ecpg/preproc/type.h b/src/interfaces/ecpg/preproc/type.h index 69bea16b52d..92d87055ee9 100644 --- a/src/interfaces/ecpg/preproc/type.h +++ b/src/interfaces/ecpg/preproc/type.h @@ -45,7 +45,7 @@ void ECPGfree_type(struct ECPGtype *); size is the maxsize in case it is a varchar. Otherwise it is the size of the variable (required to do array fetches of records). */ -void ECPGdump_a_type(FILE *, const char *name, struct ECPGtype *, const char *); +void ECPGdump_a_type(FILE *, const char *, struct ECPGtype *, const char *, struct ECPGtype *, const char *, const char *); /* A simple struct to keep a variable and its type. */ struct ECPGtemp_type @@ -71,5 +71,6 @@ enum WHEN struct when { enum WHEN code; - char *str; + char *command; + char *str; }; diff --git a/src/interfaces/ecpg/test/Makefile b/src/interfaces/ecpg/test/Makefile index 164d4978c83..50768a6541b 100644 --- a/src/interfaces/ecpg/test/Makefile +++ b/src/interfaces/ecpg/test/Makefile @@ -1,16 +1,14 @@ all: test2 perftest test2: test2.c - gcc -g -I ../include -I ../../libpq -o test2 test2.c -L../lib -lecpg -L../../libpq -lpq -lcrypt + gcc -g -I ../include -I ../../libpq -o test2 test2.c -L../lib -lecpg -L../../libpq -lpq -lcrypt --static test2.c: test2.pgc - ecpg test2.pgc + ../preproc/ecpg test2.pgc perftest: perftest.c - gcc -g -I ../include -I ../../libpq -o perftest perftest.c -L../lib -lecpg -L../../libpq -lpq -lcrypt + gcc -g -I ../include -I ../../libpq -o perftest perftest.c -L../lib -lecpg -L../../libpq -lpq -lcrypt --static perftest.c: perftest.pgc - ecpg perftest.pgc + ../preproc/ecpg perftest.pgc clean: /bin/rm test2 test2.c perftest perftest.c log - -dep depend: diff --git a/src/interfaces/ecpg/test/perftest.pgc b/src/interfaces/ecpg/test/perftest.pgc index 46df24b4d52..45ca62abfe1 100644 --- a/src/interfaces/ecpg/test/perftest.pgc +++ b/src/interfaces/ecpg/test/perftest.pgc @@ -16,7 +16,8 @@ print_result(long sec, long usec, char *text) usec+=1000000; } printf("I needed %ld seconds and %ld microseconds for the %s test.\n", sec, usec, text); - exec sql vacuum analyze; + exec sql vacuum; + sleep(1); } int @@ -27,9 +28,9 @@ exec sql begin declare section; exec sql end declare section; struct timeval tvs, tve; - exec sql connect 'mm'; + exec sql connect mm; - exec sql create table perftest1(number int4, ascii char16); + exec sql create table perftest1(number int4, ascii char(16)); exec sql create unique index number1 on perftest1(number); @@ -100,6 +101,16 @@ exec sql end declare section; print_result(tve.tv_sec - tvs.tv_sec, tve.tv_usec - tvs.tv_usec, "update"); + gettimeofday(&tvs, NULL); + + exec sql delete from perftest2; + + exec sql commit; + + gettimeofday(&tve, NULL); + + print_result(tve.tv_sec - tvs.tv_sec, tve.tv_usec - tvs.tv_usec, "delete"); + exec sql drop index number2; exec sql drop table perftest2; diff --git a/src/interfaces/ecpg/test/test2.pgc b/src/interfaces/ecpg/test/test2.pgc index 6500ba89a94..e0bdac9287a 100644 --- a/src/interfaces/ecpg/test/test2.pgc +++ b/src/interfaces/ecpg/test/test2.pgc @@ -2,8 +2,6 @@ exec sql include header_test; -extern void ECPGdebug(int n, FILE *dbgs); - static int not_found = 0; static void set_not_found(void) @@ -18,39 +16,53 @@ exec sql begin declare section; struct personal_struct { varchar name[8]; struct birth_struct { long born; short age; - } birth; + } birth; } personal; + struct personal_indicator { short name; + struct birth_indicator { short born; + int age; + } ind_birth; + } ind_personal; + long ind_married; + char married[9]="a"; exec sql end declare section; - char msg[128]; + char msg[128], command[128]; FILE *dbgs; if ((dbgs = fopen("log", "w")) != NULL) - ECPGdebug(1, dbgs); + ECPGdebug(1, dbgs); strcpy(msg, "connect"); - exec sql connect 'mm'; + exec sql connect mm; strcpy(msg, "create"); - exec sql create table meskes(name char8, born int4, age int2); + exec sql create table meskes(name char(8), born integer, age smallint, married char(8)); strcpy(msg, "insert"); - exec sql insert into meskes(name, born, age) values ('Petra', 19661202, 31); - exec sql insert into meskes(name, born, age) values ('Michael', 19660117, 32); + exec sql insert into meskes(name, born, age, married) values ('Petra', 19661202, 31, '19900404'); + exec sql insert into meskes(name, born, age, married) values ('Michael', 19660117, 32, '19900404'); exec sql insert into meskes(name, born, age) values ('Carsten', 19910103, 7); exec sql insert into meskes(name, born, age) values ('Marc', 19930907, 4); - exec sql insert into meskes(name, born, age) values ('Chris', 19970923, 0); + + sprintf(command, "insert into meskes(name, born, age) values ('Chris', 19970923, 0)"); + strcpy(msg, "execute"); + exec sql execute immediate :command; + + strcpy(msg, "commit"); + exec sql commit; strcpy(msg, "declare"); exec sql declare cur cursor for - select name, born, age from meskes; + select name, born, age, married from meskes; + strcpy(msg, "open"); exec sql open cur; while (not_found == 0) { strcpy(msg, "fetch"); - exec sql fetch cur into :personal; + exec sql fetch cur into :personal:ind_personal, :married:ind_married; if (not_found == 0) - printf ("%8.8s was born %d (age = %d)\n", personal.name.arr, personal.birth.born, personal.birth.age); + printf ("%8.8s was born %d (age = %d) %s%s\n", personal.name.arr, personal.birth.born, personal.birth.age, ind_married ? "" : "and married ", ind_married ? "" : married); } strcpy(msg, "close"); @@ -63,7 +75,7 @@ exec sql end declare section; exec sql commit; if (dbgs != NULL) - fclose(dbgs); + fclose(dbgs); return (0); } -- GitLab