*** empty log message ***
authorMichael Meskes
Wed, 1 Mar 2000 12:49:43 +0000 (12:49 +0000)
committerMichael Meskes
Wed, 1 Mar 2000 12:49:43 +0000 (12:49 +0000)
src/interfaces/ecpg/ChangeLog
src/interfaces/ecpg/TODO
src/interfaces/ecpg/include/ecpgerrno.h
src/interfaces/ecpg/include/ecpglib.h
src/interfaces/ecpg/include/sql3types.h
src/interfaces/ecpg/lib/data.c
src/interfaces/ecpg/lib/descriptor.c
src/interfaces/ecpg/lib/ecpglib.c
src/interfaces/ecpg/lib/error.c
src/interfaces/ecpg/lib/typename.c
src/interfaces/ecpg/preproc/preproc.y

index 0e6b37ae833b7ecdccebb6d8f7026ba9a78cd580..c4d47d976bcbeb87f06a773a1bd7fb1fcb04143d 100644 (file)
@@ -832,5 +832,10 @@ Wed Feb 23 17:08:28 CET 2000
 Fri Feb 25 16:13:11 CET 2000
 
    - Fixed some bugs I created when I cleaned up, thanks Christof.
+
+Wed Mar  1 10:49:03 CET 2000
+
+   - Synced preproc.y with gram.y.
+   - Added output of arrays.
    - Set library version to 3.1.0.
    - Set ecpg version to 2.7.0.
index 84edb5a2b55674b3416c48511f7349ed73e28a96..f75a7b364922fc20e7029134d4d2086a2109bf2a 100644 (file)
@@ -16,8 +16,6 @@ the parser.
 it would be nice to be able to use :var[:index] or :var[] as
 cvariable for an array var
 
-How can one insert arrays from c variables?
-
 What happens to the output variable during read if there was an
 indicator-error? 
 
@@ -26,10 +24,7 @@ Add a semantic check level, e.g. check if a table really exists.
 It would be nice if there was a alternative library using SPI functions
 instead of libpq so we can write backend functions using ecpg.
 
-make ECPGnumeric_lvalue more accurate by using something like ECPGdump_a_*
-
 remove space_or_nl and line_end from pgc.l
 
 Missing statements:
- - exec sql ifdef
  - SQLSTATE
index 85b891688d64baabdaf46c895c97c9ff0aaad972..37443badcd2add18ede253f490b3b8b710d1d1cf 100644 (file)
 #define ECPG_OUT_OF_MEMORY -ENOMEM
 
 /* first we have a set of ecpg messages, they start at 200 */
-#define ECPG_UNSUPPORTED   -200
-#define ECPG_TOO_MANY_ARGUMENTS -201
-#define ECPG_TOO_FEW_ARGUMENTS -202
-#define ECPG_TOO_MANY_MATCHES  -203
-#define ECPG_INT_FORMAT        -204
-#define ECPG_UINT_FORMAT   -205
-#define ECPG_FLOAT_FORMAT  -206
-#define ECPG_CONVERT_BOOL  -207
-#define ECPG_EMPTY     -208
-#define ECPG_MISSING_INDICATOR -209
-
-#define ECPG_NO_CONN       -220
-#define ECPG_NOT_CONN      -221
-
-#define ECPG_INVALID_STMT  -230
+#define ECPG_UNSUPPORTED       -200
+#define ECPG_TOO_MANY_ARGUMENTS    -201
+#define ECPG_TOO_FEW_ARGUMENTS     -202
+#define ECPG_TOO_MANY_MATCHES      -203
+#define ECPG_INT_FORMAT            -204
+#define ECPG_UINT_FORMAT       -205
+#define ECPG_FLOAT_FORMAT      -206
+#define ECPG_CONVERT_BOOL      -207
+#define ECPG_EMPTY         -208
+#define ECPG_MISSING_INDICATOR     -209
+#define ECPG_NO_ARRAY          -210
+#define ECPG_DATA_NOT_ARRAY        -211
+
+#define ECPG_NO_CONN           -220
+#define ECPG_NOT_CONN          -221
+
+#define ECPG_INVALID_STMT      -230
 
 /* dynamic SQL related */
-#define ECPG_UNKNOWN_DESCRIPTOR    -240
+#define ECPG_UNKNOWN_DESCRIPTOR        -240
 #define ECPG_INVALID_DESCRIPTOR_INDEX  -241
 #define ECPG_UNKNOWN_DESCRIPTOR_ITEM   -242
-#define ECPG_VAR_NOT_NUMERIC   -243
-#define ECPG_VAR_NOT_CHAR  -244
+#define ECPG_VAR_NOT_NUMERIC       -243
+#define ECPG_VAR_NOT_CHAR      -244
 
 /* finally the backend error messages, they start at 400 */
-#define ECPG_PGSQL     -400
-#define ECPG_TRANS     -401
-#define ECPG_CONNECT       -402
+#define ECPG_PGSQL         -400
+#define ECPG_TRANS         -401
+#define ECPG_CONNECT           -402
 
 #endif  /* !_ECPG_ERROR_H */
index fc416bd4fb3e9fc7b6e784c113b8088be861cdab..1e527ce369eaec80c40a475019466e1e83343472 100644 (file)
@@ -30,12 +30,12 @@ extern      "C"
 
 /* Here are some methods used by the lib. */
 /* Returns a pointer to a string containing a simple type name. */
-   const char *ECPGtype_name(enum ECPGttype);
    bool get_data(PGresult *, int, int, int, enum ECPGttype type,
-           enum ECPGttype, void *, void *, long, long);
+           enum ECPGttype, void *, void *, long, long, bool);
    char *ecpg_alloc(long, int);
    char *ecpg_strdup(const char *, int);
    const char *ECPGtype_name(enum ECPGttype);
+   unsigned int ECPGDynamicType(Oid);
    
 /* and some vars */
    extern struct auto_mem *auto_allocs;
index c844975b4ad948cb79a40f969e069dc3ad4702e5..34224c7577e18fed033b045d5a467a8cf8ef6636 100644 (file)
@@ -2,7 +2,7 @@
  *
  * Copyright (c) 2000, Christof Petig 
  *
- * $Header: /cvsroot/pgsql/src/interfaces/ecpg/include/sql3types.h,v 1.1 2000/02/16 16:18:03 meskes Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/ecpg/include/sql3types.h,v 1.2 2000/03/01 12:49:41 meskes Exp $
  */
 
 /* chapter 13.1 table 2: Codes used for SQL data types in Dynamic SQL */
index 5b30eb1ca4bb69c3f0150a4b1e1da95b7d062921..af3c78ce591d4d57e38c655c2570a49210df19e5 100644 (file)
@@ -8,13 +8,26 @@
 bool
 get_data(PGresult *results, int act_tuple, int act_field, int lineno,
     enum ECPGttype type, enum ECPGttype ind_type,
-    void *var, void *ind, long varcharsize, long offset)
+    void *var, void *ind, long varcharsize, long offset,
+    bool isarray)
 {
    char *pval = (char *)PQgetvalue(results, act_tuple, act_field);
 
    ECPGlog("get_data line %d: RESULT: %s\n", lineno, pval ? pval : "");
 
    /* Now the pval is a pointer to the value. */
+   /* let's check is it really is an array if it should be */
+   if (isarray)
+   {
+       if (*pval !=  '{')
+       {
+           ECPGlog("get_data data entry does not look like an array in line %d\n", lineno);
+           ECPGraise(lineno, ECPG_DATA_NOT_ARRAY, NULL);
+           return(false);
+       }
+       else ++pval;
+   }
+
    /* We will have to decode the value */
 
    /*
@@ -48,8 +61,10 @@ get_data(PGresult *results, int act_tuple, int act_field, int lineno,
            break;
    }
    
-   switch (type)
-   {
+   do
+   {   
+      switch (type)
+      {
        long        res;
        unsigned long   ures;
        double      dres;
@@ -61,7 +76,8 @@ get_data(PGresult *results, int act_tuple, int act_field, int lineno,
            if (pval)
            {
                res = strtol(pval, &scan_length, 10);
-               if (*scan_length != '\0')   /* Garbage left */
+               if ((isarray && *scan_length != ',' && *scan_length != '}')
+                   || (!isarray && *scan_length != '\0'))  /* Garbage left */
                {
                    ECPGraise(lineno, ECPG_INT_FORMAT, pval);
                    return (false);
@@ -94,7 +110,8 @@ get_data(PGresult *results, int act_tuple, int act_field, int lineno,
            if (pval)
            {
                ures = strtoul(pval, &scan_length, 10);
-               if (*scan_length != '\0')   /* Garbage left */
+               if ((isarray && *scan_length != ',' && *scan_length != '}')
+                   || (!isarray && *scan_length != '\0'))  /* Garbage left */
                {
                    ECPGraise(lineno, ECPG_UINT_FORMAT, pval);
                    return (false);
@@ -127,7 +144,8 @@ get_data(PGresult *results, int act_tuple, int act_field, int lineno,
            if (pval)
            {
                dres = strtod(pval, &scan_length);
-               if (*scan_length != '\0')   /* Garbage left */
+               if ((isarray && *scan_length != ',' && *scan_length != '}')
+                   || (!isarray && *scan_length != '\0'))  /* Garbage left */
                {
                    ECPGraise(lineno, ECPG_FLOAT_FORMAT, pval);
                    return (false);
@@ -246,7 +264,23 @@ get_data(PGresult *results, int act_tuple, int act_field, int lineno,
            ECPGraise(lineno, ECPG_UNSUPPORTED, ECPGtype_name(type));
            return (false);
            break;
-   }
-   
+      }
+      if (isarray)
+      {
+       bool string = false;
+       
+       /* set array to next entry */
+       ++act_tuple;
+       
+       /* set pval to the next entry */
+       for (; string || (*pval != ',' && *pval != '}'); ++pval)
+           if (*pval == '"')
+               string = string ? false : true;
+       
+       if (*pval == ',')
+           ++pval;
+      }
+   } while (isarray && *pval != '}');
+
    return (true);
 }
index 1f0c5536f799b10edaf75cdc39bb451789ed7626..a27915ce5c129ad076e4bbddb63ef052b03b403d 100644 (file)
@@ -25,28 +25,6 @@ static PGresult
    return NULL;
 } 
 
-static unsigned int
-ECPGDynamicType(Oid type)
-{
-   switch(type)
-   {
-       case 16:    return SQL3_BOOLEAN;    /* bool */
-       case 21:    return SQL3_SMALLINT;   /* int2 */
-       case 23:    return SQL3_INTEGER;    /* int4 */
-       case 25:    return SQL3_CHARACTER;  /* text */
-       case 700:   return SQL3_REAL;       /* float4 */
-       case 701:   return SQL3_DOUBLE_PRECISION;   /* float8 */
-       case 1042:  return SQL3_CHARACTER;  /* bpchar */
-       case 1043:  return SQL3_CHARACTER_VARYING;  /* varchar */
-       case 1082:  return SQL3_DATE_TIME_TIMESTAMP;    /* date */
-       case 1083:  return SQL3_DATE_TIME_TIMESTAMP;    /* time */
-       case 1184:  return SQL3_DATE_TIME_TIMESTAMP;    /* datetime */
-       case 1296:  return SQL3_DATE_TIME_TIMESTAMP;    /* timestamp */
-       case 1700:  return SQL3_NUMERIC;    /* numeric */
-       default:    return -type;
-   }
-}
-
 static unsigned int
 ECPGDynamicType_DDT(Oid type)
 {
@@ -61,7 +39,6 @@ ECPGDynamicType_DDT(Oid type)
    }
 }
 
-
 bool
 ECPGget_desc_header(int lineno, char * desc_name, int *count)
 {
@@ -266,7 +243,7 @@ ECPGget_desc(int lineno, char *desc_name, int index, ...)
                    ECPGlog("ECPGget_desc: TYPE = %d\n", ECPGDynamicType_DDT(PQftype(ECPGresult, index)));
                            break;
            case ECPGd_data:
-               if (!get_data(ECPGresult, 0, index, lineno, vartype, ECPGt_NO_INDICATOR, var, NULL, varcharsize, offset))
+               if (!get_data(ECPGresult, 0, index, lineno, vartype, ECPGt_NO_INDICATOR, var, NULL, varcharsize, offset, false))
                    return (false);                         
                    
                break;
index 2891eefe084ea795a954b7844863a5da192a28f9..c3c262bea4cdd79d240c2dee78ebf2cf7fed4da5 100644 (file)
@@ -24,6 +24,7 @@
 #include 
 #include 
 #include 
+#include 
 
 /* variables visible to the programs */
 static struct sqlca sqlca_init =
@@ -689,23 +690,46 @@ ECPGexecute(struct statement * stmt)
                    isarray = 0;
                    if (PQresultStatus(query) == PGRES_TUPLES_OK) {
                        isarray = atol((char *)PQgetvalue(query, 0, 0));
+                       if (ECPGDynamicType(PQftype(results, act_field)) == SQL3_CHARACTER ||
+                           (PQftype(results, act_field)) == SQL3_CHARACTER_VARYING)
+                       {
+                           /* arrays of character strings are not yet implemented */
+                           isarray = false;
+                       }
                        ECPGlog("ECPGexecute line %d: TYPE database: %d C: %d array: %s\n", stmt->lineno, PQftype(results, act_field), var->type, isarray ? "yes" : "no");
                    }
                    PQclear(query);
 
-                   /*
-                    * if we don't have enough space, we cannot read all
-                    * tuples
-                    */
-                   if ((var->arrsize > 0 && ntuples > var->arrsize) || (var->ind_arrsize > 0 && ntuples > var->ind_arrsize))
+                   if (!isarray)
                    {
-                       ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d don't fit into array of %d\n",
+                       /*
+                        * if we don't have enough space, we cannot read all
+                        * tuples
+                        */
+                       if ((var->arrsize > 0 && ntuples > var->arrsize) || (var->ind_arrsize > 0 && ntuples > var->ind_arrsize))
+                       {
+                           ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d don't fit into array of %d\n",
                                stmt->lineno, ntuples, var->arrsize);
-                       ECPGraise(stmt->lineno, ECPG_TOO_MANY_MATCHES, NULL);
-                       status = false;
-                       break;
+                           ECPGraise(stmt->lineno, ECPG_TOO_MANY_MATCHES, NULL);
+                           status = false;
+                           break;
+                       }
                    }
-
+                   else
+                   {
+                       /*
+                        * since we read an array, the variable has to be
+                        * an array too
+                        */
+                       if (var->arrsize == 0)
+                       {
+                           ECPGlog("ECPGexecute line %d: variable is not an array\n");
+                           ECPGraise(stmt->lineno, ECPG_NO_ARRAY, NULL);
+                           status = false;
+                           break;
+                       } 
+                   }
+                   
                    /*
                     * allocate memory for NULL pointers
                     */
@@ -745,7 +769,7 @@ ECPGexecute(struct statement * stmt)
                    {
                        if (!get_data(results, act_tuple, act_field, stmt->lineno,
                                 var->type, var->ind_type, var->value,
-                                var->ind_value, var->varcharsize, var->offset))
+                                var->ind_value, var->varcharsize, var->offset, isarray))
                                 status = false;
                    }
                    var = var->next;
@@ -1067,13 +1091,9 @@ ECPGlog(const char *format,...)
  *
  * Copyright (c) 2000, Christof Petig 
  *
- * $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/ecpglib.c,v 1.60 2000/02/23 19:25:43 meskes Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/ecpglib.c,v 1.61 2000/03/01 12:49:42 meskes Exp $
  */
 
-/* I borrowed the include files from ecpglib.c, maybe we don't need all of them */
-
-#include 
-
 PGconn *ECPG_internal_get_connection(char *name);
 
 extern struct descriptor
index aa63fe94397b986e3e49a6079287628163bc7ea2..54e48a55271aa8e44b8b6fa43a5efb38c325ec68 100644 (file)
@@ -67,6 +67,16 @@ ECPGraise(int line, int code, const char *str)
            snprintf(sqlca.sqlerrm.sqlerrmc,sizeof(sqlca.sqlerrm.sqlerrmc),
                "NULL value without indicator in line %d.", line);
            break;
+       
+       case ECPG_NO_ARRAY: 
+           snprintf(sqlca.sqlerrm.sqlerrmc,sizeof(sqlca.sqlerrm.sqlerrmc),
+               "variable is not an array in line %d.", line);
+           break;
+           
+       case ECPG_DATA_NOT_ARRAY: 
+           snprintf(sqlca.sqlerrm.sqlerrmc,sizeof(sqlca.sqlerrm.sqlerrmc),
+               "data read from backend is not an array in line %d.", line);
+           break;
            
        case ECPG_NO_CONN: 
            snprintf(sqlca.sqlerrm.sqlerrmc,sizeof(sqlca.sqlerrm.sqlerrmc),
index af87b160aa9e1108a645ae77656b7d3a3c9825f0..1999ab82d7519ed124402c8948259f3c00131a51 100644 (file)
@@ -1,5 +1,8 @@
 #include 
 #include 
+#include 
+#include 
+
 /*
  * This function is used to generate the correct type names.
  */
@@ -39,3 +42,25 @@ ECPGtype_name(enum ECPGttype typ)
    }
    return NULL;
 }
+
+unsigned int
+ECPGDynamicType(Oid type)
+{
+   switch(type)
+   {
+       case 16:    return SQL3_BOOLEAN;    /* bool */
+       case 21:    return SQL3_SMALLINT;   /* int2 */
+       case 23:    return SQL3_INTEGER;    /* int4 */
+       case 25:    return SQL3_CHARACTER;  /* text */
+       case 700:   return SQL3_REAL;       /* float4 */
+       case 701:   return SQL3_DOUBLE_PRECISION;   /* float8 */
+       case 1042:  return SQL3_CHARACTER;  /* bpchar */
+       case 1043:  return SQL3_CHARACTER_VARYING;  /* varchar */
+       case 1082:  return SQL3_DATE_TIME_TIMESTAMP;    /* date */
+       case 1083:  return SQL3_DATE_TIME_TIMESTAMP;    /* time */
+       case 1184:  return SQL3_DATE_TIME_TIMESTAMP;    /* datetime */
+       case 1296:  return SQL3_DATE_TIME_TIMESTAMP;    /* timestamp */
+       case 1700:  return SQL3_NUMERIC;    /* numeric */
+       default:    return -type;
+   }
+}
index ed5e27db3a3709698cf9ef963f660bd593fb7b06..c1fe45c4a4440e62e00c01ee20934d8651519417 100644 (file)
@@ -275,7 +275,7 @@ make_name(void)
 
 %type     Iconst Fconst Sconst TransactionStmt CreateStmt UserId
 %type     CreateAsElement OptCreateAs CreateAsList CreateAsStmt
-%type     OptInherit key_reference comment_text
+%type     OptInherit key_reference comment_text ConstraintDeferrabilitySpec
 %type      key_match ColLabel SpecialRuleRelation ColId columnDef
 %type      ColConstraint ColConstraintElem NumericOnly FloatOnly
 %type      OptTableElementList OptTableElement TableConstraint
@@ -283,7 +283,7 @@ make_name(void)
 %type      target_list target_el update_target_list alias_clause
 %type      update_target_el opt_id relation_name database_name
 %type      access_method attr_name class index_name name func_name
-%type      file_name AexprConst ParamNo TypeId c_expr ColQualListWithNull
+%type      file_name AexprConst ParamNo TypeId c_expr
 %type     in_expr_nodes a_expr b_expr TruncateStmt CommentStmt
 %type     opt_indirection expr_list extract_list extract_arg
 %type     position_list substr_list substr_from alter_column_action
@@ -292,8 +292,8 @@ make_name(void)
 %type     opt_decimal Character character opt_varying opt_charset
 %type     opt_collate Datetime datetime opt_timezone opt_interval
 %type     numeric a_expr_or_null row_expr row_descriptor row_list
-%type     SelectStmt SubSelect result OptTemp OptTempType OptTempScope
-%type     opt_table opt_all sort_clause sortby_list ColQualifier
+%type     SelectStmt SubSelect result OptTemp ConstraintAttributeSpec
+%type     opt_table opt_all sort_clause sortby_list ConstraintAttr 
 %type     sortby OptUseOp opt_inh_star relation_name_list name_list
 %type     group_clause having_clause from_clause opt_distinct
 %type     join_outer where_clause relation_expr sub_type
@@ -329,18 +329,16 @@ make_name(void)
 %type     GrantStmt privileges operation_commalist operation
 %type     opt_cursor opt_lmode ConstraintsSetStmt comment_tg
 %type     case_expr when_clause_list case_default case_arg when_clause
-%type      select_clause opt_select_limit select_limit_value TimeClause
+%type      select_clause opt_select_limit select_limit_value ConstraintTimeSpec
 %type      select_offset_value using_expr join_expr ReindexStmt
 %type     using_list from_expr join_clause join_type
 %type     join_qual update_list join_clause join_clause_with_union
 %type     opt_level opt_lock lock_type users_in_new_group_clause
-%type      OptConstrFromTable comment_op ConstraintAttribute
+%type      OptConstrFromTable comment_op OptTempTableName
 %type      constraints_set_list constraints_set_namelist comment_fn
 %type     constraints_set_mode comment_type comment_cl comment_ag
 %type     CreateGroupStmt AlterGroupStmt DropGroupStmt key_delete
-%type     ColConstraintWithNull ColConstraintElemWithNull NotNull
-%type     join_expr_with_union DefaultClause DefaultExpr PrimaryKey
-%type     DeferrabilityClause opt_force key_update
+%type     join_expr_with_union opt_force key_update
 /***
 #ifdef ENABLE_ORACLE_JOIN_SYNTAX
 %type     oracle_list oracle_expr oracle_outer
@@ -991,24 +989,26 @@ CreateStmt:  CREATE OptTemp TABLE relation_name '(' OptTableElementList ')'
                }
        ;
 
-OptTemp:  OptTempType                           { $$ = $1; }
-                | OptTempScope OptTempType { $$ = cat2_str($1,$2); }
-                ;
+/*
+ * Redundancy here is needed to avoid shift/reduce conflicts,
+ * since TEMP is not a reserved word.  See also OptTempTableName.
+ */
 
-OptTempType:     TEMP      { $$ = make_str("temp"); }
-       | TEMPORARY { $$ = make_str("temporary"); }
-       | /* EMPTY */   { $$ = EMPTY; }
+OptTemp:   TEMPORARY       { $$ = make_str("temporary"); }
+       | TEMP          { $$ = make_str("temp"); }
+       | LOCAL TEMPORARY   { $$ = make_str("local temporary"); }
+       | LOCAL TEMP        { $$ = make_str("local temp"); }
+       | GLOBAL TEMPORARY  {
+                     mmerror(ET_ERROR, "GLOBAL TEMPORARY TABLE is not currently supported");
+                     $$ = make_str("global temporary");
+                   }
+       | GLOBAL TEMP       {
+                     mmerror(ET_ERROR, "GLOBAL TEMPORARY TABLE is not currently supported");
+                     $$ = make_str("global temp");
+                   }
+       | /*EMPTY*/     { $$ = EMPTY; }
        ;
 
-OptTempScope:  GLOBAL
-               {
-                    mmerror(ET_ERROR, "GLOBAL TEMPORARY TABLE is not currently supported");
-                    $$ = make_str("global");
-               }
-             | LOCAL { $$ = make_str("local"); }
-             ;
-
-
 OptTableElementList:  OptTableElementList ',' OptTableElement
                {
                    $$ = cat_str(3, $1, make_str(","), $3);
@@ -1024,7 +1024,7 @@ OptTableElement:  columnDef       { $$ = $1; }
            | TableConstraint   { $$ = $1; }
        ;
 
-columnDef:  ColId Typename ColQualifier opt_collate
+columnDef:  ColId Typename ColQualList opt_collate
                {
                    if (strlen($4) > 0)
                    {
@@ -1033,7 +1033,7 @@ columnDef:  ColId Typename ColQualifier opt_collate
                    }
                    $$ = cat_str(4, $1, $2, $3, $4);
                }
-   | ColId SERIAL ColQualifier opt_collate
+   | ColId SERIAL ColQualList opt_collate
        {
            if (strlen($4) > 0)
            {
@@ -1044,55 +1044,18 @@ columnDef:  ColId Typename ColQualifier opt_collate
        }
        ;
 
-/*
- * ColQualifier encapsulates an entire column qualification,
- * including DEFAULT, constraints, and constraint attributes.
- * Note that the DefaultClause handles the empty case.
- */
-ColQualifier:  DefaultClause ColQualList           { $$ = cat2_str($1, $2); }
-       | NotNull DefaultClause ColQualListWithNull { $$ = cat_str(3, $1, $2, $3); }
-       | DefaultExpr NotNull ColQualListWithNull   { $$ = cat_str(3, $1, $2, $3); }
-                | DefaultExpr NotNull              { $$ = cat2_str($1, $2); }
-                | NotNull DefaultClause                { $$ = cat2_str($1, $2); }
-       | NULL_P DefaultClause ColQualListWithNull  { $$ = cat_str(3, make_str("null"), $2, $3); }
-                | NULL_P DefaultClause             { $$ = cat2_str(make_str("null"), $2); }
-                | DefaultClause                        { $$ = $1; }
-       ;
-
-/*
- * 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).
- */
-DefaultClause:  DefaultExpr     { $$ = $1; }
-       | /*EMPTY*/ { $$ = EMPTY; }
-                ;
-
-DefaultExpr:  DEFAULT NULL_P       { $$ = make_str("default null"); }
-       | DEFAULT b_expr    { $$ = cat2_str(make_str("default"), $2); }
-       ;
-
 ColQualList:  ColQualList ColConstraint    { $$ = cat2_str($1,$2); }
-           | ColConstraint { $$ = $1; }
+           | /*EMPTY*/ { $$ = EMPTY; }
        ;
 
-ColQualListWithNull:  ColConstraintWithNull ColQualListWithNull
-           { $$ = cat2_str($1, $2); }
-       |  ColConstraintWithNull
-           { $$ = $1; }
-
 ColConstraint: CONSTRAINT name ColConstraintElem
                {
                    $$ = cat_str(3, make_str("constraint"), $2, $3);
                }
        | ColConstraintElem
                { $$ = $1; }
-       ;
-
-ColConstraintWithNull: CONSTRAINT name ColConstraintElemWithNull
-           { $$ = cat_str(3, make_str("constraint"), $2, $3); }
-       | ColConstraintElemWithNull
-           { $$ = $1; }
+       | ConstraintAttr
+               { $$ = $1; }
        ;
 
 /* DEFAULT NULL is already the default for Postgres.
@@ -1106,40 +1069,56 @@ ColConstraintWithNull:  CONSTRAINT name ColConstraintElemWithNull
  * shift/reduce conflicts with WITH TIME ZONE anyway.
  * - thomas 1999-01-08
  */
-ColConstraintElem:  ColConstraintElemWithNull
-                                {
-                                        $$ = $1;
-                                }
-                        | UNIQUE
+ColConstraintElem:     NOT NULL_P
+               {
+                   $$ = make_str("not null");
+               }
+           | NULL_P
+               {
+                   $$ = make_str("null");
+               }
+           | UNIQUE
                {
                    $$ = make_str("unique");
                }
-           | PrimaryKey
+           | PRIMARY KEY
                {
-                   $$ = $1;
+                   $$ = make_str("primary key");
                }
-           ;
-
-
-ColConstraintElemWithNull:  CHECK '(' a_expr ')'
+           | CHECK '(' a_expr ')'
                {
-                   $$ = cat_str(3, make_str("check("), $3, make_str(")"));
+                   $$ = cat_str(3, make_str("check ("), $3, make_str(")"));
                }
-           | REFERENCES ColId opt_column_list
-               key_match key_actions ConstraintAttribute
+           | DEFAULT NULL_P
                {
-                   $$ = cat_str(6, make_str("references"), $2, $3, $4, $5, $6);
+                   $$ = make_str("default null");
                }
-           | REFERENCES ColId opt_column_list
-               key_match key_actions
+           | DEFAULT b_expr
                {
-                   $$ = cat_str(5, make_str("references"), $2, $3, $4, $5);
+                   $$ = cat2_str(make_str("default"), $2);
                }
-       ;
-
-PrimaryKey:  PRIMARY KEY   { $$ = make_str("primary key"); }
+           |  REFERENCES ColId opt_column_list key_match key_actions 
+               {
+                   $$ = cat_str(5, make_str("references"), $2, $3, $4, $5);  
+               }
+           ;
 
-NotNull:  NOT NULL_P       { $$ = make_str("not null"); }
+/*
+ * ConstraintAttr represents constraint attributes, which we parse as if
+ * they were independent constraint clauses, in order to avoid shift/reduce
+ * conflicts (since NOT might start either an independent NOT NULL clause
+ * or an attribute).  analyze.c is responsible for attaching the attribute
+ * information to the preceding "real" constraint node, and for complaining
+ * if attribute clauses appear in the wrong place or wrong combinations.
+ *
+ * See also ConstraintAttributeSpec, which can be used in places where
+ * there is no parsing conflict.
+ */
+ConstraintAttr: DEFERRABLE     { $$ = make_str("deferrable"); }
+       | NOT DEFERRABLE    { $$ = make_str("not deferrable"); }
+       | INITIALLY DEFERRED    { $$ = make_str("initially deferred"); }
+       | INITIALLY IMMEDIATE   { $$ = make_str("initially immediate"); }
+       ;
 
 /* ConstraintElem specifies constraint syntax which is not embedded into
  *  a column definition. ColConstraintElem specifies the embedded form.
@@ -1161,20 +1140,15 @@ ConstraintElem:  CHECK '(' a_expr ')'
                {
                    $$ = cat_str(3, make_str("unique("), $3, make_str(")"));
                }
-       | PrimaryKey '(' columnList ')'
+       | PRIMARY KEY '(' columnList ')'
                {
-                   $$ = cat_str(3, make_str("primary key("), $3, make_str(")"));
+                   $$ = cat_str(3, make_str("primary key("), $4, make_str(")"));
                }
        | FOREIGN KEY '(' columnList ')' REFERENCES ColId opt_column_list
-           key_match key_actions ConstraintAttribute
+           key_match key_actions ConstraintAttributeSpec
                {
                    $$ = cat_str(8, make_str("foreign key("), $4, make_str(") references"), $7, $8, $9, $10, $11);
                }
-       | FOREIGN KEY '(' columnList ')' REFERENCES ColId opt_column_list
-           key_match key_actions
-               {
-                   $$ = cat_str(7, make_str("foreign key("), $4, make_str(") references"), $7, $8, $9, $10);
-               }
        ;
 
 key_match:  MATCH FULL
@@ -1347,7 +1321,7 @@ CreateTrigStmt:  CREATE TRIGGER name TriggerActionTime TriggerEvents ON
                }
    |   CREATE CONSTRAINT TRIGGER name AFTER TriggerEvents ON
                                 relation_name OptConstrFromTable
-               ConstraintAttribute
+               ConstraintAttributeSpec
                                 FOR EACH ROW EXECUTE PROCEDURE
                name '(' TriggerFuncArgs ')'
                {
@@ -1422,18 +1396,18 @@ OptConstrFromTable:                     /* Empty */
                                 }
                 ;
 
-ConstraintAttribute: DeferrabilityClause
+ConstraintAttributeSpec: ConstraintDeferrabilitySpec
                 {  $$ = $1; }
-   | TimeClause
-       {   $$ = $1; }
-   | DeferrabilityClause TimeClause
-       {
+   | 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);
+           $$ = cat2_str($1, $2);
        }
-   | TimeClause DeferrabilityClause
+   | ConstraintTimeSpec
+       {   $$ = $1; }
+   | ConstraintTimeSpec ConstraintDeferrabilitySpec
        {
            if (strcmp($2, "deferrable") != 0 && strcmp($1, "initially deferrable") == 0 )
                mmerror(ET_ERROR, "INITIALLY DEFERRED constraint must be DEFERRABLE");
@@ -1442,11 +1416,11 @@ ConstraintAttribute: DeferrabilityClause
        }
    ;
 
-DeferrabilityClause: NOT DEFERRABLE    { $$ = make_str("not deferrable"); }
+ConstraintDeferrabilitySpec: NOT DEFERRABLE    { $$ = make_str("not deferrable"); }
                    | DEFERRABLE    { $$ = make_str("deferrable"); }
                 ;
 
-TimeClause: INITIALLY IMMEDIATE        { $$ = make_str("initially immediate"); }
+ConstraintTimeSpec: INITIALLY IMMEDIATE        { $$ = make_str("initially immediate"); }
                 | INITIALLY DEFERRED   { $$ = make_str("initially deferrable"); }
                 ;
 
@@ -2545,13 +2519,57 @@ SubSelect:     SELECT opt_distinct target_list
                }
        ;
 
-result:  INTO OptTemp opt_table relation_name      { FoundInto = 1;
-                             $$= cat_str(4, make_str("into"), $2, $3, $4);
-                           }
-       | INTO into_list            { $$ = EMPTY; }
-       | /*EMPTY*/             { $$ = EMPTY; }
+result:  INTO OptTempTableName         {
+                       FoundInto = 1;
+                       $$= cat2_str(make_str("into"), $2);
+                   }
+       | INTO into_list    { $$ = EMPTY; }
+       | /*EMPTY*/     { $$ = EMPTY; }
        ;
 
+/*
+ * Redundancy here is needed to avoid shift/reduce conflicts,
+ * since TEMP is not a reserved word.  See also OptTemp.
+ *
+ * The result is a cons cell (not a true list!) containing
+ * a boolean and a table name.
+ */
+OptTempTableName:  TEMPORARY opt_table relation_name
+           {
+               $$ = cat_str(3, make_str("temporary"), $2, $3);
+           }
+                       | TEMP opt_table relation_name
+           {
+               $$ = cat_str(3, make_str("temp"), $2, $3);
+           }
+                       | LOCAL TEMPORARY opt_table relation_name
+           {
+               $$ = cat_str(3, make_str("local temporary"), $3, $4);
+           }
+                       | LOCAL TEMP opt_table relation_name
+           {
+               $$ = cat_str(3, make_str("local temp"), $3, $4);
+           }
+                       | GLOBAL TEMPORARY opt_table relation_name
+                        {
+               mmerror(ET_ERROR, "GLOBAL TEMPORARY TABLE is not currently supported");
+               $$ = cat_str(3, make_str("global temporary"), $3, $4);
+                        }
+                       | GLOBAL TEMP opt_table relation_name
+                        {
+               mmerror(ET_ERROR, "GLOBAL TEMPORARY TABLE is not currently supported");
+               $$ = cat_str(3, make_str("global temp"), $3, $4);
+                        }
+                       | TABLE relation_name
+           {
+               $$ = cat2_str(make_str("table"), $2);
+           }
+                       | relation_name
+           {
+               $$ = $1;
+           }
+                ;
+
 opt_table:  TABLE                  { $$ = make_str("table"); }
        | /*EMPTY*/             { $$ = EMPTY; }
        ;
@@ -3839,7 +3857,6 @@ ColId:  ident                 { $$ = $1; }
        | 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"); }
@@ -3853,7 +3870,6 @@ ColId:  ident                 { $$ = $1; }
        | INCREMENT         { $$ = make_str("increment"); }
        | INDEX             { $$ = make_str("index"); }
        | INHERITS          { $$ = make_str("inherits"); }
-       | INITIALLY         { $$ = make_str("initially"); }
        | INSENSITIVE           { $$ = make_str("insensitive"); }
        | INSTEAD           { $$ = make_str("instead"); }
        | INTERVAL          { $$ = make_str("interval"); }
@@ -3900,6 +3916,8 @@ ColId:  ident                 { $$ = $1; }
        | STDIN                         { $$ = make_str("stdin"); }
        | STDOUT                        { $$ = make_str("stdout"); }
        | SYSID                         { $$ = make_str("sysid"); }
+       | TEMP              { $$ = make_str("temp"); }
+       | TEMPORARY         { $$ = make_str("temporary"); }
        | TIME              { $$ = make_str("time"); }
        | TIMESTAMP         { $$ = make_str("timestamp"); }
        | TIMEZONE_HOUR                 { $$ = make_str("timezone_hour"); }
@@ -3977,6 +3995,7 @@ ColLabel:  ColId          { $$ = $1; }
        | CURRENT_USER      { $$ = make_str("current_user"); }
        | DEC           { $$ = make_str("dec"); }
        | DECIMAL       { $$ = make_str("decimal"); }
+       | DEFERRABLE        { $$ = make_str("deferrable"); }
        | DO            { $$ = make_str("do"); }
        | ELSE                  { $$ = make_str("else"); }
        | END_TRANS             { $$ = make_str("end"); }
@@ -3987,6 +4006,7 @@ ColLabel:  ColId          { $$ = $1; }
        | FOREIGN       { $$ = make_str("foreign"); }
        | GLOBAL        { $$ = make_str("global"); }
        | GROUP         { $$ = make_str("group"); }
+       | INITIALLY     { $$ = make_str("initially"); }
        | LISTEN        { $$ = make_str("listen"); }
        | LOAD          { $$ = make_str("load"); }
        | LOCK_P        { $$ = make_str("lock"); }