]> granicus.if.org Git - postgresql/blobdiff - src/interfaces/ecpg/preproc/preproc.y
*** empty log message ***
[postgresql] / src / interfaces / ecpg / preproc / preproc.y
index 7f2949373f7d672669d0d49adc2159aaa1f89553..0c492febf015490705312f59f4ce8e30ebbdd78e 100644 (file)
@@ -1,28 +1,36 @@
 /* Copyright comment */
 %{
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
+#include <stdarg.h>
+
+#include "postgres.h"
+#include "access/htup.h"
 #include "catalog/catname.h"
 #include "utils/numeric.h"
+#include "utils/memutils.h"
+#include "storage/bufpage.h"
 
-#include "type.h"
 #include "extern.h"
 
 #ifdef MULTIBYTE
 #include "mb/pg_wchar.h"
 #endif
 
-#define STRUCT_DEPTH 128
+#define EMPTY make_str("")
 
 /*
  * Variables containing simple states.
  */
-static int     struct_level = 0;
-static char    errortext[128];
-static int      QueryIsRule = 0, ForUpdateNotAllowed = 0;
-static enum ECPGttype actual_type[STRUCT_DEPTH];
+int    struct_level = 0;
+char   errortext[128];
+static char    *connection = NULL;
+static char *descriptor_name = NULL;
+static char *descriptor_index= NULL;
+static int      QueryIsRule = 0, ForUpdateNotAllowed = 0, FoundInto = 0;
+static int     FoundSort = 0;
+static int     initializer = 0;
+static struct this_type actual_type[STRUCT_DEPTH];
 static char     *actual_storage[STRUCT_DEPTH];
+static char     *actual_startline[STRUCT_DEPTH];
 
 /* temporarily store struct members while creating the data structure */
 struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH] = { NULL };
@@ -30,22 +38,328 @@ struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH] = { NULL };
 struct ECPGtype ecpg_no_indicator = {ECPGt_NO_INDICATOR, 0L, {NULL}};
 struct variable no_indicator = {"no_indicator", &ecpg_no_indicator, 0, NULL};
 
+struct ECPGtype ecpg_query = {ECPGt_char_variable, 0L, {NULL}};
+
+/* variable lookup */
+
+static struct variable * find_variable(char * name);
+static void whenever_action(int mode);
+
+/*
+ * Handle parsing errors and warnings
+ */
+void
+mmerror(enum errortype type, char * error)
+{
+
+    switch(type)
+    {
+       case ET_WARN: 
+               fprintf(stderr, "%s:%d: WARNING: %s\n", input_filename, yylineno, error); 
+               break;
+       case ET_ERROR:
+               fprintf(stderr, "%s:%d: ERROR: %s\n", input_filename, yylineno, error);
+               ret_value = PARSE_ERROR;
+               break;
+       case ET_FATAL:
+               fprintf(stderr, "%s:%d: ERROR: %s\n", input_filename, yylineno, error);
+               exit(PARSE_ERROR);
+    }
+}
+
 /*
  * Handle the filename and line numbering.
  */
 char * input_filename = NULL;
 
-static void
+void
 output_line_number()
 {
     if (input_filename)
        fprintf(yyout, "\n#line %d \"%s\"\n", yylineno, input_filename);
 }
 
+static void
+output_simple_statement(char *cmd)
+{
+       fputs(cmd, yyout);
+       output_line_number();
+        free(cmd);
+}
+
+/*
+ * assignment handling function (descriptor)
+ */
+static struct assignment *assignments;
+
+static void push_assignment(char *var,char *value)
+{
+       struct assignment *new=(struct assignment *)mm_alloc(sizeof(struct assignment));
+       
+       new->next=assignments;
+       new->variable=mm_alloc(strlen(var)+1);
+       strcpy(new->variable,var);
+       new->value=mm_alloc(strlen(value)+1);
+       strcpy(new->value,value);
+       assignments=new;
+}
+
+static void drop_assignments(void)
+{      while (assignments)
+       {       struct assignment *old_head=assignments;
+               assignments=old_head->next;
+               free(old_head->variable);
+               free(old_head->value);
+               free(old_head);
+       }
+}
+
+/* XXX: these should be more accurate (consider ECPGdump_a_* ) */
+static void ECPGnumeric_lvalue(FILE *f,char *name)
+{      const struct variable *v=find_variable(name);
+       switch(v->type->typ)
+       {       case ECPGt_short:
+               case ECPGt_int: 
+               case ECPGt_long:
+               case ECPGt_unsigned_short:
+               case ECPGt_unsigned_int:
+               case ECPGt_unsigned_long:
+                       fputs(name,yyout);
+                       break;
+               default:
+                       snprintf(errortext,sizeof errortext,"variable %s: numeric type needed"
+                                       ,name);
+                       mmerror(ET_ERROR,errortext);
+                       break;
+       }
+}
+
+static void ECPGstring_buffer(FILE *f,char *name)
+{   const struct variable *v=find_variable(name);
+       switch(v->type->typ)
+       {       case ECPGt_varchar:
+                       fprintf(yyout,"%s.arr",name);
+                       break;
+                       
+               case ECPGt_char:
+               case ECPGt_unsigned_char:
+                       fputs(name,yyout);
+                       break;
+                       
+               default:
+                       snprintf(errortext,sizeof errortext,"variable %s: character type needed"
+                                       ,name);
+                       mmerror(ET_ERROR,errortext);
+                       break;
+       }
+}
+
+static void ECPGstring_length(FILE *f,char *name)
+{   const struct variable *v=find_variable(name);
+       switch(v->type->typ)
+       {       case ECPGt_varchar:
+               case ECPGt_char:
+               case ECPGt_unsigned_char:
+                   if (!v->type->size) 
+                   {   snprintf(errortext,sizeof errortext,"zero length char variable %s for assignment",
+                                                                                       v->name);
+                       mmerror(ET_ERROR,errortext);
+                   }
+                       fprintf(yyout,"%ld",v->type->size);
+                       break;
+               default:
+                       snprintf(errortext,sizeof errortext,"variable %s: character type needed"
+                                       ,name);
+                       mmerror(ET_ERROR,errortext);
+                       break;
+       }
+}
+
+static void ECPGdata_assignment(char *variable,char *index_plus_1)
+{      const struct variable *v=find_variable(variable);
+       fprintf(yyout,"\t\t\tif (!PQgetisnull(ECPGresult,0,(%s)-1))\n",index_plus_1);
+       switch(v->type->typ)
+       {       case ECPGt_short:
+               case ECPGt_int: /* use the same conversion as ecpglib does */
+               case ECPGt_long:
+                       fprintf(yyout,"\t\t\t\t%s=strtol(PQgetvalue(ECPGresult,0,(%s)-1),NULL,10);\n"
+                                                                                       ,variable,index_plus_1);
+                       break;
+               case ECPGt_unsigned_short:
+               case ECPGt_unsigned_int:
+               case ECPGt_unsigned_long:
+                       fprintf(yyout,"\t\t\t\t%s=strtoul(PQgetvalue(ECPGresult,0,(%s)-1),NULL,10);\n"
+                                                                                       ,variable,index_plus_1);
+                       break;
+               case ECPGt_float:
+               case ECPGt_double:
+                       fprintf(yyout,"\t\t\t\t%s=strtod(PQgetvalue(ECPGresult,0,(%s)-1),NULL);\n"
+                                                                                       ,variable,index_plus_1);
+                       break;
+                       
+               case ECPGt_bool:
+                       fprintf(yyout,"\t\t\t\t%s=PQgetvalue(ECPGresult,0,(%s)-1)[0]=='t';\n"
+                                                                                       ,variable,index_plus_1);
+                       break;
+               
+               case ECPGt_varchar:
+                       fprintf(yyout,"\t\t\t{\tstrncpy(%s.arr,PQgetvalue(ECPGresult,0,(%s)-1),%ld);\n"
+                                                                                       ,variable,index_plus_1,v->type->size);
+                       fprintf(yyout,"\t\t\t\t%s.len=strlen(PQgetvalue(ECPGresult,0,(%s)-1)\n"
+                                                                                       ,variable,index_plus_1);
+                       fprintf(yyout,"\t\t\t\tif (%s.len>%ld) { %s.len=%ld; sqlca.sqlwarn[0]=sqlca.sqlwarn[1]='W'; }\n"
+                                                                                       ,variable,v->type->size,variable,v->type->size);
+                       fputs("\t\t\t}\n",yyout);                                                                                       
+                       break;
+                       
+               case ECPGt_char:
+               case ECPGt_unsigned_char:
+                   if (!v->type->size) 
+                   {   snprintf(errortext,sizeof errortext,"zero length char variable %s for DATA assignment",
+                                                                                       v->name);
+                       mmerror(ET_ERROR,errortext);
+                   }
+                       fprintf(yyout,"\t\t\t{\tstrncpy(%s,PQgetvalue(ECPGresult,0,(%s)-1),%ld);\n"
+                                                                                       ,variable,index_plus_1,v->type->size);
+                       fprintf(yyout,"\t\t\t\tif (strlen(PQgetvalue(ECPGresult,0,(%s)-1))>=%ld)\n"
+                               "\t\t\t\t{ %s[%ld]=0; sqlca.sqlwarn[0]=sqlca.sqlwarn[1]='W'; }\n"
+                                                                                       ,index_plus_1,v->type->size,variable,v->type->size-1);
+                       fputs("\t\t\t}\n",yyout);                                                                                       
+                       break;
+                       
+               default:
+                       snprintf(errortext,sizeof errortext,"unknown variable type %d for DATA assignment"
+                                       ,v->type->typ);
+                       mmerror(ET_ERROR,errortext);
+                       break;
+       }
+}
+
+static void
+output_get_descr_header(char *desc_name)
+{      struct assignment *results;
+       fprintf(yyout,"{\tPGresult *ECPGresult=ECPGresultByDescriptor(%d, \"%s\");\n"
+                                                                                                       ,yylineno,desc_name);
+       fputs("\tif (ECPGresult)\n\t{",yyout);
+       for (results=assignments;results!=NULL;results=results->next)
+       {       if (!strcasecmp(results->value,"count"))
+               {       fputs("\t\t",yyout);
+                       ECPGnumeric_lvalue(yyout,results->variable);
+                       fputs("=PQnfields(ECPGresult);\n",yyout);
+               }
+               else
+               {       snprintf(errortext,sizeof errortext,"unknown descriptor header item '%s'",results->value);
+                       mmerror(ET_WARN,errortext);
+               }
+       }
+       drop_assignments();
+       fputs("}",yyout);
+       
+       whenever_action(2|1);
+}
+
+static void
+output_get_descr(char *desc_name)
+{      struct assignment *results;
+       int flags=0;
+       const int DATA_SEEN=1;
+       const int INDICATOR_SEEN=2;
+       
+       fprintf(yyout,"{\tPGresult *ECPGresult=ECPGresultByDescriptor(%d, \"%s\");\n"
+                                                                                                       ,yylineno,desc_name);
+       fputs("\tif (ECPGresult)\n\t{",yyout);
+       fprintf(yyout,"\tif (PQntuples(ECPGresult)<1) ECPGraise(%d,ECPG_NOT_FOUND);\n",yylineno);
+       fprintf(yyout,"\t\telse if (%s<1 || %s>PQnfields(ECPGresult))\n"
+                       "\t\t\tECPGraise(%d,ECPG_INVALID_DESCRIPTOR_INDEX);\n"
+                               ,descriptor_index,descriptor_index,yylineno);
+       fputs("\t\telse\n\t\t{\n",yyout);
+       for (results=assignments;results!=NULL;results=results->next)
+       {       if (!strcasecmp(results->value,"type"))
+               {       fputs("\t\t\t",yyout);
+                       ECPGnumeric_lvalue(yyout,results->variable);
+                       fprintf(yyout,"=ECPGDynamicType(PQftype(ECPGresult,(%s)-1));\n",descriptor_index);
+               }
+               else if (!strcasecmp(results->value,"datetime_interval_code"))
+               {       fputs("\t\t\t",yyout);
+                       ECPGnumeric_lvalue(yyout,results->variable);
+                       fprintf(yyout,"=ECPGDynamicType_DDT(PQftype(ECPGresult,(%s)-1));\n",descriptor_index);
+               }
+               else if (!strcasecmp(results->value,"length"))
+               {       fputs("\t\t\t",yyout);
+                       ECPGnumeric_lvalue(yyout,results->variable);
+                       fprintf(yyout,"=PQfmod(ECPGresult,(%s)-1)-VARHDRSZ;\n",descriptor_index);
+               }
+               else if (!strcasecmp(results->value,"octet_length"))
+               {       fputs("\t\t\t",yyout);
+                       ECPGnumeric_lvalue(yyout,results->variable);
+                       fprintf(yyout,"=PQfsize(ECPGresult,(%s)-1);\n",descriptor_index);
+               }
+               else if (!strcasecmp(results->value,"returned_length")
+                       || !strcasecmp(results->value,"returned_octet_length"))
+               {       fputs("\t\t\t",yyout);
+                       ECPGnumeric_lvalue(yyout,results->variable);
+                       fprintf(yyout,"=PQgetlength(ECPGresult,0,(%s)-1);\n",descriptor_index);
+               }
+               else if (!strcasecmp(results->value,"precision"))
+               {       fputs("\t\t\t",yyout);
+                       ECPGnumeric_lvalue(yyout,results->variable);
+                       fprintf(yyout,"=PQfmod(ECPGresult,(%s)-1)>>16;\n",descriptor_index);
+               }
+               else if (!strcasecmp(results->value,"scale"))
+               {       fputs("\t\t\t",yyout);
+                       ECPGnumeric_lvalue(yyout,results->variable);
+                       fprintf(yyout,"=(PQfmod(ECPGresult,(%s)-1)-VARHDRSZ)&0xffff;\n",descriptor_index);
+               }
+               else if (!strcasecmp(results->value,"nullable"))
+               {       mmerror(ET_WARN,"nullable is always 1");
+                       fputs("\t\t\t",yyout);
+                       ECPGnumeric_lvalue(yyout,results->variable);
+                       fprintf(yyout,"=1;\n");
+               }
+               else if (!strcasecmp(results->value,"key_member"))
+               {       mmerror(ET_WARN,"key_member is always 0");
+                       fputs("\t\t\t",yyout);
+                       ECPGnumeric_lvalue(yyout,results->variable);
+                       fprintf(yyout,"=0;\n");
+               }
+               else if (!strcasecmp(results->value,"name"))
+               {       fputs("\t\t\tstrncpy(",yyout);
+                       ECPGstring_buffer(yyout,results->variable);
+                       fprintf(yyout,",PQfname(ECPGresult,(%s)-1),",descriptor_index);
+                       ECPGstring_length(yyout,results->variable);
+                       fputs(");\n",yyout);
+               }
+               else if (!strcasecmp(results->value,"indicator"))
+               {       flags|=INDICATOR_SEEN;
+                   fputs("\t\t\t",yyout);
+                       ECPGnumeric_lvalue(yyout,results->variable);
+                       fprintf(yyout,"=-PQgetisnull(ECPGresult,0,(%s)-1);\n",descriptor_index);
+               }
+               else if (!strcasecmp(results->value,"data"))
+               {       flags|=DATA_SEEN;
+                   ECPGdata_assignment(results->variable,descriptor_index);
+               }
+               else
+               {       snprintf(errortext,sizeof errortext,"unknown descriptor header item '%s'",results->value);
+                       mmerror(ET_WARN,errortext);
+               }
+       }
+       if (flags==DATA_SEEN) /* no indicator */
+       {       fprintf(yyout,"\t\t\tif (PQgetisnull(ECPGresult,0,(%s)-1))\n"
+                                       "\t\t\t\tECPGraise(%d,ECPG_MISSING_INDICATOR);\n"
+                               ,descriptor_index,yylineno);
+       }
+       drop_assignments();
+       fputs("\t\t}\n\t}\n",yyout);
+       
+       whenever_action(2|1);
+}
+
 /*
  * store the whenever action here
  */
-static struct when when_error, when_nf;
+struct when when_error, when_nf, when_warn;
 
 static void
 print_action(struct when *w)
@@ -70,18 +384,26 @@ print_action(struct when *w)
 static void
 whenever_action(int mode)
 {
-       if (mode == 1 && when_nf.code != W_NOTHING)
+       if ((mode&1) ==  1 && when_nf.code != W_NOTHING)
        {
                output_line_number();
                fprintf(yyout, "\nif (sqlca.sqlcode == ECPG_NOT_FOUND) ");
                print_action(&when_nf);
        }
+       if (when_warn.code != W_NOTHING)
+        {
+               output_line_number();
+                fprintf(yyout, "\nif (sqlca.sqlwarn[0] == 'W') ");
+               print_action(&when_warn);
+        }
        if (when_error.code != W_NOTHING)
         {
                output_line_number();
                 fprintf(yyout, "\nif (sqlca.sqlcode < 0) ");
                print_action(&when_error);
         }
+       if ((mode&2) == 2)
+               fputc('}', yyout);
        output_line_number();
 }
 
@@ -111,8 +433,6 @@ new_variable(const char * name, struct ECPGtype * type)
     return(p);
 }
 
-static struct variable * find_variable(char * name);
-
 static struct variable *
 find_struct_member(char *name, char *str, struct ECPGstruct_member *members)
 {
@@ -136,7 +456,8 @@ find_struct_member(char *name, char *str, struct ECPGstruct_member *members)
                           case ECPGt_array:
                                return(new_variable(name, ECPGmake_array_type(members->typ->u.element, members->typ->size)));
                           case ECPGt_struct:
-                               return(new_variable(name, ECPGmake_struct_type(members->typ->u.members)));
+                          case ECPGt_union:
+                               return(new_variable(name, ECPGmake_struct_type(members->typ->u.members, members->typ->typ)));
                           default:
                                return(new_variable(name, ECPGmake_simple_type(members->typ->typ, members->typ->size)));
                        }
@@ -167,14 +488,39 @@ find_struct(char * name, char *next)
     *next = '\0';
     p = find_variable(name);
 
-    /* restore the name, we will need it later on */
-    *next = c;
     if (c == '-')
     {
+       if (p->type->typ != ECPGt_struct && p->type->typ != ECPGt_union)
+        {
+                sprintf(errortext, "variable %s is not a pointer", name);
+                mmerror(ET_FATAL, errortext);
+        }
+
+       if (p->type->u.element->typ  != ECPGt_struct && p->type->u.element->typ != ECPGt_union)
+        {
+                sprintf(errortext, "variable %s is not a pointer to a structure or a union", name);
+                mmerror(ET_FATAL, errortext);
+        }
+
+       /* restore the name, we will need it later on */
+       *next = c;
        next++;
+
        return find_struct_member(name, next, p->type->u.element->u.members);
     }
-    else return find_struct_member(name, next, p->type->u.members);
+    else
+    {
+       if (p->type->typ != ECPGt_struct && p->type->typ != ECPGt_union)
+       {
+               sprintf(errortext, "variable %s is neither a structure nor a union", name);
+               mmerror(ET_FATAL, errortext);
+       }
+
+       /* restore the name, we will need it later on */
+       *next = c;
+
+       return find_struct_member(name, next, p->type->u.members);
+    }
 }
 
 static struct variable *
@@ -209,7 +555,7 @@ find_variable(char * name)
     if (p == NULL)
     {
        sprintf(errortext, "The variable %s is not declared", name);
-       yyerror(errortext);
+       mmerror(ET_FATAL, errortext);
     }
 
     return(p);
@@ -315,6 +661,7 @@ check_indicator(struct ECPGtype *var)
                        break;
 
                case ECPGt_struct:
+               case ECPGt_union:
                        for (p = var->u.members; p; p = p->next)
                                check_indicator(p->typ);
                        break;
@@ -323,32 +670,72 @@ check_indicator(struct ECPGtype *var)
                        check_indicator(var->u.element);
                        break;
                default: 
-                       yyerror ("indicator variable must be integer type");
+                       mmerror(ET_ERROR, "indicator variable must be integer type");
                        break;
        }
 }
 
-static char *
-make1_str(const char *str)
-{
-        char * res_str = (char *)mm_alloc(strlen(str) + 1);
+/*
+ * descriptor name lookup
+ */
+static struct descriptor *descriptors;
 
-       strcpy(res_str, str);
-       return res_str;
+static void add_descriptor(char *name,char *connection)
+{
+       struct descriptor *new=(struct descriptor *)mm_alloc(sizeof(struct descriptor));
+       
+       new->next=descriptors;
+       new->name=mm_alloc(strlen(name)+1);
+       strcpy(new->name,name);
+       if (connection) 
+       {       new->connection=mm_alloc(strlen(connection)+1);
+               strcpy(new->connection,connection);
+       }
+       else new->connection=connection;
+       descriptors=new;
 }
 
-static char *
-make2_str(char *str1, char *str2)
-{ 
-       char * res_str  = (char *)mm_alloc(strlen(str1) + strlen(str2) + 1);
+static void drop_descriptor(char *name,char *connection)
+{      struct descriptor *i;
+       struct descriptor **lastptr=&descriptors;
+       for (i=descriptors;i;lastptr=&i->next,i=i->next)
+       {       if (!strcmp(name,i->name))
+               {       if ((!connection && !i->connection) 
+                               || (connection && i->connection 
+                                       && !strcmp(connection,i->connection)))
+                       {       *lastptr=i->next;
+                               if (i->connection) free(i->connection);
+                               free(i->name);
+                               free(i);
+                               return;
+                       }
+               }
+       }
+       snprintf(errortext,sizeof errortext,"unknown descriptor %s",name);
+       mmerror(ET_WARN,errortext);
+}
 
-       strcpy(res_str, str1);
-       strcat(res_str, str2);
-       free(str1);
-       free(str2);
-       return(res_str);
+static struct descriptor *lookup_descriptor(char *name,char *connection)
+{      struct descriptor *i;
+       for (i=descriptors;i;i=i->next)
+       {       if (!strcmp(name,i->name))
+               {       if ((!connection && !i->connection) 
+                               || (connection && i->connection 
+                                       && !strcmp(connection,i->connection)))
+                       {       return i;
+                       }
+               }
+       }
+       snprintf(errortext,sizeof errortext,"unknown descriptor %s",name);
+       mmerror(ET_WARN,errortext);
+       return NULL;
 }
 
+/*
+ * string concatenation
+ */
+
 static char *
 cat2_str(char *str1, char *str2)
 { 
@@ -363,109 +750,59 @@ cat2_str(char *str1, char *str2)
 }
 
 static char *
-make3_str(char *str1, char *str2, 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);
-       free(str1);
-       free(str2);
-       free(str3);
-        return(res_str);
-}    
+cat_str(int count, ...)
+{
+       va_list         args;
+       int             i; 
+       char            *res_str;
 
-static char *
-cat3_str(char *str1, char *str2, 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);
-       free(str1);
-       free(str2);
-       free(str3);
-        return(res_str);
-}    
+       va_start(args, count);
 
-static char *
-make4_str(char *str1, char *str2, char *str3, 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);
-       free(str1);
-       free(str2);
-       free(str3);
-       free(str4);
-        return(res_str);
+       res_str = va_arg(args, char *);
+
+       /* now add all other strings */
+       for (i = 1; i < count; i++)
+               res_str = cat2_str(res_str, va_arg(args, char *));
+
+       va_end(args);
+
+       return(res_str);
 }
 
 static char *
-cat4_str(char *str1, char *str2, char *str3, 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);
-       free(str1);
-       free(str2);
-       free(str3);
-       free(str4);
-        return(res_str);
+make_str(const char *str)
+{
+        char * res_str = (char *)mm_alloc(strlen(str) + 1);
+
+       strcpy(res_str, str);
+       return res_str;
 }
 
 static char *
-make5_str(char *str1, char *str2, char *str3, char *str4, 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);
+make2_str(char *str1, char *str2)
+{ 
+       char * res_str  = (char *)mm_alloc(strlen(str1) + strlen(str2) + 1);
+
+       strcpy(res_str, str1);
+       strcat(res_str, str2);
        free(str1);
        free(str2);
-       free(str3);
-       free(str4);
-       free(str5);
-        return(res_str);
-}    
+       return(res_str);
+}
 
 static char *
-cat5_str(char *str1, char *str2, char *str3, char *str4, 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, " ");
+make3_str(char *str1, char *str2, 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);
-       strcat(res_str, " ");
-       strcat(res_str, str4);
-       strcat(res_str, " ");
-       strcat(res_str, str5);
        free(str1);
        free(str2);
        free(str3);
-       free(str4);
-       free(str5);
-        return(res_str);
-}    
+       return(res_str);
+}
 
 static char *
 make_name(void)
@@ -477,17 +814,35 @@ make_name(void)
        return(name);
 }
 
+static char *
+hashline_number()
+{
+    if (input_filename)
+    {
+       char* line = mm_alloc(strlen("\n#line %d \"%s\"\n") + 21 + strlen(input_filename));
+       sprintf(line, "\n#line %d \"%s\"\n", yylineno, input_filename);
+
+       return line;
+    }
+
+    return EMPTY;
+}
+
 static void
 output_statement(char * stmt, int mode)
 {
        int i, j=strlen(stmt);
 
-       fputs("ECPGdo(__LINE__, \"", yyout);
+       fprintf(yyout, "{ ECPGdo(__LINE__, %s, \"", connection ? connection : "NULL");
 
        /* do this char by char as we have to filter '\"' */
-       for (i = 0;i < j; i++)
+       for (i = 0;i < j; i++) {
                if (stmt[i] != '\"')
                        fputc(stmt[i], yyout);
+               else
+                       fputs("\\\"", yyout);
+       }
+
        fputs("\", ", yyout);
 
        /* dump variables to C file*/
@@ -495,69 +850,201 @@ output_statement(char * stmt, int mode)
        fputs("ECPGt_EOIT, ", yyout);
        dump_variables(argsresult, 1);
        fputs("ECPGt_EORT);", yyout);
+       mode |= 2;
        whenever_action(mode);
        free(stmt);
+       if (connection != NULL)
+               free(connection);
 }
 
-%}
-
-%union {
-       double                  dval;
-        int                     ival;
-       char *                  str;
-       struct when             action;
-       struct index            index;
-       int                     tagname;
-       struct this_type        type;
-       enum ECPGttype          type_enum;
-}
+static void
+output_statement_desc(char * stmt, int mode)
+{
+       int i, j=strlen(stmt);
 
-/* special embedded SQL token */
-%token         SQL_BREAK SQL_CALL SQL_CONNECT SQL_CONNECTION SQL_CONTINUE
-%token         SQL_DISCONNECT SQL_FOUND SQL_GO SQL_GOTO
-%token         SQL_IDENTIFIED SQL_IMMEDIATE SQL_INDICATOR SQL_OPEN SQL_RELEASE
-%token         SQL_SECTION SQL_SEMI SQL_SQLERROR SQL_SQLPRINT SQL_START
-%token         SQL_STOP SQL_WHENEVER
+       fprintf(yyout, "{ ECPGdo_descriptor(__LINE__, %s, \"%s\", \"", 
+               connection ? connection : "NULL", descriptor_name);
 
-/* C token */
-%token         S_ANYTHING S_AUTO S_BOOL S_CHAR S_CONST S_DOUBLE S_ENUM S_EXTERN
-%token         S_FLOAT S_INT S
-%token         S_LONG S_REGISTER S_SHORT S_SIGNED S_STATIC S_STRUCT 
-%token         S_UNSIGNED S_VARCHAR
+       /* do this char by char as we have to filter '\"' */
+       for (i = 0;i < j; i++) {
+               if (stmt[i] != '\"')
+                       fputc(stmt[i], yyout);
+               else
+                       fputs("\\\"", yyout);
+       }
 
-/* I need this and don't know where it is defined inside the backend */
-%token         TYPECAST
+       fputs("\");", yyout);
 
-/* Keywords (in SQL92 reserved words) */
-%token  ABSOLUTE, ACTION, ADD, ALL, ALTER, AND, ANY, AS, ASC,
-                BEGIN_TRANS, BETWEEN, BOTH, BY,
-                CASCADE, CASE, CAST, CHAR, CHARACTER, CHECK, CLOSE,
-               COALESCE, 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,
-                ELSE, END_TRANS, EXCEPT, EXECUTE, EXISTS, EXTRACT,
-                FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL,
-                GRANT, GROUP, HAVING, HOUR_P,
-                IN, INNER_P, INSENSITIVE, INSERT, INTERSECT, INTERVAL, INTO, IS,
-                ISOLATION, JOIN, KEY, LANGUAGE, LEADING, LEFT, LEVEL, LIKE, LOCAL,
-                MATCH, MINUTE_P, MONTH_P, NAMES,
-                NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULLIF, NULL_P, NUMERIC,
-                OF, ON, ONLY, OPTION, OR, ORDER, OUTER_P,
-                PARTIAL, POSITION, PRECISION, PRIMARY, PRIOR, PRIVILEGES, PROCEDURE, PUBLIC,
-                READ, REFERENCES, RELATIVE, REVOKE, RIGHT, ROLLBACK,
-                SCROLL, SECOND_P, SELECT, SET, SUBSTRING,
-                TABLE, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE,
-               TO, TRAILING, TRANSACTION, TRIM, TRUE_P,
-                UNION, UNIQUE, UPDATE, USER, USING,
-                VALUES, VARCHAR, VARYING, VIEW,
-                WHEN, WHERE, WITH, WORK, YEAR_P, ZONE
+       mode |= 2;
+       whenever_action(mode);
+       free(stmt);
+       if (connection != NULL)
+               free(connection);
+       free(descriptor_name);
+}
 
-/* Keywords (in SQL3 reserved words) */
-%token  TRIGGER
+static struct typedefs *
+get_typedef(char *name)
+{
+       struct typedefs *this;
+
+       for (this = types; this && strcmp(this->name, name); this = this->next);
+       if (!this)
+       {
+               sprintf(errortext, "invalid datatype '%s'", name);
+               mmerror(ET_FATAL, errortext);
+       }
+
+       return(this);
+}
+
+static void
+adjust_array(enum ECPGttype type_enum, int *dimension, int *length, int type_dimension, int type_index, bool pointer)
+{
+       if (type_index >= 0) 
+       {
+               if (*length >= 0)
+                       mmerror(ET_FATAL, "No multi-dimensional array support");
+
+               *length = type_index;
+       }
+                      
+       if (type_dimension >= 0)
+       {
+               if (*dimension >= 0 && *length >= 0)
+                       mmerror(ET_FATAL, "No multi-dimensional array support");
+
+               if (*dimension >= 0)
+                       *length = *dimension;
+
+               *dimension = type_dimension;
+       }
+
+       if (*length >= 0 && *dimension >= 0 && pointer)
+               mmerror(ET_FATAL, "No multi-dimensional array support");
+
+       switch (type_enum)
+       {
+          case ECPGt_struct:
+          case ECPGt_union:
+               /* pointer has to get dimension 0 */
+                if (pointer)
+               {
+                   *length = *dimension;
+                    *dimension = 0;
+               }
+
+                if (*length >= 0)
+                   mmerror(ET_FATAL, "No multi-dimensional array support for structures");
+
+                break;
+           case ECPGt_varchar:
+               /* pointer has to get dimension 0 */
+                if (pointer)
+                    *dimension = 0;
+
+                /* one index is the string length */
+                if (*length < 0)
+                {
+                   *length = *dimension;
+                   *dimension = -1;
+                }
+
+                break;
+           case ECPGt_char:
+           case ECPGt_unsigned_char:
+               /* pointer has to get length 0 */
+                if (pointer)
+                    *length=0;
+
+                /* one index is the string length */
+                if (*length < 0)
+                {
+                   *length = (*dimension < 0) ? 1 : *dimension;
+                   *dimension = -1;
+                }
+
+                break;
+           default:
+               /* a pointer has dimension = 0 */
+                if (pointer) {
+                    *length = *dimension;
+                   *dimension = 0;
+               }
+
+                if (*length >= 0)
+                   mmerror(ET_FATAL, "No multi-dimensional array support for simple data types");
+
+                break;
+       }
+}
+
+%}
+
+%union {
+       double                  dval;
+        int                     ival;
+       char *                  str;
+       struct when             action;
+       struct index            index;
+       int                     tagname;
+       struct this_type        type;
+       enum ECPGttype          type_enum;
+}
+
+/* special embedded SQL token */
+%token         SQL_ALLOCATE SQL_AT SQL_AUTOCOMMIT SQL_BOOL SQL_BREAK 
+%token         SQL_CALL SQL_CONNECT SQL_CONNECTION SQL_CONTINUE
+%token         SQL_DEALLOCATE SQL_DESCRIPTOR SQL_DISCONNECT SQL_ENUM 
+%token         SQL_FOUND SQL_FREE SQL_GET SQL_GO SQL_GOTO
+%token         SQL_IDENTIFIED SQL_INDICATOR SQL_INT SQL_LONG
+%token         SQL_OFF SQL_OPEN SQL_PREPARE SQL_RELEASE SQL_REFERENCE
+%token         SQL_SECTION SQL_SHORT SQL_SIGNED SQL_SQL 
+%token         SQL_SQLERROR SQL_SQLPRINT
+%token         SQL_SQLWARNING SQL_START SQL_STOP SQL_STRUCT SQL_UNSIGNED
+%token         SQL_VALUE SQL_VAR SQL_WHENEVER
+
+/* C token */
+%token         S_ANYTHING S_AUTO S_CONST S_EXTERN
+%token         S_REGISTER S_STATIC S_VOLATILE
+
+/* I need this and don't know where it is defined inside the backend */
+%token         TYPECAST
+
+/* Keywords (in SQL92 reserved words) */
+%token  ABSOLUTE, ACTION, ADD, ALL, ALTER, AND, ANY, AS, ASC,
+                BEGIN_TRANS, BETWEEN, BOTH, BY,
+                CASCADE, CASE, CAST, CHAR, CHARACTER, CHECK, CLOSE,
+               COALESCE, COLLATE, COLUMN, COMMIT, 
+                CONSTRAINT, CONSTRAINTS, CREATE, CROSS, CURRENT, CURRENT_DATE,
+                CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, CURSOR,
+                DAY_P, DEC, DECIMAL, DECLARE, DEFAULT, DELETE, DESC, DISTINCT, DOUBLE, DROP,
+                ELSE, END_TRANS, EXCEPT, EXECUTE, EXISTS, EXTRACT,
+                FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL,
+                GLOBAL, GRANT, GROUP, HAVING, HOUR_P,
+                IN, INNER_P, INSENSITIVE, INSERT, INTERSECT, INTERVAL, INTO, IS,
+                ISOLATION, JOIN, KEY, LANGUAGE, LEADING, LEFT, LEVEL, LIKE, LOCAL,
+                MATCH, MINUTE_P, MONTH_P, NAMES,
+                NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULLIF, NULL_P, NUMERIC,
+                OF, ON, ONLY, OPTION, OR, ORDER, OUTER_P,
+                PARTIAL, POSITION, PRECISION, PRIMARY, PRIOR, PRIVILEGES, PROCEDURE, PUBLIC,
+                READ, REFERENCES, RELATIVE, REVOKE, RIGHT, ROLLBACK,
+                SCROLL, SECOND_P, SELECT, SESSION_USER, SET, SUBSTRING,
+                TABLE, TEMP, TEMPORARY, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR,
+               TIMEZONE_MINUTE, TO, TRAILING, TRANSACTION, TRIM, TRUE_P,
+                UNION, UNIQUE, UPDATE, USER, USING,
+                VALUES, VARCHAR, VARYING, VIEW,
+                WHEN, WHERE, WITH, WORK, YEAR_P, ZONE
+
+/* Keywords (in SQL3 reserved words) */
+%token DEFERRABLE, DEFERRED,
+               IMMEDIATE, INITIALLY,
+               PENDANT,
+               RESTRICT,
+               TRIGGER
 
 /* Keywords (in SQL92 non-reserved words) */
-%token  TYPE_P
+%token  COMMITTED, SERIALIZABLE, TYPE_P
 
 /* Keywords for Postgres support (not in SQL92 reserved words)
  *
@@ -565,16 +1052,20 @@ output_statement(char * stmt, int mode)
  * when some sort of pg_privileges relation is introduced.
  * - Todd A. Brandys 1998-01-01?
  */
-%token  ABORT_TRANS, AFTER, AGGREGATE, ANALYZE, BACKWARD, BEFORE, BINARY,
-               CACHE, CLUSTER, COPY, CREATEDB, CREATEUSER, CYCLE,
-                DATABASE, DELIMITERS, DO, EACH, ENCODING, EXPLAIN, EXTEND,
+%token  ABORT_TRANS, ACCESS, AFTER, AGGREGATE, ANALYZE,
+               BACKWARD, BEFORE, BINARY,
+               CACHE, CLUSTER, COMMENT, COPY, CREATEDB, CREATEUSER, CYCLE,
+                DATABASE, DELIMITERS, DO,
+               EACH, ENCODING, EXCLUSIVE, EXPLAIN, EXTEND,
                 FORWARD, FUNCTION, HANDLER,
                 INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL,
-                LANCOMPILER, LISTEN, UNLISTEN, LOAD, LOCATION, LOCK_P, MAXVALUE, MINVALUE, MOVE,
+                LANCOMPILER, LIMIT, LISTEN, UNLISTEN, LOAD, LOCATION, LOCK_P,
+               MAXVALUE, MINVALUE, MODE, MOVE,
                 NEW,  NOCREATEDB, NOCREATEUSER, NONE, NOTHING, NOTIFY, NOTNULL,
-               OIDS, OPERATOR, PASSWORD, PROCEDURAL,
-                RECIPE, RENAME, RESET, RETURNS, ROW, RULE,
-                SERIAL, SEQUENCE, SETOF, SHOW, START, STATEMENT, STDIN, STDOUT, TRUSTED,
+               OFFSET, OIDS, OPERATOR, PASSWORD, PROCEDURAL,
+                RENAME, RESET, RETURNS, ROW, RULE,
+                SEQUENCE, SERIAL, SETOF, SHARE, SHOW, START, STATEMENT, STDIN, STDOUT, SYSID
+               TRUNCATE, TRUSTED,
                 UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
 
 /* Special keywords, not in the query language - see the "lex" file */
@@ -594,12 +1085,14 @@ output_statement(char * stmt, int mode)
 %nonassoc      LIKE
 %nonassoc      BETWEEN
 %nonassoc      IN
-%nonassoc      Op                              /* multi-character ops and user-defined operators */
+%left          Op                              /* multi-character ops and user-defined operators */
 %nonassoc      NOTNULL
 %nonassoc      ISNULL
+%nonassoc      NULL_P
 %nonassoc      IS
 %left          '+' '-'
-%left          '*' '/'
+%left          '*' '/' '%'
+%left          '^'
 %left          '|'                             /* this is the relation union op, not logical or */
 /* Unary Operators */
 %right         ':'
@@ -607,126 +1100,157 @@ output_statement(char * stmt, int mode)
 %right         UMINUS
 %left          '.'
 %left          '[' ']'
-%nonassoc      TYPECAST
+%left          TYPECAST
 %left          UNION INTERSECT EXCEPT
 
 %type  <str>   Iconst Fconst Sconst TransactionStmt CreateStmt UserId
 %type  <str>   CreateAsElement OptCreateAs CreateAsList CreateAsStmt
-%type  <str>   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 NumericOnly FloatOnly
+%type  <str>   OptInherit key_reference key_action comment_text
+%type  <str>    key_match ColLabel SpecialRuleRelation ColId columnDef
+%type  <str>    ColConstraint ColConstraintElem NumericOnly FloatOnly
 %type  <str>    OptTableElementList OptTableElement TableConstraint
-%type  <str>    ConstraintElem key_actions constraint_list ColPrimaryKey
-%type  <str>    res_target_list res_target_el res_target_list2
-%type  <str>    res_target_el2 opt_id relation_name database_name
+%type  <str>    ConstraintElem key_actions ColPrimaryKey ColQualList
+%type  <str>    target_list target_el update_target_list alias_clause
+%type  <str>    update_target_el 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 TypeId
-%type  <str>   in_expr_nodes not_in_expr_nodes a_expr b_expr
+%type  <str>    file_name AexprConst ParamNo TypeId c_expr ColQualListWithNull
+%type  <str>   in_expr_nodes a_expr b_expr TruncateStmt CommentStmt
 %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>   position_list substr_list substr_from alter_column_action
+%type  <str>   trim_list in_expr substr_for attr attrs drop_behavior
+%type  <str>   Typename SimpleTypename 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 SubSelect result
-%type  <str>   opt_table opt_union opt_unique sort_clause sortby_list
+%type  <str>   SelectStmt SubSelect result OptTemp OptTempType OptTempScope
+%type  <str>   opt_table opt_all sort_clause sortby_list ColQualifier
 %type  <str>   sortby OptUseOp opt_inh_star relation_name_list name_list
-%type  <str>   group_clause having_clause from_clause c_list 
-%type  <str>   from_list from_val join_expr join_outer join_spec join_list
-%type  <str>   join_using where_clause relation_expr row_op sub_type
+%type  <str>   group_clause having_clause from_clause opt_distinct
+%type  <str>   join_outer where_clause relation_expr sub_type
 %type  <str>   opt_column_list insert_rest InsertStmt OptimizableStmt
 %type  <str>    columnList DeleteStmt LockStmt UpdateStmt CursorStmt
-%type  <str>    NotifyStmt columnElem copy_dirn c_expr UnlistenStmt
+%type  <str>    NotifyStmt columnElem copy_dirn UnlistenStmt copy_null
 %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_with_copy FetchStmt direction fetch_how_many from_in
+%type  <str>    ClosePortalStmt DropStmt 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>    opt_instead event event_object RuleActionList opt_using
+%type  <str>   RuleActionStmtOrEmpty RuleActionMulti func_as
+%type  <str>    RuleStmt opt_column opt_name oper_argtypes sysid_clause
 %type  <str>    MathOp RemoveFuncStmt aggr_argtype for_update_clause
-%type  <str>    RemoveAggrStmt remove_type RemoveStmt ExtendStmt RecipeStmt
+%type  <str>    RemoveAggrStmt remove_type RemoveStmt ExtendStmt
 %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>    VariableResetStmt AlterTableStmt DropUserStmt from_list
 %type  <str>    user_passwd_clause user_createdb_clause opt_trans
-%type  <str>    user_createuser_clause user_group_list user_group_clause
+%type  <str>    user_createuser_clause user_list user_group_clause
 %type  <str>    CreateUserStmt AlterUserStmt CreateSeqStmt OptSeqList
 %type  <str>    OptSeqElem TriggerForSpec TriggerForOpt TriggerForType
-%type  <str>   DropTrigStmt TriggerOneEvent TriggerEvents
+%type  <str>   DropTrigStmt TriggerOneEvent TriggerEvents RuleActionStmt
 %type  <str>    TriggerActionTime CreateTrigStmt DropPLangStmt PLangTrusted
 %type  <str>    CreatePLangStmt IntegerOnly TriggerFuncArgs TriggerFuncArg
-%type  <str>    ViewStmt LoadStmt CreatedbStmt opt_database1 opt_database2 location
-%type  <str>    DestroydbStmt ClusterStmt grantee RevokeStmt encoding
+%type  <str>    ViewStmt LoadStmt CreatedbStmt createdb_opt_encoding
+%type  <str>   createdb_opt_location opt_encoding AlterTableStmt
+%type  <str>    DropdbStmt ClusterStmt grantee RevokeStmt table_expr
 %type  <str>   GrantStmt privileges operation_commalist operation
-%type  <str>   cursor_clause opt_cursor opt_readonly opt_of opt_lmode
+%type  <str>   opt_cursor opt_lmode ConstraintsSetStmt comment_tg
 %type  <str>   case_expr when_clause_list case_default case_arg when_clause
-%type  <str>    select_w_o_sort 
+%type  <str>    select_clause opt_select_limit select_limit_value
+%type  <str>    select_offset_value using_expr join_expr
+%type  <str>   using_list from_expr join_clause join_type
+%type  <str>   join_qual update_list join_clause join_clause_with_union
+%type  <str>   opt_level opt_lock lock_type users_in_new_group_clause
+%type  <str>    OptConstrFromTable comment_op ConstraintAttributeSpec
+%type  <str>    constraints_set_list constraints_set_namelist comment_fn
+%type  <str>   constraints_set_mode comment_type comment_cl comment_ag
+%type  <str>   ConstraintDeferrabilitySpec ConstraintTimeSpec 
+%type  <str>   CreateGroupStmt AlterGroupStmt DropGroupStmt
+%type  <str>   ColConstraintWithNull ColConstraintElemWithNull
+%type  <str>   join_expr_with_union
+/***
+#ifdef ENABLE_ORACLE_JOIN_SYNTAX
+%type  <str>   oracle_list oracle_expr oracle_outer
+#endif
+***/
 
-%type  <str>   ECPGWhenever ECPGConnect connection_target ECPGOpen open_opts
-%type  <str>   indicator ECPGExecute ecpg_expr dotext
-%type  <str>    storage_clause opt_initializer vartext c_anything blockstart
-%type  <str>    blockend variable_list variable var_anything do_anything
-%type  <str>   opt_pointer cvariable ECPGDisconnect dis_name
+%type  <str>   ECPGWhenever ECPGConnect connection_target ECPGOpen
+%type  <str>   indicator ECPGExecute ECPGPrepare ecpg_using
+%type  <str>    storage_clause opt_initializer c_anything blockstart
+%type  <str>    blockend variable_list variable c_thing c_term
+%type  <str>   opt_pointer cvariable ECPGDisconnect dis_name storage_modifier
 %type  <str>   stmt symbol opt_symbol ECPGRelease execstring server_name
-%type  <str>   connection_object opt_server opt_port c_thing
+%type  <str>   connection_object opt_server opt_port c_stuff opt_reference
 %type  <str>    user_name opt_user char_variable ora_user ident
-%type  <str>    db_prefix server opt_options opt_connection_name
-%type  <str>   ECPGSetConnection c_line cpp_line s_enum
-%type  <str>   enum_type
-
-%type  <type_enum> simple_type
+%type  <str>    db_prefix server opt_options opt_connection_name c_list
+%type  <str>   ECPGSetConnection cpp_line ECPGTypedef c_args
+%type  <str>   enum_type civariableonly ECPGCursorStmt ECPGDeallocate
+%type  <str>   ECPGFree ECPGDeclare ECPGVar opt_at enum_definition
+%type  <str>    struct_type s_struct declaration declarations variable_declarations
+%type  <str>    s_struct s_union union_type ECPGSetAutocommit on_off
+%type  <str>   ECPGAllocateDescr ECPGDeallocateDescr
+%type  <str>   ECPGGetDescriptor ECPGGetDescriptorHeader 
+%type  <str>   FetchDescriptorStmt
+
+%type  <type_enum> simple_type signed_type unsigned_type varchar_type
 
 %type  <type>  type
 
 %type  <action> action
 
-%type  <index> opt_array_bounds nest_array_bounds
+%type  <index> opt_array_bounds opt_type_array_bounds
 
+%type  <ival>  Iresult
 %%
 prog: statements;
 
 statements: /* empty */
        | statements statement
 
-statement: ecpgstart stmt SQL_SEMI
+statement: ecpgstart opt_at stmt ';'   { connection = NULL; }
+       | ecpgstart stmt ';'
        | ECPGDeclaration
        | c_thing                       { fprintf(yyout, "%s", $1); free($1); }
        | cpp_line                      { fprintf(yyout, "%s", $1); free($1); }
        | blockstart                    { fputs($1, yyout); free($1); }
        | blockend                      { fputs($1, yyout); free($1); }
 
-stmt:  AddAttrStmt                     { output_statement($1, 0); }
+opt_at:        SQL_AT connection_target        { connection = $2; }
+
+stmt:  AlterTableStmt                  { output_statement($1, 0); }
+               | AlterGroupStmt        { output_statement($1, 0); }
                | AlterUserStmt         { output_statement($1, 0); }
                | ClosePortalStmt       { output_statement($1, 0); }
+               | CommentStmt           { output_statement($1, 0); }
                | CopyStmt              { output_statement($1, 0); }
                | CreateStmt            { output_statement($1, 0); }
                | CreateAsStmt          { output_statement($1, 0); }
+               | CreateGroupStmt       { output_statement($1, 0); }
                | CreateSeqStmt         { output_statement($1, 0); }
                | CreatePLangStmt       { output_statement($1, 0); }
                | CreateTrigStmt        { output_statement($1, 0); }
                | CreateUserStmt        { output_statement($1, 0); }
                | ClusterStmt           { output_statement($1, 0); }
                | DefineStmt            { output_statement($1, 0); }
-               | DestroyStmt           { output_statement($1, 0); }
+               | DropStmt              { output_statement($1, 0); }
+               | TruncateStmt          { output_statement($1, 0); }
+               | DropGroupStmt         { output_statement($1, 0); }
                | DropPLangStmt         { output_statement($1, 0); }
                | DropTrigStmt          { output_statement($1, 0); }
                | DropUserStmt          { output_statement($1, 0); }
                | ExtendStmt            { output_statement($1, 0); }
                | ExplainStmt           { output_statement($1, 0); }
                | FetchStmt             { output_statement($1, 1); }
+               | FetchDescriptorStmt           { output_statement_desc($1, 1); }
                | GrantStmt             { output_statement($1, 0); }
                | IndexStmt             { output_statement($1, 0); }
                | ListenStmt            { output_statement($1, 0); }
                | UnlistenStmt          { output_statement($1, 0); }
                | LockStmt              { output_statement($1, 0); }
                | ProcedureStmt         { output_statement($1, 0); }
-               | RecipeStmt            { output_statement($1, 0); }
                | RemoveAggrStmt        { output_statement($1, 0); }
                | RemoveOperStmt        { output_statement($1, 0); }
                | RemoveFuncStmt        { output_statement($1, 0); }
@@ -735,43 +1259,81 @@ stmt:  AddAttrStmt                       { output_statement($1, 0); }
                | RevokeStmt            { output_statement($1, 0); }
                 | OptimizableStmt      {
                                                if (strncmp($1, "/* " , sizeof("/* ")-1) == 0)
-                                               {
-                                                       fputs($1, yyout);
-                                                       free($1);
-                                               }
+                                                       output_simple_statement($1);
                                                else
                                                        output_statement($1, 1);
                                        }
                | RuleStmt              { output_statement($1, 0); }
                | TransactionStmt       {
-                                               fprintf(yyout, "ECPGtrans(__LINE__, \"%s\");", $1);
-                                               whenever_action(0);
+                                               fprintf(yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", $1);
+                                               whenever_action(2);
                                                free($1);
                                        }
                | ViewStmt              { output_statement($1, 0); }
                | LoadStmt              { output_statement($1, 0); }
                | CreatedbStmt          { output_statement($1, 0); }
-               | DestroydbStmt         { output_statement($1, 0); }
+               | DropdbStmt            { output_statement($1, 0); }
                | VacuumStmt            { output_statement($1, 0); }
                | VariableSetStmt       { output_statement($1, 0); }
                | VariableShowStmt      { output_statement($1, 0); }
                | VariableResetStmt     { output_statement($1, 0); }
+               | ConstraintsSetStmt    { output_statement($1, 0); }
+               | ECPGAllocateDescr     {       fprintf(yyout,"ECPGallocate_desc(__LINE__, \"%s\");",$1);
+                                                               whenever_action(0);
+                                                               free($1);
+                                                       }
                | ECPGConnect           {
-                                               fprintf(yyout, "no_auto_trans = %d;\n", no_auto_trans);
-                                               fprintf(yyout, "ECPGconnect(__LINE__, %s);", $1);
-                                               whenever_action(0);
+                                               if (connection)
+                                                       mmerror(ET_ERROR, "no at option for connect statement.\n");
+
+                                               fprintf(yyout, "{ ECPGconnect(__LINE__, %s, %d);", $1, autocommit);
+                                               whenever_action(2);
                                                free($1);
                                        } 
+               | ECPGCursorStmt        {
+                                               output_simple_statement($1);
+                                       }
+               | ECPGDeallocate        {
+                                               if (connection)
+                                                       mmerror(ET_ERROR, "no at option for connect statement.\n");
+
+                                               fputc('{', yyout);
+                                               fputs($1, yyout);
+                                               whenever_action(2);
+                                               free($1);
+                                       }
+               | ECPGDeallocateDescr   {       fprintf(yyout,"ECPGdeallocate_desc(__LINE__, \"%s\");",$1);
+                                                                       whenever_action(0);
+                                                                       free($1);
+                                                               }
+               | ECPGDeclare           {
+                                               output_simple_statement($1);
+                                       }
                | ECPGDisconnect        {
-                                               fprintf(yyout, "ECPGdisconnect(__LINE__, \"%s\");", $1); 
-                                               whenever_action(0);
+                                               if (connection)
+                                                       mmerror(ET_ERROR, "no at option for disconnect statement.\n");
+
+                                               fprintf(yyout, "{ ECPGdisconnect(__LINE__, \"%s\");", $1); 
+                                               whenever_action(2);
                                                free($1);
                                        } 
                | ECPGExecute           {
-                                               fprintf(yyout, "ECPGdo(__LINE__, %s, ECPGt_EOIT, ECPGt_EORT);", $1);
-                                               whenever_action(0);
+                                               output_statement($1, 0);
+                                       }
+               | ECPGFree              {
+                                               fprintf(yyout, "{ ECPGdeallocate(__LINE__, \"%s\");", $1);
+
+                                               whenever_action(2);
                                                free($1);
                                        }
+               | ECPGGetDescriptor     {       
+                                               lookup_descriptor($1,connection);
+                                               output_get_descr($1);
+                                       }
+               | ECPGGetDescriptorHeader       {       
+                                               lookup_descriptor($1,connection);
+                                               output_get_descr_header($1);
+                                       }
                | ECPGOpen              {       
                                                struct cursor *ptr;
                                                 
@@ -784,29 +1346,61 @@ stmt:  AddAttrStmt                       { output_statement($1, 0); }
                                                if (ptr == NULL)
                                                {
                                                        sprintf(errortext, "trying to open undeclared cursor %s\n", $1);
-                                                       yyerror(errortext);
+                                                       mmerror(ET_ERROR, errortext);
                                                }
                   
-                                               fprintf(yyout, "ECPGdo(__LINE__, \"%s\",", ptr->command);
+                                               fprintf(yyout, "{ ECPGdo(__LINE__, %s, \"%s\",", ptr->connection ? ptr->connection : "NULL", ptr->command);
                                                /* dump variables to C file*/
                                                dump_variables(ptr->argsinsert, 0);
+                                               dump_variables(argsinsert, 0);
                                                fputs("ECPGt_EOIT, ", yyout);
                                                dump_variables(ptr->argsresult, 0);
                                                fputs("ECPGt_EORT);", yyout);
-                                               whenever_action(0);
+                                               whenever_action(2);
+                                               free($1);
+                                       }
+               | ECPGPrepare           {
+                                               if (connection)
+                                                       mmerror(ET_ERROR, "no at option for set connection statement.\n");
+
+                                               fprintf(yyout, "{ ECPGprepare(__LINE__, %s);", $1); 
+                                               whenever_action(2);
                                                free($1);
                                        }
                | ECPGRelease           { /* output already done */ }
+               | ECPGSetAutocommit     {
+                                               fprintf(yyout, "{ ECPGsetcommit(__LINE__, \"%s\", %s);", $1, connection ? connection : "NULL");
+                                               whenever_action(2);
+                                                       free($1);
+                                       }
                | ECPGSetConnection     {
-                                               fprintf(yyout, "ECPGsetconn(__LINE__, %s);", $1);
-                                               whenever_action(0);
+                                               if (connection)
+                                                       mmerror(ET_ERROR, "no at option for set connection statement.\n");
+
+                                               fprintf(yyout, "{ ECPGsetconn(__LINE__, %s);", $1);
+                                               whenever_action(2);
                                                        free($1);
                                        }
+               | ECPGTypedef           {
+                                               if (connection)
+                                                       mmerror(ET_ERROR, "no at option for typedef statement.\n");
+
+                                               output_simple_statement($1);
+                                       }
+               | ECPGVar               {
+                                               if (connection)
+                                                       mmerror(ET_ERROR, "no at option for var statement.\n");
+
+                                               output_simple_statement($1);
+                                       }
                | ECPGWhenever          {
-                                               fputs($1, yyout);
-                                               output_line_number();
-                                               free($1);
+                                               if (connection)
+                                                       mmerror(ET_ERROR, "no at option for whenever statement.\n");
+
+                                               output_simple_statement($1);
                                        }
+               ;
+
 
 /*
  * We start with a lot of stuff that's very similar to the backend's parsing
@@ -819,69 +1413,89 @@ stmt:  AddAttrStmt                       { output_statement($1, 0); }
  *
  *****************************************************************************/
 
-CreateUserStmt:  CREATE USER UserId user_passwd_clause user_createdb_clause
-                       user_createuser_clause user_group_clause user_valid_clause
+CreateUserStmt: CREATE USER UserId
+               user_createdb_clause user_createuser_clause user_group_clause
+               user_valid_clause
                                {
-                                       $$ = cat3_str(cat5_str(make1_str("create user"), $3, $4, $5, $6), $7, $8);
+                                       $$ = cat_str(6, make_str("create user"), $3, $4, $5, $6, $7);
                                }
+               | CREATE USER UserId WITH sysid_clause user_passwd_clause
+               user_createdb_clause user_createuser_clause user_group_clause
+               user_valid_clause
+               {
+                                       $$ = cat_str(9, make_str("create user"), $3, make_str("with"), $5, $6, $7, $8, $9, $10);
+               }
                ;
 
 /*****************************************************************************
  *
- * Alter a postresql DBMS user
+ * Alter a postgresql DBMS user
  *
  *
  *****************************************************************************/
 
-AlterUserStmt:  ALTER USER UserId user_passwd_clause user_createdb_clause
-                       user_createuser_clause user_group_clause user_valid_clause
+AlterUserStmt:  ALTER USER UserId user_createdb_clause
+                               user_createuser_clause user_valid_clause
+                               {
+                                       $$ = cat_str(5, make_str("alter user"), $3, $4, $5, $6);
+                               }
+                       | ALTER USER UserId WITH PASSWORD Sconst
+                               user_createdb_clause
+                               user_createuser_clause user_valid_clause
                                {
-                                       $$ = cat3_str(cat5_str(make1_str("alter user"), $3, $4, $5, $6), $7, $8);
+                                       $$ = cat_str(7, make_str("alter user"), $3, make_str("with password"), $6, $7, $8, $9);
                                }
                ;
 
 /*****************************************************************************
  *
- * Drop a postresql DBMS user
+ * Drop a postgresql DBMS user
  *
  *
  *****************************************************************************/
 
-DropUserStmt:  DROP USER UserId
+DropUserStmt:  DROP USER user_list
                                {
-                                       $$ = cat2_str(make1_str("drop user"), $3);
+                                       $$ = cat2_str(make_str("drop user"), $3);
                                }
                ;
 
-user_passwd_clause:  WITH PASSWORD UserId      { $$ = cat2_str(make1_str("with password") , $3); }
-                       | /*EMPTY*/             { $$ = make1_str(""); }
+user_passwd_clause:  PASSWORD Sconst   { $$ = cat2_str(make_str("password") , $2); }
+                       | /*EMPTY*/     { $$ = EMPTY; }
                ;
 
+sysid_clause:  SYSID Iconst            { if (atoi($2) <= 0)
+                                               mmerror(ET_ERROR, "sysid must be positive");
+
+                                         $$ = cat2_str(make_str("sysid"), $2); }
+                       | /*EMPTY*/     { $$ = EMPTY; }
+                ;
+
 user_createdb_clause:  CREATEDB
                                {
-                                       $$ = make1_str("createdb");
+                                       $$ = make_str("createdb");
                                }
                        | NOCREATEDB
                                {
-                                       $$ = make1_str("nocreatedb");
+                                       $$ = make_str("nocreatedb");
                                }
-                       | /*EMPTY*/             { $$ = make1_str(""); }
+                       | /*EMPTY*/             { $$ = EMPTY; }
                ;
 
 user_createuser_clause:  CREATEUSER
                                {
-                                       $$ = make1_str("createuser");
+                                       $$ = make_str("createuser");
                                }
                        | NOCREATEUSER
                                {
-                                       $$ = make1_str("nocreateuser");
+                                       $$ = make_str("nocreateuser");
                                }
                        | /*EMPTY*/             { $$ = NULL; }
                ;
 
-user_group_list:  user_group_list ',' UserId
+user_list:  user_list ',' UserId
                                {
-                                       $$ = cat3_str($1, make1_str(","), $3);
+                                       $$ = cat_str(3, $1, make_str(","), $3);
                                }
                        | UserId
                                {
@@ -889,14 +1503,68 @@ user_group_list:  user_group_list ',' UserId
                                }
                ;
 
-user_group_clause:  IN GROUP user_group_list   { $$ = cat2_str(make1_str("in group"), $3); }
-                       | /*EMPTY*/             { $$ = make1_str(""); }
+user_group_clause:  IN GROUP user_list
+                       {
+                               $$ = cat2_str(make_str("in group"), $3); 
+                       }
+                       | /*EMPTY*/             { $$ = EMPTY; }
                ;
 
-user_valid_clause:  VALID UNTIL Sconst                 { $$ = cat2_str(make1_str("valid until"), $3);; }
-                       | /*EMPTY*/                     { $$ = make1_str(""); }
+user_valid_clause:  VALID UNTIL Sconst                 { $$ = cat2_str(make_str("valid until"), $3); }
+                       | /*EMPTY*/                     { $$ = EMPTY; }
                ;
 
+
+/*****************************************************************************
+ *
+ * Create a postgresql group
+ *
+ *
+ ****************************************************************************/
+CreateGroupStmt: CREATE GROUP UserId
+                 {
+                       $$ = cat2_str(make_str("create group"), $3);
+                }
+               | CREATE GROUP UserId WITH sysid_clause users_in_new_group_clause
+                 {
+                       $$ = cat_str(5, make_str("create group"), $3, make_str("with"), $5, $6);
+                 }
+                ;
+
+users_in_new_group_clause:  USER user_list   { $$ = cat2_str(make_str("user"), $2); }
+                            | /* EMPTY */          { $$ = EMPTY; }
+               ;
+
+
+/*****************************************************************************
+ *
+ * Alter a postgresql group
+ *
+ *
+ *****************************************************************************/
+AlterGroupStmt: ALTER GROUP UserId ADD USER user_list
+                {
+                       $$ = cat_str(4, make_str("alter group"), $3, make_str("add user"), $6);
+                }
+                | ALTER GROUP UserId DROP USER user_list
+                {
+                       $$ = cat_str(4, make_str("alter group"), $3, make_str("drop user"), $6);
+                }
+                ;
+
+/*****************************************************************************
+ *
+ * Drop a postgresql group
+ *
+ *
+ *****************************************************************************/
+DropGroupStmt: DROP GROUP UserId
+               {
+                       $$ = cat2_str(make_str("drop group"), $3);
+               }
+               ;
+
+
 /*****************************************************************************
  *
  * Set PG internal variable
@@ -908,114 +1576,161 @@ user_valid_clause:  VALID UNTIL Sconst                 { $$ = cat2_str(make1_str("valid until"
 
 VariableSetStmt:  SET ColId TO var_value
                                {
-                                       $$ = cat4_str(make1_str("set"), $2, make1_str("to"), $4);
+                                       $$ = cat_str(4, make_str("set"), $2, make_str("to"), $4);
                                }
                | SET ColId '=' var_value
                                {
-                                       $$ = cat4_str(make1_str("set"), $2, make1_str("="), $4);
+                                       $$ = cat_str(4, make_str("set"), $2, make_str("="), $4);
                                }
                | SET TIME ZONE zone_value
                                {
-                                       $$ = cat2_str(make1_str("set time zone"), $4);
-                               }
-               | SET TRANSACTION ISOLATION LEVEL READ ColId
-                               {
-                                       if (strcasecmp($6, "COMMITTED"))
-                                       {
-                                                sprintf(errortext, "syntax error at or near \"%s\"", $6);
-                                               yyerror(errortext);
-                                       }
-
-                                       $$ = cat2_str(make1_str("set transaction isolation level read"), $6);
+                                       $$ = cat2_str(make_str("set time zone"), $4);
                                }
-               | SET TRANSACTION ISOLATION LEVEL ColId
+               | SET TRANSACTION ISOLATION LEVEL opt_level
                                {
-                                       if (strcasecmp($5, "SERIALIZABLE"))
-                                       {
-                                                sprintf(errortext, "syntax error at or near \"%s\"", $5);
-                                                yyerror(errortext);
-                                       }
-
-                                       $$ = cat2_str(make1_str("set transaction isolation level read"), $5);
+                                       $$ = cat2_str(make_str("set transaction isolation level"), $5);
                                }
-               | SET NAMES encoding
+               | SET NAMES opt_encoding
                                 {
-#ifdef MB
-                                       $$ = cat2_str(make1_str("set names"), $3);
+#ifdef MULTIBYTE
+                                       $$ = cat2_str(make_str("set names"), $3);
 #else
-                                        yyerror("SET NAMES is not supported");
+                                        mmerror(ET_ERROR, "SET NAMES is not supported.");
 #endif
                                 }
                 ;
 
+opt_level:  READ COMMITTED      { $$ = make_str("read committed"); }
+               | SERIALIZABLE   { $$ = make_str("serializable"); }
+               ;
+
+
 var_value:  Sconst                     { $$ = $1; }
-               | DEFAULT                       { $$ = make1_str("default"); }
+               | DEFAULT                       { $$ = make_str("default"); }
                ;
 
 zone_value:  Sconst                    { $$ = $1; }
-               | DEFAULT                       { $$ = make1_str("default"); }
-               | LOCAL                         { $$ = make1_str("local"); }
+               | DEFAULT                       { $$ = make_str("default"); }
+               | LOCAL                         { $$ = make_str("local"); }
+               ;
+
+opt_encoding:  Sconst          { $$ = $1; }
+               | DEFAULT       { $$ = make_str("default"); }
+               | /*EMPTY*/     { $$ = EMPTY; }
                ;
 
 VariableShowStmt:  SHOW ColId
                                {
-                                       $$ = cat2_str(make1_str("show"), $2);
+                                       $$ = cat2_str(make_str("show"), $2);
                                }
                | SHOW TIME ZONE
                                {
-                                       $$ = make1_str("show time zone");
+                                       $$ = make_str("show time zone");
                                }
                | SHOW TRANSACTION ISOLATION LEVEL
                                {
-                                       $$ = make1_str("show transaction isolation level");
+                                       $$ = make_str("show transaction isolation level");
                                }
                ;
 
 VariableResetStmt:     RESET ColId
                                {
-                                       $$ = cat2_str(make1_str("reset"), $2);
+                                       $$ = cat2_str(make_str("reset"), $2);
                                }
                | RESET TIME ZONE
                                {
-                                       $$ = make1_str("reset time zone");
+                                       $$ = make_str("reset time zone");
                                }
                | RESET TRANSACTION ISOLATION LEVEL
                                {
-                                       $$ = make1_str("reset transaction isolation level");
+                                       $$ = make_str("reset transaction isolation level");
                                }
                ;
 
+ConstraintsSetStmt:    SET CONSTRAINTS constraints_set_list constraints_set_mode
+                               {
+                                       $$ = cat_str(3, make_str("set constraints"), $3, $4);
+                               }
+               ;
+
+constraints_set_list:  ALL
+                               {
+                                       $$ = make_str("all");
+                               }
+               | constraints_set_namelist
+                               {
+                                       $$ = $1;
+                               }
+               ;
+
+
+constraints_set_namelist:      IDENT
+                               {
+                                       $$ = $1;
+                               }
+               | constraints_set_namelist ',' IDENT
+                               {
+                                       $$ = cat_str(3, $1, make_str(","), $3);
+                               }
+               ;
+
+constraints_set_mode:  DEFERRED
+                               {
+                                       $$ = make_str("deferred");
+                               }
+               | IMMEDIATE
+                               {
+                                       $$ = make_str("immediate");
+                               }
+               ;
 
 /*****************************************************************************
  *
  *             QUERY :
- *                             addattr ( attr1 = type1 .. attrn = typen ) to <relname> [*]
+ *
+ *     ALTER TABLE variations
  *
  *****************************************************************************/
 
-AddAttrStmt:  ALTER TABLE relation_name opt_inh_star alter_clause
-                               {
-                                       $$ = cat4_str(make1_str("alter table"), $3, $4, $5);
-                               }
+AlterTableStmt:
+/* ALTER TABLE <name> ADD [COLUMN] <coldef> */
+        ALTER TABLE relation_name opt_inh_star ADD opt_column columnDef
+               {
+                       $$ = cat_str(6, make_str("alter table"), $3, $4, make_str("add"), $6, $7);
+               }
+/* ALTER TABLE <name> ALTER [COLUMN] <colname> {SET DEFAULT <expr>|DROP
+DEFAULT} */
+       | ALTER TABLE relation_name opt_inh_star ALTER opt_column ColId
+               alter_column_action
+               {
+                       $$ = cat_str(7, make_str("alter table"), $3, $4, make_str("alter"), $6, $7, $8);
+               }
+/* ALTER TABLE <name> DROP [COLUMN] <name> {RESTRICT|CASCADE} */
+       | ALTER TABLE relation_name opt_inh_star DROP opt_column ColId drop_behavior
+               {
+                       $$ = cat_str(7, make_str("alter table"), $3, $4, make_str("drop"), $6, $7, $8);
+               }
+/* ALTER TABLE <name> ADD CONSTRAINT ... */
+       | ALTER TABLE relation_name opt_inh_star ADD TableConstraint
+               {
+                       $$ = cat_str(5, make_str("alter table"), $3, $4, make_str("add"), $6);
+               }
+/* ALTER TABLE <name> DROP CONSTRAINT ... */
+       | ALTER TABLE relation_name opt_inh_star DROP CONSTRAINT name drop_behavior
+               {
+                       $$ = cat_str(6, make_str("alter table"), $3, $4, make_str("drop constraint"), $7, $8);
+               }
                ;
 
-alter_clause:  ADD opt_column columnDef
-                               {
-                                       $$ = cat3_str(make1_str("add"), $2, $3);
-                               }
-                       | ADD '(' OptTableElementList ')'
-                               {
-                                       $$ = make3_str(make1_str("add("), $3, make1_str(")"));
-                               }
-                       | 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"); }
-               ;
+alter_column_action:
+        SET DEFAULT a_expr     { $$ = cat2_str(make_str("set default"), $3); }
+        | SET DEFAULT NULL_P    { $$ = make_str("set default null"); }
+        | DROP DEFAULT          { $$ = make_str("drop default"); }
+        ;
+
+drop_behavior: CASCADE { $$ = make_str("cascade"); }
+               | RESTRICT { $$ = make_str("restrict"); }
+        ;
 
 /*****************************************************************************
  *
@@ -1026,10 +1741,13 @@ alter_clause:  ADD opt_column columnDef
 
 ClosePortalStmt:  CLOSE opt_id
                                {
-                                       $$ = cat2_str(make1_str("close"), $2);
+                                       $$ = cat2_str(make_str("close"), $2);
                                }
                ;
 
+opt_id:  ColId         { $$ = $1; }
+       | /*EMPTY*/     { $$ = NULL; }
+       ;
 
 /*****************************************************************************
  *
@@ -1039,16 +1757,16 @@ ClosePortalStmt:  CLOSE opt_id
  *
  *****************************************************************************/
 
-CopyStmt:  COPY opt_binary relation_name opt_with_copy copy_dirn copy_file_name copy_delimiter
+CopyStmt:  COPY opt_binary relation_name opt_with_copy copy_dirn copy_file_name copy_delimiter copy_null
                                {
-                                       $$ = cat3_str(cat5_str(make1_str("copy"), $2, $3, $4, $5), $6, $7);
+                                       $$ = cat_str(8, make_str("copy"), $2, $3, $4, $5, $6, $7, $8);
                                }
                ;
 
 copy_dirn:     TO
-                               { $$ = make1_str("to"); }
+                               { $$ = make_str("to"); }
                | FROM
-                               { $$ = make1_str("from"); }
+                               { $$ = make_str("from"); }
                ;
 
 /*
@@ -1057,26 +1775,32 @@ copy_dirn:      TO
  * stdout. We silently correct the "typo".              - AY 9/94
  */
 copy_file_name:  Sconst                                        { $$ = $1; }
-               | STDIN                                 { $$ = make1_str("stdin"); }
-               | STDOUT                                { $$ = make1_str("stdout"); }
+               | STDIN                                 { $$ = make_str("stdin"); }
+               | STDOUT                                { $$ = make_str("stdout"); }
                ;
 
-opt_binary:  BINARY                                    { $$ = make1_str("binary"); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+opt_binary:  BINARY                                    { $$ = make_str("binary"); }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
-opt_with_copy: WITH OIDS                               { $$ = make1_str("with oids"); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+opt_with_copy: WITH OIDS                               { $$ = make_str("with oids"); }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
 /*
  * the default copy delimiter is tab but the user can configure it
  */
-copy_delimiter:  USING DELIMITERS Sconst               { $$ = cat2_str(make1_str("using delimiters"), $3); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+copy_delimiter:  opt_using DELIMITERS Sconst           { $$ = cat_str(3, $1, make_str("delimiters"), $3); }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
+opt_using:     USING           { $$ = make_str("using"); }
+               | /* EMPTY */   { $$ = EMPTY; }
+               ;
 
+copy_null:     WITH NULL_P AS Sconst   { $$ = cat2_str(make_str("with null as"), $4); }
+               | /* EMPTY */   { $$ = EMPTY; }
+               ;
 
 /*****************************************************************************
  *
@@ -1085,22 +1809,40 @@ copy_delimiter:  USING DELIMITERS Sconst                { $$ = cat2_str(make1_str("using delim
  *
  *****************************************************************************/
 
-CreateStmt:  CREATE TABLE relation_name '(' OptTableElementList ')'
+CreateStmt:  CREATE OptTemp TABLE relation_name '(' OptTableElementList ')'
                                OptInherit
                                {
-                                       $$ = cat4_str(make1_str("create table"), $3,  make3_str(make1_str("("), $5, make1_str(")")), $7);
+                                       $$ = cat_str(8, make_str("create"), $2, make_str("table"), $4, make_str("("), $6, make_str(")"), $8);
                                }
                ;
 
+OptTemp:  OptTempType                           { $$ = $1; }
+                | OptTempScope OptTempType     { $$ = cat2_str($1,$2); }
+                ;
+
+OptTempType:     TEMP          { $$ = make_str("temp"); }
+               | TEMPORARY     { $$ = make_str("temporary"); }
+               | /* EMPTY */   { $$ = EMPTY; }
+               ;
+
+OptTempScope:  GLOBAL
+               {
+                    mmerror(ET_ERROR, "GLOBAL TEMPORARY TABLE is not currently supported");
+                    $$ = make_str("global");
+               }
+             | LOCAL { $$ = make_str("local"); }
+             ;
+
+
 OptTableElementList:  OptTableElementList ',' OptTableElement
                                {
-                                       $$ = cat3_str($1, make1_str(","), $3);
+                                       $$ = cat_str(3, $1, make_str(","), $3);
                                }
                        | OptTableElement
                                {
                                        $$ = $1;
                                }
-                       | /*EMPTY*/     { $$ = make1_str(""); }
+                       | /*EMPTY*/     { $$ = EMPTY; }
                ;
 
 OptTableElement:  columnDef            { $$ = $1; }
@@ -1109,176 +1851,104 @@ OptTableElement:  columnDef           { $$ = $1; }
 
 columnDef:  ColId Typename ColQualifier
                                {
-                                       $$ = cat3_str($1, $2, $3);
+                                       $$ = cat_str(3, $1, $2, $3);
                                }
        | ColId SERIAL ColPrimaryKey
                {
-                       $$ = make3_str($1, make1_str(" serial "), $3);
+                       $$ = cat_str(3, $1, make_str(" serial "), $3);
                }
                ;
 
-ColQualifier:  ColQualList     { $$ = $1; }
-                       | /*EMPTY*/     { $$ = make1_str(""); }
+ColQualifier:  ColQualList                     { $$ = $1; }
+               | NULL_P ColQualListWithNull    { $$ = cat2_str(make_str("null"), $2); }
+                | NULL_P                       { $$ = make_str("null"); }
+                | /*EMPTY*/                    { $$ = EMPTY; }
                ;
 
 ColQualList:  ColQualList ColConstraint        { $$ = cat2_str($1,$2); }
-                       | ColConstraint         { $$ = $1; }
+                       | ColConstraint { $$ = $1; }
                ;
 
+ColQualListWithNull:  ColQualListWithNull ColConstraintWithNull
+                       { $$ = cat2_str($1, $2); }
+               |  ColConstraintWithNull
+                       { $$ = $1; }
 ColPrimaryKey:  PRIMARY KEY
                 {
-                       $$ = make1_str("primary key");
+                       $$ = make_str("primary key");
                 }
               | /*EMPTY*/
                {
-                       $$ = make1_str("");
+                       $$ = EMPTY;
                }
                 ;
 
 ColConstraint:
                CONSTRAINT name ColConstraintElem
                                {
-                                       $$ = cat3_str(make1_str("constraint"), $2, $3);
+                                       $$ = cat_str(3, make_str("constraint"), $2, $3);
                                }
                | ColConstraintElem
                                { $$ = $1; }
                ;
 
-/* The column constraint WITH NULL gives a shift/reduce error
- * because it requires yacc to look more than one token ahead to
- * resolve WITH TIME ZONE and WITH NULL.
- * So, leave it out of the syntax for now.
-                       | WITH NULL_P
-                               {
-                                       $$ = NULL;
-                               }
- * - thomas 1998-09-12
- *
- * DEFAULT NULL is already the default for Postgres.
- * Bue define it here and carry it forward into the system
+ColConstraintWithNull:
+               CONSTRAINT name ColConstraintElemWithNull
+                       { $$ = cat_str(3, make_str("constraint"), $2, $3); }
+               | ColConstraintElemWithNull
+                       { $$ = $1; }
+               ;
+
+/* DEFAULT NULL is already the default for Postgres.
+ * But define it here and carry it forward into the system
  * to make it explicit.
  * - thomas 1998-09-13
+ *
+ * WITH NULL and NULL are not SQL92-standard syntax elements,
+ * so leave them out. Use DEFAULT NULL to explicitly indicate
+ * that a column may have that value. WITH NULL leads to
+ * shift/reduce conflicts with WITH TIME ZONE anyway.
+ * - thomas 1999-01-08
+ *
+ * DEFAULT expression must be b_expr not a_expr to prevent shift/reduce
+ * conflict on NOT (since NOT might start a subsequent NOT NULL constraint,
+ * or be part of a_expr NOT LIKE or similar constructs).
  */
-ColConstraintElem:  CHECK '(' constraint_expr ')'
-                               {
-                                       $$ = make3_str(make1_str("check("), $3, make1_str(")"));
-                               }
-                       | DEFAULT NULL_P
-                               {
-                                       $$ = make1_str("default null");
-                               }
-                       | DEFAULT default_expr
-                               {
-                                       $$ = cat2_str(make1_str("default"), $2);
-                               }
-                       | NOT NULL_P
-                               {
-                                       $$ = make1_str("not null");
-                               }
-                       | UNIQUE
+ColConstraintElem:  ColConstraintElemWithNull
+                                {
+                                        $$ = $1;
+                                }
+                        | NOT NULL_P
+                                {
+                                        $$ = make_str("not null");
+                                }
+                        | UNIQUE
                                {
-                                       $$ = make1_str("unique");
+                                       $$ = make_str("unique");
                                }
                        | PRIMARY KEY
                                {
-                                       $$ = make1_str("primary key");
-                               }
-                       | REFERENCES ColId opt_column_list key_match key_actions
-                               {
-                                       fprintf(stderr, "CREATE TABLE/FOREIGN KEY clause ignored; not yet implemented");
-                                       $$ = make1_str("");
+                                       $$ = make_str("primary key");
                                }
-               ;
+                       ;
 
-default_list:  default_list ',' default_expr
+
+ColConstraintElemWithNull:  CHECK '(' a_expr ')'
                                {
-                                       $$ = cat3_str($1, make1_str(","), $3);
+                                       $$ = cat_str(3, make_str("check("), $3, make_str(")"));
                                }
-                       | default_expr
+                       | DEFAULT NULL_P
                                {
-                                       $$ = $1;
+                                       $$ = make_str("default null");
                                }
-               ;
-
-/* The Postgres default column value is NULL.
- * Rather than carrying DEFAULT NULL forward as a clause,
- * let's just have it be a no-op.
-                        | NULL_P
-                               {       $$ = make1_str("null"); }
- * - thomas 1998-09-13
- */
-
-default_expr:  AexprConst
-                               {       $$ = $1; }
-                       | '-' default_expr %prec UMINUS
-                               {       $$ = cat2_str(make1_str("-"), $2); }
-                       | default_expr '+' default_expr
-                               {       $$ = cat3_str($1, make1_str("+"), $3); }
-                       | default_expr '-' default_expr
-                               {       $$ = cat3_str($1, make1_str("-"), $3); }
-                       | default_expr '/' default_expr
-                               {       $$ = cat3_str($1, make1_str("/"), $3); }
-                       | default_expr '*' default_expr
-                               {       $$ = cat3_str($1, make1_str("*"), $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
-                               {       $$ = cat2_str(make1_str(":"), $2); }
-*/
-                       | ';' default_expr
-                               {       $$ = cat2_str(make1_str(";"), $2); }
-                       | '|' default_expr
-                               {       $$ = cat2_str(make1_str("|"), $2); }
-                       | default_expr TYPECAST Typename
-                               {       $$ = cat3_str($1, make1_str("::"), $3); }
-                       | CAST '(' default_expr AS Typename ')'
-                               {
-                                       $$ = cat3_str(make2_str(make1_str("cast("), $3) , make1_str("as"), make2_str($5, make1_str(")")));
-                               }
-                       | '(' default_expr ')'
-                               {       $$ = make3_str(make1_str("("), $2, make1_str(")")); }
-                       | func_name '(' ')'
-                               {       $$ = cat2_str($1, make1_str("()")); }
-                       | func_name '(' default_list ')'
-                               {       $$ = cat2_str($1, make3_str(make1_str("("), $3, make1_str(")"))); }
-                       | default_expr Op default_expr
-                               {
-                                       if (!strcmp("<=", $2) || !strcmp(">=", $2))
-                                               yyerror("boolean expressions not supported in DEFAULT");
-                                       $$ = cat3_str($1, $2, $3);
-                               }
-                       | Op default_expr
-                               {       $$ = cat2_str($1, $2); }
-                       | default_expr Op
-                               {       $$ = cat2_str($1, $2); }
-                       /* XXX - thomas 1997-10-07 v6.2 function-specific code to be changed */
-                       | CURRENT_DATE
-                               {       $$ = make1_str("current_date"); }
-                       | CURRENT_TIME
-                               {       $$ = make1_str("current_time"); }
-                       | CURRENT_TIME '(' Iconst ')'
+                       | DEFAULT b_expr
                                {
-                                       if ($3 != 0)
-                                               fprintf(stderr, "CURRENT_TIME(%s) precision not implemented; zero used instead",$3);
-                                       $$ = "current_time";
+                                       $$ = cat2_str(make_str("default"), $2);
                                }
-                       | CURRENT_TIMESTAMP
-                               {       $$ = make1_str("current_timestamp"); }
-                       | CURRENT_TIMESTAMP '(' Iconst ')'
+                       | REFERENCES ColId opt_column_list key_match key_actions
                                {
-                                       if ($3 != 0)
-                                               fprintf(stderr, "CURRENT_TIMESTAMP(%s) precision not implemented; zero used instead",$3);
-                                       $$ = "current_timestamp";
+                                       $$ = cat_str(5, make_str("references"), $2, $3, $4, $5);
                                }
-                       | CURRENT_USER
-                               {       $$ = make1_str("current_user"); }
-                       | USER
-                               {       $$ = make1_str("user"); }
                ;
 
 /* ConstraintElem specifies constraint syntax which is not embedded into
@@ -1287,181 +1957,84 @@ default_expr:  AexprConst
  */
 TableConstraint:  CONSTRAINT name ConstraintElem
                                {
-                                               $$ = cat3_str(make1_str("constraint"), $2, $3);
+                                               $$ = cat_str(3, make_str("constraint"), $2, $3);
                                }
                | ConstraintElem
                                { $$ = $1; }
                ;
 
-ConstraintElem:  CHECK '(' constraint_expr ')'
+ConstraintElem:  CHECK '(' a_expr ')'
                                {
-                                       $$ = make3_str(make1_str("check("), $3, make1_str(")"));
+                                       $$ = cat_str(3, make_str("check("), $3, make_str(")"));
                                }
                | UNIQUE '(' columnList ')'
                                {
-                                       $$ = make3_str(make1_str("unique("), $3, make1_str(")"));
+                                       $$ = cat_str(3, make_str("unique("), $3, make_str(")"));
                                }
                | PRIMARY KEY '(' columnList ')'
                                {
-                                       $$ = make3_str(make1_str("primary key("), $4, make1_str(")"));
+                                       $$ = cat_str(3, make_str("primary key("), $4, make_str(")"));
                                }
                | 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
-                               {
-                                       $$ = cat3_str($1, make1_str(","), $3);
-                               }
-                       | constraint_expr
-                               {
-                                       $$ = $1;
+                                       $$ = cat_str(7, make_str("foreign key("), $4, make_str(") references"), $7, $8, $9, $10);
                                }
                ;
 
-constraint_expr:  AexprConst
-                               {       $$ = $1; }
-                       | NULL_P
-                               {       $$ = make1_str("null"); }
-                       | ColId
-                               {
-                                       $$ = $1;
-                               }
-                       | '-' constraint_expr %prec UMINUS
-                               {       $$ = cat2_str(make1_str("-"), $2); }
-                       | constraint_expr '+' constraint_expr
-                               {       $$ = cat3_str($1, make1_str("+"), $3); }
-                       | constraint_expr '-' constraint_expr
-                               {       $$ = cat3_str($1, make1_str("-"), $3); }
-                       | constraint_expr '/' constraint_expr
-                               {       $$ = cat3_str($1, make1_str("/"), $3); }
-                       | constraint_expr '*' constraint_expr
-                               {       $$ = cat3_str($1, make1_str("*"), $3); }
-                       | constraint_expr '=' constraint_expr
-                               {       $$ = cat3_str($1, make1_str("="), $3); }
-                       | constraint_expr '<' constraint_expr
-                               {       $$ = cat3_str($1, make1_str("<"), $3); }
-                       | constraint_expr '>' constraint_expr
-                               {       $$ = cat3_str($1, make1_str(">"), $3); }
-/* this one doesn't work with embedded sql anyway
-                       | ':' constraint_expr
-                               {       $$ = cat2_str(make1_str(":"), $2); }
-*/
-                       | ';' constraint_expr
-                               {       $$ = cat2_str(make1_str(";"), $2); }
-                       | '|' constraint_expr
-                               {       $$ = cat2_str(make1_str("|"), $2); }
-                       | constraint_expr TYPECAST Typename
-                               {
-                                       $$ = cat3_str($1, make1_str("::"), $3);
-                               }
-                       | CAST '(' constraint_expr AS Typename ')'
-                               {
-                                       $$ = cat3_str(make2_str(make1_str("cast("), $3), make1_str("as"), make2_str($5, make1_str(")"))); 
-                               }
-                       | '(' constraint_expr ')'
-                               {       $$ = make3_str(make1_str("("), $2, make1_str(")")); }
-                       | func_name '(' ')'
-                               {
-                               {       $$ = cat2_str($1, make1_str("()")); }
-                               }
-                       | func_name '(' constraint_list ')'
-                               {
-                                       $$ = cat2_str($1, make3_str(make1_str("("), $3, make1_str(")")));
-                               }
-                       | constraint_expr Op constraint_expr
-                               {       $$ = cat3_str($1, $2, $3); }
-                       | constraint_expr LIKE constraint_expr
-                               {       $$ = cat3_str($1, make1_str("like"), $3); }
-                       | constraint_expr NOT LIKE constraint_expr
-                               {       $$ = cat3_str($1, make1_str("not like"), $4); }
-                       | constraint_expr AND constraint_expr
-                               {       $$ = cat3_str($1, make1_str("and"), $3); }
-                       | constraint_expr OR constraint_expr
-                               {       $$ = cat3_str($1, make1_str("or"), $3); }
-                       | NOT constraint_expr
-                               {       $$ = cat2_str(make1_str("not"), $2); }
-                       | Op constraint_expr
-                               {       $$ = cat2_str($1, $2); }
-                       | constraint_expr Op
-                               {       $$ = cat2_str($1, $2); }
-                       | constraint_expr ISNULL
-                               {       $$ = cat2_str($1, make1_str("isnull")); }
-                       | constraint_expr IS NULL_P
-                               {       $$ = cat2_str($1, make1_str("is null")); }
-                       | constraint_expr NOTNULL
-                               {       $$ = cat2_str($1, make1_str("notnull")); }
-                       | constraint_expr IS NOT NULL_P
-                               {       $$ = cat2_str($1, make1_str("is not null")); }
-                       | constraint_expr IS TRUE_P
-                               {       $$ = cat2_str($1, make1_str("is true")); }
-                       | constraint_expr IS FALSE_P
-                               {       $$ = cat2_str($1, make1_str("is false")); }
-                       | constraint_expr IS NOT TRUE_P
-                               {       $$ = cat2_str($1, make1_str("is not true")); }
-                       | constraint_expr IS NOT FALSE_P
-                               {       $$ = cat2_str($1, make1_str("is not false")); }
-                       | constraint_expr IN '(' c_list ')'
-                               {       $$ = cat4_str($1, make1_str("in ("), $4, make1_str(")")); }
-                       | constraint_expr NOT IN '(' c_list ')'
-                               {       $$ = cat4_str($1, make1_str("not in ("), $5, make1_str(")")); }
-                       | constraint_expr BETWEEN c_expr AND c_expr
-                               {       $$ = cat5_str($1, make1_str("between"), $3, make1_str("and"), $5); }
-                       | constraint_expr NOT BETWEEN c_expr AND c_expr
-                               {       $$ = cat5_str($1, make1_str("not between"), $4, make1_str("and"), $6); }
-               ;
-c_list:  c_list ',' c_expr
-       {
-               $$ = make3_str($1, make1_str(", "), $3);
-       }
-       | c_expr
-       {
-               $$ = $1;
-       }
-
-c_expr:  AexprConst
-       {
-               $$ = $1;
-       }
-
-key_match:  MATCH FULL                                 { $$ = make1_str("match full"); }
-               | MATCH PARTIAL                                 { $$ = make1_str("match partial"); }
-               | /*EMPTY*/                                     { $$ = make1_str(""); }
+key_match:  MATCH FULL
+               {
+                        $$ = make_str("match full");
+               }
+               | MATCH PARTIAL         
+               {
+                       mmerror(ET_WARN, "FOREIGN KEY match type PARTIAL not implemented yet");
+                       $$ = make_str("match partial");
+               }
+               | /*EMPTY*/
+               {
+                       $$ = EMPTY;
+               }
                ;
 
-key_actions:  key_action key_action            { $$ = cat2_str($1, $2); }
-               | key_action                                    { $$ = $1; }
-               | /*EMPTY*/                                     { $$ = make1_str(""); }
+key_actions:  key_action key_action    { $$ = cat2_str($1, $2); }
+               | key_action            { $$ = $1; }
+               | /*EMPTY*/             { $$ = EMPTY; }
                ;
 
-key_action:  ON DELETE key_reference   { $$ = cat2_str(make1_str("on delete"), $3); }
-               | ON UPDATE key_reference               { $$ = cat2_str(make1_str("on update"), $3); }
+key_action:  ON DELETE key_reference   { $$ = cat2_str(make_str("on delete"), $3); }
+               | ON UPDATE key_reference               { $$ = cat2_str(make_str("on update"), $3); }
                ;
 
-key_reference:  NO ACTION      { $$ = make1_str("no action"); }
-               | CASCADE       { $$ = make1_str("cascade"); }
-               | SET DEFAULT   { $$ = make1_str("set default"); }
-               | SET NULL_P    { $$ = make1_str("set null"); }
+key_reference:  NO ACTION      { $$ = make_str("no action"); }
+               | RESTRICT      { $$ = make_str("restrict"); }
+               | CASCADE       { $$ = make_str("cascade"); }
+               | SET DEFAULT   { $$ = make_str("set default"); }
+               | SET NULL_P    { $$ = make_str("set null"); }
                ;
 
-OptInherit:  INHERITS '(' relation_name_list ')' { $$ = make3_str(make1_str("inherits ("), $3, make1_str(")")); }
-               | /*EMPTY*/ { $$ = make1_str(""); }
+OptInherit:  INHERITS '(' relation_name_list ')' { $$ = cat_str(3, make_str("inherits ("), $3, make_str(")")); }
+               | /*EMPTY*/ { $$ = EMPTY; }
                ;
 
-CreateAsStmt:  CREATE TABLE relation_name OptCreateAs AS SubSelect
+/*
+ * Note: CREATE TABLE ... AS SELECT ... is just another spelling for
+ * SELECT ... INTO.
+ */
+
+CreateAsStmt:  CREATE OptTemp TABLE relation_name OptCreateAs AS SelectStmt
                {
-                       $$ = cat5_str(make1_str("create table"), $3, $4, make1_str("as"), $6); 
+                       if (FoundInto == 1)
+                               mmerror(ET_ERROR, "CREATE TABLE/AS SELECT may not specify INTO");
+
+                       $$ = cat_str(7, make_str("create"), $2, make_str("table"), $4, $5, make_str("as"), $7); 
                }
                ;
 
-OptCreateAs:  '(' CreateAsList ')' { $$ = make3_str(make1_str("("), $2, make1_str(")")); }
-                       | /*EMPTY*/ { $$ = make1_str(""); }     
+OptCreateAs:  '(' CreateAsList ')' { $$ = cat_str(3, make_str("("), $2, make_str(")")); }
+                       | /*EMPTY*/ { $$ = EMPTY; }     
                ;
 
-CreateAsList:  CreateAsList ',' CreateAsElement        { $$ = cat3_str($1, make1_str(","), $3); }
+CreateAsList:  CreateAsList ',' CreateAsElement        { $$ = cat_str(3, $1, make_str(","), $3); }
                        | CreateAsElement       { $$ = $1; }
                ;
 
@@ -1477,38 +2050,38 @@ CreateAsElement:  ColId { $$ = $1; }
 
 CreateSeqStmt:  CREATE SEQUENCE relation_name OptSeqList
                                {
-                                       $$ = cat3_str(make1_str("create sequence"), $3, $4);
+                                       $$ = cat_str(3, make_str("create sequence"), $3, $4);
                                }
                ;
 
 OptSeqList:  OptSeqList OptSeqElem
                                { $$ = cat2_str($1, $2); }
-                       |       { $$ = make1_str(""); }
+                       |       { $$ = EMPTY; }
                ;
 
 OptSeqElem:  CACHE IntegerOnly
                                {
-                                       $$ = cat2_str(make1_str("cache"), $2);
+                                       $$ = cat2_str(make_str("cache"), $2);
                                }
                        | CYCLE
                                {
-                                       $$ = make1_str("cycle");
+                                       $$ = make_str("cycle");
                                }
                        | INCREMENT IntegerOnly
                                {
-                                       $$ = cat2_str(make1_str("increment"), $2);
+                                       $$ = cat2_str(make_str("increment"), $2);
                                }
                        | MAXVALUE IntegerOnly
                                {
-                                       $$ = cat2_str(make1_str("maxvalue"), $2);
+                                       $$ = cat2_str(make_str("maxvalue"), $2);
                                }
                        | MINVALUE IntegerOnly
                                {
-                                       $$ = cat2_str(make1_str("minvalue"), $2);
+                                       $$ = cat2_str(make_str("minvalue"), $2);
                                }
                        | START IntegerOnly
                                {
-                                       $$ = cat2_str(make1_str("start"), $2);
+                                       $$ = cat2_str(make_str("start"), $2);
                                }
                ;
 
@@ -1521,7 +2094,7 @@ FloatOnly:  Fconst
                                }
                        | '-' Fconst
                                {
-                                       $$ = cat2_str(make1_str("-"), $2);
+                                       $$ = cat2_str(make_str("-"), $2);
                                }
                ;
 
@@ -1532,7 +2105,7 @@ IntegerOnly:  Iconst
                                }
                        | '-' Iconst
                                {
-                                       $$ = cat2_str(make1_str("-"), $2);
+                                       $$ = cat2_str(make_str("-"), $2);
                                }
                ;
 
@@ -1547,16 +2120,16 @@ IntegerOnly:  Iconst
 CreatePLangStmt:  CREATE PLangTrusted PROCEDURAL LANGUAGE Sconst 
                        HANDLER def_name LANCOMPILER Sconst
                        {
-                               $$ = cat4_str(cat5_str(make1_str("create"), $2, make1_str("precedural language"), $5, make1_str("handler")), $7, make1_str("langcompiler"), $9);
+                               $$ = cat_str(8, make_str("create"), $2, make_str("precedural language"), $5, make_str("handler"), $7, make_str("langcompiler"), $9);
                        }
                ;
 
-PLangTrusted:          TRUSTED { $$ = make1_str("trusted"); }
-                       |       { $$ = make1_str(""); }
+PLangTrusted:          TRUSTED { $$ = make_str("trusted"); }
+                       |       { $$ = EMPTY; }
 
 DropPLangStmt:  DROP PROCEDURAL LANGUAGE Sconst
                        {
-                               $$ = cat2_str(make1_str("drop procedural language"), $4);
+                               $$ = cat2_str(make_str("drop procedural language"), $4);
                        }
                ;
 
@@ -1572,12 +2145,20 @@ CreateTrigStmt:  CREATE TRIGGER name TriggerActionTime TriggerEvents ON
                                relation_name TriggerForSpec EXECUTE PROCEDURE
                                name '(' TriggerFuncArgs ')'
                                {
-                                       $$ = cat2_str(cat5_str(cat5_str(make1_str("create trigger"), $3, $4, $5, make1_str("on")), $7, $8, make1_str("execute procedure"), $11), make3_str(make1_str("("), $13, make1_str(")")));
+                                       $$ = cat_str(12, make_str("create trigger"), $3, $4, $5, make_str("on"), $7, $8, make_str("execute procedure"), $11, make_str("("), $13, make_str(")"));
+                               }
+       |       CREATE CONSTRAINT TRIGGER name AFTER TriggerEvents ON
+                                relation_name OptConstrFromTable
+                               ConstraintAttributeSpec
+                                FOR EACH ROW EXECUTE PROCEDURE
+                               name '(' TriggerFuncArgs ')'
+                               {
+                                       $$ = cat_str(13, make_str("create constraint trigger"), $4, make_str("after"), $6, make_str("on"), $8, $9, $10, make_str("for each row execute procedure"), $16, make_str("("), $18, make_str(")"));
                                }
                ;
 
-TriggerActionTime:  BEFORE                             { $$ = make1_str("before"); }
-                       | AFTER                         { $$ = make1_str("after"); }
+TriggerActionTime:  BEFORE                             { $$ = make_str("before"); }
+                       | AFTER                         { $$ = make_str("after"); }
                ;
 
 TriggerEvents: TriggerOneEvent
@@ -1586,39 +2167,39 @@ TriggerEvents:  TriggerOneEvent
                                }
                        | TriggerOneEvent OR TriggerOneEvent
                                {
-                                       $$ = cat3_str($1, make1_str("or"), $3);
+                                       $$ = cat_str(3, $1, make_str("or"), $3);
                                }
                        | TriggerOneEvent OR TriggerOneEvent OR TriggerOneEvent
                                {
-                                       $$ = cat5_str($1, make1_str("or"), $3, make1_str("or"), $5);
+                                       $$ = cat_str(5, $1, make_str("or"), $3, make_str("or"), $5);
                                }
                ;
 
-TriggerOneEvent:  INSERT                               { $$ = make1_str("insert"); }
-                       | DELETE                        { $$ = make1_str("delete"); }
-                       | UPDATE                        { $$ = make1_str("update"); }
+TriggerOneEvent:  INSERT                               { $$ = make_str("insert"); }
+                       | DELETE                        { $$ = make_str("delete"); }
+                       | UPDATE                        { $$ = make_str("update"); }
                ;
 
 TriggerForSpec:  FOR TriggerForOpt TriggerForType
                                {
-                                       $$ = cat3_str(make1_str("for"), $2, $3);
+                                       $$ = cat_str(3, make_str("for"), $2, $3);
                                }
                ;
 
-TriggerForOpt:  EACH                                   { $$ = make1_str("each"); }
-                       | /*EMPTY*/                     { $$ = make1_str(""); }
+TriggerForOpt:  EACH                                   { $$ = make_str("each"); }
+                       | /*EMPTY*/                     { $$ = EMPTY; }
                ;
 
-TriggerForType:  ROW                                   { $$ = make1_str("row"); }
-                       | STATEMENT                     { $$ = make1_str("statement"); }
+TriggerForType:  ROW                                   { $$ = make_str("row"); }
+                       | STATEMENT                     { $$ = make_str("statement"); }
                ;
 
 TriggerFuncArgs:  TriggerFuncArg
                                { $$ = $1; }
                        | TriggerFuncArgs ',' TriggerFuncArg
-                               { $$ = cat3_str($1, make1_str(","), $3); }
+                               { $$ = cat_str(3, $1, make_str(","), $3); }
                        | /*EMPTY*/
-                               { $$ = make1_str(""); }
+                               { $$ = EMPTY; }
                ;
 
 TriggerFuncArg:  Iconst
@@ -1633,9 +2214,61 @@ TriggerFuncArg:  Iconst
                        | ident         {  $$ = $1; }
                ;
 
+OptConstrFromTable:                     /* Empty */
+                                {
+                                        $$ = EMPTY;
+                                }
+                | FROM relation_name
+                                {
+                                        $$ = cat2_str(make_str("from"), $2);
+                                }
+                ;
+
+ConstraintAttributeSpec: ConstraintDeferrabilitySpec
+                {      $$ = $1; }
+       | ConstraintDeferrabilitySpec ConstraintTimeSpec
+               {
+                       if (strcmp($1, "deferrable") != 0 && strcmp($2, "initially deferrable") == 0 )
+                               mmerror(ET_ERROR, "INITIALLY DEFERRED constraint must be DEFERRABLE");
+
+                       $$ = cat2_str($1, $2);
+               }
+       | ConstraintTimeSpec
+               {       $$ = $1; }
+       | ConstraintTimeSpec ConstraintDeferrabilitySpec
+               {
+                       if (strcmp($2, "deferrable") != 0 && strcmp($1, "initially deferrable") == 0 )
+                               mmerror(ET_ERROR, "INITIALLY DEFERRED constraint must be DEFERRABLE");
+
+                       $$ = cat2_str($1, $2);
+               }
+       | /* Empty */
+                       { $$ = 0; }
+       ;
+
+ConstraintDeferrabilitySpec: NOT DEFERRABLE
+                                {
+                                        $$ = make_str("not deferrable");
+                                }
+                | DEFERRABLE
+                                {
+                                        $$ = make_str("deferrable");
+                                }
+                ;
+
+ConstraintTimeSpec: INITIALLY IMMEDIATE
+                                {
+                                        $$ = make_str("initially immediate");
+                                }
+                | INITIALLY DEFERRED
+                                {
+                                        $$ = make_str("initially deferrable");
+                                }
+                ;
+
 DropTrigStmt:  DROP TRIGGER name ON relation_name
                                {
-                                       $$ = cat4_str(make1_str("drop trigger"), $3, make1_str("on"), $5);
+                                       $$ = cat_str(4, make_str("drop trigger"), $3, make_str("on"), $5);
                                }
                ;
 
@@ -1648,7 +2281,7 @@ DropTrigStmt:  DROP TRIGGER name ON relation_name
 
 DefineStmt:  CREATE def_type def_rest
                                {
-                                       $$ = cat3_str(make1_str("create"), $2, $3);
+                                       $$ = cat_str(3, make_str("create"), $2, $3);
                                }
                ;
 
@@ -1658,27 +2291,26 @@ def_rest:  def_name definition
                                }
                ;
 
-def_type:  OPERATOR            { $$ = make1_str("operator"); }
-               | TYPE_P        { $$ = make1_str("type"); }
-               | AGGREGATE     { $$ = make1_str("aggregate"); }
+def_type:  OPERATOR            { $$ = make_str("operator"); }
+               | TYPE_P        { $$ = make_str("type"); }
+               | AGGREGATE     { $$ = make_str("aggregate"); }
                ;
 
-def_name:  PROCEDURE           { $$ = make1_str("procedure"); }
-               | JOIN          { $$ = make1_str("join"); }
+def_name:  PROCEDURE           { $$ = make_str("procedure"); }
+               | JOIN          { $$ = make_str("join"); }
                | ColId         { $$ = $1; }
-               | MathOp        { $$ = $1; }
-               | Op            { $$ = $1; }
+               | all_Op        { $$ = $1; }
                ;
 
-definition:  '(' def_list ')'                          { $$ = make3_str(make1_str("("), $2, make1_str(")")); }
+definition:  '(' def_list ')'                          { $$ = cat_str(3, make_str("("), $2, make_str(")")); }
                ;
 
 def_list:  def_elem                                    { $$ = $1; }
-               | def_list ',' def_elem                 { $$ = cat3_str($1, make1_str(","), $3); }
+               | def_list ',' def_elem                 { $$ = cat_str(3, $1, make_str(","), $3); }
                ;
 
 def_elem:  def_name '=' def_arg        {
-                                       $$ = cat3_str($1, make1_str("="), $3);
+                                       $$ = cat_str(3, $1, make_str("="), $3);
                                }
                | def_name
                                {
@@ -1686,7 +2318,7 @@ def_elem:  def_name '=' def_arg   {
                                }
                | DEFAULT '=' def_arg
                                {
-                                       $$ = cat2_str(make1_str("default ="), $3);
+                                       $$ = cat2_str(make_str("default ="), $3);
                                }
                ;
 
@@ -1696,28 +2328,38 @@ def_arg:  ColId                 {  $$ = $1; }
                | Sconst        {  $$ = $1; }
                | SETOF ColId
                                {
-                                       $$ = cat2_str(make1_str("setof"), $2);
+                                       $$ = cat2_str(make_str("setof"), $2);
                                }
                ;
 
 /*****************************************************************************
  *
  *             QUERY:
- *                             destroy <relname1> [, <relname2> .. <relnameN> ]
+ *                             drop <relname1> [, <relname2> .. <relnameN> ]
  *
  *****************************************************************************/
 
-DestroyStmt:  DROP TABLE relation_name_list
+DropStmt:  DROP TABLE relation_name_list
                                {
-                                       $$ = cat2_str(make1_str("drop table"), $3);
+                                       $$ = cat2_str(make_str("drop table"), $3);
                                }
                | DROP SEQUENCE relation_name_list
                                {
-                                       $$ = cat2_str(make1_str("drop sequence"), $3);
+                                       $$ = cat2_str(make_str("drop sequence"), $3);
                                }
                ;
 
-
+/*****************************************************************************
+ *
+ *             QUERY:
+ *                             truncate table relname
+ *
+ *****************************************************************************/
+TruncateStmt:  TRUNCATE opt_table relation_name
+                               {
+                                       $$ = cat2_str(make_str("drop table"), $3);
+                               }
+                       ;
 
 /*****************************************************************************
  *
@@ -1728,44 +2370,131 @@ DestroyStmt:  DROP TABLE relation_name_list
  *
  *****************************************************************************/
 
-FetchStmt:     FETCH opt_direction fetch_how_many opt_portal_name INTO into_list
+FetchStmt:     FETCH direction fetch_how_many from_in name INTO into_list
                                {
-                                       if (strncmp($2, "relative", strlen("relative")) == 0 && atol($3) == 0L)
-                                               yyerror("FETCH/RELATIVE at current position is not supported");
+                                       if (strcmp($2, "relative") == 0 && atol($3) == 0L)
+                                               mmerror(ET_ERROR, "FETCH/RELATIVE at current position is not supported");
 
-                                       $$ = cat4_str(make1_str("fetch"), $2, $3, $4);
+                                       $$ = cat_str(5, make_str("fetch"), $2, $3, $4, $5);
+                               }
+               |       FETCH fetch_how_many from_in name INTO into_list
+                               {
+                                       $$ = cat_str(4, make_str("fetch"), $2, $3, $4);
+                               }
+               |       FETCH direction from_in name INTO into_list
+                               {
+                                       $$ = cat_str(4, make_str("fetch"), $2, $3, $4);
+                               }
+               |       FETCH from_in name INTO into_list
+                               {
+                                       $$ = cat_str(3, make_str("fetch"), $2, $3);
+                               }
+               |       FETCH name INTO into_list
+                               {
+                                       $$ = cat2_str(make_str("fetch"), $2);
+                               }
+               |       MOVE direction fetch_how_many from_in name
+                               {
+                                       $$ = cat_str(5, make_str("move"), $2, $3, $4, $5);
+                               }
+               |       MOVE fetch_how_many from_in name
+                               {
+                                       $$ = cat_str(4, make_str("move"), $2, $3, $4);
+                               }
+               |       MOVE direction from_in name
+                               {
+                                       $$ = cat_str(4, make_str("move"), $2, $3, $4);
+                               }
+               |       MOVE from_in name
+                               {
+                                       $$ = cat_str(3, make_str("move"), $2, $3);
                                }
-               |       MOVE opt_direction fetch_how_many opt_portal_name
+               |       MOVE name
                                {
-                                       $$ = cat4_str(make1_str("fetch"), $2, $3, $4);
+                                       $$ = cat2_str(make_str("move"), $2);
                                }
                ;
 
-opt_direction: FORWARD         { $$ = make1_str("forward"); }
-               | BACKWARD      { $$ = make1_str("backward"); }
-               | RELATIVE      { $$ = make1_str("relative"); }
-                | ABSOLUTE
-                               {
-                                       fprintf(stderr, "FETCH/ABSOLUTE not supported, using RELATIVE");
-                                       $$ = make1_str("absolute");
+direction:     FORWARD         { $$ = make_str("forward"); }
+               | BACKWARD      { $$ = make_str("backward"); }
+               | RELATIVE      { $$ = make_str("relative"); }
+                | ABSOLUTE     {
+                                       mmerror(ET_WARN, "FETCH/ABSOLUTE not supported, backend will use RELATIVE");
+                                       $$ = make_str("absolute");
                                }
-               | /*EMPTY*/     { $$ = make1_str(""); /* default */ }
                ;
 
 fetch_how_many:   Iconst        { $$ = $1; }
-               | '-' Iconst    { $$ = make2_str(make1_str("-"), $2); }
-               | ALL           { $$ = make1_str("all"); }
-               | NEXT          { $$ = make1_str("next"); }
-               | PRIOR         { $$ = make1_str("prior"); }
-               | /*EMPTY*/     { $$ = make1_str(""); /*default*/ }
+               | '-' Iconst    { $$ = cat2_str(make_str("-"), $2); }
+               | ALL           { $$ = make_str("all"); }
+               | NEXT          { $$ = make_str("next"); }
+               | PRIOR         { $$ = make_str("prior"); }
                ;
 
-opt_portal_name:  IN name              { $$ = cat2_str(make1_str("in"), $2); }
-               | FROM name             { $$ = cat2_str(make1_str("from"), $2); }
-/*             | name                  { $$ = cat2_str(make1_str("in"), $1); */
-               | /*EMPTY*/             { $$ = make1_str(""); }
+from_in:   IN  { $$ = make_str("in"); }
+       | FROM  { $$ = make_str("from"); }
+       ;
+
+/*****************************************************************************
+ *
+ *  The COMMENT ON statement can take different forms based upon the type of
+ *  the object associated with the comment. The form of the statement is:
+ *
+ *  COMMENT ON [ [ DATABASE | INDEX | RULE | SEQUENCE | TABLE | TYPE | VIEW ]
+ *               <objname> | AGGREGATE <aggname> <aggtype> | FUNCTION
+ *              <funcname> (arg1, arg2, ...) | OPERATOR <op>
+ *              (leftoperand_typ rightoperand_typ) | TRIGGER <triggername> ON
+ *              <relname> ] IS 'text'
+ *
+ *****************************************************************************/
+CommentStmt:   COMMENT ON comment_type name IS comment_text
+                        {
+                                $$ = cat_str(5, make_str("comment on"), $3, $4, make_str("is"), $6);
+                        }
+                | COMMENT ON comment_cl relation_name '.' attr_name IS comment_text
+                        { 
+                                $$ = cat_str(7, make_str("comment on"), $3, $4, make_str("."), $6, make_str("is"), $8);
+                       }
+                | COMMENT ON comment_ag name aggr_argtype IS comment_text
+                        {
+                                $$ = cat_str(6, make_str("comment on"), $3, $4, $5, make_str("is"), $7);
+                       }
+               | COMMENT ON comment_fn func_name func_args IS comment_text
+                       {
+                                $$ = cat_str(6, make_str("comment on"), $3, $4, $5, make_str("is"), $7);
+                       }
+               | COMMENT ON comment_op all_Op '(' oper_argtypes ')' IS comment_text
+                       {
+                               $$ = cat_str(7, make_str("comment on"), $3, $4, make_str("("), $6, make_str(") is"), $9);
+                       }
+               | COMMENT ON comment_tg name ON relation_name IS comment_text
+                        {
+                                $$ = cat_str(7, make_str("comment on"), $3, $4, make_str("on"), $6, make_str("is"), $8);
+                       }
+                       ;
+
+comment_type:  DATABASE        { $$ = make_str("database"); }
+                | INDEX                { $$ = make_str("idnex"); }
+                | RULE         { $$ = make_str("rule"); }
+                | SEQUENCE     { $$ = make_str("sequence"); }
+                | TABLE                { $$ = make_str("table"); }
+                | TYPE_P       { $$ = make_str("type"); }
+                | VIEW         { $$ = make_str("view"); }
                ;
 
+comment_cl:    COLUMN          { $$ = make_str("column"); }
+
+comment_ag:    AGGREGATE       { $$ = make_str("aggregate"); }
+
+comment_fn:    FUNCTION                { $$ = make_str("function"); }
+
+comment_op:    OPERATOR                { $$ = make_str("operator"); }
+
+comment_tg:    TRIGGER         { $$ = make_str("trigger"); }
+
+comment_text:    Sconst                { $$ = $1; }
+               | NULL_P                { $$ = make_str("null"); }
+               ;
 
 /*****************************************************************************
  *
@@ -1776,17 +2505,17 @@ opt_portal_name:  IN name               { $$ = cat2_str(make1_str("in"), $2); }
 
 GrantStmt:  GRANT privileges ON relation_name_list TO grantee opt_with_grant
                                {
-                                       $$ = cat2_str(cat5_str(make1_str("grant"), $2, make1_str("on"), $4, make1_str("to")), $6);
+                                       $$ = cat_str(7, make_str("grant"), $2, make_str("on"), $4, make_str("to"), $6);
                                }
                ;
 
 privileges:  ALL PRIVILEGES
                                {
-                                $$ = make1_str("all privileges");
+                                $$ = make_str("all privileges");
                                }
                | ALL
                                {
-                                $$ = make1_str("all");
+                                $$ = make_str("all");
                                }
                | operation_commalist
                                {
@@ -1800,39 +2529,39 @@ operation_commalist:  operation
                                }
                | operation_commalist ',' operation
                                {
-                                               $$ = cat3_str($1, make1_str(","), $3);
+                                               $$ = cat_str(3, $1, make_str(","), $3);
                                }
                ;
 
 operation:  SELECT
                                {
-                                               $$ = make1_str("select");
+                                               $$ = make_str("select");
                                }
                | INSERT
                                {
-                                               $$ = make1_str("insert");
+                                               $$ = make_str("insert");
                                }
                | UPDATE
                                {
-                                               $$ = make1_str("update");
+                                               $$ = make_str("update");
                                }
                | DELETE
                                {
-                                               $$ = make1_str("delete");
+                                               $$ = make_str("delete");
                                }
                | RULE
                                {
-                                               $$ = make1_str("rule");
+                                               $$ = make_str("rule");
                                }
                ;
 
 grantee:  PUBLIC
                                {
-                                               $$ = make1_str("public");
+                                               $$ = make_str("public");
                                }
                | GROUP ColId
                                {
-                                               $$ = cat2_str(make1_str("group"), $2);
+                                               $$ = cat2_str(make_str("group"), $2);
                                }
                | ColId
                                {
@@ -1842,7 +2571,7 @@ grantee:  PUBLIC
 
 opt_with_grant:  WITH GRANT OPTION
                                {
-                                       yyerror("WITH GRANT OPTION is not supported.  Only relation owners can set privileges");
+                                       mmerror(ET_ERROR, "WITH GRANT OPTION is not supported.  Only relation owners can set privileges");
                                 }
                | /*EMPTY*/ 
                ;
@@ -1857,7 +2586,7 @@ opt_with_grant:  WITH GRANT OPTION
 
 RevokeStmt:  REVOKE privileges ON relation_name_list FROM grantee
                                {
-                                       $$ = cat2_str(cat5_str(make1_str("revoke"), $2, make1_str("on"), $4, make1_str("from")), $6);
+                                       $$ = cat_str(7, make_str("revoke"), $2, make_str("on"), $4, make_str("from"), $6);
                                }
                ;
 
@@ -1878,41 +2607,41 @@ IndexStmt:      CREATE index_opt_unique INDEX index_name ON relation_name
                                {
                                        /* should check that access_method is valid,
                                           etc ... but doesn't */
-                                       $$ = cat5_str(cat5_str(make1_str("create"), $2, make1_str("index"), $4, make1_str("on")), $6, $7, make3_str(make1_str("("), $9, make1_str(")")), $11);
+                                       $$ = cat_str(11, make_str("create"), $2, make_str("index"), $4, make_str("on"), $6, $7, make_str("("), $9, make_str(")"), $11);
                                }
                ;
 
-index_opt_unique:  UNIQUE      { $$ = make1_str("unique"); }
-               | /*EMPTY*/     { $$ = make1_str(""); }
+index_opt_unique:  UNIQUE      { $$ = make_str("unique"); }
+               | /*EMPTY*/     { $$ = EMPTY; }
                ;
 
-access_method_clause:  USING access_method     { $$ = cat2_str(make1_str("using"), $2); }
-               | /*EMPTY*/                     { $$ = make1_str(""); }
+access_method_clause:  USING access_method     { $$ = cat2_str(make_str("using"), $2); }
+               | /*EMPTY*/                     { $$ = EMPTY; }
                ;
 
 index_params:  index_list                      { $$ = $1; }
                | func_index                    { $$ = $1; }
                ;
 
-index_list:  index_list ',' index_elem         { $$ = cat3_str($1, make1_str(","), $3); }
+index_list:  index_list ',' index_elem         { $$ = cat_str(3, $1, make_str(","), $3); }
                | index_elem                    { $$ = $1; }
                ;
 
 func_index:  func_name '(' name_list ')' opt_type opt_class
                                {
-                                       $$ = cat4_str($1, make3_str(make1_str("("), $3, ")"), $5, $6);
+                                       $$ = cat_str(6, $1, make_str("("), $3, ")", $5, $6);
                                }
                  ;
 
 index_elem:  attr_name opt_type opt_class
                                {
-                                       $$ = cat3_str($1, $2, $3);
+                                       $$ = cat_str(3, $1, $2, $3);
                                }
                ;
 
-opt_type:  ':' Typename                { $$ = cat2_str(make1_str(":"), $2); }
-               | FOR Typename  { $$ = cat2_str(make1_str("for"), $2); }
-               | /*EMPTY*/     { $$ = make1_str(""); }
+opt_type:  ':' Typename                { $$ = cat2_str(make_str(":"), $2); }
+               | FOR Typename  { $$ = cat2_str(make_str("for"), $2); }
+               | /*EMPTY*/     { $$ = EMPTY; }
                ;
 
 /* opt_class "WITH class" conflicts with preceeding opt_type
@@ -1922,8 +2651,8 @@ opt_type:  ':' Typename           { $$ = cat2_str(make1_str(":"), $2); }
  *             | WITH class                                                    { $$ = $2; }
  */
 opt_class:  class                              { $$ = $1; }
-               | USING class                   { $$ = cat2_str(make1_str("using"), $2); }
-               | /*EMPTY*/                     { $$ = make1_str(""); }
+               | USING class                   { $$ = cat2_str(make_str("using"), $2); }
+               | /*EMPTY*/                     { $$ = EMPTY; }
                ;
 
 /*****************************************************************************
@@ -1935,7 +2664,7 @@ opt_class:  class                         { $$ = $1; }
 
 ExtendStmt:  EXTEND INDEX index_name where_clause
                                {
-                                       $$ = cat3_str(make1_str("extend index"), $3, $4);
+                                       $$ = cat_str(3, make_str("extend index"), $3, $4);
                                }
                ;
 
@@ -1946,13 +2675,13 @@ ExtendStmt:  EXTEND INDEX index_name where_clause
  *                             execute recipe <recipeName>
  *
  *****************************************************************************/
-
+/* NOT USED
 RecipeStmt:  EXECUTE RECIPE recipe_name
                                {
-                                       $$ = cat2_str(make1_str("execute recipe"), $3);
+                                       $$ = cat2_str(make_str("execute recipe"), $3);
                                }
                ;
-
+*/
 /*****************************************************************************
  *
  *             QUERY:
@@ -1970,32 +2699,35 @@ RecipeStmt:  EXECUTE RECIPE recipe_name
  *****************************************************************************/
 
 ProcedureStmt: CREATE FUNCTION func_name func_args
-                        RETURNS func_return opt_with AS Sconst LANGUAGE Sconst
+                        RETURNS func_return opt_with AS func_as LANGUAGE Sconst
                                {
-                                       $$ = cat2_str(cat5_str(cat5_str(make1_str("create function"), $3, $4, make1_str("returns"), $6), $7, make1_str("as"), $9, make1_str("language")), $11);
+                                       $$ = cat_str(10, make_str("create function"), $3, $4, make_str("returns"), $6, $7, make_str("as"), $9, make_str("language"), $11);
                                }
 
-opt_with:  WITH definition                     { $$ = cat2_str(make1_str("with"), $2); }
-               | /*EMPTY*/                     { $$ = make1_str(""); }
+opt_with:  WITH definition                     { $$ = cat2_str(make_str("with"), $2); }
+               | /*EMPTY*/                     { $$ = EMPTY; }
                ;
 
-func_args:  '(' func_args_list ')'             { $$ = make3_str(make1_str("("), $2, make1_str(")")); }
-               | '(' ')'                       { $$ = make1_str("()"); }
+func_args:  '(' func_args_list ')'             { $$ = cat_str(3, make_str("("), $2, make_str(")")); }
+               | '(' ')'                       { $$ = make_str("()"); }
                ;
 
 func_args_list:  TypeId                                { $$ = $1; }
                | func_args_list ',' TypeId
-                               {       $$ = cat3_str($1, make1_str(","), $3); }
+                               {       $$ = cat_str(3, $1, make_str(","), $3); }
                ;
 
+func_as: Sconst                                { $$ = $1; }
+               | Sconst ',' Sconst     { $$ = cat_str(3, $1, make_str(","), $3); }
+
 func_return:  set_opt TypeId
                                {
                                        $$ = cat2_str($1, $2);
                                }
                ;
 
-set_opt:  SETOF                                        { $$ = make1_str("setof"); }
-               | /*EMPTY*/                     { $$ = make1_str(""); }
+set_opt:  SETOF                                        { $$ = make_str("setof"); }
+               | /*EMPTY*/                     { $$ = EMPTY; }
                ;
 
 
@@ -2018,62 +2750,51 @@ set_opt:  SETOF                                 { $$ = make1_str("setof"); }
 
 RemoveStmt:  DROP remove_type name
                                {
-                                       $$ = cat3_str(make1_str("drop"), $2, $3);;
+                                       $$ = cat_str(3, make_str("drop"), $2, $3);
                                }
                ;
 
-remove_type:  TYPE_P           {  $$ = make1_str("type"); }
-               | INDEX         {  $$ = make1_str("index"); }
-               | RULE          {  $$ = make1_str("rule"); }
-               | VIEW          {  $$ = make1_str("view"); }
+remove_type:  TYPE_P           {  $$ = make_str("type"); }
+               | INDEX         {  $$ = make_str("index"); }
+               | RULE          {  $$ = make_str("rule"); }
+               | VIEW          {  $$ = make_str("view"); }
                ;
 
 
 RemoveAggrStmt:  DROP AGGREGATE name aggr_argtype
                                {
-                                               $$ = cat3_str(make1_str("drop aggregate"), $3, $4);
+                                               $$ = cat_str(3, make_str("drop aggregate"), $3, $4);
                                }
                ;
 
 aggr_argtype:  name                    { $$ = $1; }
-               | '*'                   { $$ = make1_str("*"); }
+               | '*'                   { $$ = make_str("*"); }
                ;
 
 
 RemoveFuncStmt:  DROP FUNCTION func_name func_args
                                {
-                                               $$ = cat3_str(make1_str("drop function"), $3, $4);
+                                               $$ = cat_str(3, make_str("drop function"), $3, $4);
                                }
                ;
 
 
 RemoveOperStmt:  DROP OPERATOR all_Op '(' oper_argtypes ')'
                                {
-                                       $$ = cat3_str(make1_str("drop operator"), $3, make3_str(make1_str("("), $5, make1_str(")")));
+                                       $$ = cat_str(5, make_str("drop operator"), $3, make_str("("), $5, make_str(")"));
                                }
                ;
 
-all_Op:  Op | MathOp;
-
-MathOp:        '+'                             { $$ = make1_str("+"); }
-               | '-'                   { $$ = make1_str("-"); }
-               | '*'                   { $$ = make1_str("*"); }
-               | '/'                   { $$ = make1_str("/"); }
-               | '<'                   { $$ = make1_str("<"); }
-               | '>'                   { $$ = make1_str(">"); }
-               | '='                   { $$ = make1_str("="); }
-               ;
-
 oper_argtypes: name
                                {
-                                  yyerror("parser: argument type missing (use NONE for unary operators)");
+                                  mmerror(ET_ERROR, "parser: argument type missing (use NONE for unary operators)");
                                }
                | name ',' name
-                               { $$ = cat3_str($1, make1_str(","), $3); }
+                               { $$ = cat_str(3, $1, make_str(","), $3); }
                | NONE ',' name                 /* left unary */
-                               { $$ = cat2_str(make1_str("none,"), $3); }
+                               { $$ = cat2_str(make_str("none,"), $3); }
                | name ',' NONE                 /* right unary */
-                               { $$ = cat2_str($1, make1_str(", none")); }
+                               { $$ = cat2_str($1, make_str(", none")); }
                ;
 
 
@@ -2088,16 +2809,16 @@ oper_argtypes:  name
 RenameStmt:  ALTER TABLE relation_name opt_inh_star
                                  RENAME opt_column opt_name TO name
                                {
-                                       $$ = cat4_str(cat5_str(make1_str("alter table"), $3, $4, make1_str("rename"), $6), $7, make1_str("to"), $9);
+                                       $$ = cat_str(8, make_str("alter table"), $3, $4, make_str("rename"), $6, $7, make_str("to"), $9);
                                }
                ;
 
 opt_name:  name                                                        { $$ = $1; }
-               | /*EMPTY*/                                     { $$ = make1_str(""); }
+               | /*EMPTY*/                                     { $$ = EMPTY; }
                ;
 
-opt_column:  COLUMN                                    { $$ = make1_str("colmunn"); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+opt_column:  COLUMN                                    { $$ = make_str("colmunn"); }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
 
@@ -2113,43 +2834,38 @@ opt_column:  COLUMN                                     { $$ = make1_str("colmunn"); }
 RuleStmt:  CREATE RULE name AS
                   { QueryIsRule=1; }
                   ON event TO event_object where_clause
-                  DO opt_instead OptStmtList
+                  DO opt_instead RuleActionList
                                {
-                                       $$ = cat2_str(cat5_str(cat5_str(make1_str("create rule"), $3, make1_str("as on"), $7, make1_str("to")), $9, $10, make1_str("do"), $12), $13);
+                                       $$ = cat_str(10, make_str("create rule"), $3, make_str("as on"), $7, make_str("to"), $9, $10, make_str("do"), $12, $13);
                                }
                ;
 
-OptStmtList:  NOTHING                                  { $$ = make1_str("nothing"); }
-               | OptimizableStmt                       { $$ = $1; }
-               | '[' OptStmtBlock ']'                  { $$ = cat3_str(make1_str("["), $2, make1_str("]")); }
-/***S*I*D***/
-/* We comment this out because it produces a shift / reduce conflict 
- * with the select_w_o_sort rule */
-/*             | '(' OptStmtBlock ')'                  { $$ = cat3_str(make1_str("("), $2, make1_str(")")); }*/
-               ;
+RuleActionList:  NOTHING                               { $$ = make_str("nothing"); }
+               | SelectStmt                            { $$ = $1; }
+               | RuleActionStmt                        { $$ = $1; }
+               | '[' RuleActionMulti ']'               { $$ = cat_str(3, make_str("["), $2, make_str("]")); }
+               | '(' RuleActionMulti ')'               { $$ = cat_str(3, make_str("("), $2, make_str(")")); }
+                ;
 
-OptStmtBlock:  OptStmtMulti
-                               {  $$ = $1; }
-               | OptimizableStmt
-                               { $$ = $1; }
+/* the thrashing around here is to discard "empty" statements... */
+RuleActionMulti:  RuleActionMulti ';' RuleActionStmtOrEmpty
+                                {  $$ = cat_str(3, $1, make_str(";"), $3); }
+               | RuleActionStmtOrEmpty
+                               { $$ = cat2_str($1, make_str(";")); }
                ;
 
-OptStmtMulti:  OptStmtMulti OptimizableStmt ';'
-                               {  $$ = cat3_str($1, $2, make1_str(";")); }
-/***S*I***/
-/* We comment the next rule because it seems to be redundant
- * and produces 16 shift/reduce conflicts with the new SelectStmt rule
- * needed for EXCEPT and INTERSECT. So far I did not notice any
- * violations by removing the rule! */
-/*             | OptStmtMulti OptimizableStmt
-                               {  $$ = cat2_str($1, $2); }*/
-               | OptimizableStmt ';'
-                               { $$ = cat2_str($1, make1_str(";")); }
-               ;
+RuleActionStmt:        InsertStmt
+                | UpdateStmt
+                | DeleteStmt
+               | NotifyStmt
+                ;
+RuleActionStmtOrEmpty: RuleActionStmt  { $$ = $1; }
+               |       /*EMPTY*/        { $$ = EMPTY; }
+               ;
 
 event_object:  relation_name '.' attr_name
                                {
-                                       $$ = make3_str($1, make1_str("."), $3);
+                                       $$ = make3_str($1, make_str("."), $3);
                                }
                | relation_name
                                {
@@ -2158,14 +2874,14 @@ event_object:  relation_name '.' attr_name
                ;
 
 /* change me to select, update, etc. some day */
-event: SELECT                                  { $$ = make1_str("select"); }
-               | UPDATE                        { $$ = make1_str("update"); }
-               | DELETE                        { $$ = make1_str("delete"); }
-               | INSERT                        { $$ = make1_str("insert"); }
+event: SELECT                                  { $$ = make_str("select"); }
+               | UPDATE                        { $$ = make_str("update"); }
+               | DELETE                        { $$ = make_str("delete"); }
+               | INSERT                        { $$ = make_str("insert"); }
                 ;
 
-opt_instead:  INSTEAD                                  { $$ = make1_str("instead"); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+opt_instead:  INSTEAD                                  { $$ = make_str("instead"); }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
 
@@ -2179,23 +2895,23 @@ opt_instead:  INSTEAD                                   { $$ = make1_str("instead"); }
 
 NotifyStmt:  NOTIFY relation_name
                                {
-                                       $$ = cat2_str(make1_str("notify"), $2);
+                                       $$ = cat2_str(make_str("notify"), $2);
                                }
                ;
 
 ListenStmt:  LISTEN relation_name
                                {
-                                       $$ = cat2_str(make1_str("listen"), $2);
+                                       $$ = cat2_str(make_str("listen"), $2);
                                 }
 ;
 
 UnlistenStmt:  UNLISTEN relation_name
                                {
-                                       $$ = cat2_str(make1_str("unlisten"), $2);
+                                       $$ = cat2_str(make_str("unlisten"), $2);
                                 }
                | UNLISTEN '*'
                                {
-                                       $$ = make1_str("unlisten *");
+                                       $$ = make_str("unlisten *");
                                 }
 ;
 
@@ -2203,19 +2919,15 @@ UnlistenStmt:  UNLISTEN relation_name
  *
  *              Transactions:
  *
- *              abort transaction
- *                              (ABORT)
- *              begin transaction
- *                              (BEGIN)
- *              end transaction  
- *                              (END)
+ *       BEGIN / COMMIT / ROLLBACK
+ *      (also older versions END / ABORT)
  *
  *****************************************************************************/
-TransactionStmt:  ABORT_TRANS opt_trans        { $$ = make1_str("rollback"); }
-       | BEGIN_TRANS opt_trans         { $$ = make1_str("begin transaction"); }
-       | COMMIT opt_trans              { $$ = make1_str("commit"); }
-       | END_TRANS opt_trans                   { $$ = make1_str("commit"); }
-       | ROLLBACK opt_trans                    { $$ = make1_str("rollback"); }
+TransactionStmt:  ABORT_TRANS opt_trans        { $$ = make_str("rollback"); }
+       | BEGIN_TRANS opt_trans         { $$ = make_str("begin transaction"); }
+       | COMMIT opt_trans              { $$ = make_str("commit"); }
+       | END_TRANS opt_trans                   { $$ = make_str("commit"); }
+       | ROLLBACK opt_trans                    { $$ = make_str("rollback"); }
 
 opt_trans: WORK        { $$ = ""; }
        | TRANSACTION   { $$ = ""; }
@@ -2231,7 +2943,7 @@ opt_trans: WORK   { $$ = ""; }
 
 ViewStmt:  CREATE VIEW name AS SelectStmt
                                {
-                                       $$ = cat4_str(make1_str("create view"), $3, make1_str("as"), $5);
+                                       $$ = cat_str(4, make_str("create view"), $3, make_str("as"), $5);
                                }
                ;
 
@@ -2239,68 +2951,76 @@ ViewStmt:  CREATE VIEW name AS SelectStmt
 /*****************************************************************************
  *
  *             QUERY:
- *                             load make1_str("filename")
+ *                             load make_str("filename")
  *
  *****************************************************************************/
 
 LoadStmt:  LOAD file_name
                                {
-                                       $$ = cat2_str(make1_str("load"), $2);
+                                       $$ = cat2_str(make_str("load"), $2);
                                }
                ;
 
 
 /*****************************************************************************
  *
- *             QUERY:
- *                             createdb dbname
+ *             CREATE DATABASE
+ *
  *
  *****************************************************************************/
 
-CreatedbStmt:  CREATE DATABASE database_name WITH opt_database1 opt_database2
-                               {
-                                       if (strlen($5) == 0 || strlen($6) == 0) 
-                                               yyerror("CREATE DATABASE WITH requires at least an option");
-#ifndef MULTIBYTE
-                                       if (strlen($6) != 0)
-                                               yyerror("WITH ENCODING is not supported");
-#endif
-                                       $$ = cat5_str(make1_str("create database"), $3, make1_str("with"), $5, $6);
-                               }
+CreatedbStmt:  CREATE DATABASE database_name WITH createdb_opt_location createdb_opt_encoding
+                       {
+                               if (strlen($5) == 0 || strlen($6) == 0) 
+                                       mmerror(ET_ERROR, "CREATE DATABASE WITH requires at least an option");
+
+                               $$ = cat_str(5, make_str("create database"), $3, make_str("with"), $5, $6);
+                       }
                | CREATE DATABASE database_name
-                               {
-                                       $$ = cat2_str(make1_str("create database"), $3);
-                               }
+                               {
+                               $$ = cat2_str(make_str("create database"), $3);
+                       }
                ;
 
-opt_database1:  LOCATION '=' location  { $$ = cat2_str(make1_str("location ="), $3); }
-               | /*EMPTY*/                     { $$ = make1_str(""); }
+createdb_opt_location:  LOCATION '=' Sconst    { $$ = cat2_str(make_str("location ="), $3); }
+               | LOCATION '=' DEFAULT          { $$ = make_str("location = default"); }
+               | /*EMPTY*/                     { $$ = EMPTY; }
                ;
 
-opt_database2:  ENCODING '=' encoding   { $$ = cat2_str(make1_str("encoding ="), $3); }
+createdb_opt_encoding:  ENCODING '=' Sconst  
+                       {
+#ifndef MULTIBYTE
+                               mmerror(ET_ERROR, "Multi-byte support is not enabled.");
+#endif
+                               $$ = cat2_str(make_str("encoding ="), $3);
+                       }
+               | ENCODING '=' Iconst  
+                       {
+#ifndef MULTIBYTE
+                               mmerror(ET_ERROR, "Multi-byte support is not enabled.");
+#endif
+                               $$ = cat2_str(make_str("encoding ="), $3);
+                       }
+               | ENCODING '=' DEFAULT
+                       {
+#ifndef MULTIBYTE
+                               mmerror(ET_ERROR, "Multi-byte support is not enabled.");
+#endif
+                               $$ = make_str("encoding = default");
+                       }
                 | /*EMPTY*/            { $$ = NULL; }
                 ;
 
-location:  Sconst                              { $$ = $1; }
-               | DEFAULT                       { $$ = make1_str("default"); }
-               | /*EMPTY*/                     { $$ = make1_str(""); }
-               ;
-
-encoding:  Sconst              { $$ = $1; }
-               | DEFAULT       { $$ = make1_str("default"); }
-               | /*EMPTY*/     { $$ = make1_str(""); }
-               ;
-
 /*****************************************************************************
  *
- *             QUERY:
- *                             destroydb dbname
+ *             DROP DATABASE
+ *
  *
  *****************************************************************************/
 
-DestroydbStmt: DROP DATABASE database_name
+DropdbStmt:    DROP DATABASE database_name
                                {
-                                       $$ = cat2_str(make1_str("drop database"), $3);
+                                       $$ = cat2_str(make_str("drop database"), $3);
                                }
                ;
 
@@ -2314,7 +3034,7 @@ DestroydbStmt:    DROP DATABASE database_name
 
 ClusterStmt:  CLUSTER index_name ON relation_name
                                {
-                                  $$ = cat4_str(make1_str("cluster"), $2, make1_str("on"), $4);
+                                  $$ = cat_str(4, make_str("cluster"), $2, make_str("on"), $4);
                                }
                ;
 
@@ -2328,32 +3048,32 @@ ClusterStmt:  CLUSTER index_name ON relation_name
 
 VacuumStmt:  VACUUM opt_verbose opt_analyze
                                {
-                                       $$ = cat3_str(make1_str("vacuum"), $2, $3);
+                                       $$ = cat_str(3, make_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 \"(\"");
-                                       $$ = cat5_str(make1_str("vacuum"), $2, $3, $4, $5);
+                                               mmerror(ET_ERROR, "VACUUM syntax error at or near \"(\"\n\tRelations name must be specified");
+                                       $$ = cat_str(5, make_str("vacuum"), $2, $3, $4, $5);
                                }
                ;
 
-opt_verbose:  VERBOSE                                  { $$ = make1_str("verbose"); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+opt_verbose:  VERBOSE                                  { $$ = make_str("verbose"); }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
-opt_analyze:  ANALYZE                                  { $$ = make1_str("analyse"); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+opt_analyze:  ANALYZE                                  { $$ = make_str("analyse"); }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
-opt_va_list:  '(' va_list ')'                          { $$ = make3_str(make1_str("("), $2, make1_str(")")); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+opt_va_list:  '(' va_list ')'                          { $$ = cat_str(3, make_str("("), $2, make_str(")")); }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
 va_list:  name
                                { $$=$1; }
                | va_list ',' name
-                               { $$=cat3_str($1, make1_str(","), $3); }
+                               { $$=cat_str(3, $1, make_str(","), $3); }
                ;
 
 
@@ -2366,7 +3086,7 @@ va_list:  name
 
 ExplainStmt:  EXPLAIN opt_verbose OptimizableStmt
                                {
-                                       $$ = cat3_str(make1_str("explain"), $2, $3);
+                                       $$ = cat_str(3, make_str("explain"), $2, $3);
                                }
                ;
 
@@ -2398,7 +3118,6 @@ OptimizableStmt:  SelectStmt
  *
  *****************************************************************************/
 
-/***S*I***/
 /* This rule used 'opt_column_list' between 'relation_name' and 'insert_rest'
  * originally. When the second rule of 'insert_rest' was changed to use
  * the new 'SelectStmt' rule (for INTERSECT and EXCEPT) it produced a shift/red uce
@@ -2406,39 +3125,48 @@ OptimizableStmt:  SelectStmt
  * the same statements without any shift/reduce conflicts */
 InsertStmt:  INSERT INTO relation_name insert_rest
                                {
-                                       $$ = cat3_str(make1_str("insert into"), $3, $4);
+                                       $$ = cat_str(3, make_str("insert into"), $3, $4);
                                }
                ;
 
-insert_rest:  VALUES '(' res_target_list2 ')'
+insert_rest:  VALUES '(' target_list ')'
                                {
-                                       $$ = make3_str(make1_str("values("), $3, make1_str(")"));
+                                       $$ = cat_str(3, make_str("values("), $3, make_str(")"));
                                }
                | DEFAULT VALUES
                                {
-                                       $$ = make1_str("default values");
+                                       $$ = make_str("default values");
                                }
+/* We want the full power of SelectStatements including INTERSECT and EXCEPT
+ * for insertion.  However, we can't support sort or limit clauses.
+ */
                | SelectStmt
                                {
-                                       $$ = $1
+                                       if (FoundSort != 0)
+                                               mmerror(ET_ERROR, "ORDER BY is not allowed in INSERT/SELECT");
+
+                                       $$ = $1;
                                }
-               | '(' columnList ')' VALUES '(' res_target_list2 ')'
+               | '(' columnList ')' VALUES '(' target_list ')'
                                {
-                                       $$ = make5_str(make1_str("("), $2, make1_str(") values ("), $6, make1_str(")"));
+                                       $$ = cat_str(5, make_str("("), $2, make_str(") values ("), $6, make_str(")"));
                                }
                | '(' columnList ')' SelectStmt
                                {
-                                       $$ = make4_str(make1_str("("), $2, make1_str(")"), $4);
+                                       if (FoundSort != 0)
+                                               mmerror(ET_ERROR, "ORDER BY is not all owed in INSERT/SELECT");
+
+                                       $$ = cat_str(4, make_str("("), $2, make_str(")"), $4);
                                }
                ;
 
-opt_column_list:  '(' columnList ')'                   { $$ = make3_str(make1_str("("), $2, make1_str(")")); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+opt_column_list:  '(' columnList ')'                   { $$ = cat_str(3, make_str("("), $2, make_str(")")); }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
 columnList:
                  columnList ',' columnElem
-                               { $$ = cat3_str($1, make1_str(","), $3); }
+                               { $$ = cat_str(3, $1, make_str(","), $3); }
                | columnElem
                                { $$ = $1; }
                ;
@@ -2460,88 +3188,29 @@ columnElem:  ColId opt_indirection
 DeleteStmt:  DELETE FROM relation_name
                         where_clause
                                {
-                                       $$ = cat3_str(make1_str("delete from"), $3, $4);
+                                       $$ = cat_str(3, make_str("delete from"), $3, $4);
                                }
                ;
 
-LockStmt:  LOCK_P opt_table relation_name
-                               {
-                                       $$ = cat3_str(make1_str("lock"), $2, $3);
-                               }
-               |       LOCK_P opt_table relation_name IN opt_lmode ROW IDENT IDENT
-                               {
-                                       if (strcasecmp($8, "MODE"))
-                                       {
-                                                sprintf(errortext, "syntax error at or near \"%s\"", $8);
-                                               yyerror(errortext);
-                                       }
-                                       if ($5 != NULL)
-                                        {
-                                                if (strcasecmp($5, "SHARE"))
-                                               {
-                                                        sprintf(errortext, "syntax error at or near \"%s\"", $5);
-                                                       yyerror(errortext);
-                                               }
-                                                if (strcasecmp($7, "EXCLUSIVE"))
-                                               {
-                                                       sprintf(errortext, "syntax error at or near \"%s\"", $7);
-                                                       yyerror(errortext);
-                                               }
-                                       }
-                                        else
-                                        {
-                                                if (strcasecmp($7, "SHARE") && strcasecmp($7, "EXCLUSIVE"))
-                                               {
-                                                               sprintf(errortext, "syntax error at or near \"%s\"", $7);
-                                                       yyerror(errortext);
-                                               }
-                                        }
-
-                                       $$=cat4_str(cat5_str(make1_str("lock"), $2, $3, make1_str("in"), $5), make1_str("row"), $7, $8);
-                               }
-               |       LOCK_P opt_table relation_name IN IDENT IDENT IDENT
-                               {
-                                       if (strcasecmp($7, "MODE"))
-                                       {
-                                                sprintf(errortext, "syntax error at or near \"%s\"", $7);
-                                                yyerror(errortext);
-                                       }                                
-                                        if (strcasecmp($5, "ACCESS"))
-                                       {
-                                                sprintf(errortext, "syntax error at or near \"%s\"", $5);
-                                                yyerror(errortext);
-                                       }
-                                        if (strcasecmp($6, "SHARE") && strcasecmp($6, "EXCLUSIVE"))
-                                       {
-                                                sprintf(errortext, "syntax error at or near \"%s\"", $6);
-                                                yyerror(errortext);
-                                       }
-
-                                       $$=cat3_str(cat5_str(make1_str("lock"), $2, $3, make1_str("in"), $5), $6, $7);
-                               }
-               |       LOCK_P opt_table relation_name IN IDENT IDENT
+LockStmt:  LOCK_P opt_table relation_name opt_lock
                                {
-                                       if (strcasecmp($6, "MODE"))
-                                       {
-                                                sprintf(errortext, "syntax error at or near \"%s\"", $6);
-                                               yyerror(errortext);
-                                       }
-                                        if (strcasecmp($5, "SHARE") && strcasecmp($5, "EXCLUSIVE"))
-                                       {
-                                                sprintf(errortext, "syntax error at or near \"%s\"", $5);
-                                                yyerror(errortext);
-                                       }
-
-                                       $$=cat2_str(cat5_str(make1_str("lock"), $2, $3, make1_str("in"), $5), $6);
+                                       $$ = cat_str(4, make_str("lock"), $2, $3, $4);
                                }
                ;
 
-opt_lmode:      IDENT           { $$ = $1; }
-                | /*EMPTY*/     { $$ = make1_str(""); }
+opt_lock:  IN lock_type MODE            { $$ = cat_str(3, make_str("in"), $2, make_str("mode")); }
+                | /*EMPTY*/             { $$ = EMPTY;}
                 ;
 
+lock_type:  SHARE ROW EXCLUSIVE        { $$ = make_str("share row exclusive"); }
+                | ROW opt_lmode         { $$ = cat2_str(make_str("row"), $2);}
+                | ACCESS opt_lmode      { $$ = cat2_str(make_str("access"), $2);}
+                | opt_lmode             { $$ = $1; }
+                ;
 
-
+opt_lmode:      SHARE                           { $$ = make_str("share"); }
+                | EXCLUSIVE                     { $$ = make_str("exclusive"); }
+                ;
 
 /*****************************************************************************
  *
@@ -2551,11 +3220,11 @@ opt_lmode:      IDENT           { $$ = $1; }
  *****************************************************************************/
 
 UpdateStmt:  UPDATE relation_name
-                         SET res_target_list
+                         SET update_target_list
                          from_clause
                          where_clause
                                {
-                                       $$ = cat2_str(cat5_str(make1_str("update"), $2, make1_str("set"), $4, $5), $6);
+                                       $$ = cat_str(6, make_str("update"), $2, make_str("set"), $4, $5, $6);
                                }
                ;
 
@@ -2566,7 +3235,9 @@ UpdateStmt:  UPDATE relation_name
  *                             CURSOR STATEMENTS
  *
  *****************************************************************************/
-CursorStmt:  DECLARE name opt_cursor CURSOR FOR SelectStmt cursor_clause
+CursorStmt:  DECLARE name opt_cursor CURSOR FOR
+               { ForUpdateNotAllowed = 1; }
+            SelectStmt
                                {
                                        struct cursor *ptr, *this;
        
@@ -2574,9 +3245,9 @@ CursorStmt:  DECLARE name opt_cursor CURSOR FOR SelectStmt cursor_clause
                                        {
                                                if (strcmp($2, ptr->name) == 0)
                                                {
-                                                       /* re-definition is a bug*/
+                                                       /* re-definition is a bug */
                                                        sprintf(errortext, "cursor %s already defined", $2);
-                                                       yyerror(errortext);
+                                                       mmerror(ET_ERROR, errortext);
                                                }
                                        }
                         
@@ -2585,38 +3256,25 @@ CursorStmt:  DECLARE name opt_cursor CURSOR FOR SelectStmt cursor_clause
                                        /* initial definition */
                                        this->next = cur;
                                        this->name = $2;
-                                       this->command =  cat2_str(cat5_str(make1_str("declare"), mm_strdup($2), $3, make1_str("cursor for"), $6), $7);
+                                       this->connection = connection;
+                                       this->command =  cat_str(5, make_str("declare"), mm_strdup($2), $3, make_str("cursor for"), $7);
                                        this->argsinsert = argsinsert;
                                        this->argsresult = argsresult;
                                        argsinsert = argsresult = NULL;
                                                                                        
                                        cur = this;
                                        
-                                       $$ = cat3_str(make1_str("/*"), mm_strdup(this->command), make1_str("*/"));
+                                       $$ = cat_str(3, make_str("/*"), mm_strdup(this->command), make_str("*/"));
                                }
                ;
 
-opt_cursor:  BINARY             { $$ = make1_str("binary"); }
-               | INSENSITIVE   { $$ = make1_str("insensitive"); }
-               | SCROLL         { $$ = make1_str("scroll"); }
-               | INSENSITIVE SCROLL    { $$ = make1_str("insensitive scroll"); }
-               | /*EMPTY*/      { $$ = make1_str(""); }
-               ;
-
-cursor_clause:  FOR opt_readonly       { $$ = cat2_str(make1_str("for"), $2); }
-               | /*EMPTY*/              { $$ = make1_str(""); }
-
-               ;
-
-opt_readonly:  READ ONLY               { $$ = make1_str("read only"); }
-               | UPDATE opt_of
-                       {
-                               yyerror("DECLARE/UPDATE not supported; Cursors must be READ ONLY.");
-                       }
+opt_cursor:  BINARY                    { $$ = make_str("binary"); }
+               | INSENSITIVE           { $$ = make_str("insensitive"); }
+               | SCROLL                        { $$ = make_str("scroll"); }
+               | INSENSITIVE SCROLL    { $$ = make_str("insensitive scroll"); }
+               | /*EMPTY*/             { $$ = EMPTY; }
                ;
 
-opt_of:  OF columnList { $$ = make2_str(make1_str("of"), $2); }
-
 /*****************************************************************************
  *
  *             QUERY:
@@ -2624,20 +3282,23 @@ opt_of:  OF columnList { $$ = make2_str(make1_str("of"), $2); }
  *
  *****************************************************************************/
 
-/***S*I***/
 /* The new 'SelectStmt' rule adapted for the optional use of INTERSECT EXCEPT a nd UNION
  * accepts the use of '(' and ')' to select an order of set operations.
+ * The rule returns a SelectStmt Node having the set operations attached to
+ * unionClause and intersectClause (NIL if no set operations were present)
  */
-SelectStmt:  select_w_o_sort sort_clause for_update_clause
-                               {
-                                       if (strlen($3) > 0 && ForUpdateNotAllowed != 0)
-                                                       yyerror("SELECT FOR UPDATE is not allowed in this context");
 
-                                       ForUpdateNotAllowed = 0;
-                                       $$ = cat3_str($1, $2, $3);
-                               }
+SelectStmt:      select_clause
+                       { FoundSort = 0; }
+                sort_clause for_update_clause opt_select_limit
+                       {
+                               if (strlen($4) > 0 && ForUpdateNotAllowed != 0)
+                                               mmerror(ET_ERROR, "FOR UPDATE is not allowed in this context");
+
+                               ForUpdateNotAllowed = 0;
+                               $$ = cat_str(4, $1, $3, $4, $5);
+                       }
 
-/***S*I***/ 
 /* This rule parses Select statements including UNION INTERSECT and EXCEPT.
  * '(' and ')' can be used to specify the order of the operations 
  * (UNION EXCEPT INTERSECT). Without the use of '(' and ')' we want the
@@ -2645,67 +3306,72 @@ SelectStmt:  select_w_o_sort sort_clause for_update_clause
  *
  *  The sort_clause is not handled here!
  */
-select_w_o_sort: '(' select_w_o_sort ')'
+select_clause: '(' select_clause ')'
                         {
-                               $$ = make3_str(make1_str("("), $2, make1_str(")")); 
+                                $$ = cat_str(3, make_str("("), $2, make_str(")")); 
                         }
                 | SubSelect
                         {
-                               $$ = $1; 
+                               FoundInto = 0;
+                                $$ = $1; 
                         }
-                | select_w_o_sort EXCEPT select_w_o_sort
+                | select_clause EXCEPT select_clause
                        {
-                               $$ = cat3_str($1, make1_str("except"), $3);
+                               $$ = cat_str(3, $1, make_str("except"), $3);
                                ForUpdateNotAllowed = 1;
                        }
-               | select_w_o_sort UNION opt_union select_w_o_sort
+               | select_clause UNION opt_all select_clause
                        {
-                               $$ = cat3_str($1, make1_str("union"), $3);
+                               $$ = cat_str(4, $1, make_str("union"), $3, $4);
                                ForUpdateNotAllowed = 1;
                        }
-               | select_w_o_sort INTERSECT opt_union select_w_o_sort
+               | select_clause INTERSECT opt_all select_clause
                        {
-                               $$ = cat3_str($1, make1_str("intersect"), $3);
+                               $$ = cat_str(3, $1, make_str("intersect"), $3);
                                ForUpdateNotAllowed = 1;
                        }
                ;
 
-/***S*I***/
-SubSelect:     SELECT opt_unique res_target_list2
+SubSelect:     SELECT opt_distinct target_list
                          result from_clause where_clause
                          group_clause having_clause
                                {
-                                       $$ = cat4_str(cat5_str(make1_str("select"), $2, $3, $4, $5), $6, $7, $8);
                                        if (strlen($7) > 0 || strlen($8) > 0)
                                                ForUpdateNotAllowed = 1;
+                                       $$ = cat_str(8, make_str("select"), $2, $3, $4, $5, $6, $7, $8);
                                }
                ;
 
-result:  INTO opt_table relation_name                  { $$= cat3_str(make1_str("into"), $2, $3); }
-               | INTO into_list                        { $$ = make1_str(""); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+result:  INTO OptTemp opt_table relation_name          { FoundInto = 1;
+                                                         $$= cat_str(4, make_str("into"), $2, $3, $4);
+                                                       }
+               | INTO into_list                        { $$ = EMPTY; }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
-opt_table:  TABLE                                      { $$ = make1_str("table"); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+opt_table:  TABLE                                      { $$ = make_str("table"); }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
-opt_union:  ALL                                                { $$ = make1_str("all"); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+opt_all:  ALL                                          { $$ = make_str("all"); }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
-opt_unique:  DISTINCT                                  { $$ = make1_str("distinct"); }
-               | DISTINCT ON ColId                     { $$ = cat2_str(make1_str("distinct on"), $3); }
-               | ALL                                   { $$ = make1_str("all"); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+opt_distinct:  DISTINCT                                        { $$ = make_str("distinct"); }
+               | DISTINCT ON '(' expr_list ')'         { $$ = cat_str(3, make_str("distinct on ("), $4, make_str(")")); }
+               | ALL                                   { $$ = make_str("all"); }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
-sort_clause:  ORDER BY sortby_list                     { $$ = cat2_str(make1_str("order by"), $3); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+sort_clause:  ORDER BY sortby_list     { 
+                                               FoundSort = 1;
+                                               $$ = cat2_str(make_str("order by"), $3);
+                                       }
+               | /*EMPTY*/             { $$ = EMPTY; }
                ;
 
 sortby_list:  sortby                                   { $$ = $1; }
-               | sortby_list ',' sortby                { $$ = cat3_str($1, make1_str(","), $3); }
+               | sortby_list ',' sortby                { $$ = cat_str(3, $1, make_str(","), $3); }
                ;
 
 sortby: a_expr OptUseOp
@@ -2714,13 +3380,34 @@ sortby: a_expr OptUseOp
                                 }
                ;
 
-OptUseOp:  USING Op                            { $$ = cat2_str(make1_str("using"), $2); }
-               | USING '<'                     { $$ = make1_str("using <"); }
-               | USING '>'                     { $$ = make1_str("using >"); }
-               | ASC                           { $$ = make1_str("asc"); }
-               | DESC                          { $$ = make1_str("desc"); }
-               | /*EMPTY*/                     { $$ = make1_str(""); }
-               ;
+OptUseOp:  USING all_Op                                { $$ = cat2_str(make_str("using"), $2); }
+               | ASC                           { $$ = make_str("asc"); }
+               | DESC                          { $$ = make_str("desc"); }
+               | /*EMPTY*/                     { $$ = EMPTY; }
+               ;
+
+opt_select_limit:      LIMIT select_limit_value ',' select_offset_value
+                       { $$ = cat_str(4, make_str("limit"), $2, make_str(","), $4); }
+               | LIMIT select_limit_value OFFSET select_offset_value
+                       { $$ = cat_str(4, make_str("limit"), $2, make_str("offset"), $4); }
+               | LIMIT select_limit_value
+                       { $$ = cat2_str(make_str("limit"), $2); }
+               | OFFSET select_offset_value LIMIT select_limit_value
+                       { $$ = cat_str(4, make_str("offset"), $2, make_str("limit"), $4); }
+               | OFFSET select_offset_value
+                       { $$ = cat2_str(make_str("offset"), $2); }
+               | /* EMPTY */
+                       { $$ = EMPTY; }
+               ;
+
+select_limit_value:    Iconst  { $$ = $1; }
+                       | ALL   { $$ = make_str("all"); }
+                       | PARAM { $$ = make_name(); }
+               ;
+
+select_offset_value:   Iconst  { $$ = $1; }
+                       | PARAM { $$ = make_name(); }
+               ;
 
 /*
  *     jimmy bell-style recursive queries aren't supported in the
@@ -2729,8 +3416,8 @@ OptUseOp:  USING Op                               { $$ = cat2_str(make1_str("using"), $2); }
  *     ...however, recursive addattr and rename supported.  make special
  *     cases for these.
  */
-opt_inh_star:  '*'                                     { $$ = make1_str("*"); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+opt_inh_star:  '*'                                     { $$ = make_str("*"); }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
 relation_name_list:  name_list { $$ = $1; };
@@ -2738,120 +3425,194 @@ relation_name_list:  name_list { $$ = $1; };
 name_list:  name
                                {       $$ = $1; }
                | name_list ',' name
-                               {       $$ = cat3_str($1, make1_str(","), $3); }
+                               {       $$ = cat_str(3, $1, make_str(","), $3); }
                ;
 
-group_clause:  GROUP BY expr_list                      { $$ = cat2_str(make1_str("groub by"), $3); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+group_clause:  GROUP BY expr_list                      { $$ = cat2_str(make_str("group by"), $3); }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
 having_clause:  HAVING a_expr
                                {
-                                       $$ = cat2_str(make1_str("having"), $2);
+                                       $$ = cat2_str(make_str("having"), $2);
                                }
-               | /*EMPTY*/             { $$ = make1_str(""); }
+               | /*EMPTY*/             { $$ = EMPTY; }
                ;
 
-for_update_clause:
-                       FOR UPDATE
-                        {
-                                $$ = make1_str("for update");
-                        }
-                |       FOR UPDATE OF va_list
-                        {
-                                $$ = cat2_str(make1_str("for update of"), $4);
-                        }
-                | /* EMPTY */
-                        {
-                                $$ = make1_str("");
-                        }
+for_update_clause:  FOR UPDATE update_list
+               {
+                       $$ = make_str("for update"); 
+               }
+               | FOR READ ONLY
+               {
+                       $$ = make_str("for read only");
+               }
+               | /* EMPTY */
+                {
+                        $$ = EMPTY;
+                }
                 ;
+update_list:  OF va_list
+              {
+                       $$ = cat2_str(make_str("of"), $2);
+             }
+              | /* EMPTY */
+              {
+                        $$ = EMPTY;
+              }
+              ;
 
 /*****************************************************************************
  *
  *     clauses common to all Optimizable Stmts:
- *             from_clause             -
- *             where_clause    -
+ *             from_clause     - allow list of both JOIN expressions and table names
+ *             where_clause    - qualifications for joins or restrictions
  *
  *****************************************************************************/
 
-from_clause:  FROM '(' relation_expr join_expr JOIN relation_expr join_spec ')'
-                               {
-                                       yyerror("JOIN not yet implemented");
-                               }
-               | FROM from_list        { $$ = cat2_str(make1_str("from"), $2); }
-               | /*EMPTY*/             { $$ = make1_str(""); }
+from_clause:    FROM from_list         { $$ = cat2_str(make_str("from"), $2); }
+/***
+#ifdef ENABLE_ORACLE_JOIN_SYNTAX
+               | FROM oracle_list      { $$ = cat2_str(make_str("from"), $2); }
+#endif
+***/
+               | FROM from_expr        { $$ = cat2_str(make_str("from"), $2); }
+               | /* EMPTY */           { $$ = EMPTY; }
                ;
 
-from_list:     from_list ',' from_val
-                               { $$ = cat3_str($1, make1_str(","), $3); }
-               | from_val CROSS JOIN from_val
-                               { yyerror("CROSS JOIN not yet implemented"); }
-               | from_val
-                               { $$ = $1; }
-               ;
+from_list:  from_list ',' table_expr   { $$ = cat_str(3, $1, make_str(","), $3); }
+                | table_expr           { $$ = $1; }
+                ;
 
-from_val:  relation_expr AS ColLabel
-                               {
-                                       $$ = cat3_str($1, make1_str("as"), $3);
-                               }
-               | relation_expr ColId
-                               {
-                                       $$ = cat2_str($1, $2);
-                               }
-               | relation_expr
-                               {
-                                       $$ = $1;
-                               }
-               ;
+/***********
+ * This results in one shift/reduce conflict, presumably due to the trailing "(
+ * - Thomas 1999-09-20
+ *
+#ifdef ENABLE_ORACLE_JOIN_SYNTAX
+oracle_list:  oracle_expr      { $$ = $1; }
+                ;
 
-join_expr:  NATURAL join_expr                                  { $$ = cat2_str(make1_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"); }
+oracle_expr:  ColId ',' ColId oracle_outer
+               {
+                       mmerror(ET_ERROR, "Oracle OUTER JOIN not yet supported");
+                       $$ = cat_str(3, $1, make_str(","), $3); }
+               }
+               |  oracle_outer ColId ',' ColId
+               {
+                       mmerror(ET_ERROR, "Oracle OUTER JOIN not yet supported");
+                       $$ = cat_str(4, $1, $2, make_str(","), $3); } 
+               }
                ;
 
-join_outer:  OUTER_P                           { $$ = make1_str("outer"); }
-               | /*EMPTY*/                     { $$ = make1_str("");  /* no qualifiers */ }
-               ;
+oracle_outer:  '(' '+' ')'     { $$ = make_str("(+)"); }
+                ;
+#endif
+*************/
 
-join_spec:     ON '(' a_expr ')'                       { $$ = make3_str(make1_str("on ("), $3, make1_str(")")); }
-               | USING '(' join_list ')'               { $$ = make3_str(make1_str("using ("), $3, make1_str(")")); }
-               | /*EMPTY*/                             { $$ = make1_str("");  /* no qualifiers */ }
-               ;
+from_expr:  '(' join_clause_with_union ')' alias_clause
+                                { $$ = cat_str(4, make_str("("), $2, make_str(")"), $4); }
+                | join_clause
+                                { $$ = $1; }
+                ;
 
-join_list:  join_using                                 { $$ = $1; }
-               | join_list ',' join_using              { $$ = cat3_str($1, make1_str(","), $3); }
-               ;
+table_expr:  relation_expr alias_clause
+                                {
+                                        $$ = cat2_str($1, $2);
+                                }
+                ;
 
-join_using:  ColId
-                               {
-                                       $$ = $1;
-                               }
-               | ColId '.' ColId
-                               {
-                                       $$ = make3_str($1, make1_str("."), $3);
-                               }
-               | Iconst
-                               {
-                                       $$ = $1;;
-                               }
+alias_clause:  AS ColId '(' name_list ')'
+                       { $$ = cat_str(5, make_str("as"), $2, make_str("("), $4, make_str(")")); }
+               | AS ColId
+                       { $$ = cat2_str(make_str("as"), $2); }
+               | ColId '(' name_list ')'
+                       { $$ = cat_str(4, $1, make_str("("), $3, make_str(")")); }
+               | ColId
+                       { $$ = $1; }
+               | /*EMPTY*/
+                       { $$ = EMPTY; }
                ;
 
-where_clause:  WHERE a_expr                    { $$ = cat2_str(make1_str("where"), $2); }
-               | /*EMPTY*/                             { $$ = make1_str("");  /* no qualifiers */ }
-               ;
+/* A UNION JOIN is the same as a FULL OUTER JOIN which *omits*
+ * all result rows which would have matched on an INNER JOIN.
+ * Syntactically, must enclose the UNION JOIN in parens to avoid
+ * conflicts with SELECT/UNION.
+ */
+join_clause:  join_clause join_expr
+                       { $$ = cat2_str($1, $2); }
+               | table_expr join_expr
+                       { $$ = cat2_str($1, $2); }
+                ;
+
+/* This is everything but the left side of a join.
+ * Note that a CROSS JOIN is the same as an unqualified
+ * inner join, so just pass back the right-side table.
+ * A NATURAL JOIN implicitly matches column names between
+ * tables, and the shape is determined by which columns are
+ * in common. We'll collect columns during the later transformations.
+ */
+join_expr:  join_type JOIN table_expr join_qual
+                                {
+                                       $$ = cat_str(4, $1, make_str("join"), $3, $4);
+                                }
+                | NATURAL join_type JOIN table_expr
+                                {
+                                       $$ = cat_str(4, make_str("natural"), $2, make_str("join"), $4);
+                                }
+                | CROSS JOIN table_expr
+                                {      $$ = cat2_str(make_str("cross join"), $3); }
+                ;
+
+join_clause_with_union:  join_clause_with_union join_expr_with_union
+                                { $$ = cat2_str($1, $2); }
+                | table_expr join_expr_with_union 
+                                { $$ = cat2_str($1, $2); }
+                ;
+
+join_expr_with_union:  join_expr
+                                { $$ = $1; }
+                | UNION JOIN table_expr
+                               { $$ = cat2_str(make_str("union join"), $3); }
+               ;
+
+/* OUTER is just noise... */
+join_type:  FULL join_outer            { $$ = cat2_str(make_str("full"), $2); }
+               | LEFT join_outer       { $$ = cat2_str(make_str("left"), $2); }
+                | RIGHT join_outer      { $$ = cat2_str(make_str("right"), $2); }
+                | OUTER_P               { $$ = make_str("outer"); }
+                | INNER_P               { $$ = make_str("inner"); }
+                | /* EMPTY */           { $$ = EMPTY; }
+               ;
+
+join_outer:  OUTER_P                           { $$ = make_str("outer"); }
+               | /*EMPTY*/                     { $$ = EMPTY;  /* no qualifiers */ }
+               ;
+
+/* JOIN qualification clauses
+ * Possibilities are:
+ *  USING ( column list ) allows only unqualified column names,
+ *                        which must match between tables.
+ *  ON expr allows more general qualifications.
+ * - thomas 1999-01-07
+ */
+
+join_qual:  USING '(' using_list ')'                   { $$ = cat_str(3, make_str("using ("), $3, make_str(")")); }
+               | ON a_expr                            { $$ = cat2_str(make_str("on"), $2); }
+                ;
+
+using_list:  using_list ',' using_expr                  { $$ = cat_str(3, $1, make_str(","), $3); }
+               | using_expr                            { $$ = $1; }
+               ;
+
+using_expr:  ColId
+                               {
+                                       $$ = $1;
+                               }
+               ;
+
+where_clause:  WHERE a_expr                    { $$ = cat2_str(make_str("where"), $2); }
+               | /*EMPTY*/                             { $$ = EMPTY;  /* no qualifiers */ }
+               ;
 
 relation_expr: relation_name
                                {
@@ -2861,48 +3622,41 @@ relation_expr:  relation_name
                | relation_name '*'                               %prec '='
                                {
                                        /* inheritance query */
-                                       $$ = cat2_str($1, make1_str("*"));
+                                       $$ = cat2_str($1, make_str("*"));
                                }
 
-opt_array_bounds:  '[' ']' nest_array_bounds
+opt_array_bounds:  '[' ']' opt_array_bounds
                        {
                             $$.index1 = 0;
                             $$.index2 = $3.index1;
-                            $$.str = cat2_str(make1_str("[]"), $3.str);
+                            $$.str = cat2_str(make_str("[]"), $3.str);
                         }
-               | '[' Iconst ']' nest_array_bounds
+               | '[' Iresult ']' opt_array_bounds
                        {
-                            $$.index1 = atol($2);
+                           char *txt = mm_alloc(20L);
+
+                           sprintf (txt, "%d", $2);
+                            $$.index1 = $2;
                             $$.index2 = $4.index1;
-                            $$.str = cat4_str(make1_str("["), $2, make1_str("]"), $4.str);
+                            $$.str = cat_str(4, make_str("["), txt, make_str("]"), $4.str);
                         }
                | /* EMPTY */
                        {
                             $$.index1 = -1;
                             $$.index2 = -1;
-                            $$.str= make1_str("");
+                            $$.str= EMPTY;
                         }
                ;
 
-nest_array_bounds:     '[' ']' nest_array_bounds
-                        {
-                            $$.index1 = 0;
-                            $$.index2 = $3.index1;
-                            $$.str = cat2_str(make1_str("[]"), $3.str);
-                        }
-               | '[' Iconst ']' nest_array_bounds
-                       {
-                            $$.index1 = atol($2);
-                            $$.index2 = $4.index1;
-                            $$.str = cat4_str(make1_str("["), $2, make1_str("]"), $4.str);
-                        }
-               | /* EMPTY */
-                       {
-                            $$.index1 = -1;
-                            $$.index2 = -1;
-                            $$.str= make1_str("");
-                        }
-                ;
+Iresult:       Iconst                  { $$ = atol($1); }
+       |       '(' Iresult ')'         { $$ = $2; }
+       |       Iresult '+' Iresult     { $$ = $1 + $3; }
+       |       Iresult '-' Iresult     { $$ = $1 - $3; }
+       |       Iresult '*' Iresult     { $$ = $1 * $3; }
+       |       Iresult '/' Iresult     { $$ = $1 / $3; }
+       |       Iresult '%' Iresult     { $$ = $1 % $3; }
+       ;
+
 
 /*****************************************************************************
  *
@@ -2914,20 +3668,20 @@ nest_array_bounds:      '[' ']' nest_array_bounds
  *
  *****************************************************************************/
 
-Typename:  Array opt_array_bounds
+Typename:  SimpleTypename opt_array_bounds
                                {
                                        $$ = cat2_str($1, $2.str);
                                }
-               | Character     { $$ = $1; }
-               | SETOF Array
+               | SETOF SimpleTypename
                                {
-                                       $$ = cat2_str(make1_str("setof"), $2);
+                                       $$ = cat2_str(make_str("setof"), $2);
                                }
                ;
 
-Array:  Generic
+SimpleTypename:  Generic       { $$ = $1; }
                | Datetime      { $$ = $1; }
                | Numeric       { $$ = $1; }
+               | Character     { $$ = $1; }
                ;
 
 Generic:  generic
@@ -2937,7 +3691,39 @@ Generic:  generic
                ;
 
 generic:  ident                                        { $$ = $1; }
-               | TYPE_P                        { $$ = make1_str("type"); }
+               | TYPE_P                        { $$ = make_str("type"); }
+               | SQL_AT                        { $$ = make_str("at"); }
+               | SQL_AUTOCOMMIT                { $$ = make_str("autocommit"); }
+               | SQL_BOOL                      { $$ = make_str("bool"); }
+               | SQL_BREAK                     { $$ = make_str("break"); }
+               | SQL_CALL                      { $$ = make_str("call"); }
+               | SQL_CONNECT                   { $$ = make_str("connect"); }
+               | SQL_CONNECTION                { $$ = make_str("connection"); }
+               | SQL_CONTINUE                  { $$ = make_str("continue"); }
+               | SQL_DEALLOCATE                { $$ = make_str("deallocate"); }
+               | SQL_DISCONNECT                { $$ = make_str("disconnect"); }
+               | SQL_FOUND                     { $$ = make_str("found"); }
+               | SQL_GO                        { $$ = make_str("go"); }
+               | SQL_GOTO                      { $$ = make_str("goto"); }
+               | SQL_IDENTIFIED                { $$ = make_str("identified"); }
+               | SQL_INDICATOR                 { $$ = make_str("indicator"); }
+               | SQL_INT                       { $$ = make_str("int"); }
+               | SQL_LONG                      { $$ = make_str("long"); }
+               | SQL_OFF                       { $$ = make_str("off"); }
+               | SQL_OPEN                      { $$ = make_str("open"); }
+               | SQL_PREPARE                   { $$ = make_str("prepare"); }
+               | SQL_RELEASE                   { $$ = make_str("release"); }
+               | SQL_SECTION                   { $$ = make_str("section"); }
+               | SQL_SHORT                     { $$ = make_str("short"); }
+               | SQL_SIGNED                    { $$ = make_str("signed"); }
+               | SQL_SQLERROR                  { $$ = make_str("sqlerror"); }
+               | SQL_SQLPRINT                  { $$ = make_str("sqlprint"); }
+               | SQL_SQLWARNING                { $$ = make_str("sqlwarning"); }
+               | SQL_STOP                      { $$ = make_str("stop"); }
+               | SQL_STRUCT                    { $$ = make_str("struct"); }
+               | SQL_UNSIGNED                  { $$ = make_str("unsigned"); }
+               | SQL_VAR                       { $$ = make_str("var"); }
+               | SQL_WHENEVER                  { $$ = make_str("whenever"); }
                ;
 
 /* SQL92 numeric data types
@@ -2947,43 +3733,47 @@ generic:  ident                                 { $$ = $1; }
  */
 Numeric:  FLOAT opt_float
                                {
-                                       $$ = cat2_str(make1_str("float"), $2);
+                                       $$ = cat2_str(make_str("float"), $2);
                                }
                | DOUBLE PRECISION
                                {
-                                       $$ = make1_str("double precision");
+                                       $$ = make_str("double precision");
                                }
                | DECIMAL opt_decimal
                                {
-                                       $$ = cat2_str(make1_str("decimal"), $2);
+                                       $$ = cat2_str(make_str("decimal"), $2);
+                               }
+               | DEC opt_decimal
+                               {
+                                       $$ = cat2_str(make_str("dec"), $2);
                                }
                | NUMERIC opt_numeric
                                {
-                                       $$ = cat2_str(make1_str("numeric"), $2);
+                                       $$ = cat2_str(make_str("numeric"), $2);
                                }
                ;
 
 numeric:  FLOAT
-                               {       $$ = make1_str("float"); }
+                               {       $$ = make_str("float"); }
                | DOUBLE PRECISION
-                               {       $$ = make1_str("double precision"); }
+                               {       $$ = make_str("double precision"); }
                | DECIMAL
-                               {       $$ = make1_str("decimal"); }
+                               {       $$ = make_str("decimal"); }
                | NUMERIC
-                               {       $$ = make1_str("numeric"); }
+                               {       $$ = make_str("numeric"); }
                ;
 
 opt_float:  '(' Iconst ')'
                                {
                                        if (atol($2) < 1)
-                                               yyerror("precision for FLOAT must be at least 1");
+                                               mmerror(ET_ERROR, "precision for FLOAT must be at least 1");
                                        else if (atol($2) >= 16)
-                                               yyerror("precision for FLOAT must be less than 16");
-                                       $$ = make3_str(make1_str("("), $2, make1_str(")"));
+                                               mmerror(ET_ERROR, "precision for FLOAT must be less than 16");
+                                       $$ = cat_str(3, make_str("("), $2, make_str(")"));
                                }
                | /*EMPTY*/
                                {
-                                       $$ = make1_str("");
+                                       $$ = EMPTY;
                                }
                ;
 
@@ -2991,25 +3781,25 @@ opt_numeric:  '(' Iconst ',' Iconst ')'
                                {
                                        if (atol($2) < 1 || atol($2) > NUMERIC_MAX_PRECISION) {
                                                sprintf(errortext, "NUMERIC precision %s must be between 1 and %d", $2, NUMERIC_MAX_PRECISION);
-                                               yyerror(errortext);
+                                               mmerror(ET_ERROR, errortext);
                                        }
                                        if (atol($4) < 0 || atol($4) > atol($2)) {
                                                sprintf(errortext, "NUMERIC scale %s must be between 0 and precision %s", $4, $2);
-                                               yyerror(errortext);
+                                               mmerror(ET_ERROR, errortext);
                                        }
-                                       $$ = cat3_str(make2_str(make1_str("("), $2), make1_str(","), make2_str($4, make1_str(")")));
+                                       $$ = cat_str(5, make_str("("), $2, make_str(","), $4, make_str(")"));
                                }
                | '(' Iconst ')'
                                {
                                        if (atol($2) < 1 || atol($2) > NUMERIC_MAX_PRECISION) {
                                                sprintf(errortext, "NUMERIC precision %s must be between 1 and %d", $2, NUMERIC_MAX_PRECISION);
-                                               yyerror(errortext);
+                                               mmerror(ET_ERROR, errortext);
                                        }
-                                       $$ = make3_str(make1_str("("), $2, make1_str(")"));
+                                       $$ = cat_str(3, make_str("("), $2, make_str(")"));
                                }
                | /*EMPTY*/
                                {
-                                       $$ = make1_str("");
+                                       $$ = EMPTY;
                                }
                ;
 
@@ -3017,54 +3807,47 @@ opt_decimal:  '(' Iconst ',' Iconst ')'
                                {
                                        if (atol($2) < 1 || atol($2) > NUMERIC_MAX_PRECISION) {
                                                sprintf(errortext, "NUMERIC precision %s must be between 1 and %d", $2, NUMERIC_MAX_PRECISION);
-                                               yyerror(errortext);
+                                               mmerror(ET_ERROR, errortext);
                                        }
                                        if (atol($4) < 0 || atol($4) > atol($2)) {
                                                sprintf(errortext, "NUMERIC scale %s must be between 0 and precision %s", $4, $2);
-                                               yyerror(errortext);
+                                               mmerror(ET_ERROR, errortext);
                                        }
-                                       $$ = cat3_str(make2_str(make1_str("("), $2), make1_str(","), make2_str($4, make1_str(")")));
+                                       $$ = cat_str(5, make_str("("), $2, make_str(","), $4, make_str(")"));
                                }
                | '(' Iconst ')'
                                {
                                        if (atol($2) < 1 || atol($2) > NUMERIC_MAX_PRECISION) {
                                                sprintf(errortext, "NUMERIC precision %s must be between 1 and %d", $2, NUMERIC_MAX_PRECISION);
-                                               yyerror(errortext);
+                                               mmerror(ET_ERROR, errortext);
                                        }
-                                       $$ = make3_str(make1_str("("), $2, make1_str(")"));
+                                       $$ = cat_str(3, make_str("("), $2, make_str(")"));
                                }
                | /*EMPTY*/
                                {
-                                       $$ = make1_str("");
+                                       $$ = EMPTY;
                                }
                ;
 
-/* SQL92 character data types
+/* 
+ * 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("internal parsing error; unrecognized character type");
-                                       if (atol($3) < 1) {
-                                               sprintf(errortext, "length for '%s' type must be at least 1",$1);
-                                               yyerror(errortext);
+                                       if (atol($3) < 1)
+                                       {
+                                               sprintf(errortext, "length for type '%s' type must be at least 1",$1);
+                                               mmerror(ET_ERROR, 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);
+                                       else if (atol($3) > MaxAttrSize)
+                                       {
+                                               sprintf(errortext, "length for type '%s' cannot exceed %ld", $1, MaxAttrSize);
+                                               mmerror(ET_ERROR, errortext);
                                        }
 
-                                       $$ = cat2_str($1, make3_str(make1_str("("), $3, make1_str(")")));
+                                       $$ = cat_str(4, $1, make_str("("), $3, make_str(")"));
                                }
                | character
                                {
@@ -3074,27 +3857,30 @@ Character:  character '(' Iconst ')'
 
 character:  CHARACTER opt_varying opt_charset opt_collate
                                {
-                                       if (strlen($4) > 0) 
-                                               fprintf(stderr, "COLLATE %s not yet implemented",$4);
+                                       if (strlen($4) > 0)
+                                       {
+                                               sprintf(errortext, "COLLATE %s not yet implemented", $4);
+                                               mmerror(ET_WARN, errortext);
+                                       }
 
-                                       $$ = cat4_str(make1_str("character"), $2, $3, $4);
+                                       $$ = cat_str(4, make_str("character"), $2, $3, $4);
                                }
-               | CHAR opt_varying      { $$ = cat2_str(make1_str("char"), $2); }
-               | VARCHAR               { $$ = make1_str("varchar"); }
-               | NATIONAL CHARACTER opt_varying { $$ = cat2_str(make1_str("national character"), $3); }
-               | NCHAR opt_varying             { $$ = cat2_str(make1_str("nchar"), $2); }
+               | CHAR opt_varying      { $$ = cat2_str(make_str("char"), $2); }
+               | VARCHAR               { $$ = make_str("varchar"); }
+               | NATIONAL CHARACTER opt_varying { $$ = cat2_str(make_str("national character"), $3); }
+               | NCHAR opt_varying             { $$ = cat2_str(make_str("nchar"), $2); }
                ;
 
-opt_varying:  VARYING                  { $$ = make1_str("varying"); }
-               | /*EMPTY*/                     { $$ = make1_str(""); }
+opt_varying:  VARYING                  { $$ = make_str("varying"); }
+               | /*EMPTY*/                     { $$ = EMPTY; }
                ;
 
-opt_charset:  CHARACTER SET ColId      { $$ = cat2_str(make1_str("character set"), $3); }
-               | /*EMPTY*/                             { $$ = make1_str(""); }
+opt_charset:  CHARACTER SET ColId      { $$ = cat2_str(make_str("character set"), $3); }
+               | /*EMPTY*/                             { $$ = EMPTY; }
                ;
 
-opt_collate:  COLLATE ColId            { $$ = cat2_str(make1_str("collate"), $2); }
-               | /*EMPTY*/                                     { $$ = make1_str(""); }
+opt_collate:  COLLATE ColId            { $$ = cat2_str(make_str("collate"), $2); }
+               | /*EMPTY*/                                     { $$ = EMPTY; }
                ;
 
 Datetime:  datetime
@@ -3103,45 +3889,45 @@ Datetime:  datetime
                                }
                | TIMESTAMP opt_timezone
                                {
-                                       $$ = cat2_str(make1_str("timestamp"), $2);
+                                       $$ = cat2_str(make_str("timestamp"), $2);
                                }
                | TIME
                                {
-                                       $$ = make1_str("time");
+                                       $$ = make_str("time");
                                }
                | INTERVAL opt_interval
                                {
-                                       $$ = cat2_str(make1_str("interval"), $2);
+                                       $$ = cat2_str(make_str("interval"), $2);
                                }
                ;
 
-datetime:  YEAR_P                                                              { $$ = make1_str("year"); }
-               | MONTH_P                                                               { $$ = make1_str("month"); }
-               | DAY_P                                                                 { $$ = make1_str("day"); }
-               | HOUR_P                                                                { $$ = make1_str("hour"); }
-               | MINUTE_P                                                              { $$ = make1_str("minute"); }
-               | SECOND_P                                                              { $$ = make1_str("second"); }
+datetime:  YEAR_P                                                              { $$ = make_str("year"); }
+               | MONTH_P                                                               { $$ = make_str("month"); }
+               | DAY_P                                                                 { $$ = make_str("day"); }
+               | HOUR_P                                                                { $$ = make_str("hour"); }
+               | MINUTE_P                                                              { $$ = make_str("minute"); }
+               | SECOND_P                                                              { $$ = make_str("second"); }
                ;
 
-opt_timezone:  WITH TIME ZONE                          { $$ = make1_str("with time zone"); }
-               | /*EMPTY*/                                     { $$ = make1_str(""); }
+opt_timezone:  WITH TIME ZONE                          { $$ = make_str("with time zone"); }
+               | /*EMPTY*/                                     { $$ = EMPTY; }
                ;
 
 opt_interval:  datetime                                        { $$ = $1; }
-               | YEAR_P TO MONTH_P                     { $$ = make1_str("year to #month"); }
-               | DAY_P TO HOUR_P                       { $$ = make1_str("day to hour"); }
-               | DAY_P TO MINUTE_P                     { $$ = make1_str("day to minute"); }
-               | DAY_P TO SECOND_P                     { $$ = make1_str("day to second"); }
-               | HOUR_P TO MINUTE_P                    { $$ = make1_str("hour to minute"); }
-               | MINUTE_P TO SECOND_P                  { $$ = make1_str("minute to second"); }
-               | HOUR_P TO SECOND_P                    { $$ = make1_str("hour to second"); }
-               | /*EMPTY*/                                     { $$ = make1_str(""); }
+               | YEAR_P TO MONTH_P                     { $$ = make_str("year to #month"); }
+               | DAY_P TO HOUR_P                       { $$ = make_str("day to hour"); }
+               | DAY_P TO MINUTE_P                     { $$ = make_str("day to minute"); }
+               | DAY_P TO SECOND_P                     { $$ = make_str("day to second"); }
+               | HOUR_P TO MINUTE_P                    { $$ = make_str("hour to minute"); }
+               | MINUTE_P TO SECOND_P                  { $$ = make_str("minute to second"); }
+               | HOUR_P TO SECOND_P                    { $$ = make_str("hour to second"); }
+               | /*EMPTY*/                                     { $$ = EMPTY; }
                ;
 
 
 /*****************************************************************************
  *
- *     expression grammar, still needs some cleanup
+ *     expression grammar
  *
  *****************************************************************************/
 
@@ -3149,65 +3935,50 @@ a_expr_or_null:  a_expr
                                { $$ = $1; }
                | NULL_P
                                {
-                                       $$ = make1_str("null");
+                                       $$ = make_str("null");
                                }
                ;
 
 /* Expressions using row descriptors
  * Define row_descriptor to allow yacc to break the reduce/reduce conflict
  *  with singleton expressions.
- * Eliminated lots of code by defining row_op and sub_type clauses.
- * However, can not consolidate EXPR_LINK case with others subselects
- *  due to shift/reduce conflict with the non-subselect clause (the parser
- *  would have to look ahead more than one token to resolve the conflict).
- * - thomas 1998-05-09
  */
 row_expr: '(' row_descriptor ')' IN '(' SubSelect ')'
                                {
-                                       $$ = make5_str(make1_str("("), $2, make1_str(") in ("), $6, make1_str(")"));
+                                       $$ = cat_str(5, make_str("("), $2, make_str(") in ("), $6, make_str(")"));
                                }
                | '(' row_descriptor ')' NOT IN '(' SubSelect ')'
                                {
-                                       $$ = make5_str(make1_str("("), $2, make1_str(") not in ("), $7, make1_str(")"));
+                                       $$ = cat_str(5, make_str("("), $2, make_str(") not in ("), $7, make_str(")"));
                                }
-               | '(' row_descriptor ')' row_op sub_type  '(' SubSelect ')'
+               | '(' row_descriptor ')' all_Op sub_type  '(' SubSelect ')'
                                {
-                                       $$ = make4_str(make5_str(make1_str("("), $2, make1_str(")"), $4, $5), make1_str("("), $7, make1_str(")"));
+                                       $$ = cat_str(8, make_str("("), $2, make_str(")"), $4, $5, make_str("("), $7, make_str(")"));
                                }
-               | '(' row_descriptor ')' row_op '(' SubSelect ')'
+               | '(' row_descriptor ')' all_Op '(' SubSelect ')'
                                {
-                                       $$ = make3_str(make5_str(make1_str("("), $2, make1_str(")"), $4, make1_str("(")), $6, make1_str(")"));
+                                       $$ = cat_str(7, make_str("("), $2, make_str(")"), $4, make_str("("), $6, make_str(")"));
                                }
-               | '(' row_descriptor ')' row_op '(' row_descriptor ')'
+               | '(' row_descriptor ')' all_Op '(' row_descriptor ')'
                                {
-                                       $$ = cat3_str(make3_str(make1_str("("), $2, make1_str(")")), $4, make3_str(make1_str("("), $6, make1_str(")")));
+                                       $$ = cat_str(7, make_str("("), $2, make_str(")"), $4, make_str("("), $6, make_str(")"));
                                }
                ;
 
 row_descriptor:  row_list ',' a_expr
                                {
-                                       $$ = cat3_str($1, make1_str(","), $3);
+                                       $$ = cat_str(3, $1, make_str(","), $3);
                                }
                ;
 
-row_op:  Op                    { $$ = $1; }
-       | '<'                   { $$ = "<"; }
-        | '='                   { $$ = "="; }
-        | '>'                   { $$ = ">"; }
-        | '+'                   { $$ = "+"; }
-        | '-'                   { $$ = "-"; }
-        | '*'                   { $$ = "*"; }
-        | '/'                   { $$ = "/"; }
-              ;
-
-sub_type:  ANY                  { $$ = make1_str("ANY"); }
-         | ALL                  { $$ = make1_str("ALL"); }
+sub_type:  ANY                  { $$ = make_str("ANY"); }
+         | ALL                  { $$ = make_str("ALL"); }
               ;
 
 
 row_list:  row_list ',' a_expr
                                {
-                                       $$ = cat3_str($1, make1_str(","), $3);
+                                       $$ = cat_str(3, $1, make_str(","), $3);
                                }
                | a_expr
                                {
@@ -3215,558 +3986,385 @@ row_list:  row_list ',' a_expr
                                }
                ;
 
+all_Op:  Op | MathOp;
+
+MathOp:        '+'                             { $$ = make_str("+"); }
+               | '-'                   { $$ = make_str("-"); }
+               | '*'                   { $$ = make_str("*"); }
+               | '%'                   { $$ = make_str("%"); }
+                | '^'                   { $$ = make_str("^"); }
+                | '|'                   { $$ = make_str("|"); }
+               | '/'                   { $$ = make_str("/"); }
+               | '<'                   { $$ = make_str("<"); }
+               | '>'                   { $$ = make_str(">"); }
+               | '='                   { $$ = make_str("="); }
+               ;
+
 /* General expressions
  * 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/expressions are allowed in a BETWEEN clause
- *  if surrounded by parens.
+ *
+ * We have two expression types: a_expr is the unrestricted kind, and
+ * b_expr is a subset that must be used in some places to avoid shift/reduce
+ * conflicts.  For example, we can't do BETWEEN as "BETWEEN a_expr AND a_expr"
+ * because that use of AND conflicts with AND as a boolean operator.  So,
+ * b_expr is used in BETWEEN and we remove boolean keywords from b_expr.
+ *
+ * Note that '(' a_expr ')' is a b_expr, so an unrestricted expression can
+ * always be used by surrounding it with parens.
+ *
+ * c_expr is all the productions that are common to a_expr and b_expr;
+ * it's factored out just to eliminate redundant coding.
  */
 
-a_expr:  attr opt_indirection
-                               {
-                                       $$ = cat2_str($1, $2);
-                               }
-               | row_expr
-                               {       $$ = $1;  }
-               | AexprConst
-                               {       $$ = $1;  }
-               | ColId
-                               {
-                                       $$ = $1;
-                               }
+a_expr:  c_expr
+                               {       $$ = $1; }
+               | a_expr TYPECAST Typename
+                               {       $$ = cat_str(3, $1, make_str("::"), $3); }
+                /*
+                 * Can't collapse this into prior rule by using a_expr_or_null;
+                 * that creates reduce/reduce conflicts.  Grumble.
+                 */
+                | NULL_P TYPECAST Typename
+                                {
+                                       $$ = cat2_str(make_str("null::"), $3);
+                                }
+               /*
+                 * These operators must be called out explicitly in order to make use
+                 * of yacc/bison's automatic operator-precedence handling.  All other
+                 * operator names are handled by the generic productions using "Op",
+                 * below; and all those operators will have the same precedence.
+                 *
+                 * If you add more explicitly-known operators, be sure to add them
+                 * also to b_expr and to the MathOp list above.
+                 */
                | '-' a_expr %prec UMINUS
-                               {       $$ = cat2_str(make1_str("-"), $2); }
+                               {       $$ = cat2_str(make_str("-"), $2); }
+               | '%' a_expr
+                               {       $$ = cat2_str(make_str("%"), $2); }
+               | '^' a_expr
+                               {       $$ = cat2_str(make_str("^"), $2); }
+               | '|' a_expr
+                               {       $$ = cat2_str(make_str("|"), $2); }
+/* not possible in embedded sql                | ':' a_expr    
+                               {       $$ = cat2_str(make_str(":"), $2); }
+*/
+               | ';' a_expr
+                               {       $$ = cat2_str(make_str(";"), $2); }
+               | a_expr '%'
+                               {       $$ = cat2_str($1, make_str("%")); }
+               | a_expr '^'
+                               {       $$ = cat2_str($1, make_str("^")); }
+               | a_expr '|'
+                               {       $$ = cat2_str($1, make_str("|")); }
                | a_expr '+' a_expr
-                               {       $$ = cat3_str($1, make1_str("+"), $3); }
+                               {       $$ = cat_str(3, $1, make_str("+"), $3); }
                | a_expr '-' a_expr
-                               {       $$ = cat3_str($1, make1_str("-"), $3); }
-               | a_expr '/' a_expr
-                               {       $$ = cat3_str($1, make1_str("/"), $3); }
+                               {       $$ = cat_str(3, $1, make_str("-"), $3); }
                | a_expr '*' a_expr
-                               {       $$ = cat3_str($1, make1_str("*"), $3); }
+                               {       $$ = cat_str(3, $1, make_str("*"), $3); }
+               | a_expr '/' a_expr
+                               {       $$ = cat_str(3, $1, make_str("/"), $3); }
+               | a_expr '%' a_expr
+                               {       $$ = cat_str(3, $1, make_str("%"), $3); }
+               | a_expr '^' a_expr
+                               {       $$ = cat_str(3, $1, make_str("^"), $3); }
+               | a_expr '|' a_expr
+                               {       $$ = cat_str(3, $1, make_str("|"), $3); }
                | a_expr '<' a_expr
-                               {       $$ = cat3_str($1, make1_str("<"), $3); }
+                               {       $$ = cat_str(3, $1, make_str("<"), $3); }
                | a_expr '>' a_expr
-                               {       $$ = cat3_str($1, make1_str(">"), $3); }
+                               {       $$ = cat_str(3, $1, make_str(">"), $3); }
+               | a_expr '=' NULL_P
+                                {       $$ = cat2_str($1, make_str("= NULL")); }
+               /* We allow this for standards-broken SQL products, like MS stuff */
+               | NULL_P '=' a_expr
+                                {       $$ = cat2_str(make_str("= NULL"), $3); }
                | a_expr '=' a_expr
-                               {       $$ = cat3_str($1, make1_str("="), $3); }
-/* not possible in embedded sql                | ':' a_expr
-                               {       $$ = cat2_str(make1_str(":"), $2); }
-*/
-               | ';' a_expr
-                               {       $$ = cat2_str(make1_str(";"), $2); }
-               | '|' a_expr
-                               {       $$ = cat2_str(make1_str("|"), $2); }
-               | a_expr TYPECAST Typename
-                               {
-                                       $$ = cat3_str($1, make1_str("::"), $3);
-                               }
-               | CAST '(' a_expr AS Typename ')'
-                               {
-                                       $$ = cat3_str(make2_str(make1_str("cast("), $3), make1_str("as"), make2_str($5, make1_str(")")));
-                               }
-               | '(' a_expr_or_null ')'
-                               {       $$ = make3_str(make1_str("("), $2, make1_str(")")); }
+                               {       $$ = cat_str(3, $1, make_str("="), $3); }
                | a_expr Op a_expr
-                               {       $$ = cat3_str($1, $2, $3);      }
-               | a_expr LIKE a_expr
-                               {       $$ = cat3_str($1, make1_str("like"), $3); }
-               | a_expr NOT LIKE a_expr
-                               {       $$ = cat3_str($1, make1_str("not like"), $4); }
+                               {       $$ = cat_str(3, $1, $2, $3); }
                | Op a_expr
                                {       $$ = cat2_str($1, $2); }
                | a_expr Op
                                {       $$ = cat2_str($1, $2); }
-               | func_name '(' '*' ')'
-                               {
-                                       $$ = cat2_str($1, make1_str("(*)")); 
-                               }
-               | func_name '(' ')'
-                               {
-                                       $$ = cat2_str($1, make1_str("()")); 
-                               }
-               | func_name '(' expr_list ')'
-                               {
-                                       $$ = make4_str($1, make1_str("("), $3, make1_str(")")); 
-                               }
-               | CURRENT_DATE
-                               {
-                                       $$ = make1_str("current_date");
-                               }
-               | CURRENT_TIME
-                               {
-                                       $$ = make1_str("current_time");
-                               }
-               | CURRENT_TIME '(' Iconst ')'
-                               {
-                                       if (atol($3) != 0)
-                                               fprintf(stderr,"CURRENT_TIME(%s) precision not implemented; zero used instead", $3);
-                                       $$ = make1_str("current_time");
-                               }
-               | CURRENT_TIMESTAMP
-                               {
-                                       $$ = make1_str("current_timestamp");
-                               }
-               | CURRENT_TIMESTAMP '(' Iconst ')'
-                               {
-                                       if (atol($3) != 0)
-                                               fprintf(stderr,"CURRENT_TIMESTAMP(%s) precision not implemented; zero used instead",$3);
-                                       $$ = make1_str("current_timestamp");
-                               }
-               | CURRENT_USER
-                               {
-                                       $$ = make1_str("current_user");
-                               }
-               | USER
-                               {
-                                       $$ = make1_str("user");
-                               }
-
-               | EXISTS '(' SubSelect ')'
-                               {
-                                       $$ = make3_str(make1_str("exists("), $3, make1_str(")"));
-                               }
-               | EXTRACT '(' extract_list ')'
-                               {
-                                       $$ = make3_str(make1_str("extract("), $3, make1_str(")"));
-                               }
-               | POSITION '(' position_list ')'
-                               {
-                                       $$ = make3_str(make1_str("position("), $3, make1_str(")"));
-                               }
-               | SUBSTRING '(' substr_list ')'
-                               {
-                                       $$ = make3_str(make1_str("substring("), $3, make1_str(")"));
-                               }
-               /* various trim expressions are defined in SQL92 - thomas 1997-07-19 */
-               | TRIM '(' BOTH trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim(both"), $4, make1_str(")"));
-                               }
-               | TRIM '(' LEADING trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim(leading"), $4, make1_str(")"));
-                               }
-               | TRIM '(' TRAILING trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim(trailing"), $4, make1_str(")"));
-                               }
-               | TRIM '(' trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim("), $3, make1_str(")"));
-                               }
+               | a_expr AND a_expr
+                               {       $$ = cat_str(3, $1, make_str("and"), $3); }
+               | a_expr OR a_expr
+                               {       $$ = cat_str(3, $1, make_str("or"), $3); }
+               | NOT a_expr
+                               {       $$ = cat2_str(make_str("not"), $2); }
+               | a_expr LIKE a_expr
+                               {       $$ = cat_str(3, $1, make_str("like"), $3); }
+               | a_expr NOT LIKE a_expr
+                               {       $$ = cat_str(3, $1, make_str("not like"), $4); }
                | a_expr ISNULL
-                               {       $$ = cat2_str($1, make1_str("isnull")); }
+                               {       $$ = cat2_str($1, make_str("isnull")); }
                | a_expr IS NULL_P
-                               {       $$ = cat2_str($1, make1_str("is null")); }
+                               {       $$ = cat2_str($1, make_str("is null")); }
                | a_expr NOTNULL
-                               {       $$ = cat2_str($1, make1_str("notnull")); }
+                               {       $$ = cat2_str($1, make_str("notnull")); }
                | a_expr IS NOT NULL_P
-                               {       $$ = cat2_str($1, make1_str("is not null")); }
+                               {       $$ = cat2_str($1, make_str("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
-                               {
-                               {       $$ = cat2_str($1, make1_str("is true")); }
-                               }
+                               {       $$ = cat2_str($1, make_str("is true")); }
                | a_expr IS NOT FALSE_P
-                               {
-                               {       $$ = cat2_str($1, make1_str("is not false")); }
-                               }
+                               {       $$ = cat2_str($1, make_str("is not false")); }
                | a_expr IS FALSE_P
-                               {
-                               {       $$ = cat2_str($1, make1_str("is false")); }
-                               }
+                               {       $$ = cat2_str($1, make_str("is false")); }
                | a_expr IS NOT TRUE_P
-                               {
-                               {       $$ = cat2_str($1, make1_str("is not true")); }
-                               }
+                               {       $$ = cat2_str($1, make_str("is not true")); }
                | a_expr BETWEEN b_expr AND b_expr
                                {
-                                       $$ = cat5_str($1, make1_str("between"), $3, make1_str("and"), $5); 
+                                       $$ = cat_str(5, $1, make_str("between"), $3, make_str("and"), $5); 
                                }
                | a_expr NOT BETWEEN b_expr AND b_expr
                                {
-                                       $$ = cat5_str($1, make1_str("not between"), $4, make1_str("and"), $6); 
+                                       $$ = cat_str(5, $1, make_str("not between"), $4, make_str("and"), $6); 
                                }
                | a_expr IN '(' in_expr ')'
                                {
-                                       $$ = make4_str($1, make1_str("in ("), $4, make1_str(")")); 
-                               }
-               | a_expr NOT IN '(' not_in_expr ')'
-                               {
-                                       $$ = make4_str($1, make1_str("not in ("), $5, make1_str(")")); 
-                               }
-               | a_expr Op '(' SubSelect ')'
-                               {
-                                       $$ = cat3_str($1, $2, make3_str(make1_str("("), $4, make1_str(")"))); 
-                               }
-               | a_expr '+' '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("+("), $4, make1_str(")")); 
-                               }
-               | a_expr '-' '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("-("), $4, make1_str(")")); 
-                               }
-               | a_expr '/' '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("/("), $4, make1_str(")")); 
-                               }
-               | a_expr '*' '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("*("), $4, make1_str(")")); 
-                               }
-               | a_expr '<' '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("<("), $4, make1_str(")")); 
-                               }
-               | a_expr '>' '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str(">("), $4, make1_str(")")); 
-                               }
-               | a_expr '=' '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("=("), $4, make1_str(")")); 
-                               }
-               | a_expr Op ANY '(' SubSelect ')'
-                               {
-                                       $$ = cat3_str($1, $2, make3_str(make1_str("any("), $5, make1_str(")"))); 
-                               }
-               | a_expr '+' ANY '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("+any("), $5, make1_str(")")); 
-                               }
-               | a_expr '-' ANY '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("-any("), $5, make1_str(")")); 
-                               }
-               | a_expr '/' ANY '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("/any("), $5, make1_str(")")); 
-                               }
-               | a_expr '*' ANY '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("*any("), $5, make1_str(")")); 
+                                       $$ = cat_str(4, $1, make_str(" in ("), $4, make_str(")")); 
                                }
-               | a_expr '<' ANY '(' SubSelect ')'
+               | a_expr NOT IN '(' in_expr ')'
                                {
-                                       $$ = make4_str($1, make1_str("<any("), $5, make1_str(")")); 
+                                       $$ = cat_str(4, $1, make_str(" not in ("), $5, make_str(")")); 
                                }
-               | a_expr '>' ANY '(' SubSelect ')'
+               | a_expr all_Op sub_type '(' SubSelect ')'
                                {
-                                       $$ = make4_str($1, make1_str(">any("), $5, make1_str(")")); 
+                                       $$ = cat_str(6, $1, $2, $3, make_str("("), $5, make_str(")")); 
                                }
-               | a_expr '=' ANY '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("=any("), $5, make1_str(")")); 
-                               }
-               | a_expr Op ALL '(' SubSelect ')'
-                               {
-                                       $$ = cat3_str($1, $2, make3_str(make1_str("all ("), $5, make1_str(")"))); 
-                               }
-               | a_expr '+' ALL '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("+all("), $5, make1_str(")")); 
-                               }
-               | a_expr '-' ALL '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("-all("), $5, make1_str(")")); 
-                               }
-               | a_expr '/' ALL '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("/all("), $5, make1_str(")")); 
-                               }
-               | a_expr '*' ALL '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("*all("), $5, make1_str(")")); 
-                               }
-               | a_expr '<' ALL '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("<all("), $5, make1_str(")")); 
-                               }
-               | a_expr '>' ALL '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str(">all("), $5, make1_str(")")); 
-                               }
-               | a_expr '=' ALL '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("=all("), $5, make1_str(")")); 
-                               }
-               | a_expr AND a_expr
-                               {       $$ = cat3_str($1, make1_str("and"), $3); }
-               | a_expr OR a_expr
-                               {       $$ = cat3_str($1, make1_str("or"), $3); }
-               | NOT a_expr
-                               {       $$ = cat2_str(make1_str("not"), $2); }
-               | case_expr
+               | row_expr
                                {       $$ = $1; }
                | cinputvariable
-                               { $$ = make1_str(";;"); }
+                               { $$ = make_str("?"); }
                ;
 
 /* Restricted expressions
+ *
  * 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.
+ *
+ * Presently, AND, NOT, IS, IN, and NULL are the a_expr keywords that would
+ * cause trouble in the places where b_expr is used.  For simplicity, we
+ * just eliminate all the boolean-keyword-operator productions from b_expr.
  */
-b_expr:  attr opt_indirection
+b_expr:  c_expr
                                {
-                                       $$ = cat2_str($1, $2);
+                                       $$ = $1;
                                }
-               | AexprConst
-                               {       $$ = $1;  }
-               | ColId
+               | b_expr TYPECAST Typename
                                {
-                                       $$ = $1;
+                                       $$ = cat_str(3, $1, make_str("::"), $3);
                                }
+                | NULL_P TYPECAST Typename
+                                {
+                                       $$ = cat2_str(make_str("null::"), $3);
+                                }
                | '-' b_expr %prec UMINUS
-                               {       $$ = cat2_str(make1_str("-"), $2); }
+                               {       $$ = cat2_str(make_str("-"), $2); }
+               | '%' b_expr
+                               {       $$ = cat2_str(make_str("%"), $2); }
+               | '^' b_expr
+                               {       $$ = cat2_str(make_str("^"), $2); }
+               | '|' b_expr
+                               {       $$ = cat2_str(make_str("|"), $2); }
+/* not possible in embedded sql                | ':' b_expr    
+                               {       $$ = cat2_str(make_str(":"), $2); }
+*/
+               | ';' b_expr
+                               {       $$ = cat2_str(make_str(";"), $2); }
+               | b_expr '%'
+                               {       $$ = cat2_str($1, make_str("%")); }
+               | b_expr '^'
+                               {       $$ = cat2_str($1, make_str("^")); }
+               | b_expr '|'
+                               {       $$ = cat2_str($1, make_str("|")); }
                | b_expr '+' b_expr
-                               {       $$ = cat3_str($1, make1_str("+"), $3); }
+                               {       $$ = cat_str(3, $1, make_str("+"), $3); }
                | b_expr '-' b_expr
-                               {       $$ = cat3_str($1, make1_str("-"), $3); }
-               | b_expr '/' b_expr
-                               {       $$ = cat3_str($1, make1_str("/"), $3); }
+                               {       $$ = cat_str(3, $1, make_str("-"), $3); }
                | b_expr '*' b_expr
-                               {       $$ = cat3_str($1, make1_str("*"), $3); }
-/* not possible in embedded sql                | ':' b_expr
-                               {       $$ = cat2_str(make1_str(":"), $2); }
-*/
-               | ';' b_expr
-                               {       $$ = cat2_str(make1_str(";"), $2); }
-               | '|' b_expr
-                               {       $$ = cat2_str(make1_str("|"), $2); }
-               | b_expr TYPECAST Typename
-                               {
-                                       $$ = cat3_str($1, make1_str("::"), $3);
-                               }
-               | CAST '(' b_expr AS Typename ')'
-                               {
-                                       $$ = cat3_str(make2_str(make1_str("cast("), $3), make1_str("as"), make2_str($5, make1_str(")")));
-                               }
-               | '(' a_expr ')'
-                               {       $$ = make3_str(make1_str("("), $2, make1_str(")")); }
+                               {       $$ = cat_str(3, $1, make_str("*"), $3); }
+               | b_expr '/' b_expr
+                               {       $$ = cat_str(3, $1, make_str("/"), $3); }
+               | b_expr '%' b_expr
+                               {       $$ = cat_str(3, $1, make_str("%"), $3); }
+               | b_expr '^' b_expr
+                               {       $$ = cat_str(3, $1, make_str("^"), $3); }
+               | b_expr '|' b_expr
+                               {       $$ = cat_str(3, $1, make_str("|"), $3); }
+               | b_expr '<' b_expr
+                               {       $$ = cat_str(3, $1, make_str("<"), $3); }
+               | b_expr '>' b_expr
+                               {       $$ = cat_str(3, $1, make_str(">"), $3); }
+               | b_expr '=' b_expr
+                               {       $$ = cat_str(3, $1, make_str("="), $3); }
                | b_expr Op b_expr
-                               {       $$ = cat3_str($1, $2, $3);      }
+                               {       $$ = cat_str(3, $1, $2, $3); }
                | Op b_expr
                                {       $$ = cat2_str($1, $2); }
                | b_expr Op
                                {       $$ = cat2_str($1, $2); }
+               | civariableonly
+                               {       $$ = $1; }
+               ;
+
+/*
+ * Productions that can be used in both a_expr and b_expr.
+ *
+ * Note: productions that refer recursively to a_expr or b_expr mostly
+ * cannot appear here.  However, it's OK to refer to a_exprs that occur
+ * inside parentheses, such as function arguments; that cannot introduce
+ * ambiguity to the b_expr syntax.
+ */
+c_expr:  attr
+                               {       $$ = $1;  }
+               | ColId opt_indirection
+                               {       $$ = cat2_str($1, $2);  }
+               | AexprConst
+                               {       $$ = $1;  }
+               | '(' a_expr_or_null ')'
+                               {       $$ = cat_str(3, make_str("("), $2, make_str(")")); }
+               | CAST '(' a_expr_or_null AS Typename ')'
+                               {       $$ = cat_str(5, make_str("cast("), $3, make_str("as"), $5, make_str(")")); }
+               | case_expr
+                               {       $$ = $1; }
                | func_name '(' ')'
-                               {
-                                       $$ = cat2_str($1, make1_str("()")); 
-                               }
+                               {       $$ = cat2_str($1, make_str("()"));  }
                | func_name '(' expr_list ')'
-                               {
-                                       $$ = make4_str($1, make1_str("("), $3, make1_str(")")); 
-                               }
+                               {       $$ = cat_str(4, $1, make_str("("), $3, make_str(")"));  }
+               | func_name '(' DISTINCT expr_list ')'
+                               {       $$ = cat_str(4, $1, make_str("( distinct"), $4, make_str(")"));  }
+               | func_name '(' '*' ')'
+                               {       $$ = cat2_str($1, make_str("(*)")); }
                | CURRENT_DATE
-                               {
-                                       $$ = make1_str("current_date");
-                               }
+                               {       $$ = make_str("current_date"); }
                | CURRENT_TIME
-                               {
-                                       $$ = make1_str("current_time");
-                               }
+                               {       $$ = make_str("current_time"); }
                | CURRENT_TIME '(' Iconst ')'
                                {
-                                       if ($3 != 0)
-                                               fprintf(stderr,"CURRENT_TIME(%s) precision not implemented; zero used instead", $3);
-                                       $$ = make1_str("current_time");
+                                       if (atol($3) != 0)
+                                       {
+                                               sprintf(errortext, "CURRENT_TIME(%s) precision not implemented; backend will use zero instead", $3);
+                                               mmerror(ET_WARN, errortext);
+                                       }
+
+                                       $$ = make_str("current_time");
                                }
                | CURRENT_TIMESTAMP
-                               {
-                                       $$ = make1_str("current_timestamp");
-                               }
+                               {       $$ = make_str("current_timestamp"); }
                | CURRENT_TIMESTAMP '(' Iconst ')'
                                {
                                        if (atol($3) != 0)
-                                               fprintf(stderr,"CURRENT_TIMESTAMP(%s) precision not implemented; zero used instead",$3);
-                                       $$ = make1_str("current_timestamp");
+                                       {
+                                               sprintf(errortext, "CURRENT_TIMESTAMP(%s) precision not implemented; backend will use zero instead", $3);
+                                               mmerror(ET_WARN, errortext);
+                                       }
+
+                                       $$ = make_str("current_timestamp");
                                }
                | CURRENT_USER
-                               {
-                                       $$ = make1_str("current_user");
-                               }
+                               {       $$ = make_str("current_user"); }
+               | SESSION_USER
+                               {       $$ = make_str("session_user"); }
                | USER
-                               {
-                                       $$ = make1_str("user");
-                               }
+                               {       $$ = make_str("user"); }
+               | EXTRACT '(' extract_list ')'
+                               {       $$ = cat_str(3, make_str("extract("), $3, make_str(")")); }
                | POSITION '(' position_list ')'
-                               {
-                                       $$ = make3_str(make1_str("position ("), $3, make1_str(")"));
-                               }
+                               {       $$ = cat_str(3, make_str("position("), $3, make_str(")")); }
                | SUBSTRING '(' substr_list ')'
-                               {
-                                       $$ = make3_str(make1_str("substring ("), $3, make1_str(")"));
-                               }
+                               {       $$ = cat_str(3, make_str("substring("), $3, make_str(")")); }
                /* various trim expressions are defined in SQL92 - thomas 1997-07-19 */
                | TRIM '(' BOTH trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim(both"), $4, make1_str(")"));
-                               }
+                               {       $$ = cat_str(3, make_str("trim(both"), $4, make_str(")")); }
                | TRIM '(' LEADING trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim(leading"), $4, make1_str(")"));
-                               }
+                               {       $$ = cat_str(3, make_str("trim(leading"), $4, make_str(")")); }
                | TRIM '(' TRAILING trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim(trailing"), $4, make1_str(")"));
-                               }
+                               {       $$ = cat_str(3, make_str("trim(trailing"), $4, make_str(")")); }
                | TRIM '(' trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim("), $3, make1_str(")"));
-                               }
-               | civariableonly
-                               { $$ = make1_str(";;"); }
+                               {       $$ = cat_str(3, make_str("trim("), $3, make_str(")")); }
+               | '(' SubSelect ')'
+                               {       $$ = cat_str(3, make_str("("), $2, make_str(")")); }
+               | EXISTS '(' SubSelect ')'
+                               {       $$ = cat_str(3, make_str("exists("), $3, make_str(")")); }
                ;
-
-opt_indirection:  '[' ecpg_expr ']' opt_indirection
+/* 
+ * This used to use ecpg_expr, but since there is no shift/reduce conflict
+ * anymore, we can remove ecpg_expr. - MM
+ */
+opt_indirection:  '[' a_expr ']' opt_indirection
                                {
-                                       $$ = cat4_str(make1_str("["), $2, make1_str("]"), $4);
+                                       $$ = cat_str(4, make_str("["), $2, make_str("]"), $4);
                                }
-               | '[' ecpg_expr ':' ecpg_expr ']' opt_indirection
+               | '[' a_expr ':' a_expr ']' opt_indirection
                                {
-                                       $$ = cat2_str(cat5_str(make1_str("["), $2, make1_str(":"), $4, make1_str("]")), $6);
+                                       $$ = cat_str(6, make_str("["), $2, make_str(":"), $4, make_str("]"), $6);
                                }
                | /* EMPTY */
-                               {       $$ = make1_str(""); }
+                               {       $$ = EMPTY; }
                ;
 
 expr_list:  a_expr_or_null
                                { $$ = $1; }
                | expr_list ',' a_expr_or_null
-                               { $$ = cat3_str($1, make1_str(","), $3); }
+                               { $$ = cat_str(3, $1, make_str(","), $3); }
                | expr_list USING a_expr
-                               { $$ = cat3_str($1, make1_str("using"), $3); }
+                               { $$ = cat_str(3, $1, make_str("using"), $3); }
                ;
 
 extract_list:  extract_arg FROM a_expr
                                {
-                                       $$ = cat3_str($1, make1_str("from"), $3);
+                                       $$ = cat_str(3, $1, make_str("from"), $3);
                                }
                | /* EMPTY */
-                               {       $$ = make1_str(""); }
+                               {       $$ = EMPTY; }
                | cinputvariable
-                               { $$ = make1_str(";;"); }
+                               { $$ = make_str("?"); }
                ;
 
 extract_arg:  datetime         { $$ = $1; }
-       | TIMEZONE_HOUR         { $$ = make1_str("timezone_hour"); }    
-       | TIMEZONE_MINUTE       { $$ = make1_str("timezone_minute"); }  
+       | TIMEZONE_HOUR         { $$ = make_str("timezone_hour"); }     
+       | TIMEZONE_MINUTE       { $$ = make_str("timezone_minute"); }   
                ;
 
-position_list:  position_expr IN position_expr
-                               {       $$ = cat3_str($1, make1_str("in"), $3); }
+/* position_list uses b_expr not a_expr to avoid conflict with general IN */
+position_list:  b_expr IN b_expr
+                               {       $$ = cat_str(3, $1, make_str("in"), $3); }
                | /* EMPTY */
-                               {       $$ = make1_str(""); }
-               ;
-
-position_expr:  attr opt_indirection
-                               {
-                                       $$ = cat2_str($1, $2);
-                               }
-               | AexprConst
-                               {       $$ = $1;  }
-               | '-' position_expr %prec UMINUS
-                               {       $$ = cat2_str(make1_str("-"), $2); }
-               | position_expr '+' position_expr
-                               {       $$ = cat3_str($1, make1_str("+"), $3); }
-               | position_expr '-' position_expr
-                               {       $$ = cat3_str($1, make1_str("-"), $3); }
-               | position_expr '/' position_expr
-                               {       $$ = cat3_str($1, make1_str("/"), $3); }
-               | position_expr '*' position_expr
-                               {       $$ = cat3_str($1, make1_str("*"), $3); }
-               | '|' position_expr
-                               {       $$ = cat2_str(make1_str("|"), $2); }
-               | position_expr TYPECAST Typename
-                               {
-                                       $$ = cat3_str($1, make1_str("::"), $3);
-                               }
-               | CAST '(' position_expr AS Typename ')'
-                               {
-                                       $$ = cat3_str(make2_str(make1_str("cast("), $3), make1_str("as"), make2_str($5, make1_str(")")));
-                               }
-               | '(' position_expr ')'
-                               {       $$ = make3_str(make1_str("("), $2, make1_str(")")); }
-               | position_expr Op position_expr
-                               {       $$ = cat3_str($1, $2, $3); }
-               | Op position_expr
-                               {       $$ = cat2_str($1, $2); }
-               | position_expr Op
-                               {       $$ = cat2_str($1, $2); }
-               | ColId
-                               {
-                                       $$ = $1;
-                               }
-               | func_name '(' ')'
-                               {
-                                       $$ = cat2_str($1, make1_str("()"));
-                               }
-               | func_name '(' expr_list ')'
-                               {
-                                       $$ = make4_str($1, make1_str("("), $3, make1_str(")"));
-                               }
-               | POSITION '(' position_list ')'
-                               {
-                                       $$ = make3_str(make1_str("position("), $3, make1_str(")"));
-                               }
-               | SUBSTRING '(' substr_list ')'
-                               {
-                                       $$ = make3_str(make1_str("substring("), $3, make1_str(")"));
-                               }
-               /* various trim expressions are defined in SQL92 - thomas 1997-07-19 */
-               | TRIM '(' BOTH trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim(both"), $4, make1_str(")"));
-                               }
-               | TRIM '(' LEADING trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim(leading"), $4, make1_str(")"));
-                               }
-               | TRIM '(' TRAILING trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim(trailing"), $4, make1_str(")"));
-                               }
-               | TRIM '(' trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim("), $3, make1_str(")"));
-                               }
+                               {       $$ = EMPTY; }
                ;
 
 substr_list:  expr_list substr_from substr_for
                                {
-                                       $$ = cat3_str($1, $2, $3);
+                                       $$ = cat_str(3, $1, $2, $3);
                                }
                | /* EMPTY */
-                               {       $$ = make1_str(""); }
+                               {       $$ = EMPTY; }
                ;
 
 substr_from:  FROM expr_list
-                               {       $$ = cat2_str(make1_str("from"), $2); }
+                               {       $$ = cat2_str(make_str("from"), $2); }
                | /* EMPTY */
                                {
-                                       $$ = make1_str("");
+                                       $$ = EMPTY;
                                }
                ;
 
 substr_for:  FOR expr_list
-                               {       $$ = cat2_str(make1_str("for"), $2); }
+                               {       $$ = cat2_str(make_str("for"), $2); }
                | /* EMPTY */
-                               {       $$ = make1_str(""); }
+                               {       $$ = EMPTY; }
                ;
 
 trim_list:  a_expr FROM expr_list
-                               { $$ = cat3_str($1, make1_str("from"), $3); }
+                               { $$ = cat_str(3, $1, make_str("from"), $3); }
                | FROM expr_list
-                               { $$ = cat2_str(make1_str("from"), $2); }
+                               { $$ = cat2_str(make_str("from"), $2); }
                | expr_list
                                { $$ = $1; }
                ;
@@ -3779,24 +4377,10 @@ in_expr:  SubSelect
                                {       $$ = $1; }
                ;
 
-in_expr_nodes:  AexprConst
-                               {       $$ = $1; }
-               | in_expr_nodes ',' AexprConst
-                               {       $$ = cat3_str($1, make1_str(","), $3);}
-               ;
-
-not_in_expr:  SubSelect
-                               {
-                                       $$ = $1; 
-                               }
-               | not_in_expr_nodes
-                               {       $$ = $1; }
-               ;
-
-not_in_expr_nodes:  AexprConst
+in_expr_nodes:  a_expr
                                {       $$ = $1; }
-               | not_in_expr_nodes ',' AexprConst
-                               {       $$ = cat3_str($1, make1_str(","), $3);}
+               | in_expr_nodes ',' a_expr
+                               {       $$ = cat_str(3, $1, make_str(","), $3);}
                ;
 
 /* Case clause
@@ -3815,18 +4399,16 @@ not_in_expr_nodes:  AexprConst
  * - thomas 1998-11-09
  */
 case_expr:  CASE case_arg when_clause_list case_default END_TRANS
-                                { $$ = cat5_str(make1_str("case"), $2, $3, $4, make1_str("end")); }
+                                { $$ = cat_str(5, make_str("case"), $2, $3, $4, make_str("end")); }
                 | NULLIF '(' a_expr ',' a_expr ')'
                                 {
-                                       $$ = cat5_str(make1_str("nullif("), $3, make1_str(","), $5, make1_str(")"));
+                                       $$ = cat_str(5, make_str("nullif("), $3, make_str(","), $5, make_str(")"));
 
-                                       fprintf(stderr, "NULLIF() not yet fully implemented");
+                                       mmerror(ET_WARN, "NULLIF() not yet fully implemented");
                                 }
                 | COALESCE '(' expr_list ')'
                                 {
-                                       $$ = cat3_str(make1_str("coalesce("), $3, make1_str(")"));
-
-                                       fprintf(stderr, "COALESCE() not yet fully implemented");
+                                       $$ = cat_str(3, make_str("coalesce("), $3, make_str(")"));
                                }
                ;
 
@@ -3838,42 +4420,37 @@ when_clause_list:  when_clause_list when_clause
 
 when_clause:  WHEN a_expr THEN a_expr_or_null
                                {
-                                       $$ = cat4_str(make1_str("when"), $2, make1_str("then"), $4);
+                                       $$ = cat_str(4, make_str("when"), $2, make_str("then"), $4);
                                }
                ;
 
-case_default:  ELSE a_expr_or_null     { $$ = cat2_str(make1_str("else"), $2); }
-               | /*EMPTY*/             { $$ = make1_str(""); }
+case_default:  ELSE a_expr_or_null     { $$ = cat2_str(make_str("else"), $2); }
+               | /*EMPTY*/             { $$ = EMPTY; }
                ;
 
-case_arg:  attr opt_indirection
-                               {
-                                       $$ = cat2_str($1, $2);
-                               }
-               | ColId
-                               {
+case_arg:  a_expr              {
                                        $$ = $1;
                                }
                | /*EMPTY*/
-                               {       $$ = make1_str(""); }
+                               {       $$ = EMPTY; }
                ;
 
-attr:  relation_name '.' attrs
+attr:  relation_name '.' attrs opt_indirection
                                {
-                                       $$ = make3_str($1, make1_str("."), $3);
+                                       $$ = cat_str(4, $1, make_str("."), $3, $4);
                                }
-               | ParamNo '.' attrs
+               | ParamNo '.' attrs opt_indirection
                                {
-                                       $$ = make3_str($1, make1_str("."), $3);
+                                       $$ = cat_str(4, $1, make_str("."), $3, $4);
                                }
                ;
 
 attrs:   attr_name
                                { $$ = $1; }
                | attrs '.' attr_name
-                               { $$ = make3_str($1, make1_str("."), $3); }
+                               { $$ = cat_str(3, $1, make_str("."), $3); }
                | attrs '.' '*'
-                               { $$ = make2_str($1, make1_str(".*")); }
+                               { $$ = make2_str($1, make_str(".*")); }
                ;
 
 
@@ -3883,42 +4460,17 @@ attrs:    attr_name
  *
  *****************************************************************************/
 
-res_target_list:  res_target_list ',' res_target_el
-                               {       $$ = cat3_str($1, make1_str(","),$3);  }
-               | res_target_el
-                               {       $$ = $1;  }
-               | '*'           { $$ = make1_str("*"); }
-               ;
-
-res_target_el:  ColId opt_indirection '=' a_expr_or_null
-                               {
-                                       $$ = cat4_str($1, $2, make1_str("="), $4);
-                               }
-               | attr opt_indirection
-                               {
-                                       $$ = cat2_str($1, $2);
-                               }
-               | relation_name '.' '*'
-                               {
-                                       $$ = make2_str($1, make1_str(".*"));
-                               }
-               ;
-
-/*
-** 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
-                               {       $$ = cat3_str($1, make1_str(","), $3);  }
-               | res_target_el2
+/* Target lists as found in SELECT ... and INSERT VALUES ( ... ) */
+target_list:  target_list ',' target_el
+                               {       $$ = cat_str(3, $1, make_str(","), $3);  }
+               | target_el
                                {       $$ = $1;  }
                ;
 
 /* AS is not optional because shift/red conflict with unary ops */
-res_target_el2:  a_expr_or_null AS ColLabel
+target_el:  a_expr_or_null AS ColLabel
                                {
-                                       $$ = cat3_str($1, make1_str("as"), $3);
+                                       $$ = cat_str(3, $1, make_str("as"), $3);
                                }
                | a_expr_or_null
                                {
@@ -3926,18 +4478,34 @@ res_target_el2:  a_expr_or_null AS ColLabel
                                }
                | relation_name '.' '*'
                                {
-                                       $$ = make2_str($1, make1_str(".*"));
+                                       $$ = make2_str($1, make_str(".*"));
                                }
                | '*'
                                {
-                                       $$ = make1_str("*");
+                                       $$ = make_str("*");
                                }
                ;
 
-opt_id:  ColId                                                                 { $$ = $1; }
-               | /* EMPTY */                                                   { $$ = make1_str(""); }
+/* Target list as found in UPDATE table SET ... */
+update_target_list:  update_target_list ',' update_target_el
+                               {       $$ = cat_str(3, $1, make_str(","),$3);  }
+               | update_target_el
+                               {       $$ = $1;  }
+               | '*'           { $$ = make_str("*"); }
+               ;
+
+update_target_el:  ColId opt_indirection '=' a_expr_or_null
+                               {
+                                       $$ = cat_str(4, $1, $2, make_str("="), $4);
+                               }
                ;
 
+/*****************************************************************************
+ *
+ *     Names and constants
+ *
+ *****************************************************************************/
+
 relation_name: SpecialRuleRelation
                                {
                                        $$ = $1;
@@ -3947,8 +4515,8 @@ relation_name:    SpecialRuleRelation
                                        /* disallow refs to variable system tables */
                                        if (strcmp(LogRelationName, $1) == 0
                                           || strcmp(VariableRelationName, $1) == 0) {
-                                               sprintf(errortext, make1_str("%s cannot be accessed by users"),$1);
-                                               yyerror(errortext);
+                                               sprintf(errortext, make_str("%s cannot be accessed by users"),$1);
+                                               mmerror(ET_ERROR, errortext);
                                        }
                                        else
                                                $$ = $1;
@@ -3969,7 +4537,7 @@ name:                                     ColId                   { $$ = $1; };
 func_name:                             ColId                   { $$ = $1; };
 
 file_name:                             Sconst                  { $$ = $1; };
-recipe_name:                   ident                   { $$ = $1; };
+/* NOT USED recipe_name:                       ident                   { $$ = $1; };*/
 
 /* Constants
  * Include TRUE/FALSE for SQL3 support. - thomas 1997-10-24
@@ -3986,7 +4554,10 @@ AexprConst:  Iconst
                                {
                                        $$ = $1;
                                }
-               | Typename Sconst
+                /* this rule formerly used Typename, but that causes reduce conf licts
+                  * with subscripted column names ...
+                  */
+               | SimpleTypename Sconst
                                {
                                        $$ = cat2_str($1, $2);
                                }
@@ -3994,11 +4565,11 @@ AexprConst:  Iconst
                                {       $$ = $1;  }
                | TRUE_P
                                {
-                                       $$ = make1_str("true");
+                                       $$ = make_str("true");
                                }
                | FALSE_P
                                {
-                                       $$ = make1_str("false");
+                                       $$ = make_str("false");
                                }
                ;
 
@@ -4042,75 +4613,122 @@ TypeId:  ColId
  */
 ColId:  ident                                  { $$ = $1; }
                | datetime                      { $$ = $1; }
-               | ABSOLUTE                      { $$ = make1_str("absolute"); }
-               | ACTION                        { $$ = make1_str("action"); }
-               | AFTER                         { $$ = make1_str("after"); }
-               | AGGREGATE                     { $$ = make1_str("aggregate"); }
-               | BACKWARD                      { $$ = make1_str("backward"); }
-               | BEFORE                        { $$ = make1_str("before"); }
-               | CACHE                         { $$ = make1_str("cache"); }
-               | CREATEDB                      { $$ = make1_str("createdb"); }
-               | CREATEUSER                    { $$ = make1_str("createuser"); }
-               | CYCLE                         { $$ = make1_str("cycle"); }
-               | DATABASE                      { $$ = make1_str("database"); }
-               | DELIMITERS                    { $$ = make1_str("delimiters"); }
-               | DOUBLE                        { $$ = make1_str("double"); }
-               | EACH                          { $$ = make1_str("each"); }
-               | ENCODING                      { $$ = make1_str("encoding"); }
-               | FORWARD                       { $$ = make1_str("forward"); }
-               | FUNCTION                      { $$ = make1_str("function"); }
-               | HANDLER                       { $$ = make1_str("handler"); }
-               | INCREMENT                     { $$ = make1_str("increment"); }
-               | INDEX                         { $$ = make1_str("index"); }
-               | INHERITS                      { $$ = make1_str("inherits"); }
-               | INSENSITIVE                   { $$ = make1_str("insensitive"); }
-               | INSTEAD                       { $$ = make1_str("instead"); }
-               | ISNULL                        { $$ = make1_str("isnull"); }
-               | KEY                           { $$ = make1_str("key"); }
-               | LANGUAGE                      { $$ = make1_str("language"); }
-               | LANCOMPILER                   { $$ = make1_str("lancompiler"); }
-               | LOCATION                      { $$ = make1_str("location"); }
-               | MATCH                         { $$ = make1_str("match"); }
-               | MAXVALUE                      { $$ = make1_str("maxvalue"); }
-               | MINVALUE                      { $$ = make1_str("minvalue"); }
-               | NEXT                          { $$ = make1_str("next"); }
-               | NOCREATEDB                    { $$ = make1_str("nocreatedb"); }
-               | NOCREATEUSER                  { $$ = make1_str("nocreateuser"); }
-               | NOTHING                       { $$ = make1_str("nothing"); }
-               | NOTNULL                       { $$ = make1_str("notnull"); }
-               | OF                            { $$ = make1_str("of"); }
-               | OIDS                          { $$ = make1_str("oids"); }
-               | ONLY                          { $$ = make1_str("only"); }
-               | OPERATOR                      { $$ = make1_str("operator"); }
-               | OPTION                        { $$ = make1_str("option"); }
-               | PASSWORD                      { $$ = make1_str("password"); }
-               | PRIOR                         { $$ = make1_str("prior"); }
-               | PRIVILEGES                    { $$ = make1_str("privileges"); }
-               | PROCEDURAL                    { $$ = make1_str("procedural"); }
-               | READ                          { $$ = make1_str("read"); }
-               | RECIPE                        { $$ = make1_str("recipe"); }
-               | RELATIVE                      { $$ = make1_str("relative"); }
-               | RENAME                        { $$ = make1_str("rename"); }
-               | RETURNS                       { $$ = make1_str("returns"); }
-               | ROW                           { $$ = make1_str("row"); }
-               | RULE                          { $$ = make1_str("rule"); }
-               | SCROLL                        { $$ = make1_str("scroll"); }
-               | SEQUENCE                      { $$ = make1_str("sequence"); }
-               | SERIAL                        { $$ = make1_str("serial"); }
-               | START                         { $$ = make1_str("start"); }
-               | STATEMENT                     { $$ = make1_str("statement"); }
-               | STDIN                         { $$ = make1_str("stdin"); }
-               | STDOUT                        { $$ = make1_str("stdout"); }
-               | TIME                          { $$ = make1_str("time"); }
-               | TIMESTAMP                     { $$ = make1_str("timestamp"); }
-               | TIMEZONE_HOUR                 { $$ = make1_str("timezone_hour"); }
-                | TIMEZONE_MINUTE               { $$ = make1_str("timezone_minute"); }
-               | TRIGGER                       { $$ = make1_str("trigger"); }
-               | TRUSTED                       { $$ = make1_str("trusted"); }
-               | TYPE_P                        { $$ = make1_str("type"); }
-               | VALID                         { $$ = make1_str("valid"); }
-               | VERSION                       { $$ = make1_str("version"); }
-               | ZONE                          { $$ = make1_str("zone"); }
+               | ABSOLUTE                      { $$ = make_str("absolute"); }
+               | ACCESS                        { $$ = make_str("access"); }
+               | ACTION                        { $$ = make_str("action"); }
+               | AFTER                         { $$ = make_str("after"); }
+               | AGGREGATE                     { $$ = make_str("aggregate"); }
+               | BACKWARD                      { $$ = make_str("backward"); }
+               | BEFORE                        { $$ = make_str("before"); }
+               | CACHE                         { $$ = make_str("cache"); }
+               | COMMENT                       { $$ = make_str("comment"); } 
+               | COMMITTED                     { $$ = make_str("committed"); }
+               | CONSTRAINTS                   { $$ = make_str("constraints"); }
+               | CREATEDB                      { $$ = make_str("createdb"); }
+               | CREATEUSER                    { $$ = make_str("createuser"); }
+               | CYCLE                         { $$ = make_str("cycle"); }
+               | DATABASE                      { $$ = make_str("database"); }
+               | DEFERRABLE                    { $$ = make_str("deferrable"); }
+               | DEFERRED                      { $$ = make_str("deferred"); }
+               | DELIMITERS                    { $$ = make_str("delimiters"); }
+               | DOUBLE                        { $$ = make_str("double"); }
+               | EACH                          { $$ = make_str("each"); }
+               | ENCODING                      { $$ = make_str("encoding"); }
+               | EXCLUSIVE                     { $$ = make_str("exclusive"); }
+               | FORWARD                       { $$ = make_str("forward"); }
+               | FUNCTION                      { $$ = make_str("function"); }
+               | HANDLER                       { $$ = make_str("handler"); }
+               | IMMEDIATE                     { $$ = make_str("immediate"); }
+               | INCREMENT                     { $$ = make_str("increment"); }
+               | INDEX                         { $$ = make_str("index"); }
+               | INHERITS                      { $$ = make_str("inherits"); }
+               | INITIALLY                     { $$ = make_str("initially"); }
+               | INSENSITIVE                   { $$ = make_str("insensitive"); }
+               | INSTEAD                       { $$ = make_str("instead"); }
+               | ISNULL                        { $$ = make_str("isnull"); }
+               | ISOLATION                     { $$ = make_str("isolation"); }
+               | KEY                           { $$ = make_str("key"); }
+               | LANGUAGE                      { $$ = make_str("language"); }
+               | LANCOMPILER                   { $$ = make_str("lancompiler"); }
+               | LEVEL                         { $$ = make_str("level"); }
+               | LOCATION                      { $$ = make_str("location"); }
+               | MATCH                         { $$ = make_str("match"); }
+               | MAXVALUE                      { $$ = make_str("maxvalue"); }
+               | MINVALUE                      { $$ = make_str("minvalue"); }
+               | MODE                          { $$ = make_str("mode"); }
+               | NEXT                          { $$ = make_str("next"); }
+               | NOCREATEDB                    { $$ = make_str("nocreatedb"); }
+               | NOCREATEUSER                  { $$ = make_str("nocreateuser"); }
+               | NOTHING                       { $$ = make_str("nothing"); }
+               | NOTNULL                       { $$ = make_str("notnull"); }
+               | OF                            { $$ = make_str("of"); }
+               | OIDS                          { $$ = make_str("oids"); }
+               | ONLY                          { $$ = make_str("only"); }
+               | OPERATOR                      { $$ = make_str("operator"); }
+               | OPTION                        { $$ = make_str("option"); }
+               | PASSWORD                      { $$ = make_str("password"); }
+               | PENDANT                       { $$ = make_str("pendant"); }
+               | PRIOR                         { $$ = make_str("prior"); }
+               | PRIVILEGES                    { $$ = make_str("privileges"); }
+               | PROCEDURAL                    { $$ = make_str("procedural"); }
+               | READ                          { $$ = make_str("read"); }
+               | RELATIVE                      { $$ = make_str("relative"); }
+               | RENAME                        { $$ = make_str("rename"); }
+               | RESTRICT                      { $$ = make_str("restrict"); }
+               | RETURNS                       { $$ = make_str("returns"); }
+               | ROW                           { $$ = make_str("row"); }
+               | RULE                          { $$ = make_str("rule"); }
+               | SCROLL                        { $$ = make_str("scroll"); }
+               | SEQUENCE                      { $$ = make_str("sequence"); }
+               | SERIAL                        { $$ = make_str("serial"); }
+               | SERIALIZABLE                  { $$ = make_str("serializable"); }
+               | SHARE                         { $$ = make_str("share"); }
+               | START                         { $$ = make_str("start"); }
+               | STATEMENT                     { $$ = make_str("statement"); }
+               | STDIN                         { $$ = make_str("stdin"); }
+               | STDOUT                        { $$ = make_str("stdout"); }
+               | SYSID                         { $$ = make_str("sysid"); }
+               | TIME                          { $$ = make_str("time"); }
+               | TIMESTAMP                     { $$ = make_str("timestamp"); }
+               | TIMEZONE_HOUR                 { $$ = make_str("timezone_hour"); }
+                | TIMEZONE_MINUTE               { $$ = make_str("timezone_minute"); }
+               | TRIGGER                       { $$ = make_str("trigger"); }
+               | TRUNCATE                      { $$ = make_str("truncate"); }
+               | TRUSTED                       { $$ = make_str("trusted"); }
+               | TYPE_P                        { $$ = make_str("type"); }
+               | VALID                         { $$ = make_str("valid"); }
+               | VERSION                       { $$ = make_str("version"); }
+               | ZONE                          { $$ = make_str("zone"); }
+               | SQL_AT                        { $$ = make_str("at"); }
+               | SQL_BOOL                      { $$ = make_str("bool"); }
+               | SQL_BREAK                     { $$ = make_str("break"); }
+               | SQL_CALL                      { $$ = make_str("call"); }
+               | SQL_CONNECT                   { $$ = make_str("connect"); }
+               | SQL_CONTINUE                  { $$ = make_str("continue"); }
+               | SQL_DEALLOCATE                { $$ = make_str("deallocate"); }
+               | SQL_DISCONNECT                { $$ = make_str("disconnect"); }
+               | SQL_FOUND                     { $$ = make_str("found"); }
+               | SQL_GO                        { $$ = make_str("go"); }
+               | SQL_GOTO                      { $$ = make_str("goto"); }
+               | SQL_IDENTIFIED                { $$ = make_str("identified"); }
+               | SQL_INDICATOR                 { $$ = make_str("indicator"); }
+               | SQL_INT                       { $$ = make_str("int"); }
+               | SQL_LONG                      { $$ = make_str("long"); }
+               | SQL_OFF                       { $$ = make_str("off"); }
+               | SQL_OPEN                      { $$ = make_str("open"); }
+               | SQL_PREPARE                   { $$ = make_str("prepare"); }
+               | SQL_RELEASE                   { $$ = make_str("release"); }
+               | SQL_SECTION                   { $$ = make_str("section"); }
+               | SQL_SHORT                     { $$ = make_str("short"); }
+               | SQL_SIGNED                    { $$ = make_str("signed"); }
+               | SQL_SQLERROR                  { $$ = make_str("sqlerror"); }
+               | SQL_SQLPRINT                  { $$ = make_str("sqlprint"); }
+               | SQL_SQLWARNING                { $$ = make_str("sqlwarning"); }
+               | SQL_STOP                      { $$ = make_str("stop"); }
+               | SQL_STRUCT                    { $$ = make_str("struct"); }
+               | SQL_UNSIGNED                  { $$ = make_str("unsigned"); }
+               | SQL_VAR                       { $$ = make_str("var"); }
+               | SQL_WHENEVER                  { $$ = make_str("whenever"); }
                ;
 /* Column label
  * Allowed labels in "AS" clauses.
@@ -4122,60 +4740,67 @@ ColId:  ident                                   { $$ = $1; }
  *  rather than in ColId if there was a shift/reduce conflict
  *  when used as a full identifier. - thomas 1997-11-06
  */
-ColLabel:  ColId                                               { $$ = $1; }
-               | ABORT_TRANS                                   { $$ = make1_str("abort"); }
-               | ANALYZE                                       { $$ = make1_str("analyze"); }
-               | BINARY                                        { $$ = make1_str("binary"); }
-               | CASE                                        { $$ = make1_str("case"); }
-               | CLUSTER                                               { $$ = make1_str("cluster"); }
-               | COALESCE                                        { $$ = make1_str("coalesce"); }
-               | CONSTRAINT                                    { $$ = make1_str("constraint"); }
-               | COPY                                                  { $$ = make1_str("copy"); }
-               | CROSS                                                 { $$ = make1_str("cross"); }
-               | CURRENT                                                       { $$ = make1_str("current"); }
-               | DO                                                    { $$ = make1_str("do"); }
-               | ELSE                                        { $$ = make1_str("else"); }
-               | END_TRANS                                        { $$ = make1_str("end"); }
-               | EXPLAIN                                                       { $$ = make1_str("explain"); }
-               | EXTEND                                                        { $$ = make1_str("extend"); }
-               | FALSE_P                                                       { $$ = make1_str("false"); }
-               | FOREIGN                                               { $$ = make1_str("foreign"); }
-               | GROUP                                                 { $$ = make1_str("group"); }
-               | LISTEN                                                        { $$ = make1_str("listen"); }
-               | LOAD                                                  { $$ = make1_str("load"); }
-               | LOCK_P                                                        { $$ = make1_str("lock"); }
-               | MOVE                                                  { $$ = make1_str("move"); }
-               | NEW                                                   { $$ = make1_str("new"); }
-               | NONE                                                  { $$ = make1_str("none"); }
-               | NULLIF                                        { $$ = make1_str("nullif"); }
-               | ORDER                                                 { $$ = make1_str("order"); }
-               | POSITION                                              { $$ = make1_str("position"); }
-               | PRECISION                                             { $$ = make1_str("precision"); }
-               | RESET                                                 { $$ = make1_str("reset"); }
-               | SETOF                                                 { $$ = make1_str("setof"); }
-               | SHOW                                                  { $$ = make1_str("show"); }
-               | TABLE                                                 { $$ = make1_str("table"); }
-               | THEN                                        { $$ = make1_str("then"); }
-               | TRANSACTION                                   { $$ = make1_str("transaction"); }
-               | TRUE_P                                                { $$ = make1_str("true"); }
-               | VACUUM                                        { $$ = make1_str("vacuum"); }
-               | VERBOSE                                               { $$ = make1_str("verbose"); }
-               | WHEN                                        { $$ = make1_str("when"); }
+ColLabel:  ColId                       { $$ = $1; }
+               | ABORT_TRANS           { $$ = make_str("abort"); }
+               | ANALYZE               { $$ = make_str("analyze"); }
+               | BINARY                { $$ = make_str("binary"); }
+               | CASE                  { $$ = make_str("case"); }
+               | CLUSTER               { $$ = make_str("cluster"); }
+               | COALESCE              { $$ = make_str("coalesce"); }
+               | CONSTRAINT            { $$ = make_str("constraint"); }
+               | COPY                  { $$ = make_str("copy"); }
+               | CURRENT               { $$ = make_str("current"); }
+               | CURRENT_USER          { $$ = make_str("current_user"); }
+               | DEC                   { $$ = make_str("dec"); }
+               | DECIMAL               { $$ = make_str("decimal"); }
+               | DO                    { $$ = make_str("do"); }
+               | ELSE                  { $$ = make_str("else"); }
+               | END_TRANS             { $$ = make_str("end"); }
+               | EXPLAIN               { $$ = make_str("explain"); }
+               | EXTEND                { $$ = make_str("extend"); }
+               | FALSE_P               { $$ = make_str("false"); }
+               | FLOAT                 { $$ = make_str("float"); }
+               | FOREIGN               { $$ = make_str("foreign"); }
+               | GLOBAL                { $$ = make_str("global"); }
+               | GROUP                 { $$ = make_str("group"); }
+               | LISTEN                { $$ = make_str("listen"); }
+               | LOAD                  { $$ = make_str("load"); }
+               | LOCK_P                { $$ = make_str("lock"); }
+               | MOVE                  { $$ = make_str("move"); }
+               | NEW                   { $$ = make_str("new"); }
+               | NONE                  { $$ = make_str("none"); }
+               | NULLIF                { $$ = make_str("nullif"); }
+               | NUMERIC               { $$ = make_str("numeric"); }
+               | ORDER                 { $$ = make_str("order"); }
+               | POSITION              { $$ = make_str("position"); }
+               | PRECISION             { $$ = make_str("precision"); }
+               | RESET                 { $$ = make_str("reset"); }
+               | SESSION_USER          { $$ = make_str("session_user"); }
+               | SETOF                 { $$ = make_str("setof"); }
+               | SHOW                  { $$ = make_str("show"); }
+               | TABLE                 { $$ = make_str("table"); }
+               | THEN                  { $$ = make_str("then"); }
+               | TRANSACTION           { $$ = make_str("transaction"); }
+               | TRUE_P                { $$ = make_str("true"); }
+               | USER                  { $$ = make_str("user"); }
+               | VACUUM                { $$ = make_str("vacuum"); }
+               | VERBOSE               { $$ = make_str("verbose"); }
+               | WHEN                  { $$ = make_str("when"); }
                ;
 
 SpecialRuleRelation:  CURRENT
                                {
                                        if (QueryIsRule)
-                                               $$ = make1_str("current");
+                                               $$ = make_str("current");
                                        else
-                                               yyerror("CURRENT used in non-rule query");
+                                               mmerror(ET_ERROR, "CURRENT used in non-rule query");
                                }
                | NEW
                                {
                                        if (QueryIsRule)
-                                               $$ = make1_str("new");
+                                               $$ = make_str("new");
                                        else
-                                               yyerror("NEW used in non-rule query");
+                                               mmerror(ET_ERROR, "NEW used in non-rule query");
                                }
                ;
 
@@ -4183,238 +4808,21 @@ SpecialRuleRelation:  CURRENT
  * and now special embedded SQL stuff
  */
 
-/*
- * variable declaration inside the exec sql declare block
- */
-ECPGDeclaration: sql_startdeclare variable_declarations sql_enddeclare {}
-
-sql_startdeclare : ecpgstart BEGIN_TRANS DECLARE SQL_SECTION SQL_SEMI {
-       fputs("/* exec sql begin declare section */\n", yyout);
-       output_line_number();
- }
-
-sql_enddeclare: ecpgstart END_TRANS DECLARE SQL_SECTION SQL_SEMI {
-    fputs("/* exec sql end declare section */\n", yyout); 
-    output_line_number();
-}
-
-variable_declarations: /* empty */
-       | declaration variable_declarations;
-
-declaration: storage_clause type
-       {
-               actual_storage[struct_level] = $1;
-               actual_type[struct_level] = $2.type_enum;
-               if ($2.type_enum != ECPGt_varchar && $2.type_enum != ECPGt_struct)
-                       fprintf(yyout, "%s %s", $1, $2.type_str);
-               free($2.type_str);
-       }
-       variable_list ';' { fputc(';', yyout); }
-
-storage_clause : S_EXTERN      { $$ = "extern"; }
-       | S_STATIC              { $$ = "static"; }
-       | S_SIGNED              { $$ = "signed"; }
-       | S_CONST               { $$ = "const"; }
-       | S_REGISTER            { $$ = "register"; }
-       | S_AUTO                        { $$ = "auto"; }
-       | /* empty */           { $$ = ""; }
-
-type: simple_type
-               {
-                       $$.type_enum = $1;
-                       $$.type_str = mm_strdup(ECPGtype_name($1));
-               }
-       | struct_type
-               {
-                       $$.type_enum = ECPGt_struct;
-                       $$.type_str = make1_str("");
-               }
-       | enum_type
-               {
-                       $$.type_str = $1;
-                       $$.type_enum = ECPGt_int;
-               }
-
-enum_type: s_enum '{' c_line '}'
-       {
-               $$ = cat4_str($1, make1_str("{"), $3, make1_str("}"));
-       }
-       
-s_enum: S_ENUM opt_symbol      { $$ = cat2_str(make1_str("enum"), $2); }
-
-struct_type: s_struct '{' variable_declarations '}'
-       {
-           ECPGfree_struct_member(struct_member_list[struct_level]);
-           free(actual_storage[struct_level--]);
-           fputs("} ", yyout);
-       }
-
-s_struct : S_STRUCT opt_symbol
-        {
-            struct_member_list[struct_level++] = NULL;
-            if (struct_level >= STRUCT_DEPTH)
-                 yyerror("Too many levels in nested structure definition");
-           fprintf(yyout, "struct %s {", $2);
-           free($2);
-       }
-
-opt_symbol: /* empty */        { $$ = make1_str(""); }
-       | symbol                { $$ = $1; }
-
-simple_type: S_SHORT           { $$ = ECPGt_short; }
-           | S_UNSIGNED S_SHORT { $$ = ECPGt_unsigned_short; }
-          | S_INT              { $$ = ECPGt_int; }
-           | S_UNSIGNED S_INT  { $$ = ECPGt_unsigned_int; }
-          | S_LONG             { $$ = ECPGt_long; }
-           | S_UNSIGNED S_LONG { $$ = ECPGt_unsigned_long; }
-           | S_FLOAT           { $$ = ECPGt_float; }
-           | S_DOUBLE          { $$ = ECPGt_double; }
-          | S_BOOL             { $$ = ECPGt_bool; };
-          | S_CHAR             { $$ = ECPGt_char; }
-           | S_UNSIGNED S_CHAR { $$ = ECPGt_unsigned_char; }
-          | S_VARCHAR          { $$ = ECPGt_varchar; }
-
-variable_list: variable 
-       | variable_list ','
-       {
-               if (actual_type[struct_level] != ECPGt_varchar)
-                       fputs(", ", yyout);
-               else
-                       fputs(";\n ", yyout);
-       } variable
-
-variable: opt_pointer symbol opt_array_bounds opt_initializer
-               {
-                       struct ECPGtype * type;
-                        int dimension = $3.index1; /* dimension of array */
-                        int length = $3.index2;    /* lenght of string */
-                        char dim[14L];
-
-                       switch (actual_type[struct_level])
-                       {
-                          case ECPGt_struct:
-                              /* pointer has to get dimension 0 */
-                              if (strlen($1) > 0)
-                              {
-                                   length = dimension;
-                                   dimension = 0;
-                              }
-
-                               if (length >= 0)
-                                   yyerror("No multi-dimensional array support for structures");
-
-                               if (dimension == 1 || dimension < 0)
-                                   type = ECPGmake_struct_type(struct_member_list[struct_level]);
-                               else
-                                   type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level]), dimension); 
-
-                               fprintf(yyout, "%s%s%s%s", $1, $2, $3.str, $4);
-                               break;
-                           case ECPGt_varchar:
-                              /* pointer has to get length 0 */
-                              if (strlen($1) > 0)
-                                   length=0;
-
-                               /* one index is the string length */
-                               if (length < 0)
-                               {
-                                   length = dimension;
-                                   dimension = 1;
-                               }
-
-                               if (dimension == 1)
-                                   type = ECPGmake_simple_type(actual_type[struct_level], length);
-                               else
-                                   type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level], length), dimension);
-
-                               switch(dimension)
-                               {
-                                  case 0:
-                                      strcpy(dim, "[]");
-                                      break;
-                                  case 1:
-                                      *dim = '\0';
-                                      break;
-                                  default:
-                                      sprintf(dim, "[%d]", dimension);
-                                      break;
-                                }
-                               if (length > 0)
-                                   fprintf(yyout, "%s struct varchar_%s { int len; char arr[%d]; } %s%s", actual_storage[struct_level], $2, length, $2, dim);
-                               else
-                                   fprintf(yyout, "%s struct varchar_%s { int len; char *arr; } %s%s", actual_storage[struct_level], $2, $2, dim);
-                               break;
-                           case ECPGt_char:
-                           case ECPGt_unsigned_char:
-                              /* pointer has to get length 0 */
-                              if (strlen($1) > 0)
-                                   length=0;
-
-                               /* one index is the string length */
-                               if (length < 0)
-                               {
-                                   length = (dimension < 0) ? 1 : dimension;
-                                   dimension = 1;
-                               }
-
-                               if (dimension == 1)
-                                   type = ECPGmake_simple_type(actual_type[struct_level], length);
-                               else
-                                   type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level], length), dimension);
-
-                               fprintf(yyout, "%s%s%s%s", $1, $2, $3.str, $4);
-                               break;
-                           default:
-                              /* a pointer has dimension = 0 */
-                              if (strlen($1) > 0) {
-                                   length = dimension;
-                                   dimension = 0;
-                              }
-
-                               if (length >= 0)
-                                   yyerror("No multi-dimensional array support for simple data types");
-
-                               if (dimension == 1 || dimension < 0)
-                                   type = ECPGmake_simple_type(actual_type[struct_level], 1);
-                               else
-                                   type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level], 1), dimension);
-
-                               fprintf(yyout, "%s%s%s%s", $1, $2, $3.str, $4);
-                               break;
-                       }
-
-                       if (struct_level == 0)
-                               new_variable($2, type);
-                       else
-                               ECPGmake_struct_member($2, type, &(struct_member_list[struct_level - 1]));
-
-                       free($1);
-                       free($2);
-                       free($3.str);
-                       free($4);
-               }
-
-opt_initializer: /* empty */           { $$ = make1_str(""); }
-       | '=' vartext                   { $$ = make2_str(make1_str("="), $2); }
-
-opt_pointer: /* empty */       { $$ = make1_str(""); }
-       | '*'                   { $$ = make1_str("*"); }
-
 /*
  * the exec sql connect statement: connect to the given database 
  */
 ECPGConnect: SQL_CONNECT TO connection_target opt_connection_name opt_user
                {
-                       $$ = make5_str($3, make1_str(","), $5, make1_str(","), $4);
+                       $$ = cat_str(5, $3, make_str(","), $5, make_str(","), $4);
                 }
        | SQL_CONNECT TO DEFAULT
                {
-                       $$ = make1_str("NULL,NULL,NULL,\"DEFAULT\"");
+                       $$ = make_str("NULL,NULL,NULL,\"DEFAULT\"");
                 }
       /* also allow ORACLE syntax */
         | SQL_CONNECT ora_user
                 {
-                      $$ = make3_str(make1_str("NULL,"), $2, make1_str(",NULL"));
+                      $$ = cat_str(3, make_str("NULL,"), $2, make_str(",NULL"));
                }
 
 connection_target: database_name opt_server opt_port
@@ -4423,33 +4831,33 @@ connection_target: database_name opt_server opt_port
                  if (strlen($2) > 0 && *($2) != '@')
                  {
                    sprintf(errortext, "parse error at or near '%s'", $2);
-                   yyerror(errortext);
+                   mmerror(ET_ERROR, errortext);
                  }
 
-                 $$ = make5_str(make1_str("\""), $1, $2, $3, make1_str("\""));
+                 $$ = make3_str(make_str("\""), make3_str($1, $2, $3), make_str("\""));
                }
         |  db_prefix server opt_port '/' database_name opt_options
                 {
                  /* new style: <tcp|unix>:postgresql://server[:port][/dbname] */
-                  if (strncmp($2, "://", 3) != 0)
+                  if (strncmp($2, "://", strlen("://")) != 0)
                  {
                    sprintf(errortext, "parse error at or near '%s'", $2);
-                   yyerror(errortext);
+                   mmerror(ET_ERROR, errortext);
                  }
 
-                 if (strncmp($1, "unix", 4) == 0 && strncmp($2 + 3, "localhost", 9) != 0)
+                 if (strncmp($1, "unix", strlen("unix")) == 0 && strncmp($2 + strlen("://"), "localhost", strlen("localhost")) != 0)
                  {
                    sprintf(errortext, "unix domain sockets only work on 'localhost' but not on '%9.9s'", $2);
-                    yyerror(errortext);
+                    mmerror(ET_ERROR, errortext);
                  }
 
-                 if (strncmp($1, "unix", 4) != 0 && strncmp($1, "tcp", 3) != 0)
+                 if (strncmp($1, "unix", strlen("unix")) != 0 && strncmp($1, "tcp", strlen("tcp")) != 0)
                  {
                    sprintf(errortext, "only protocols 'tcp' and 'unix' are supported");
-                    yyerror(errortext);
+                    mmerror(ET_ERROR, errortext);
                  }
        
-                 $$ = make4_str(make5_str(make1_str("\""), $1, $2, $3, make1_str("/")), $5, $6, make1_str("\""));
+                 $$ = make2_str(make3_str(make_str("\""), $1, $2), make3_str(make3_str($3, make_str("/"), $5),  $6, make_str("\"")));
                }
        | char_variable
                 {
@@ -4468,16 +4876,16 @@ db_prefix: ident cvariable
                  if (strcmp($2, "postgresql") != 0 && strcmp($2, "postgres") != 0)
                  {
                    sprintf(errortext, "parse error at or near '%s'", $2);
-                   yyerror(errortext); 
+                   mmerror(ET_ERROR, errortext);       
                  }
 
                  if (strcmp($1, "tcp") != 0 && strcmp($1, "unix") != 0)
                  {
                    sprintf(errortext, "Illegal connection type %s", $1);
-                   yyerror(errortext);
+                   mmerror(ET_ERROR, errortext);
                  }
 
-                 $$ = make3_str($1, make1_str(":"), $2);
+                 $$ = make3_str( $1, make_str(":"), $2);
                }
         
 server: Op server_name
@@ -4485,51 +4893,51 @@ server: Op server_name
                  if (strcmp($1, "@") != 0 && strcmp($1, "://") != 0)
                  {
                    sprintf(errortext, "parse error at or near '%s'", $1);
-                   yyerror(errortext);
+                   mmerror(ET_ERROR, errortext);
                  }
 
                  $$ = make2_str($1, $2);
                }
 
 opt_server: server { $$ = $1; }
-        | /* empty */ { $$ = make1_str(""); }
+        | /* empty */ { $$ = EMPTY; }
 
 server_name: ColId   { $$ = $1; }
-        | ColId '.' server_name { $$ = make3_str($1, make1_str("."), $3); }
+        | ColId '.' server_name {      $$ = make3_str($1, make_str("."), $3); }
 
-opt_port: ':' Iconst { $$ = make2_str(make1_str(":"), $2); }
-        | /* empty */ { $$ = make1_str(""); }
+opt_port: ':' Iconst { $$ = make2_str(make_str(":"), $2); }
+        | /* empty */ { $$ = EMPTY; }
 
 opt_connection_name: AS connection_target { $$ = $2; }
-        | /* empty */ { $$ = make1_str("NULL"); }
+        | /* empty */ { $$ = make_str("NULL"); }
 
 opt_user: USER ora_user { $$ = $2; }
-          | /* empty */ { $$ = make1_str("NULL,NULL"); }
+          | /* empty */ { $$ = make_str("NULL,NULL"); }
 
 ora_user: user_name
                {
-                        $$ = make2_str($1, make1_str(",NULL"));
+                        $$ = cat2_str($1, make_str(", NULL"));
                }
-       | user_name '/' ColId
+       | user_name '/' user_name
                {
-                       $$ = make3_str($1, make1_str(","), $3);
+                       $$ = cat_str(3, $1, make_str(","), $3);
                 }
         | user_name SQL_IDENTIFIED BY user_name
                 {
-                       $$ = make3_str($1, make1_str(","), $4);
+                       $$ = cat_str(3, $1, make_str(","), $4);
                 }
         | user_name USING user_name
                 {
-                       $$ = make3_str($1, make1_str(","), $3);
+                       $$ = cat_str(3, $1, make_str(","), $3);
                 }
 
 user_name: UserId       { if ($1[0] == '\"')
                                $$ = $1;
                          else
-                               $$ = make3_str(make1_str("\""), $1, make1_str("\""));
+                               $$ = make3_str(make_str("\""), $1, make_str("\""));
                        }
         | char_variable { $$ = $1; }
-        | SCONST        { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); }
+        | SCONST        { $$ = make3_str(make_str("\""), $1, make_str("\"")); }
 
 char_variable: cvariable
                { /* check if we have a char variable */
@@ -4547,10 +4955,10 @@ char_variable: cvariable
                                 $$ = $1;
                                 break;
                             case ECPGt_varchar:
-                                $$ = make2_str($1, make1_str(".arr"));
+                                $$ = make2_str($1, make_str(".arr"));
                                 break;
                             default:
-                                yyerror("invalid datatype");
+                                mmerror(ET_ERROR, "invalid datatype");
                                 break;
                         }
                }
@@ -4558,423 +4966,873 @@ char_variable: cvariable
 opt_options: Op ColId
                {
                        if (strlen($1) == 0)
-                               yyerror("parse error");
+                               mmerror(ET_ERROR, "parse error");
                                
                        if (strcmp($1, "?") != 0)
                        {
                                sprintf(errortext, "parse error at or near %s", $1);
-                               yyerror(errortext);
+                               mmerror(ET_ERROR, errortext);
                        }
                        
-                       $$ = make2_str(make1_str("?"), $2);
+                       $$ = make2_str(make_str("?"), $2);
                }
-       | /* empty */ { $$ = make1_str(""); }
+       | /* empty */ { $$ = EMPTY; }
 
 /*
- * the exec sql disconnect statement: disconnect from the given database 
+ * Declare a prepared cursor. The syntax is different from the standard
+ * declare statement, so we create a new rule.
  */
-ECPGDisconnect: SQL_DISCONNECT dis_name { $$ = $2; }
+ECPGCursorStmt:  DECLARE name opt_cursor CURSOR FOR ident
+                               {
+                                       struct cursor *ptr, *this;
+                                       struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable));
 
-dis_name: connection_object    { $$ = $1; }
-       | CURRENT       { $$ = make1_str("CURRENT"); }
-       | ALL           { $$ = make1_str("ALL"); }
-       | /* empty */   { $$ = make1_str("CURRENT"); }
+                                       for (ptr = cur; ptr != NULL; ptr = ptr->next)
+                                       {
+                                               if (strcmp($2, ptr->name) == 0)
+                                               {
+                                                       /* re-definition is a bug */
+                                                       sprintf(errortext, "cursor %s already defined", $2);
+                                                       mmerror(ET_ERROR, errortext);
+                                               }
+                                       }
 
-connection_object: connection_target { $$ = $1; }
-       | DEFAULT       { $$ = make1_str("DEFAULT"); }
+                                       this = (struct cursor *) mm_alloc(sizeof(struct cursor));
 
-/*
- * execute a given string as sql command
- */
-ECPGExecute : EXECUTE SQL_IMMEDIATE execstring { $$ = $3; };
+                                       /* initial definition */
+                                       this->next = cur;
+                                       this->name = $2;
+                                       this->connection = connection;
+                                       this->command =  cat_str(4, make_str("declare"), mm_strdup($2), $3, make_str("cursor for ?"));
+                                       this->argsresult = NULL;
 
-execstring: cvariable |
-       CSTRING  { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); };
+                                       thisquery->type = &ecpg_query;
+                                       thisquery->brace_level = 0;
+                                       thisquery->next = NULL;
+                                       thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(\"\")") + strlen($6));
+                                       sprintf(thisquery->name, "ECPGprepared_statement(\"%s\")", $6);
 
-/*
- * open is an open cursor, at the moment this has to be removed
- */
-ECPGOpen: SQL_OPEN name open_opts {
-               $$ = $2;
-};
+                                       this->argsinsert = NULL;
+                                       add_variable(&(this->argsinsert), thisquery, &no_indicator); 
 
-open_opts: /* empty */         { $$ = make1_str(""); }
-       | USING cvariable       {
-                                       yyerror ("open cursor with variables not implemented yet");
+                                       cur = this;
+                                       
+                                       $$ = cat_str(3, make_str("/*"), mm_strdup(this->command), make_str("*/"));
                                }
+               ;
 
 /*
- * for compatibility with ORACLE we will also allow the keyword RELEASE
- * after a transaction statement to disconnect from the database.
+ * the exec sql deallocate prepare command to deallocate a previously
+ * prepared statement
  */
+ECPGDeallocate:        SQL_DEALLOCATE SQL_PREPARE ident        { $$ = cat_str(3, make_str("ECPGdeallocate(__LINE__, \""), $3, make_str("\");")); }
 
-ECPGRelease: TransactionStmt SQL_RELEASE
+/*
+ * variable declaration inside the exec sql declare block
+ */
+ECPGDeclaration: sql_startdeclare
        {
-               if (strncmp($1, "begin", 5) == 0)
-                        yyerror("RELEASE does not make sense when beginning a transaction");
-
-               fprintf(yyout, "ECPGtrans(__LINE__, \"%s\");", $1);
-               whenever_action(0);
-               fprintf(yyout, "ECPGdisconnect(\"\");"); 
-               whenever_action(0);
-               free($1);
+               fputs("/* exec sql begin declare section */", yyout);
        }
+       variable_declarations sql_enddeclare
+       {
+               fprintf(yyout, "%s/* exec sql end declare section */", $3);
+               free($3);
+               output_line_number();
+       }
+
+sql_startdeclare : ecpgstart BEGIN_TRANS DECLARE SQL_SECTION ';' {}
+
+sql_enddeclare: ecpgstart END_TRANS DECLARE SQL_SECTION ';' {}
+
+variable_declarations:  /* empty */ { $$ = EMPTY; }
+                       | declarations { $$ = $1; }
+
+declarations:  declaration { $$ = $1; }
+                       | declarations declaration { $$ = cat2_str($1, $2); }
+
+declaration: storage_clause storage_modifier
+       {
+               actual_storage[struct_level] = cat2_str(mm_strdup($1), mm_strdup($2));
+               actual_startline[struct_level] = hashline_number();
+       }
+       type
+       {
+               actual_type[struct_level].type_enum = $4.type_enum;
+               actual_type[struct_level].type_dimension = $4.type_dimension;
+               actual_type[struct_level].type_index = $4.type_index;
+
+               /* we do not need the string "varchar" for output */
+               /* so replace it with an empty string */
+               if ($4.type_enum == ECPGt_varchar)
+               {
+                       free($4.type_str);
+                       $4.type_str=EMPTY;
+               }
+       }
+       variable_list ';'
+       {
+               $$ = cat_str(6, actual_startline[struct_level], $1, $2, $4.type_str, $6, make_str(";\n"));
+       }
+
+storage_clause : S_EXTERN      { $$ = make_str("extern"); }
+       | S_STATIC              { $$ = make_str("static"); }
+       | S_REGISTER            { $$ = make_str("register"); }
+       | S_AUTO                        { $$ = make_str("auto"); }
+       | /* empty */           { $$ = EMPTY; }
+
+storage_modifier : S_CONST      { $$ = make_str("const"); }
+       | S_VOLATILE             { $$ = make_str("volatile"); }
+       | /* empty */            { $$ = EMPTY; }
+
+type: simple_type
+               {
+                       $$.type_enum = $1;
+                       $$.type_str = mm_strdup(ECPGtype_name($1));
+                       $$.type_dimension = -1;
+                       $$.type_index = -1;
+               }
+       | varchar_type
+               {
+                       $$.type_enum = ECPGt_varchar;
+                       $$.type_str = make_str("varchar");;
+                       $$.type_dimension = -1;
+                       $$.type_index = -1;
+               }
+       | struct_type
+               {
+                       $$.type_enum = ECPGt_struct;
+                       $$.type_str = $1;
+                       $$.type_dimension = -1;
+                       $$.type_index = -1;
+               }
+       | union_type
+               {
+                       $$.type_enum = ECPGt_union;
+                       $$.type_str = $1;
+                       $$.type_dimension = -1;
+                       $$.type_index = -1;
+               }
+       | enum_type
+               {
+                       $$.type_str = $1;
+                       $$.type_enum = ECPGt_int;
+                       $$.type_dimension = -1;
+                       $$.type_index = -1;
+               }
+       | symbol
+               {
+                       /* this is for typedef'ed types */
+                       struct typedefs *this = get_typedef($1);
+
+                       $$.type_str = (this->type->type_enum == ECPGt_varchar) ? EMPTY : mm_strdup(this->name);
+                        $$.type_enum = this->type->type_enum;
+                       $$.type_dimension = this->type->type_dimension;
+                       $$.type_index = this->type->type_index;
+                       struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
+               }
+
+enum_type: SQL_ENUM opt_symbol enum_definition
+       {
+               $$ = cat_str(3, make_str("enum"), $2, $3);
+       }
+       |  SQL_ENUM symbol
+       {
+               $$ = cat2_str(make_str("enum"), $2);
+       }
+
+enum_definition: '{' c_list '}'        { $$ = cat_str(3, make_str("{"), $2, make_str("}")); }
+
+struct_type: s_struct '{' variable_declarations '}'
+       {
+           ECPGfree_struct_member(struct_member_list[struct_level]);
+           free(actual_storage[struct_level--]);
+           $$ = cat_str(4, $1, make_str("{"), $3, make_str("}"));
+       }
+
+union_type: s_union '{' variable_declarations '}'
+       {
+           ECPGfree_struct_member(struct_member_list[struct_level]);
+           free(actual_storage[struct_level--]);
+           $$ = cat_str(4, $1, make_str("{"), $3, make_str("}"));
+       }
+
+s_struct: SQL_STRUCT opt_symbol
+        {
+            struct_member_list[struct_level++] = NULL;
+            if (struct_level >= STRUCT_DEPTH)
+                 mmerror(ET_ERROR, "Too many levels in nested structure definition");
+
+           /* reset this variable so we see if there was */
+           /* an initializer specified */
+           initializer = 0;
+
+           $$ = cat2_str(make_str("struct"), $2);
+       }
+
+s_union: UNION opt_symbol
+        {
+            struct_member_list[struct_level++] = NULL;
+            if (struct_level >= STRUCT_DEPTH)
+                 mmerror(ET_ERROR, "Too many levels in nested structure definition");
+
+           /* reset this variable so we see if there was */
+           /* an initializer specified */
+           initializer = 0;
+
+           $$ = cat2_str(make_str("union"), $2);
+       }
+
+opt_symbol: /* empty */        { $$ = EMPTY; }
+       | symbol                { $$ = $1; }
+
+simple_type: unsigned_type             { $$=$1; }
+       |       opt_signed signed_type  { $$=$2; }
+       ;
+
+unsigned_type: SQL_UNSIGNED SQL_SHORT                  { $$ = ECPGt_unsigned_short; }
+               | SQL_UNSIGNED SQL_SHORT SQL_INT        { $$ = ECPGt_unsigned_short; }
+               | SQL_UNSIGNED                          { $$ = ECPGt_unsigned_int; }
+               | SQL_UNSIGNED SQL_INT                  { $$ = ECPGt_unsigned_int; }
+               | SQL_UNSIGNED SQL_LONG                 { $$ = ECPGt_unsigned_long; }
+               | SQL_UNSIGNED SQL_LONG SQL_INT         { $$ = ECPGt_unsigned_long; }
+               | SQL_UNSIGNED CHAR                     { $$ = ECPGt_unsigned_char; }
+               ;
+
+signed_type: SQL_SHORT          { $$ = ECPGt_short; }
+           | SQL_SHORT SQL_INT  { $$ = ECPGt_short; }
+           | SQL_INT            { $$ = ECPGt_int; }
+           | SQL_LONG           { $$ = ECPGt_long; }
+           | SQL_LONG SQL_INT   { $$ = ECPGt_long; }
+           | SQL_BOOL          { $$ = ECPGt_bool; };
+           | FLOAT             { $$ = ECPGt_float; }
+           | DOUBLE            { $$ = ECPGt_double; }
+           | CHAR              { $$ = ECPGt_char; }
+          ;
+
+opt_signed:    SQL_SIGNED
+       |       /* EMPTY */
+       ;
+
+varchar_type:  VARCHAR         { $$ = ECPGt_varchar; }
+
+variable_list: variable 
+       {
+               $$ = $1;
+       }
+       | variable_list ',' variable
+       {
+               $$ = cat_str(3, $1, make_str(","), $3);
+       }
+
+variable: opt_pointer symbol opt_array_bounds opt_initializer
+               {
+                       struct ECPGtype * type;
+                        int dimension = $3.index1; /* dimension of array */
+                        int length = $3.index2;    /* lenght of string */
+                        char dim[14L], ascii_len[12];
+
+                       adjust_array(actual_type[struct_level].type_enum, &dimension, &length, actual_type[struct_level].type_dimension, actual_type[struct_level].type_index, strlen($1));
+
+                       switch (actual_type[struct_level].type_enum)
+                       {
+                          case ECPGt_struct:
+                          case ECPGt_union:
+                               if (dimension < 0)
+                                   type = ECPGmake_struct_type(struct_member_list[struct_level], actual_type[struct_level].type_enum);
+                               else
+                                   type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level], actual_type[struct_level].type_enum), dimension); 
+
+                               $$ = cat_str(4, $1, mm_strdup($2), $3.str, $4);
+                               break;
+                           case ECPGt_varchar:
+                               if (dimension == -1)
+                                   type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length);
+                               else
+                                   type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length), dimension);
+
+                               switch(dimension)
+                               {
+                                  case 0:
+                                 case -1:
+                                  case 1:
+                                      *dim = '\0';
+                                      break;
+                                  default:
+                                      sprintf(dim, "[%d]", dimension);
+                                      break;
+                               }
+                              sprintf(ascii_len, "%d", length);
+
+                               if (length == 0)
+                                  mmerror(ET_ERROR, "pointer to varchar are not implemented");
+
+                              if (dimension == 0)
+                                  $$ = cat_str(7, mm_strdup(actual_storage[struct_level]), make2_str(make_str(" struct varchar_"), mm_strdup($2)), make_str(" { int len; char arr["), mm_strdup(ascii_len), make_str("]; } *"), mm_strdup($2), $4);
+                              else
+                                   $$ = cat_str(8, mm_strdup(actual_storage[struct_level]), make2_str(make_str(" struct varchar_"), mm_strdup($2)), make_str(" { int len; char arr["), mm_strdup(ascii_len), make_str("]; } "), mm_strdup($2), mm_strdup(dim), $4);
+
+                               break;
+                           case ECPGt_char:
+                           case ECPGt_unsigned_char:
+                               if (dimension == -1)
+                                   type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length);
+                               else
+                                   type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length), dimension);
+
+                              $$ = cat_str(4, $1, mm_strdup($2), $3.str, $4);
+                               break;
+                           default:
+                               if (dimension < 0)
+                                   type = ECPGmake_simple_type(actual_type[struct_level].type_enum, 1);
+                               else
+                                   type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, 1), dimension);
+
+                              $$ = cat_str(4, $1, mm_strdup($2), $3.str, $4);
+                               break;
+                       }
+
+                       if (struct_level == 0)
+                               new_variable($2, type);
+                       else
+                               ECPGmake_struct_member($2, type, &(struct_member_list[struct_level - 1]));
+
+                       free($2);
+               }
+
+opt_initializer: /* empty */           { $$ = EMPTY; }
+       | '=' c_term                    { 
+                                               initializer = 1;
+                                               $$ = cat2_str(make_str("="), $2);
+                                       }
+
+opt_pointer: /* empty */       { $$ = EMPTY; }
+       | '*'                   { $$ = make_str("*"); }
+
+/*
+ * As long as the prepare statement is not supported by the backend, we will
+ * try to simulate it here so we get dynamic SQL 
+ */
+ECPGDeclare: DECLARE STATEMENT ident
+       {
+               /* this is only supported for compatibility */
+               $$ = cat_str(3, make_str("/* declare statement"), $3, make_str("*/"));
+       }
+/*
+ * the exec sql disconnect statement: disconnect from the given database 
+ */
+ECPGDisconnect: SQL_DISCONNECT dis_name { $$ = $2; }
+
+dis_name: connection_object    { $$ = $1; }
+       | CURRENT       { $$ = make_str("CURRENT"); }
+       | ALL           { $$ = make_str("ALL"); }
+       | /* empty */   { $$ = make_str("CURRENT"); }
+
+connection_object: connection_target { $$ = $1; }
+       | DEFAULT       { $$ = make_str("DEFAULT"); }
+
+/*
+ * execute a given string as sql command
+ */
+ECPGExecute : EXECUTE IMMEDIATE execstring
+       { 
+               struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable));
+
+               thisquery->type = &ecpg_query;
+               thisquery->brace_level = 0;
+               thisquery->next = NULL;
+               thisquery->name = $3;
+
+               add_variable(&argsinsert, thisquery, &no_indicator); 
+
+               $$ = make_str("?");
+       }
+       | EXECUTE ident 
+       {
+               struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable));
+
+               thisquery->type = &ecpg_query;
+               thisquery->brace_level = 0;
+               thisquery->next = NULL;
+               thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(\"\")") + strlen($2));
+               sprintf(thisquery->name, "ECPGprepared_statement(\"%s\")", $2);
+
+               add_variable(&argsinsert, thisquery, &no_indicator); 
+       } ecpg_using
+       {
+               $$ = make_str("?");
+       }
+
+execstring: char_variable |
+       CSTRING  { $$ = make3_str(make_str("\""), $1, make_str("\"")); };
+
+/*
+ * the exec sql free command to deallocate a previously
+ * prepared statement
+ */
+ECPGFree:      SQL_FREE ident  { $$ = $2; }
+
+/*
+ * open is an open cursor, at the moment this has to be removed
+ */
+ECPGOpen: SQL_OPEN name ecpg_using {
+               $$ = $2;
+};
+
+ecpg_using: /* empty */                { $$ = EMPTY; }
+       | USING variablelist    {
+                                       /* mmerror ("open cursor with variables not implemented yet"); */
+                                       $$ = EMPTY;
+                               }
+
+variablelist: cinputvariable | cinputvariable ',' variablelist
+
+/*
+ * As long as the prepare statement is not supported by the backend, we will
+ * try to simulate it here so we get dynamic SQL 
+ */
+ECPGPrepare: SQL_PREPARE ident FROM execstring
+       {
+               $$ = cat2_str(make3_str(make_str("\""), $2, make_str("\",")), $4);
+       }
+
+/*
+ * dynamic SQL: descriptor based access
+ *     written by Christof Petig <christof.petig@wtal.de>
+ */
+
+/*
+ * deallocate a descriptor
+ */
+ECPGDeallocateDescr:   SQL_DEALLOCATE SQL_DESCRIPTOR ident     
+{      drop_descriptor($3,connection);
+       $$ = $3;
+}
+
+/*
+ * allocate a descriptor
+ */
+ECPGAllocateDescr:     SQL_ALLOCATE SQL_DESCRIPTOR ident       
+{   add_descriptor($3,connection);
+       $$ = $3;
+}
+
+/*
+ * read from descriptor
+ */
+
+ECPGGetDescHeaderItem: cvariable '=' ident  {
+               push_assignment($1,$3);
+}
+
+ECPGGetDescItem: cvariable '=' ident  {
+               push_assignment($1,$3);
+}
+       | cvariable '=' TYPE_P {
+               push_assignment($1,"type");
+}
+       | cvariable '=' PRECISION {
+               push_assignment($1,"precision");
+}
+       | cvariable '=' SQL_INDICATOR {
+               push_assignment($1,"indicator");
+}
+
+ECPGGetDescHeaderItems: ECPGGetDescHeaderItem
+       | ECPGGetDescHeaderItems ',' ECPGGetDescHeaderItem;
+ECPGGetDescItems: ECPGGetDescItem
+       | ECPGGetDescItems ',' ECPGGetDescItem;
+ECPGGetDescriptorHeader:       SQL_GET SQL_DESCRIPTOR ident ECPGGetDescHeaderItems
+{  $$ = $3; }
+
+ECPGGetDescriptor:     SQL_GET SQL_DESCRIPTOR ident SQL_VALUE cvariable ECPGGetDescItems
+{  $$ = $3; descriptor_index=$5; }
+       |       SQL_GET SQL_DESCRIPTOR ident SQL_VALUE Iconst ECPGGetDescItems
+{  $$ = $3; descriptor_index=$5; }
+
+/*
+ *  fetch [ in | from ] <portalname> into sql descriptor <name>
+ */
+
+FetchDescriptorStmt:   FETCH from_in name INTO SQL_SQL SQL_DESCRIPTOR ident
+                               {
+                                       $$ = cat_str(3, make_str("fetch"), $2, $3);
+                                       descriptor_name=$7;
+                               }
+               |       FETCH name INTO SQL_SQL SQL_DESCRIPTOR ident
+                               {
+                                       $$ = cat2_str(make_str("fetch"), $2);
+                                       descriptor_name=$6;
+                               }
+               ;
+
+/*
+ * for compatibility with ORACLE we will also allow the keyword RELEASE
+ * after a transaction statement to disconnect from the database.
+ */
+
+ECPGRelease: TransactionStmt SQL_RELEASE
+       {
+               if (strcmp($1, "begin") == 0)
+                        mmerror(ET_ERROR, "RELEASE does not make sense when beginning a transaction");
+
+               fprintf(yyout, "ECPGtrans(__LINE__, %s, \"%s\");",
+                               connection ? connection : "NULL", $1);
+               whenever_action(0);
+               fprintf(yyout, "ECPGdisconnect(__LINE__, \"\");"); 
+               whenever_action(0);
+               free($1);
+       }
+
+/* 
+ * set/reset the automatic transaction mode, this needs a differnet handling
+ * as the other set commands
+ */
+ECPGSetAutocommit:  SET SQL_AUTOCOMMIT to_equal on_off
+                       {
+                               $$ = $4;
+                        }
+
+on_off:        ON              { $$ = make_str("on"); }
+       | SQL_OFF       { $$ = make_str("off"); }
+
+to_equal:      TO | '=';
 
 /* 
  * set the actual connection, this needs a differnet handling as the other
  * set commands
  */
-ECPGSetConnection:  SET SQL_CONNECTION connection_object
+ECPGSetConnection:  SET SQL_CONNECTION to_equal connection_object
                        {
-                               $$ = $3;
+                               $$ = $4;
                         }
+
 /*
- * whenever statement: decide what to do in case of error/no data found
- * according to SQL standards we miss: SQLSTATE, CONSTRAINT, SQLEXCEPTION
- * and SQLWARNING
+ * define a new type for embedded SQL
+ */
+ECPGTypedef: TYPE_P symbol IS type opt_type_array_bounds opt_reference
+       {
+               /* add entry to list */
+               struct typedefs *ptr, *this;
+               int dimension = $5.index1;
+               int length = $5.index2;
+
+               if (($4.type_enum == ECPGt_struct ||
+                    $4.type_enum == ECPGt_union) &&
+                   initializer == 1)
+                       mmerror(ET_ERROR, "Initializer not allowed in EXEC SQL VAR command");
+
+               for (ptr = types; ptr != NULL; ptr = ptr->next)
+               {
+                       if (strcmp($2, ptr->name) == 0)
+                       {
+                               /* re-definition is a bug */
+                               sprintf(errortext, "Type %s already defined", $2);
+                               mmerror(ET_ERROR, errortext);
+                       }
+               }
+
+               adjust_array($4.type_enum, &dimension, &length, $4.type_dimension, $4.type_index, strlen($6));
+
+               this = (struct typedefs *) mm_alloc(sizeof(struct typedefs));
+
+               /* initial definition */
+               this->next = types;
+               this->name = $2;
+               this->type = (struct this_type *) mm_alloc(sizeof(struct this_type));
+               this->type->type_enum = $4.type_enum;
+               this->type->type_str = mm_strdup($2);
+               this->type->type_dimension = dimension; /* dimension of array */
+               this->type->type_index = length;    /* lenght of string */
+               this->struct_member_list = ($4.type_enum == ECPGt_struct || $4.type_enum == ECPGt_union) ?
+                       struct_member_list[struct_level] : NULL;
+
+               if ($4.type_enum != ECPGt_varchar &&
+                   $4.type_enum != ECPGt_char &&
+                   $4.type_enum != ECPGt_unsigned_char &&
+                   this->type->type_index >= 0)
+                            mmerror(ET_ERROR, "No multi-dimensional array support for simple data types");
+
+               types = this;
+
+               $$ = cat_str(7, make_str("/* exec sql type"), mm_strdup($2), make_str("is"), mm_strdup($4.type_str), mm_strdup($5.str), $6, make_str("*/"));
+       }
+
+opt_type_array_bounds:  '[' ']' opt_type_array_bounds
+                       {
+                            $$.index1 = 0;
+                            $$.index2 = $3.index1;
+                            $$.str = cat2_str(make_str("[]"), $3.str);
+                        }
+               | '(' ')' opt_type_array_bounds
+                       {
+                            $$.index1 = 0;
+                            $$.index2 = $3.index1;
+                            $$.str = cat2_str(make_str("[]"), $3.str);
+                        }
+               | '[' Iresult ']' opt_type_array_bounds
+                       {
+                           char *txt = mm_alloc(20L);
+
+                           sprintf (txt, "%d", $2);
+                            $$.index1 = $2;
+                            $$.index2 = $4.index1;
+                            $$.str = cat_str(4, make_str("["), txt, make_str("]"), $4.str);
+                        }
+               | '(' Iresult ')' opt_type_array_bounds
+                       {
+                           char *txt = mm_alloc(20L);
+
+                           sprintf (txt, "%d", $2);
+                            $$.index1 = $2;
+                            $$.index2 = $4.index1;
+                            $$.str = cat_str(4, make_str("["), txt, make_str("]"), $4.str);
+                        }
+               | /* EMPTY */
+                       {
+                            $$.index1 = -1;
+                            $$.index2 = -1;
+                            $$.str= EMPTY;
+                        }
+               ;
+
+opt_reference: SQL_REFERENCE { $$ = make_str("reference"); }
+       | /* empty */        { $$ = EMPTY; }
 
+/*
+ * define the type of one variable for embedded SQL
+ */
+ECPGVar: SQL_VAR symbol IS type opt_type_array_bounds opt_reference
+       {
+               struct variable *p = find_variable($2);
+               int dimension = $5.index1;
+               int length = $5.index2;
+               struct ECPGtype * type;
+
+               if (($4.type_enum == ECPGt_struct ||
+                    $4.type_enum == ECPGt_union) &&
+                   initializer == 1)
+                       mmerror(ET_ERROR, "Initializer not allowed in EXEC SQL VAR command");
+
+               adjust_array($4.type_enum, &dimension, &length, $4.type_dimension, $4.type_index, strlen($6));
+
+               switch ($4.type_enum)
+               {
+                  case ECPGt_struct:
+                  case ECPGt_union:
+                        if (dimension < 0)
+                            type = ECPGmake_struct_type(struct_member_list[struct_level], $4.type_enum);
+                        else
+                            type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level], $4.type_enum), dimension); 
+                        break;
+                   case ECPGt_varchar:
+                        if (dimension == -1)
+                            type = ECPGmake_simple_type($4.type_enum, length);
+                        else
+                            type = ECPGmake_array_type(ECPGmake_simple_type($4.type_enum, length), dimension);
+
+                       break;
+                   case ECPGt_char:
+                   case ECPGt_unsigned_char:
+                        if (dimension == -1)
+                            type = ECPGmake_simple_type($4.type_enum, length);
+                        else
+                            type = ECPGmake_array_type(ECPGmake_simple_type($4.type_enum, length), dimension);
+
+                       break;
+                  default:
+                       if (length >= 0)
+                           mmerror(ET_ERROR, "No multi-dimensional array support for simple data types");
+
+                        if (dimension < 0)
+                            type = ECPGmake_simple_type($4.type_enum, 1);
+                        else
+                            type = ECPGmake_array_type(ECPGmake_simple_type($4.type_enum, 1), dimension);
+
+                       break;
+               }       
+
+               ECPGfree_type(p->type);
+               p->type = type;
+
+               $$ = cat_str(7, make_str("/* exec sql var"), mm_strdup($2), make_str("is"), mm_strdup($4.type_str), mm_strdup($5.str), $6, make_str("*/"));
+       }
+
+/*
+ * whenever statement: decide what to do in case of error/no data found
+ * according to SQL standards we lack: SQLSTATE, CONSTRAINT and SQLEXCEPTION
  */
 ECPGWhenever: SQL_WHENEVER SQL_SQLERROR action {
        when_error.code = $<action>3.code;
        when_error.command = $<action>3.command;
-       $$ = cat3_str(make1_str("/* exec sql whenever sqlerror "), $3.str, make1_str("; */\n"));
+       $$ = cat_str(3, make_str("/* exec sql whenever sqlerror "), $3.str, make_str("; */\n"));
 }
        | SQL_WHENEVER NOT SQL_FOUND action {
        when_nf.code = $<action>4.code;
        when_nf.command = $<action>4.command;
-       $$ = cat3_str(make1_str("/* exec sql whenever not found "), $4.str, make1_str("; */\n"));
+       $$ = cat_str(3, make_str("/* exec sql whenever not found "), $4.str, make_str("; */\n"));
+}
+       | SQL_WHENEVER SQL_SQLWARNING action {
+       when_warn.code = $<action>3.code;
+       when_warn.command = $<action>3.command;
+       $$ = cat_str(3, make_str("/* exec sql whenever sql_warning "), $3.str, make_str("; */\n"));
 }
 
 action : SQL_CONTINUE {
        $<action>$.code = W_NOTHING;
        $<action>$.command = NULL;
-       $<action>$.str = make1_str("continue");
+       $<action>$.str = make_str("continue");
 }
        | SQL_SQLPRINT {
        $<action>$.code = W_SQLPRINT;
        $<action>$.command = NULL;
-       $<action>$.str = make1_str("sqlprint");
+       $<action>$.str = make_str("sqlprint");
 }
        | SQL_STOP {
        $<action>$.code = W_STOP;
        $<action>$.command = NULL;
-       $<action>$.str = make1_str("stop");
+       $<action>$.str = make_str("stop");
 }
        | SQL_GOTO name {
         $<action>$.code = W_GOTO;
-        $<action>$.command = $2;
-       $<action>$.str = cat2_str(make1_str("goto "), $2);
+        $<action>$.command = strdup($2);
+       $<action>$.str = cat2_str(make_str("goto "), $2);
 }
        | SQL_GO TO name {
         $<action>$.code = W_GOTO;
-        $<action>$.command = $3;
-       $<action>$.str = cat2_str(make1_str("goto "), $3);
+        $<action>$.command = strdup($3);
+       $<action>$.str = cat2_str(make_str("goto "), $3);
 }
-       | DO name '(' dotext ')' {
+       | DO name '(' c_args ')' {
        $<action>$.code = W_DO;
-       $<action>$.command = make4_str($2, make1_str("("), $4, make1_str(")"));
-       $<action>$.str = cat2_str(make1_str("do"), mm_strdup($<action>$.command));
+       $<action>$.command = cat_str(4, $2, make_str("("), $4, make_str(")"));
+       $<action>$.str = cat2_str(make_str("do"), mm_strdup($<action>$.command));
 }
        | DO SQL_BREAK {
         $<action>$.code = W_BREAK;
         $<action>$.command = NULL;
-        $<action>$.str = make1_str("break");
+        $<action>$.str = make_str("break");
 }
-       | SQL_CALL name '(' dotext ')' {
+       | SQL_CALL name '(' c_args ')' {
        $<action>$.code = W_DO;
-       $<action>$.command = make4_str($2, make1_str("("), $4, make1_str(")"));
-       $<action>$.str = cat2_str(make1_str("call"), mm_strdup($<action>$.command));
+       $<action>$.command = cat_str(4, $2, make_str("("), $4, make_str(")"));
+       $<action>$.str = cat2_str(make_str("call"), mm_strdup($<action>$.command));
 }
 
 /* some other stuff for ecpg */
-
-ecpg_expr:  attr opt_indirection
-                               {
-                                       $$ = cat2_str($1, $2);
-                               }
-               | row_expr
-                               {       $$ = $1;  }
-               | AexprConst
-                               {       $$ = $1;  }
-               | ColId
-                               {
-                                       $$ = $1;
-                               }
+/*
+ * no longer used
+ecpg_expr:  c_expr 
+                               {       $$ = $1; }
+               | a_expr TYPECAST Typename
+                               {       $$ = cat_str(3, $1, make_str("::"), $3); }
                | '-' ecpg_expr %prec UMINUS
-                               {       $$ = cat2_str(make1_str("-"), $2); }
+                               {       $$ = cat2_str(make_str("-"), $2); }
+               | '%' ecpg_expr
+                               {       $$ = cat2_str(make_str("%"), $2); }
+               | '^' ecpg_expr
+                               {       $$ = cat2_str(make_str("^"), $2); }
+               | '|' ecpg_expr
+                               {       $$ = cat2_str(make_str("|"), $2); }
+               | ';' a_expr
+                               {       $$ = cat2_str(make_str(";"), $2); }
+               | a_expr '%'
+                               {       $$ = cat2_str($1, make_str("%")); }
+               | a_expr '^'
+                               {       $$ = cat2_str($1, make_str("^")); }
+               | a_expr '|'
+                               {       $$ = cat2_str($1, make_str("|")); }
                | a_expr '+' ecpg_expr
-                               {       $$ = cat3_str($1, make1_str("+"), $3); }
+                               {       $$ = cat_str(3, $1, make_str("+"), $3); }
                | a_expr '-' ecpg_expr
-                               {       $$ = cat3_str($1, make1_str("-"), $3); }
-               | a_expr '/' ecpg_expr
-                               {       $$ = cat3_str($1, make1_str("/"), $3); }
+                               {       $$ = cat_str(3, $1, make_str("-"), $3); }
                | a_expr '*' ecpg_expr
-                               {       $$ = cat3_str($1, make1_str("*"), $3); }
+                               {       $$ = cat_str(3, $1, make_str("*"), $3); }
+               | a_expr '/' ecpg_expr
+                               {       $$ = cat_str(3, $1, make_str("/"), $3); }
+               | a_expr '%' ecpg_expr
+                               {       $$ = cat_str(3, $1, make_str("%"), $3); }
+               | a_expr '^' ecpg_expr
+                               {       $$ = cat_str(3, $1, make_str("^"), $3); }
+               | a_expr '|' ecpg_expr
+                               {       $$ = cat_str(3, $1, make_str("|"), $3); }
                | a_expr '<' ecpg_expr
-                               {       $$ = cat3_str($1, make1_str("<"), $3); }
+                               {       $$ = cat_str(3, $1, make_str("<"), $3); }
                | a_expr '>' ecpg_expr
-                               {       $$ = cat3_str($1, make1_str(">"), $3); }
+                               {       $$ = cat_str(3, $1, make_str(">"), $3); }
+               | a_expr '=' NULL_P
+                                {       $$ = cat2_str($1, make_str("= NULL")); }
+               | NULL_P '=' ecpg_expr
+                                {       $$ = cat2_str(make_str("= NULL"), $3); }
                | a_expr '=' ecpg_expr
-                               {       $$ = cat3_str($1, make1_str("="), $3); }
-       /*      | ':' ecpg_expr
-                               {       $$ = cat2_str(make1_str(":"), $2); }*/
-               | ';' ecpg_expr
-                               {       $$ = cat2_str(make1_str(";"), $2); }
-               | '|' ecpg_expr
-                               {       $$ = cat2_str(make1_str("|"), $2); }
-               | a_expr TYPECAST Typename
-                               {
-                                       $$ = cat3_str($1, make1_str("::"), $3);
-                               }
-               | CAST '(' a_expr AS Typename ')'
-                               {
-                                       $$ = cat3_str(make2_str(make1_str("cast("), $3), make1_str("as"), make2_str($5, make1_str(")")));
-                               }
-               | '(' a_expr_or_null ')'
-                               {       $$ = make3_str(make1_str("("), $2, make1_str(")")); }
+                               {       $$ = cat_str(3, $1, make_str("="), $3); }
                | a_expr Op ecpg_expr
-                               {       $$ = cat3_str($1, $2, $3);      }
-               | a_expr LIKE ecpg_expr
-                               {       $$ = cat3_str($1, make1_str("like"), $3); }
-               | a_expr NOT LIKE ecpg_expr
-                               {       $$ = cat3_str($1, make1_str("not like"), $4); }
+                               {       $$ = cat_str(3, $1, make_str("="), $3); }
                | Op ecpg_expr
                                {       $$ = cat2_str($1, $2); }
                | a_expr Op
                                {       $$ = cat2_str($1, $2); }
-               | func_name '(' '*' ')'
-                               {
-                                       $$ = cat2_str($1, make1_str("(*)")); 
-                               }
-               | func_name '(' ')'
-                               {
-                                       $$ = cat2_str($1, make1_str("()")); 
-                               }
-               | func_name '(' expr_list ')'
-                               {
-                                       $$ = make4_str($1, make1_str("("), $3, make1_str(")")); 
-                               }
-               | CURRENT_DATE
-                               {
-                                       $$ = make1_str("current_date");
-                               }
-               | CURRENT_TIME
-                               {
-                                       $$ = make1_str("current_time");
-                               }
-               | CURRENT_TIME '(' Iconst ')'
-                               {
-                                       if (atol($3) != 0)
-                                               fprintf(stderr,"CURRENT_TIME(%s) precision not implemented; zero used instead", $3);
-                                       $$ = make1_str("current_time");
-                               }
-               | CURRENT_TIMESTAMP
-                               {
-                                       $$ = make1_str("current_timestamp");
-                               }
-               | CURRENT_TIMESTAMP '(' Iconst ')'
-                               {
-                                       if (atol($3) != 0)
-                                               fprintf(stderr,"CURRENT_TIMESTAMP(%s) precision not implemented; zero used instead",$3);
-                                       $$ = make1_str("current_timestamp");
-                               }
-               | CURRENT_USER
-                               {
-                                       $$ = make1_str("current_user");
-                               }
-               | EXISTS '(' SubSelect ')'
-                               {
-                                       $$ = make3_str(make1_str("exists("), $3, make1_str(")"));
-                               }
-               | EXTRACT '(' extract_list ')'
-                               {
-                                       $$ = make3_str(make1_str("extract("), $3, make1_str(")"));
-                               }
-               | POSITION '(' position_list ')'
-                               {
-                                       $$ = make3_str(make1_str("position("), $3, make1_str(")"));
-                               }
-               | SUBSTRING '(' substr_list ')'
-                               {
-                                       $$ = make3_str(make1_str("substring("), $3, make1_str(")"));
-                               }
-               /* various trim expressions are defined in SQL92 - thomas 1997-07-19 */
-               | TRIM '(' BOTH trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim(both"), $4, make1_str(")"));
-                               }
-               | TRIM '(' LEADING trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim(leading"), $4, make1_str(")"));
-                               }
-               | TRIM '(' TRAILING trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim(trailing"), $4, make1_str(")"));
-                               }
-               | TRIM '(' trim_list ')'
-                               {
-                                       $$ = make3_str(make1_str("trim("), $3, make1_str(")"));
-                               }
+               | a_expr AND ecpg_expr
+                               {       $$ = cat_str(3, $1, make_str("and"), $3); }
+               | a_expr OR ecpg_expr
+                               {       $$ = cat_str(3, $1, make_str("or"), $3); }
+               | NOT ecpg_expr
+                               {       $$ = cat2_str(make_str("not"), $2); }
+               | a_expr LIKE ecpg_expr
+                               {       $$ = cat_str(3, $1, make_str("like"), $3); }
+               | a_expr NOT LIKE ecpg_expr
+                               {       $$ = cat_str(3, $1, make_str("not like"), $4); }
                | a_expr ISNULL
-                               {       $$ = cat2_str($1, make1_str("isnull")); }
+                               {       $$ = cat2_str($1, make_str("isnull")); }
                | a_expr IS NULL_P
-                               {       $$ = cat2_str($1, make1_str("is null")); }
+                               {       $$ = cat2_str($1, make_str("is null")); }
                | a_expr NOTNULL
-                               {       $$ = cat2_str($1, make1_str("notnull")); }
+                               {       $$ = cat2_str($1, make_str("notnull")); }
                | a_expr IS NOT NULL_P
-                               {       $$ = cat2_str($1, make1_str("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
-                */
+                               {       $$ = cat2_str($1, make_str("is not null")); }
                | a_expr IS TRUE_P
-                               {
-                               {       $$ = cat2_str($1, make1_str("is true")); }
-                               }
+                               {       $$ = cat2_str($1, make_str("is true")); }
                | a_expr IS NOT FALSE_P
-                               {
-                               {       $$ = cat2_str($1, make1_str("is not false")); }
-                               }
+                               {       $$ = cat2_str($1, make_str("is not false")); }
                | a_expr IS FALSE_P
-                               {
-                               {       $$ = cat2_str($1, make1_str("is false")); }
-                               }
+                               {       $$ = cat2_str($1, make_str("is false")); }
                | a_expr IS NOT TRUE_P
-                               {
-                               {       $$ = cat2_str($1, make1_str("is not true")); }
-                               }
+                               {       $$ = cat2_str($1, make_str("is not true")); }
                | a_expr BETWEEN b_expr AND b_expr
                                {
-                                       $$ = cat5_str($1, make1_str("between"), $3, make1_str("and"), $5); 
+                                       $$ = cat_str(5, $1, make_str("between"), $3, make_str("and"), $5); 
                                }
                | a_expr NOT BETWEEN b_expr AND b_expr
                                {
-                                       $$ = cat5_str($1, make1_str("not between"), $4, make1_str("and"), $6); 
+                                       $$ = cat_str(5, $1, make_str("not between"), $4, make_str("and"), $6); 
                                }
                | a_expr IN '(' in_expr ')'
                                {
-                                       $$ = make4_str($1, make1_str("in ("), $4, make1_str(")")); 
-                               }
-               | a_expr NOT IN '(' not_in_expr ')'
-                               {
-                                       $$ = make4_str($1, make1_str("not in ("), $5, make1_str(")")); 
-                               }
-               | a_expr Op '(' SubSelect ')'
-                               {
-                                       $$ = cat3_str($1, $2, make3_str(make1_str("("), $4, make1_str(")"))); 
-                               }
-               | a_expr '+' '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("+("), $4, make1_str(")")); 
-                               }
-               | a_expr '-' '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("-("), $4, make1_str(")")); 
-                               }
-               | a_expr '/' '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("/("), $4, make1_str(")")); 
-                               }
-               | a_expr '*' '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("*("), $4, make1_str(")")); 
-                               }
-               | a_expr '<' '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("<("), $4, make1_str(")")); 
-                               }
-               | a_expr '>' '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str(">("), $4, make1_str(")")); 
-                               }
-               | a_expr '=' '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("=("), $4, make1_str(")")); 
-                               }
-               | a_expr Op ANY '(' SubSelect ')'
-                               {
-                                       $$ = cat3_str($1, $2, make3_str(make1_str("any ("), $5, make1_str(")"))); 
-                               }
-               | a_expr '+' ANY '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("+any("), $5, make1_str(")")); 
-                               }
-               | a_expr '-' ANY '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("-any("), $5, make1_str(")")); 
-                               }
-               | a_expr '/' ANY '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("/any("), $5, make1_str(")")); 
-                               }
-               | a_expr '*' ANY '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("*any("), $5, make1_str(")")); 
-                               }
-               | a_expr '<' ANY '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("<any("), $5, make1_str(")")); 
-                               }
-               | a_expr '>' ANY '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str(">any("), $5, make1_str(")")); 
-                               }
-               | a_expr '=' ANY '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("=any("), $5, make1_str(")")); 
-                               }
-               | a_expr Op ALL '(' SubSelect ')'
-                               {
-                                       $$ = make3_str($1, $2, make3_str(make1_str("all ("), $5, make1_str(")"))); 
+                                       $$ = cat_str(4, $1, make_str(" in ("), $4, make_str(")")); 
                                }
-               | a_expr '+' ALL '(' SubSelect ')'
+               | a_expr NOT IN '(' in_expr ')'
                                {
-                                       $$ = make4_str($1, make1_str("+all("), $5, make1_str(")")); 
+                                       $$ = cat_str(4, $1, make_str(" not in ("), $5, make_str(")")); 
                                }
-               | a_expr '-' ALL '(' SubSelect ')'
+               | a_expr all_Op sub_type '(' SubSelect ')'
                                {
-                                       $$ = make4_str($1, make1_str("-all("), $5, make1_str(")")); 
+                                       $$ = cat_str(4, $1, $2, $3, cat_str(3, make_str("("), $5, make_str(")"))); 
                                }
-               | a_expr '/' ALL '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("/all("), $5, make1_str(")")); 
-                               }
-               | a_expr '*' ALL '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("*all("), $5, make1_str(")")); 
-                               }
-               | a_expr '<' ALL '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("<all("), $5, make1_str(")")); 
-                               }
-               | a_expr '>' ALL '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str(">all("), $5, make1_str(")")); 
-                               }
-               | a_expr '=' ALL '(' SubSelect ')'
-                               {
-                                       $$ = make4_str($1, make1_str("=all("), $5, make1_str(")")); 
-                               }
-               | a_expr AND ecpg_expr
-                               {       $$ = cat3_str($1, make1_str("and"), $3); }
-               | a_expr OR ecpg_expr
-                               {       $$ = cat3_str($1, make1_str("or"), $3); }
-               | NOT ecpg_expr
-                               {       $$ = cat2_str(make1_str("not"), $2); }
+               | row_expr
+                               {       $$ = $1; }
                | civariableonly
-                               { $$ = make1_str(";;"); }
+                               {       $$ = $1; }
                ;
+*/
 
 into_list : coutputvariable | into_list ',' coutputvariable;
 
 ecpgstart: SQL_START { reset_variables();}
 
-dotext: /* empty */            { $$ = make1_str(""); }
-       | dotext do_anything    { $$ = make2_str($1, $2); }
-
-vartext: var_anything          { $$ = $1; }
-        | vartext var_anything { $$ = make2_str($1, $2); }
+c_args: /* empty */            { $$ = EMPTY; }
+       | c_list                { $$ = $1; }
 
 coutputvariable : cvariable indicator {
                add_variable(&argsresult, find_variable($1), ($2 == NULL) ? &no_indicator : find_variable($2)); 
@@ -4986,6 +5844,7 @@ cinputvariable : cvariable indicator {
 
 civariableonly : cvariable {
                add_variable(&argsinsert, find_variable($1), &no_indicator); 
+               $$ = make_str("?");
 }
 
 cvariable: CVARIABLE                   { $$ = $1; }
@@ -4996,7 +5855,8 @@ indicator: /* empty */                    { $$ = NULL; }
        | SQL_INDICATOR name            { check_indicator((find_variable($2))->type); $$ = $2; }
 
 ident: IDENT   { $$ = $1; }
-       | CSTRING       { $$ = $1; }
+       | CSTRING       { $$ = make3_str(make_str("\""), $1, make_str("\"")); };
+
 /*
  * C stuff
  */
@@ -5005,69 +5865,75 @@ symbol: IDENT   { $$ = $1; }
 
 cpp_line: CPP_LINE     { $$ = $1; }
 
-c_line: c_anything { $$ = $1; }
-       | c_line c_anything
-               {
-                       $$ = make2_str($1, $2);
-               }
+c_stuff: c_anything    { $$ = $1; }
+       | c_stuff c_anything
+                       {
+                               $$ = cat2_str($1, $2);
+                       }
+       | c_stuff '(' c_stuff ')'
+                       {
+                               $$ = cat_str(4, $1, make_str("("), $3, make_str(")"));
+                       }
+
+c_list: c_term                 { $$ = $1; }
+       | c_list ',' c_term     { $$ = cat_str(3, $1, make_str(","), $3); }
 
-c_thing: c_anything | ';' { $$ = make1_str(";"); }
+c_term:  c_stuff               { $$ = $1; }
+       | '{' c_list '}'        { $$ = cat_str(3, make_str("{"), $2, make_str("}")); }
+
+c_thing:       c_anything      { $$ = $1; }
+       |       '('             { $$ = make_str("("); }
+       |       ')'             { $$ = make_str(")"); }
+       |       ','             { $$ = make_str(","); }
+       |       ';'             { $$ = make_str(";"); }
 
 c_anything:  IDENT     { $$ = $1; }
-       | CSTRING       { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); }
+       | CSTRING       { $$ = make3_str(make_str("\""), $1, make_str("\"")); }
        | Iconst        { $$ = $1; }
        | Fconst        { $$ = $1; }
-       | '*'           { $$ = make1_str("*"); }
-       | S_AUTO        { $$ = make1_str("auto"); }
-       | S_BOOL        { $$ = make1_str("bool"); }
-       | S_CHAR        { $$ = make1_str("char"); }
-       | S_CONST       { $$ = make1_str("const"); }
-       | S_DOUBLE      { $$ = make1_str("double"); }
-       | S_EXTERN      { $$ = make1_str("extern"); }
-       | S_FLOAT       { $$ = make1_str("float"); }
-        | S_INT                { $$ = make1_str("int"); }
-       | S_LONG        { $$ = make1_str("long"); }
-       | S_REGISTER    { $$ = make1_str("register"); }
-       | S_SHORT       { $$ = make1_str("short"); }
-       | S_SIGNED      { $$ = make1_str("signed"); }
-       | S_STATIC      { $$ = make1_str("static"); }
-        | S_STRUCT     { $$ = make1_str("struct"); }
-       | S_UNSIGNED    { $$ = make1_str("unsigned"); }
-       | S_VARCHAR     { $$ = make1_str("varchar"); }
+       | '*'           { $$ = make_str("*"); }
+       | '+'           { $$ = make_str("+"); }
+       | '-'           { $$ = make_str("-"); }
+       | '/'           { $$ = make_str("/"); }
+       | '%'           { $$ = make_str("%"); }
        | S_ANYTHING    { $$ = make_name(); }
-        | '['          { $$ = make1_str("["); }
-       | ']'           { $$ = make1_str("]"); }
-       | '('           { $$ = make1_str("("); }
-       | ')'           { $$ = make1_str(")"); }
-       | '='           { $$ = make1_str("="); }
-       | ','           { $$ = make1_str(","); }
-
-do_anything: IDENT     { $$ = $1; }
-        | CSTRING       { $$ = make3_str(make1_str("\""), $1, make1_str("\""));}
-        | Iconst        { $$ = $1; }
-       | Fconst        { $$ = $1; }
-       | ','           { $$ = make1_str(","); }
-
-var_anything: IDENT            { $$ = $1; }
-       | CSTRING               { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); }
-       | Iconst                { $$ = $1; }
-       | Fconst                { $$ = $1; }
-       | '{' c_line '}'        { $$ = make3_str(make1_str("{"), $2, make1_str("}")); }
+       | S_AUTO        { $$ = make_str("auto"); }
+       | S_CONST       { $$ = make_str("const"); }
+       | S_EXTERN      { $$ = make_str("extern"); }
+       | S_REGISTER    { $$ = make_str("register"); }
+       | S_STATIC      { $$ = make_str("static"); }
+       | SQL_BOOL      { $$ = make_str("bool"); }
+       | SQL_ENUM      { $$ = make_str("enum"); }
+        | SQL_INT      { $$ = make_str("int"); }
+       | SQL_LONG      { $$ = make_str("long"); }
+       | SQL_SHORT     { $$ = make_str("short"); }
+       | SQL_SIGNED    { $$ = make_str("signed"); }
+        | SQL_STRUCT   { $$ = make_str("struct"); }
+       | SQL_UNSIGNED  { $$ = make_str("unsigned"); }
+       | CHAR          { $$ = make_str("char"); }
+       | DOUBLE        { $$ = make_str("double"); }
+       | FLOAT         { $$ = make_str("float"); }
+        | UNION                { $$ = make_str("union"); }
+       | VARCHAR       { $$ = make_str("varchar"); }
+        | '['          { $$ = make_str("["); }
+       | ']'           { $$ = make_str("]"); }
+/*        | '('                { $$ = make_str("("); }
+       | ')'           { $$ = make_str(")"); }*/
+       | '='           { $$ = make_str("="); }
 
 blockstart : '{' {
     braces_open++;
-    $$ = make1_str("{");
+    $$ = make_str("{");
 }
 
 blockend : '}' {
     remove_variables(braces_open--);
-    $$ = make1_str("}");
+    $$ = make_str("}");
 }
 
 %%
 
 void yyerror(char * error)
 {
-    fprintf(stderr, "%s:%d: %s\n", input_filename, yylineno, error);
-    exit(PARSE_ERROR);
+       mmerror(ET_ERROR, error);
 }