From 64e35e1468e1b4d24c980e63e568cf1f54c7057b Mon Sep 17 00:00:00 2001
From: "Marc G. Fournier" <scrappy@hub.org>
Date: Tue, 21 Apr 1998 13:23:24 +0000
Subject: [PATCH] Upgrade ECPG to 2.0

Michael Meskes <meskes@topsystem.de>
---
 src/interfaces/ecpg/ChangeLog               |   40 +
 src/interfaces/ecpg/TODO                    |   32 +-
 src/interfaces/ecpg/include/Makefile        |   12 +-
 src/interfaces/ecpg/include/ecpglib.h       |    5 -
 src/interfaces/ecpg/include/ecpgtype.h      |    3 +-
 src/interfaces/ecpg/lib/Makefile.in         |    5 +-
 src/interfaces/ecpg/lib/ecpglib.c           |  143 +-
 src/interfaces/ecpg/preproc/Makefile        |   28 +-
 src/interfaces/ecpg/preproc/c_keywords.c    |   63 +
 src/interfaces/ecpg/preproc/ecpg.c          |   27 +-
 src/interfaces/ecpg/preproc/ecpg_keywords.c |   60 +
 src/interfaces/ecpg/preproc/extern.h        |   12 +
 src/interfaces/ecpg/preproc/keywords.c      |  242 ++
 src/interfaces/ecpg/preproc/pgc.l           |  565 ++-
 src/interfaces/ecpg/preproc/preproc.y       | 4242 +++++++++++++++++--
 src/interfaces/ecpg/preproc/type.c          |   88 +-
 src/interfaces/ecpg/preproc/type.h          |    5 +-
 src/interfaces/ecpg/test/Makefile           |   10 +-
 src/interfaces/ecpg/test/perftest.pgc       |   17 +-
 src/interfaces/ecpg/test/test2.pgc          |   40 +-
 20 files changed, 5000 insertions(+), 639 deletions(-)
 create mode 100644 src/interfaces/ecpg/preproc/c_keywords.c
 create mode 100644 src/interfaces/ecpg/preproc/ecpg_keywords.c
 create mode 100644 src/interfaces/ecpg/preproc/keywords.c

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