Upgrade ECPG to 2.0
authorMarc G. Fournier
Tue, 21 Apr 1998 13:23:24 +0000 (13:23 +0000)
committerMarc G. Fournier
Tue, 21 Apr 1998 13:23:24 +0000 (13:23 +0000)
Michael Meskes 

20 files changed:
src/interfaces/ecpg/ChangeLog
src/interfaces/ecpg/TODO
src/interfaces/ecpg/include/Makefile
src/interfaces/ecpg/include/ecpglib.h
src/interfaces/ecpg/include/ecpgtype.h
src/interfaces/ecpg/lib/Makefile.in
src/interfaces/ecpg/lib/ecpglib.c
src/interfaces/ecpg/preproc/Makefile
src/interfaces/ecpg/preproc/c_keywords.c [new file with mode: 0644]
src/interfaces/ecpg/preproc/ecpg.c
src/interfaces/ecpg/preproc/ecpg_keywords.c [new file with mode: 0644]
src/interfaces/ecpg/preproc/extern.h
src/interfaces/ecpg/preproc/keywords.c [new file with mode: 0644]
src/interfaces/ecpg/preproc/pgc.l
src/interfaces/ecpg/preproc/preproc.y
src/interfaces/ecpg/preproc/type.c
src/interfaces/ecpg/preproc/type.h
src/interfaces/ecpg/test/Makefile
src/interfaces/ecpg/test/perftest.pgc
src/interfaces/ecpg/test/test2.pgc

index f20a717dbc4883e94bbebac933109061b7359b88..e06e4ffa73d7a22e9ae685dad5f59b9e82dd2e30 100644 (file)
@@ -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).
+
index 4c7c15f40f0a886d55d1094d185eeb66421717c9..c74c273274651f77017d2dfba1bc054c08823f0c 100644 (file)
@@ -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 
+
 ::=
+      
+    | 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.
index bfb6840b68c2ac0985961c55a3233fbd6bdbd7d8..caa13a8d9162c69d4e97c1a012fec985c5934f53 100644 (file)
@@ -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:
index 6dd2f92aebfd9fcffdaa461370b39f6aa26487fd..b828763fa36940cfdecb169da8a1db41f1e03517 100644 (file)
@@ -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 *);
 
index 30b1f557138ade2f11349b839772a79e67517924..54442e6e3dd6a4bea08b1aee8d3ae5dcc56e10cc 100644 (file)
@@ -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)
index f4039edf99cef72c7abc7a965a96505e79295913..926b3b546964a7bbb3d42a45764f825754a52599 100644 (file)
@@ -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)
index 87b2350bdb0207265d17b87222f298404ee7baa0..815f4bff0bc719290ac393292107880770b7b3be 100644 (file)
@@ -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)
 {
index 04d6ccb597b75ab3837b7ece84a89651bcb31cd0..73596c05fdd95b8a763d8c9ce9bcec47f3296272 100644 (file)
@@ -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 (file)
index 0000000..d50de7b
--- /dev/null
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * keywords.c--
+ *   lexical token lookup for reserved words in postgres embedded SQL
+ *
+ *-------------------------------------------------------------------------
+ */
+#include 
+#include 
+
+#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);
+}
index a97b74e748bf5d6df420d1424e36aa90e00ab96f..e156468f2b8f9875d20df64ab3b7cda72831c8fb 100644 (file)
@@ -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 \n#include \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 (file)
index 0000000..6fea8a4
--- /dev/null
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ *
+ * keywords.c--
+ *   lexical token lookup for reserved words in postgres embedded SQL
+ *
+ *-------------------------------------------------------------------------
+ */
+#include 
+#include 
+
+#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);
+}
index 53da42cfb12409497164da702ceecc71133549d3..96c7bc0ed30cde097edc5870b8320c7ffa5b38f9 100644 (file)
@@ -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 (file)
index 0000000..4d8722f
--- /dev/null
@@ -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 
+#include 
+
+#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);
+}
index 8fc341400ea421bc95d7488f1d2beed266a41889..669da63a06825d13d6c52b893891d0cd75ac5443 100644 (file)
@@ -1,6 +1,8 @@
-/* Copyright comment! */
+/* This is a modified version of src/backend/parser/scan.l */
 %{
 #include "config.h"
+
+#include 
 #include 
 #include 
 #if defined(HAVE_STRING_H)
 #else
 #include 
 #endif
+#include 
 
+#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:
+ *   binary numeric string - thomas 1997-11-16
+ *   extended C-style comments - tgl 1997-07-12
+ *   delimited identifiers (double-quoted identifiers) - tgl 1997-10-27
+ *   hexadecimal numeric string - thomas 1997-11-16
+ *   numeric strings with embedded minus sign - tgl 1997-09-05
+ *   quoted strings - tgl 1997-07-30
+ *
+ * The "extended comment" syntax closely resembles allowable operator syntax.
+ * So, when in condition , 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
+ */
+
 %%
-{exec}{ws}{sql} { BEGIN SQL; dbg(SQL_START); return SQL_START; }
-";"       { BEGIN C; dbg(SQL_SEMI); return SQL_SEMI; }
-{abort}       { dbg(SQL_ABORT); return SQL_ABORT; }
-{begin}       { dbg(SQL_BEGIN); return SQL_BEGIN; }
-{end}     { dbg(SQL_END); return SQL_END; }
-{declare}     { dbg(SQL_DECLARE); return SQL_DECLARE; }
-{execute}     { dbg(SQL_EXECUTE); return SQL_EXECUTE; }
-{immediate}   { dbg(SQL_IMMEDIATE); return SQL_IMMEDIATE; }
-{section}     { dbg(SQL_SECTION); return SQL_SECTION; }
-{connect}     { dbg(SQL_CONNECT); return SQL_CONNECT; }
-{open}        { dbg(SQL_OPEN); return SQL_OPEN; }
-{commit}      { dbg(SQL_COMMIT); return SQL_COMMIT; }
-{release}     { dbg(SQL_RELEASE); return SQL_RELEASE; }
-{work}        { dbg(SQL_WORK); return SQL_WORK; }
-{fetch}       { dbg(SQL_FETCH); return SQL_FETCH; }
-{rollback}        { dbg(SQL_ROLLBACK); return SQL_ROLLBACK; }
-{whenever}        { dbg(SQL_WHENEVER); return SQL_WHENEVER; }
-{sqlerror}        { dbg(SQL_SQLERROR); return SQL_SQLERROR; }
-{sqlprint}        { dbg(SQL_SQLPRINT); return SQL_SQLPRINT; }
-{not}{ws}{found}  { dbg(SQL_NOT_FOUND); return SQL_NOT_FOUND; }
-{continue}        { dbg(SQL_CONTINUE); return SQL_CONTINUE; }
-{into}        { dbg(SQL_INTO); return SQL_INTO; }
-{in}      { dbg(SQL_IN); return SQL_IN; }
-{goto}        { dbg(SQL_GOTO); return SQL_GOTO; }
-{go}{ws}{to}  { dbg(SQL_GOTO); return SQL_GOTO; }
-{stop}        { dbg(SQL_STOP); return SQL_STOP; }
-{do}      { dbg(SQL_DO); return SQL_DO; }
-{from}        { dbg(SQL_FROM); return SQL_FROM; }
-{transaction} { dbg(SQL_TRANSACTION); return SQL_TRANSACTION; }
-{vacuum}      { dbg(SQL_VACUUM); return SQL_VACUUM; }
-
-
-{exec}{ws}{sql}{ws}{include}    { BEGIN(incl); }
-{ws}     /* eat the whitespace */
+{comment}     { /* ignore */ }
+
+{xcline}      { /* ignore */ }
+
+{xcstar}   |
+{xcstart}     { BEGIN(xc); }
+
+{xcstop}   { BEGIN(SQL); }
+
+{xcinside} { /* ignore */ }
+
+{xbstart}     {
+                   BEGIN(xb);
+                   llen = 0;
+                   *literal = '\0';
+               }
+{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);
+               }
+{xhinside} |
+{xbinside} {
+                   if ((llen+yyleng) > (MAX_PARSE_BUFFER - 1))
+                       yyerror("ERROR: quoted string parse buffer exceeded");
+                   memcpy(literal+llen, yytext, yyleng+1);
+                   llen += yyleng;
+               }
+{xhcat}        |
+{xbcat}        {
+               }
+
+{xhstart}     {
+                   BEGIN(xh);
+                   llen = 0;
+                   *literal = '\0';
+               }
+{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);
+               }
+
+{xqstart}     {
+                   BEGIN(xq);
+                   llen = 0;
+                   *literal = '\0';
+               }
+{xqstop}   {
+                   BEGIN(SQL);
+                   yylval.str = strdup(scanstr(literal));
+                   return (SCONST);
+               }
+{xqdouble} |
+{xqinside} {
+                   if ((llen+yyleng) > (MAX_PARSE_BUFFER - 1))
+                       yyerror("ERROR: quoted string parse buffer exceeded");
+                   memcpy(literal+llen, yytext, yyleng+1);
+                   llen += yyleng;
+               }
+{xqembedded} {
+                   if ((llen+yyleng-1) > (MAX_PARSE_BUFFER - 1))
+                       yyerror("ERROR: quoted string parse buffer exceeded");
+                   memcpy(literal+llen, yytext, yyleng+1);
+                   *(literal+llen) = '\'';
+                   llen += yyleng;
+               }
+
+{xqliteral} {
+                   if ((llen+yyleng-1) > (MAX_PARSE_BUFFER - 1))
+                       yyerror("ERROR: quoted string parse buffer exceeded");
+                   memcpy(literal+llen, yytext, yyleng+1);
+                   llen += yyleng;
+               }
+{xqcat}        {
+               }
+
+
+{xdstart}     {
+                   BEGIN(xd);
+                   llen = 0;
+                   *literal = '\0';
+               }
+{xdstop}   {
+                   BEGIN(SQL);
+                   yylval.str = strdup(literal);
+                   return (IDENT);
+               }
+{xdinside} {
+                   if ((llen+yyleng) > (MAX_PARSE_BUFFER - 1))
+                       yyerror("ERROR: quoted string parse buffer exceeded");
+                   memcpy(literal+llen, yytext, yyleng+1);
+                   llen += yyleng;
+               }
+
+
+{space}*   { /* ignore */ }
+{xmstop}   {
+                   BEGIN(SQL);
+                   return (yytext[0]);
+               }
+
+
+{typecast}            {   return TYPECAST; }
+
+{self}/-[\.0-9]       {
+                   return (yytext[0]);
+               }
+{self}            {   return (yytext[0]); }
+{operator}/-[\.0-9]   {
+                   yylval.str = strdup((char*)yytext);
+                   return (Op);
+               }
+{operator}        {
+                   if (strcmp((char*)yytext,"!=") == 0)
+                       yylval.str = strdup("<>"); /* compatability */
+                   else
+                       yylval.str = strdup((char*)yytext);
+                   return (Op);
+               }
+{param}           {
+                   yylval.ival = atoi((char*)&yytext[1]);
+                   return (PARAM);
+               }
+
+{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);
+               }
+
+{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);
+                       }
+                   }
+               }
+{space}           { /* ignore */ }
+";"                   { BEGIN C; return SQL_SEMI; }
+{other}           { return (yytext[0]); }
+
+{exec}{space}{sql}      { BEGIN SQL; return SQL_START; }
+{identifier}    {
+                   ScanKeyword     *keyword;
+
+                   keyword = ScanCKeywordLookup((char*)yytext);
+                   if (keyword != NULL) {
+                       return (keyword->value);
+                   }
+                   else
+                   {
+                       yylval.str = strdup((char*)yytext);
+                       return (IDENT);
+                   }
+               }
+";"                 { return(';'); }
+{space}     { ECHO; }
+\{         { return('{'); }
+\}         { return('}'); }
+\[         { return('['); }
+\]         { return(']'); }
+\=         { return('='); }
+{other}         { return (S_ANYTHING); }
+{exec}{space}{sql}{space}{include}  { BEGIN(incl); }
+{space}      /* eat the whitespace */
 [^ \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;
            }
 ";"      { 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; }
-{ws}      ; 
-{symbol}       { dbg(S_SYMBOL); return S_SYMBOL; }
-{label}            { dbg(S_LABEL); return S_LABEL; }
-
-"!<"      { dbg(S_SYMBOL); return S_SYMBOL; }
-"!>"      { dbg(S_SYMBOL); return S_SYMBOL; }
-"!^"      { dbg(S_SYMBOL); return S_SYMBOL; }
-"!|"      { dbg(S_SYMBOL); return S_SYMBOL; }
-"!~"      { dbg(S_SYMBOL); return S_SYMBOL; }
-"!~*"     { dbg(S_SYMBOL); return S_SYMBOL; }
-"#<"      { dbg(S_SYMBOL); return S_SYMBOL; }
-"#<="     { dbg(S_SYMBOL); return S_SYMBOL; }
-"#<>"     { dbg(S_SYMBOL); return S_SYMBOL; }
-"#="      { dbg(S_SYMBOL); return S_SYMBOL; }
-"#>"      { dbg(S_SYMBOL); return S_SYMBOL; }
-"#>="     { dbg(S_SYMBOL); return S_SYMBOL; }
-"&&"      { dbg(S_SYMBOL); return S_SYMBOL; }
-"&<"      { dbg(S_SYMBOL); return S_SYMBOL; }
-"&>"      { dbg(S_SYMBOL); return S_SYMBOL; }
-"<<"      { dbg(S_SYMBOL); return S_SYMBOL; }
-"<="      { dbg(S_SYMBOL); return S_SYMBOL; }
-"<===>"       { dbg(S_SYMBOL); return S_SYMBOL; }
-"<>"      { dbg(S_SYMBOL); return S_SYMBOL; }
-""     { dbg(S_SYMBOL); return S_SYMBOL; }
-"===>"        { dbg(S_SYMBOL); return S_SYMBOL; }
-"===`"        { dbg(S_SYMBOL); return S_SYMBOL; }
-"=|="     { dbg(S_SYMBOL); return S_SYMBOL; }
-">="      { dbg(S_SYMBOL); return S_SYMBOL; }
-">>"      { dbg(S_SYMBOL); return S_SYMBOL; }
-"@@"      { dbg(S_SYMBOL); return S_SYMBOL; }
-"|/"      { dbg(S_SYMBOL); return S_SYMBOL; }
-"||/"     { dbg(S_SYMBOL); return S_SYMBOL; }
-"~*"      { dbg(S_SYMBOL); return S_SYMBOL; }
-"~="      { 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('*'); }
-
-":"       { dbg(:); return ':'; }
-"::"      { dbg(SQL_CONV); return SQL_CONV; }
-
-{ws}           { ECHO; }
-.          { dbg(.); return S_ANYTHING; }
 <>            { if (yy_buffer == NULL)
                yyterminate();
              else
@@ -260,4 +516,3 @@ int yywrap(void)
 { 
     return 1;
 }
-
index d3a2fc6d9ba47df7d70d57db02a15f504cf9e5cd..6eb4a88acd56d05759ecb202c9d924ece117facd 100644 (file)
@@ -3,23 +3,25 @@
 #include 
 #include 
 #include 
+#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  SQL_START SQL_SEMI SQL_STRING SQL_INTO SQL_IN
-%token  SQL_BEGIN SQL_END SQL_DECLARE SQL_SECTION SQL_INCLUDE 
-%token  SQL_CONNECT SQL_OPEN SQL_EXECUTE SQL_IMMEDIATE
-%token  SQL_COMMIT SQL_ROLLBACK SQL_RELEASE SQL_WORK SQL_WHENEVER
-%token  SQL_SQLERROR SQL_NOT_FOUND SQL_CONTINUE SQL_FROM SQL_FETCH
-%token  SQL_DO SQL_GOTO SQL_SQLPRINT SQL_STOP SQL_CONV
-%token  SQL_ABORT SQL_TRANSACTION SQL_VACUUM
-
-%token  S_SYMBOL S_LENGTH S_ANYTHING S_LABEL
-%token  S_VARCHAR S_VARCHAR2
-%token  S_EXTERN S_STATIC S_AUTO S_CONST S_REGISTER S_STRUCT
-%token  S_UNSIGNED S_SIGNED
-%token  S_LONG S_SHORT S_INT S_CHAR S_FLOAT S_DOUBLE S_BOOL
-%token  '[' ']' ';' ',' '{' '}' '=' '*' '(' ')'
-
-%type  type type_detailed varchar_type simple_type struct_type string_type
-/* % type  array_type pointer_type */
-%type  symbol label transactionstmt
-%type  maybe_storage_clause varchar_tag db_name cursor
-%type  simple_tag char_tag
-%type  index length
-%type  action
-%type  canything sqlanything both_anything vartext sqlcommand
-%type  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     IDENT SCONST Op
+%token    ICONST PARAM
+%token    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     Iconst Sconst TransactionStmt CreateStmt UserId
+%type     CreateAsElement OptCreateAs CreateAsList CreateAsStmt
+%type     OptArchiveType OptInherit key_reference key_action
+%type      key_match constraint_expr ColLabel SpecialRuleRelation
+%type     ColId default_expr ColQualifier columnDef ColQualList
+%type      ColConstraint ColConstraintElem default_list
+%type      OptTableElementList OptTableElement TableConstraint
+%type      ConstraintElem key_actions constraint_list TypeId
+%type      res_target_list res_target_el res_target_list2
+%type      res_target_el2 opt_id relation_name database_name
+%type      access_method attr_name class index_name name func_name
+%type      file_name recipe_name AexprConst ParamNo NumConst TypeId
+%type     in_expr_nodes not_in_expr_nodes a_expr b_expr
+%type     opt_indirection expr_list extract_list extract_arg
+%type     position_list position_expr substr_list substr_from
+%type     trim_list in_expr substr_for not_in_expr attr attrs
+%type     Typename Array Generic Numeric generic opt_float opt_numeric
+%type     opt_decimal Character character opt_varying opt_charset
+%type     opt_collate Datetime datetime opt_timezone opt_interval
+%type     numeric a_expr_or_null row_expr row_descriptor row_list
+%type     SelectStmt union_clause select_list SubSelect result
+%type     opt_table opt_union opt_unique sort_clause sortby_list
+%type     sortby OptUseOp opt_inh_star relation_name_list name_list
+%type     group_clause groupby_list groupby having_clause from_clause
+%type     from_list from_val join_expr join_outer join_spec join_list
+%type     join_using where_clause relation_expr opt_array_bounds
+%type     nest_array_bounds opt_column_list insert_rest InsertStmt
+%type      columnList DeleteStmt LockStmt UpdateStmt CursorStmt
+%type      NotifyStmt columnElem copy_dirn OptimizableStmt
+%type      copy_delimiter ListenStmt CopyStmt copy_file_name opt_binary
+%type      opt_with_copy FetchStmt opt_direction fetch_how_many opt_portal_name
+%type      ClosePortalStmt DestroyStmt VacuumStmt opt_verbose
+%type      opt_analyze opt_va_list va_list ExplainStmt index_params
+%type      index_list func_index index_elem opt_type opt_class access_method_clause
+%type      index_opt_unique IndexStmt set_opt func_return def_rest
+%type      func_args_list func_args opt_with ProcedureStmt def_arg
+%type      def_elem def_list definition def_name def_type DefineStmt
+%type      opt_instead event event_object OptStmtMulti OptStmtBlock
+%type      OptStmtList RuleStmt opt_column opt_name oper_argtypes
+%type      MathOp RemoveOperStmt RemoveFuncStmt aggr_argtype
+%type      RemoveAggrStmt remove_type RemoveStmt ExtendStmt RecipeStmt
+%type      RemoveOperStmt RenameStmt all_Op user_valid_clause
+%type      VariableSetStmt var_value zone_value VariableShowStmt
+%type      VariableResetStmt AddAttrStmt alter_clause DropUserStmt
+%type      user_passwd_clause user_createdb_clause
+%type      user_createuser_clause user_group_list user_group_clause
+%type      CreateUserStmt AlterUserStmt CreateSeqStmt OptSeqList
+%type      OptSeqElem TriggerForSpec TriggerForOpt TriggerForType
+%type     TriggerFuncArgs DropTrigStmt TriggerOneEvent TriggerEvents
+%type      TriggerActionTime CreateTrigStmt DropPLangStmt PLangTrusted
+%type      CreatePLangStmt IntegerOnly TriggerFuncArgs TriggerFuncArg
+%type      ViewStmt LoadStmt CreatedbStmt opt_database location
+%type      DestroydbStmt ClusterStmt grantee RevokeStmt
+%type     GrantStmt privileges operation_commalist operation
+
+%type     ECPGWhenever ECPGConnect db_name ECPGOpen open_opts
+%type     indicator ECPGExecute c_expr
+%type     stmt symbol
+
+%type   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  [*]
+ *
+ *****************************************************************************/
+
+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 
+ *
+ *****************************************************************************/
+
+ClosePortalStmt:  CLOSE opt_id
+               {
+                   $$ = make2_str("close", $2);
+               }
+       ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY :
+ *             COPY [BINARY]  FROM/TO
+ *             [USING DELIMITERS ]
+ *
+ *****************************************************************************/
+
+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  [,  ..  ]
+ *
+ *****************************************************************************/
+
+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  ]
+ *
+ *****************************************************************************/
+
+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  on 
+ *               using  "(" ( with )+ ")" [with
+ *               ]
+ *
+ * [where ] 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  [where ]
+ *
+ *****************************************************************************/
+
+ExtendStmt:  EXTEND INDEX index_name where_clause
+               {
+                   $$ = make3_str("extend index", $3, $4);
+               }
+       ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             execute recipe 
+ *
+ *****************************************************************************/
+
+RecipeStmt:  EXECUTE RECIPE recipe_name
+               {
+                   $$ = make2_str("execute recipe", $3);
+               }
+       ;
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             define function 
+ *                    (language = , returntype = 
+ *                     [, arch_pct = ]
+ *                     [, disk_pct = ]
+ *                     [, byte_pct = ]
+ *                     [, perbyte_cpu = ]
+ *                     [, percall_cpu = ]
+ *                     [, iscachable])
+ *                     [arg is ( { , })]
+ *                     as 
+ *
+ *****************************************************************************/
+
+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 
+ *             (REMOVE FUNCTION "funcname" (arg1, arg2, ...))
+ *     remove aggregate 
+ *             (REMOVE AGGREGATE "aggname" "aggtype")
+ *     remove operator 
+ *             (REMOVE OPERATOR "opname" (leftoperand_typ rightoperand_typ))
+ *     remove type 
+ *             (REMOVE TYPE "typename")
+ *     remove rule 
+ *             (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  in  [*] to 
+ *             rename  to 
+ *
+ *****************************************************************************/
+
+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 
+ *
+ *     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   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  '('target-list ')' [where  ]
+ *
+ *****************************************************************************/
+
+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  on 
+ *
+ *****************************************************************************/
+
+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, ")
+               }
+       | '(' 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, ")
+               }
+       | '(' 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, "
+               }
+       | 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, "
+               }
+       | 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($1.name, $1.typ);
-   free((void *)$1.name);
+        new_variable($1.name, $1.typ);
+        free((void *)$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';
-
-    $$ = name;
-}
+            | '=' {fwrite(yytext, yyleng, 1, yyout);} vartext;
 
 type : maybe_storage_clause type_detailed { $$ = $2; };
 type_detailed : varchar_type { $$ = $1; }
@@ -325,65 +3714,64 @@ type_detailed : varchar_type { $$ = $1; }
          | struct_type {$$ = $1; };
 
 varchar_type : varchar_tag symbol index {
-    if ($ndexsize>3 > 0L)
-   fprintf(yyout, "struct varchar_%s { int len; char arr[%ld]; } %s", $2, $3, $2);
+    if ($val>3 > 0L)
+   fprintf(yyout, "struct varchar_%s { int len; char arr[%d]; } %s", $2, $3, $2);
     else
-   fprintf(yyout, "struct varchar_%s { int len; char arr[]; } %s", $2, $2);
+   fprintf(yyout, "struct varchar_%s { int len; char arr[]; } %s", $2, $2);
     if (struct_level == 0)
     {
-   $$.name = $2;
-   $$.typ = ECPGmake_varchar_type(ECPGt_varchar, $ndexsize>3);
+   $$.name = $2;
+   $$.typ = ECPGmake_varchar_type(ECPGt_varchar, $val>3);
     }
     else
-   ECPGmake_record_member($2, ECPGmake_varchar_type(ECPGt_varchar, $>3), &(record_member_list[struct_level-1]));
+   ECPGmake_record_member($2, ECPGmake_varchar_type(ECPGt_varchar, $>3), &(record_member_list[struct_level-1]));
 }
 
-varchar_tag : S_VARCHAR { $$ = $1; }
-            | S_VARCHAR2 { $$ = $1; };
+varchar_tag: S_VARCHAR /*| S_VARCHAR2 */;
 
 simple_type : simple_tag symbol {
-    fprintf(yyout, "%s %s", ECPGtype_name($1), $2);
+    fprintf(yyout, "%s %s", ECPGtype_name($1), $2);
     if (struct_level == 0)
     {
-   $$.name = $2;
+   $$.name = $2;
    $$.typ = ECPGmake_simple_type($1, 1);
     }
     else
-        ECPGmake_record_member($2, ECPGmake_simple_type($1, 1), &(record_member_list[struct_level-1]));
+        ECPGmake_record_member($2, ECPGmake_simple_type($1, 1), &(record_member_list[struct_level-1]));
 }
 
 string_type : char_tag symbol index {
-    if ($ndexsize>3 > 0L)
-       fprintf(yyout, "%s %s [%ld]", ECPGtype_name($1), $2, $>3);
+    if ($val>3 > 0L)
+       fprintf(yyout, "%s %s [%d]", ECPGtype_name($1), $2, $>3);
     else
-       fprintf(yyout, "%s %s []", ECPGtype_name($1), $2);
+       fprintf(yyout, "%s %s []", ECPGtype_name($1), $2);
     if (struct_level == 0)
     {
-   $$.name = $2;
-   $$.typ = ECPGmake_simple_type($1, $ndexsize>3);
+   $$.name = $2;
+   $$.typ = ECPGmake_simple_type($1, $val>3);
     }
     else
-   ECPGmake_record_member($2, ECPGmake_simple_type($1, $>3), &(record_member_list[struct_level-1]));
+   ECPGmake_record_member($2, ECPGmake_simple_type($1, $>3), &(record_member_list[struct_level-1]));
 }
    |   char_tag '*' symbol {
-    fprintf(yyout, "%s *%s", ECPGtype_name($1), $3);
+    fprintf(yyout, "%s *%s", ECPGtype_name($1), $3);
     if (struct_level == 0)
     {
-   $$.name = $3;
+   $$.name = $3;
    $$.typ = ECPGmake_simple_type($1, 0);
     }
     else
-   ECPGmake_record_member($3, ECPGmake_simple_type($1, 0), &(record_member_list[struct_level-1]));
+   ECPGmake_record_member($3, ECPGmake_simple_type($1, 0), &(record_member_list[struct_level-1]));
 }
    |   char_tag symbol {
-    fprintf(yyout, "%s %s", ECPGtype_name($1), $2);
+    fprintf(yyout, "%s %s", ECPGtype_name($1), $2);
     if (struct_level == 0)
     {
-   $$.name = $2;
+   $$.name = $2;
    $$.typ = ECPGmake_simple_type($1, 1);
     }
     else
-        ECPGmake_record_member($2, ECPGmake_simple_type($1, 1), &(record_member_list[struct_level-1]));
+        ECPGmake_record_member($2, ECPGmake_simple_type($1, 1), &(record_member_list[struct_level-1]));
 }
 
 char_tag : S_CHAR { $$ = ECPGt_char; }
@@ -391,46 +3779,46 @@ char_tag : S_CHAR { $$ = ECPGt_char; }
 
 /*
 array_type : simple_tag symbol index {
-    if ($ndexsize>3 > 0)
-       fprintf(yyout, "%s %s [%ld]", ECPGtype_name($1), $2, $>3);
+    if ($val>3 > 0)
+       fprintf(yyout, "%s %s [%ld]", ECPGtype_name($1), $2, $>3);
     else
-       fprintf(yyout, "%s %s []", ECPGtype_name($1), $2);
+       fprintf(yyout, "%s %s []", ECPGtype_name($1), $2);
     if (struct_level == 0)
     {
-   $$.name = $2;
-   $$.typ = ECPGmake_array_type(ECPGmake_simple_type($1), $ndexsize>3);
+   $$.name = $2;
+   $$.typ = ECPGmake_array_type(ECPGmake_simple_type($1), $val>3);
     }
     else
-   ECPGmake_record_member($2, ECPGmake_array_type(ECPGmake_simple_type($1), $>3), &(record_member_list[struct_level-1]));
+   ECPGmake_record_member($2, ECPGmake_array_type(ECPGmake_simple_type($1), $>3), &(record_member_list[struct_level-1]));
 }
 
 pointer_type : simple_tag '*' symbol {
-    fprintf(yyout, "%s * %s", ECPGtype_name($1), $3);
+    fprintf(yyout, "%s * %s", ECPGtype_name($1), $3);
     if (struct_level == 0)
     {
-   $$.name = $3;
+   $$.name = $3;
    $$.typ = ECPGmake_array_type(ECPGmake_simple_type($1), 0);
     }
     else
-   ECPGmake_record_member($3, ECPGmake_array_type(ECPGmake_simple_type($1), 0), &(record_member_list[struct_level-1]));
+   ECPGmake_record_member($3, ECPGmake_array_type(ECPGmake_simple_type($1), 0), &(record_member_list[struct_level-1]));
 }
 */
 
 s_struct : S_STRUCT symbol {
     struct_level++;
-    fprintf(yyout, "struct %s {", $2);
+    fprintf(yyout, "struct %s {", $2);
 }
 
 struct_type : s_struct '{' variable_declarations '}' symbol {
     struct_level--;
     if (struct_level == 0)
     {
-   $$.name = $5;
+   $$.name = $5;
    $$.typ = ECPGmake_record_type(record_member_list[struct_level]);
     }
     else
-   ECPGmake_record_member($5, ECPGmake_record_type(record_member_list[struct_level]), &(record_member_list[struct_level-1])); 
-    fprintf(yyout, "} %s", $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 ']' { $$ = $2; }
-   | '[' ']' { $$ = 0L; }
-
-length : S_LENGTH { $$ = atol(yytext); }
-
-sqlinclude : SQL_START SQL_INCLUDE { fprintf(yyout, "#include \""); }
-   filename SQL_SEMI { fprintf(yyout, ".h\""); output_line_number(); };
+index : '[' Iconst ']' { $$ = atol($2); }
+   | '[' ']' { $$ = 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($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", $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\");", $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)
            {
-               $$="begin";
-           }
-       | transend
-           {
-               $$="end";
-           }
-       | transabort
-           {
-               $$="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 = $5.code;
-   when_error.str = $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 = $3.code;
+   when_error.command = $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 = $5.code;
-   when_nf.str=$5.str;
-   fprintf(yyout, "; */\n");
+   | SQL_WHENEVER NOT SQL_FOUND action {
+   when_nf.code = $4.code;
+   when_nf.command = $4.command;
+   $$ = make3_str("/* exec sql whenever not found ", $4.str, "; */\n");
 }
 
 action : SQL_CONTINUE {
    $$.code = W_NOTHING;
-   $$.str = NULL;
-   fprintf(yyout, "continue");
+   $$.command = NULL;
+   $$.str = "continue";
 }
        | SQL_SQLPRINT {
    $$.code = W_SQLPRINT;
-   $$.str = NULL;
-   fprintf(yyout, "sqlprint");
+   $$.command = NULL;
+   $$.str = "sqlprint";
 }
        | SQL_STOP {
    $$.code = W_STOP;
-   $$.str = NULL;
-   fprintf(yyout, "stop");
+   $$.command = NULL;
+   $$.str = "stop";
 }
-       | SQL_GOTO label {
-   $$.code = W_GOTO;
-   $$.str = $2;
-   fprintf(yyout, "goto %s", $2);
+       | SQL_GOTO name {
+        $$.code = W_GOTO;
+        $$.command = $2;
+   $$.str = make2_str("goto ", $2);
 }
-       | SQL_GOTO symbol {
+       | SQL_GO TO name {
         $$.code = W_GOTO;
-        $$.str = $2;
-   fprintf(yyout, "goto %s", $2);
+        $$.command = $3;
+   $$.str = make2_str("goto ", $3);
 }
-       | SQL_DO symbol '(' {
-   do_str = (char *) mm_alloc(do_length = strlen($2) + 4);
-   sprintf(do_str, "%s (", $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)]=')';
    $$.code = W_DO;
-   $$.str = do_str;
-   fprintf(yyout, "do %s", do_str);
+   $$.command = do_str;
+   $$.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';
-
-    $$ = 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, "
+               }
+       | 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, "
+               }
+       | 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);
index 074e8b74933dbc182831f1e9d38bc127bb0e59f3..2b43fa9976aa31e2f8a97a1467b1b09e618b1d79 100644 (file)
@@ -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);
    }
 }
 
index 69bea16b52dcf92642273ae92a1916ae29fcff83..92d87055ee9961f18001a574060012d8a4570eaa 100644 (file)
@@ -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;
 };
index 164d4978c83bca37beff161be3fafdcb0f738419..50768a6541bf6214afc17fbd3a6699f69fa8a32e 100644 (file)
@@ -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:
index 46df24b4d52698b376adfa763ddf1b4ae84c2e97..45ca62abfe156bc209faa118508484d53e607f4c 100644 (file)
@@ -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;
index 6500ba89a94a5a2b68dcfb8088ddb6bb9115e7bd..e0bdac9287a3a294d1d9ab6282f055792fa3a98e 100644 (file)
@@ -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);
 }