Simplify parsing of column constraints by treating constraint attributes
authorTom Lane
Wed, 1 Mar 2000 05:18:20 +0000 (05:18 +0000)
committerTom Lane
Wed, 1 Mar 2000 05:18:20 +0000 (05:18 +0000)
as independent clauses in the grammar.  analyze.c takes care of putting
the data where it belongs and complaining about invalid combinations.
Also, make TEMP (and TEMPORARY) non-reserved words.

src/backend/parser/analyze.c
src/backend/parser/gram.y
src/include/nodes/parsenodes.h

index a78a0571fe4b4bbac90115220ca09e5a0a65a5d0..612481deabc4c017e7241123f99bc3d564b35609 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: analyze.c,v 1.138 2000/02/29 12:28:25 wieck Exp $
+ * $Id: analyze.c,v 1.139 2000/03/01 05:18:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,6 +44,7 @@ static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt);
 
 static void transformForUpdate(Query *qry, List *forUpdate);
 static void transformFkeyGetPrimaryKey(FkConstraint *fkconstraint);
+static void transformConstraintAttrs(List *constraintList);
 static void transformColumnType(ParseState *pstate, ColumnDef *column);
 
 /* kluge to return extra info from transformCreateStmt() */
@@ -589,6 +590,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
    IndexStmt      *index,
                   *pkey = NULL;
    IndexElem      *iparam;
+   bool            saw_nullable;
 
    q = makeNode(Query);
    q->commandType = CMD_UTILITY;
@@ -621,6 +623,12 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                    FuncCall   *funccallnode;
                    CreateSeqStmt *sequence;
 
+                   /*
+                    * Create appropriate constraints for SERIAL.  We do this
+                    * in full, rather than shortcutting, so that we will
+                    * detect any conflicting constraints the user wrote
+                    * (like a different DEFAULT).
+                    */
                    sname = makeObjectName(stmt->relname, column->colname,
                                           "seq");
                    /*
@@ -644,27 +652,37 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                    constraint->raw_expr = (Node *) funccallnode;
                    constraint->cooked_expr = NULL;
                    constraint->keys = NULL;
-
-                   column->constraints = lappend(column->constraints, constraint);
+                   column->constraints = lappend(column->constraints,
+                                                 constraint);
 
                    constraint = makeNode(Constraint);
                    constraint->contype = CONSTR_UNIQUE;
                    constraint->name = makeObjectName(stmt->relname,
                                                      column->colname,
                                                      "key");
-                   column->constraints = lappend(column->constraints, constraint);
+                   column->constraints = lappend(column->constraints,
+                                                 constraint);
+
+                   constraint = makeNode(Constraint);
+                   constraint->contype = CONSTR_NOTNULL;
+                   column->constraints = lappend(column->constraints,
+                                                 constraint);
 
                    sequence = makeNode(CreateSeqStmt);
                    sequence->seqname = pstrdup(sname);
                    sequence->options = NIL;
 
                    elog(NOTICE, "CREATE TABLE will create implicit sequence '%s' for SERIAL column '%s.%s'",
-                     sequence->seqname, stmt->relname, column->colname);
+                        sequence->seqname, stmt->relname, column->colname);
 
                    blist = lcons(sequence, NIL);
                }
 
                /* Process column constraints, if any... */
+               transformConstraintAttrs(column->constraints);
+
+               saw_nullable = false;
+
                foreach(clist, column->constraints)
                {
                    constraint = lfirst(clist);
@@ -676,7 +694,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                     * to be processed later.
                     * ----------
                     */
-                   if (nodeTag(constraint) == T_FkConstraint)
+                   if (IsA(constraint, FkConstraint))
                    {
                        Ident   *id = makeNode(Ident);
                        id->name        = column->colname;
@@ -693,23 +711,19 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                    switch (constraint->contype)
                    {
                        case CONSTR_NULL:
-
-                           /*
-                            * We should mark this explicitly, so we
-                            * can tell if NULL and NOT NULL are both
-                            * specified
-                            */
-                           if (column->is_not_null)
+                           if (saw_nullable && column->is_not_null)
                                elog(ERROR, "CREATE TABLE/(NOT) NULL conflicting declaration"
                                     " for '%s.%s'", stmt->relname, column->colname);
                            column->is_not_null = FALSE;
+                           saw_nullable = true;
                            break;
 
                        case CONSTR_NOTNULL:
-                           if (column->is_not_null)
-                               elog(ERROR, "CREATE TABLE/NOT NULL already specified"
+                           if (saw_nullable && ! column->is_not_null)
+                               elog(ERROR, "CREATE TABLE/(NOT) NULL conflicting declaration"
                                     " for '%s.%s'", stmt->relname, column->colname);
                            column->is_not_null = TRUE;
+                           saw_nullable = true;
                            break;
 
                        case CONSTR_DEFAULT:
@@ -742,6 +756,13 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                            constraints = lappend(constraints, constraint);
                            break;
 
+                       case CONSTR_ATTR_DEFERRABLE:
+                       case CONSTR_ATTR_NOT_DEFERRABLE:
+                       case CONSTR_ATTR_DEFERRED:
+                       case CONSTR_ATTR_IMMEDIATE:
+                           /* transformConstraintAttrs took care of these */
+                           break;
+
                        default:
                            elog(ERROR, "parser: unrecognized constraint (internal error)");
                            break;
@@ -767,10 +788,16 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
                        constraints = lappend(constraints, constraint);
                        break;
 
+                   case CONSTR_NULL:
                    case CONSTR_NOTNULL:
                    case CONSTR_DEFAULT:
+                   case CONSTR_ATTR_DEFERRABLE:
+                   case CONSTR_ATTR_NOT_DEFERRABLE:
+                   case CONSTR_ATTR_DEFERRED:
+                   case CONSTR_ATTR_IMMEDIATE:
                        elog(ERROR, "parser: illegal context for constraint (internal error)");
                        break;
+
                    default:
                        elog(ERROR, "parser: unrecognized constraint (internal error)");
                        break;
@@ -1999,6 +2026,95 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint)
    heap_close(pkrel, AccessShareLock);
 }
 
+/*
+ * Preprocess a list of column constraint clauses
+ * to attach constraint attributes to their primary constraint nodes
+ * and detect inconsistent/misplaced constraint attributes.
+ *
+ * NOTE: currently, attributes are only supported for FOREIGN KEY primary
+ * constraints, but someday they ought to be supported for other constraints.
+ */
+static void
+transformConstraintAttrs(List *constraintList)
+{
+   Node       *lastprimarynode = NULL;
+   bool        saw_deferrability = false;
+   bool        saw_initially = false;
+   List       *clist;
+
+   foreach(clist, constraintList)
+   {
+       Node   *node = lfirst(clist);
+
+       if (! IsA(node, Constraint))
+       {
+           lastprimarynode = node;
+           /* reset flags for new primary node */
+           saw_deferrability = false;
+           saw_initially = false;
+       }
+       else
+       {
+           Constraint     *con = (Constraint *) node;
+
+           switch (con->contype)
+           {
+               case CONSTR_ATTR_DEFERRABLE:
+                   if (lastprimarynode == NULL ||
+                       ! IsA(lastprimarynode, FkConstraint))
+                       elog(ERROR, "Misplaced DEFERRABLE clause");
+                   if (saw_deferrability)
+                       elog(ERROR, "Multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed");
+                   saw_deferrability = true;
+                   ((FkConstraint *) lastprimarynode)->deferrable = true;
+                   break;
+               case CONSTR_ATTR_NOT_DEFERRABLE:
+                   if (lastprimarynode == NULL ||
+                       ! IsA(lastprimarynode, FkConstraint))
+                       elog(ERROR, "Misplaced NOT DEFERRABLE clause");
+                   if (saw_deferrability)
+                       elog(ERROR, "Multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed");
+                   saw_deferrability = true;
+                   ((FkConstraint *) lastprimarynode)->deferrable = false;
+                   if (saw_initially &&
+                       ((FkConstraint *) lastprimarynode)->initdeferred)
+                       elog(ERROR, "INITIALLY DEFERRED constraint must be DEFERRABLE");
+                   break;
+               case CONSTR_ATTR_DEFERRED:
+                   if (lastprimarynode == NULL ||
+                       ! IsA(lastprimarynode, FkConstraint))
+                       elog(ERROR, "Misplaced INITIALLY DEFERRED clause");
+                   if (saw_initially)
+                       elog(ERROR, "Multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed");
+                   saw_initially = true;
+                   ((FkConstraint *) lastprimarynode)->initdeferred = true;
+                   /* If only INITIALLY DEFERRED appears, assume DEFERRABLE */
+                   if (! saw_deferrability)
+                       ((FkConstraint *) lastprimarynode)->deferrable = true;
+                   else if (! ((FkConstraint *) lastprimarynode)->deferrable)
+                       elog(ERROR, "INITIALLY DEFERRED constraint must be DEFERRABLE");
+                   break;
+               case CONSTR_ATTR_IMMEDIATE:
+                   if (lastprimarynode == NULL ||
+                       ! IsA(lastprimarynode, FkConstraint))
+                       elog(ERROR, "Misplaced INITIALLY IMMEDIATE clause");
+                   if (saw_initially)
+                       elog(ERROR, "Multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed");
+                   saw_initially = true;
+                   ((FkConstraint *) lastprimarynode)->initdeferred = false;
+                   break;
+               default:
+                   /* Otherwise it's not an attribute */
+                   lastprimarynode = node;
+                   /* reset flags for new primary node */
+                   saw_deferrability = false;
+                   saw_initially = false;
+                   break;
+           }
+       }
+   }
+}
+
 /*
  * Special handling of type definition for a column
  */
@@ -2027,4 +2143,3 @@ transformColumnType(ParseState *pstate, ColumnDef *column)
        }
    }
 }
-
index b39f2cf175e3c15c971a5d0cbaf2fa12baf4ff29..79bd88f8f1a9b512f86e5b0328511b8fa524eeb4 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.152 2000/02/26 18:13:41 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.153 2000/03/01 05:18:19 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -168,7 +168,7 @@ static void doNegateFloat(Value *v);
 %type         operation, TriggerOneEvent
 
 %type    stmtblock, stmtmulti,
-       result, relation_name_list, OptTableElementList,
+       result, OptTempTableName, relation_name_list, OptTableElementList,
        OptInherit, definition, opt_distinct,
        opt_with, func_args, func_args_list, func_as,
        oper_argtypes, RuleActionList, RuleActionMulti,
@@ -182,7 +182,7 @@ static void doNegateFloat(Value *v);
 %type    func_return
 %type     set_opt
 
-%type     TriggerForOpt, TriggerForType, OptTemp, OptTempType, OptTempScope
+%type     TriggerForOpt, TriggerForType, OptTemp
 
 %type    for_update_clause, update_list
 %type     opt_all
@@ -263,15 +263,12 @@ static void doNegateFloat(Value *v);
 %type         TypeId
 
 %type    TableConstraint
-%type    ColQualList, ColQualifier
-%type    ColQualListWithNull
-%type    ColConstraint, ColConstraintElem, PrimaryKey, NotNull
-%type    DefaultClause, DefaultExpr
-%type    ColConstraintWithNull, ColConstraintElemWithNull
+%type    ColQualList
+%type    ColConstraint, ColConstraintElem, ConstraintAttr
 %type    key_actions, key_delete, key_update, key_reference
 %type         key_match
-%type    ConstraintAttribute, DeferrabilityClause,
-               TimeClause
+%type    ConstraintAttributeSpec, ConstraintDeferrabilitySpec,
+               ConstraintTimeSpec
 
 %type    constraints_set_list
 %type    constraints_set_namelist
@@ -987,24 +984,25 @@ CreateStmt:  CREATE OptTemp TABLE relation_name '(' OptTableElementList ')'
                }
        ;
 
-OptTemp:  OptTempType                      { $$ = $1; }
-           | OptTempScope OptTempType      { $$ = $2; }
-       ;
-
-OptTempType:  TEMP                         { $$ = TRUE; }
-           | TEMPORARY                     { $$ = TRUE; }
-           | /*EMPTY*/                     { $$ = FALSE; }
-       ;
-
-OptTempScope:  GLOBAL
+/*
+ * Redundancy here is needed to avoid shift/reduce conflicts,
+ * since TEMP is not a reserved word.  See also OptTempTableName.
+ */
+OptTemp:      TEMPORARY                        { $$ = TRUE; }
+           | TEMP                          { $$ = TRUE; }
+           | LOCAL TEMPORARY               { $$ = TRUE; }
+           | LOCAL TEMP                    { $$ = TRUE; }
+           | GLOBAL TEMPORARY
                {
                    elog(ERROR, "GLOBAL TEMPORARY TABLE is not currently supported");
                    $$ = TRUE;
                }
-           | LOCAL
+           | GLOBAL TEMP
                {
-                    $$ = FALSE;
+                   elog(ERROR, "GLOBAL TEMPORARY TABLE is not currently supported");
+                   $$ = TRUE;
                }
+           | /*EMPTY*/                     { $$ = FALSE; }
        ;
 
 OptTableElementList:  OptTableElementList ',' OptTableElement
@@ -1028,16 +1026,11 @@ OptTableElement:  columnDef                     { $$ = $1; }
            | TableConstraint                   { $$ = $1; }
        ;
 
-columnDef:  ColId Typename ColQualifier opt_collate
+columnDef:  ColId Typename ColQualList opt_collate
                {
                    ColumnDef *n = makeNode(ColumnDef);
                    n->colname = $1;
                    n->typename = $2;
-#if 0
-                   n->raw_default = NULL;
-                   n->cooked_default = NULL;
-                   n->is_not_null = FALSE;
-#endif
                    n->constraints = $3;
 
                    if ($4 != NULL)
@@ -1046,18 +1039,13 @@ columnDef:  ColId Typename ColQualifier opt_collate
 
                    $$ = (Node *)n;
                }
-           | ColId SERIAL ColQualifier opt_collate
+           | ColId SERIAL ColQualList opt_collate
                {
                    ColumnDef *n = makeNode(ColumnDef);
                    n->colname = $1;
                    n->typename = makeNode(TypeName);
                    n->typename->name = xlateSqlType("integer");
                    n->typename->typmod = -1;
-#if 0
-                   n->raw_default = NULL;
-                   n->cooked_default = NULL;
-#endif
-                   n->is_not_null = TRUE;
                    n->is_sequence = TRUE;
                    n->constraints = $3;
 
@@ -1069,141 +1057,25 @@ 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
-               {
-                   if ($1 != NULL)
-                       $$ = lcons($1, $2);
-                   else
-                       $$ = $2;
-               }
-           | NotNull DefaultClause ColQualListWithNull
-               {
-                   $$ = lcons($1, $3);
-                   if ($2 != NULL)
-                       $$ = lcons($2, $$);
-               }
-           | DefaultExpr NotNull ColQualListWithNull
-               {
-                   $$ = lcons($2, $3);
-                   if ($1 != NULL)
-                       $$ = lcons($1, $$);
-               }
-           | DefaultExpr NotNull
-               {
-                   $$ = lcons($2, NIL);
-                   if ($1 != NULL)
-                       $$ = lcons($1, $$);
-               }
-           | NotNull DefaultClause
-               {
-                   $$ = lcons($1, NIL);
-                   if ($2 != NULL)
-                       $$ = lcons($2, $$);
-               }
-           | NULL_P DefaultClause ColQualListWithNull
-               {
-                   $$ = $3;
-                   if ($2 != NULL)
-                       $$ = lcons($2, $$);
-               }
-           | NULL_P DefaultClause
-               {
-                   if ($2 != NULL)
-                       $$ = lcons($2, NIL);
-                   else
-                       $$ = NIL;
-               }
-           | DefaultClause
-               {
-                   if ($1 != NULL)
-                       $$ = lcons($1, NIL);
-                   else
-                       $$ = NIL;
-               }
-       ;
-
-/*
- * 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*/                     { $$ = NULL; }
+ColQualList:  ColQualList ColConstraint        { $$ = lappend($1, $2); }
+           | /*EMPTY*/                     { $$ = NIL; }
        ;
 
-DefaultExpr:  DEFAULT NULL_P
-               {
-                   Constraint *n = makeNode(Constraint);
-                   n->contype = CONSTR_DEFAULT;
-                   n->name = NULL;
-                   n->raw_expr = NULL;
-                   n->cooked_expr = NULL;
-                   n->keys = NULL;
-                   $$ = (Node *)n;
-               }
-           | DEFAULT b_expr
-               {
-                   Constraint *n = makeNode(Constraint);
-                   n->contype = CONSTR_DEFAULT;
-                   n->name = NULL;
-                   n->raw_expr = $2;
-                   n->cooked_expr = NULL;
-                   n->keys = NULL;
-                   $$ = (Node *)n;
-               }
-       ;
-
-ColQualList:  ColQualList ColConstraint
-               {
-                   if ($2 != NULL)
-                       $$ = lappend($1, $2);
-                   else
-                       $$ = $1;
-               }
-           | ColConstraint
-               {
-                   if ($1 != NULL)
-                       $$ = lcons($1, NIL);
-                   else
-                       $$ = NULL;
-               }
-       ;
-
-ColQualListWithNull:  ColConstraintWithNull ColQualListWithNull
-               {
-                   if ($1 != NULL)
-                       $$ = lcons($1, $2);
-                   else
-                       $$ = $2;
-               }
-           | ColConstraintWithNull
-               {
-                   if ($1 != NULL)
-                       $$ = lcons($1, NIL);
-                   else
-                       $$ = NULL;
-               }
-       ;
-
-ColConstraint: CONSTRAINT name ColConstraintElem
+ColConstraint:
+       CONSTRAINT name ColConstraintElem
                {
                    switch (nodeTag($3))
                    {
                        case T_Constraint:
                            {
                                Constraint *n = (Constraint *)$3;
-                               if (n != NULL) n->name = $2;
+                               n->name = $2;
                            }
                            break;
                        case T_FkConstraint:
                            {
                                FkConstraint *n = (FkConstraint *)$3;
-                               if (n != NULL) n->constr_name = $2;
+                               n->constr_name = $2;
                            }
                            break;
                        default:
@@ -1213,30 +1085,7 @@ ColConstraint: CONSTRAINT name ColConstraintElem
                }
        | ColConstraintElem
                { $$ = $1; }
-       ;
-
-ColConstraintWithNull:  CONSTRAINT name ColConstraintElemWithNull
-               {
-                   switch (nodeTag($3))
-                   {
-                       case T_Constraint:
-                           {
-                               Constraint *n = (Constraint *)$3;
-                               if (n != NULL) n->name = $2;
-                           }
-                           break;
-                       case T_FkConstraint:
-                           {
-                               FkConstraint *n = (FkConstraint *)$3;
-                               if (n != NULL) n->constr_name = $2;
-                           }
-                           break;
-                       default:
-                           break;
-                   }
-                   $$ = $3;
-               }
-       | ColConstraintElemWithNull
+       | ConstraintAttr
                { $$ = $1; }
        ;
 
@@ -1250,10 +1099,31 @@ ColConstraintWithNull:  CONSTRAINT name ColConstraintElemWithNull
  * 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:  ColConstraintElemWithNull
+ColConstraintElem:
+             NOT NULL_P
                {
-                   $$ = $1;
+                   Constraint *n = makeNode(Constraint);
+                   n->contype = CONSTR_NOTNULL;
+                   n->name = NULL;
+                   n->raw_expr = NULL;
+                   n->cooked_expr = NULL;
+                   n->keys = NULL;
+                   $$ = (Node *)n;
+               }
+           | NULL_P
+               {
+                   Constraint *n = makeNode(Constraint);
+                   n->contype = CONSTR_NULL;
+                   n->name = NULL;
+                   n->raw_expr = NULL;
+                   n->cooked_expr = NULL;
+                   n->keys = NULL;
+                   $$ = (Node *)n;
                }
            | UNIQUE
                {
@@ -1265,13 +1135,17 @@ ColConstraintElem:  ColConstraintElemWithNull
                    n->keys = NULL;
                    $$ = (Node *)n;
                }
-           | PrimaryKey
+           | PRIMARY KEY
                {
-                   $$ = $1;
+                   Constraint *n = makeNode(Constraint);
+                   n->contype = CONSTR_PRIMARY;
+                   n->name = NULL;
+                   n->raw_expr = NULL;
+                   n->cooked_expr = NULL;
+                   n->keys = NULL;
+                   $$ = (Node *)n;
                }
-       ;
-
-ColConstraintElemWithNull:  CHECK '(' a_expr ')'
+           | CHECK '(' a_expr ')'
                {
                    Constraint *n = makeNode(Constraint);
                    n->contype = CONSTR_CHECK;
@@ -1281,22 +1155,27 @@ ColConstraintElemWithNull:  CHECK '(' a_expr ')'
                    n->keys = NULL;
                    $$ = (Node *)n;
                }
-           | REFERENCES ColId opt_column_list
-               key_match key_actions ConstraintAttribute
+           | DEFAULT NULL_P
                {
-                   FkConstraint *n = makeNode(FkConstraint);
-                   n->constr_name      = NULL;
-                   n->pktable_name     = $2;
-                   n->fk_attrs         = NIL;
-                   n->pk_attrs         = $3;
-                   n->match_type       = $4;
-                   n->actions          = $5;
-                   n->deferrable       = (($6 & 1) != 0);
-                   n->initdeferred     = (($6 & 2) != 0);
+                   Constraint *n = makeNode(Constraint);
+                   n->contype = CONSTR_DEFAULT;
+                   n->name = NULL;
+                   n->raw_expr = NULL;
+                   n->cooked_expr = NULL;
+                   n->keys = NULL;
                    $$ = (Node *)n;
                }
-           | REFERENCES ColId opt_column_list
-               key_match key_actions
+           | DEFAULT b_expr
+               {
+                   Constraint *n = makeNode(Constraint);
+                   n->contype = CONSTR_DEFAULT;
+                   n->name = NULL;
+                   n->raw_expr = $2;
+                   n->cooked_expr = NULL;
+                   n->keys = NULL;
+                   $$ = (Node *)n;
+               }
+           | REFERENCES ColId opt_column_list key_match key_actions 
                {
                    FkConstraint *n = makeNode(FkConstraint);
                    n->constr_name      = NULL;
@@ -1305,34 +1184,49 @@ ColConstraintElemWithNull:  CHECK '(' a_expr ')'
                    n->pk_attrs         = $3;
                    n->match_type       = $4;
                    n->actions          = $5;
-                   n->deferrable       = true;
+                   n->deferrable       = false;
                    n->initdeferred     = false;
                    $$ = (Node *)n;
                }
        ;
 
-PrimaryKey:  PRIMARY KEY
+/*
+ * 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
                {
                    Constraint *n = makeNode(Constraint);
-                   n->contype = CONSTR_PRIMARY;
-                   n->name = NULL;
-                   n->raw_expr = NULL;
-                   n->cooked_expr = NULL;
-                   n->keys = NULL;
+                   n->contype = CONSTR_ATTR_DEFERRABLE;
                    $$ = (Node *)n;
                }
-       ;
-
-NotNull:  NOT NULL_P
+           | NOT DEFERRABLE
                {
                    Constraint *n = makeNode(Constraint);
-                   n->contype = CONSTR_NOTNULL;
-                   n->name = NULL;
-                   n->raw_expr = NULL;
-                   n->cooked_expr = NULL;
-                   n->keys = NULL;
+                   n->contype = CONSTR_ATTR_NOT_DEFERRABLE;
+                   $$ = (Node *)n;
+               }
+           | INITIALLY DEFERRED
+               {
+                   Constraint *n = makeNode(Constraint);
+                   n->contype = CONSTR_ATTR_DEFERRED;
+                   $$ = (Node *)n;
+               }
+           | INITIALLY IMMEDIATE
+               {
+                   Constraint *n = makeNode(Constraint);
+                   n->contype = CONSTR_ATTR_IMMEDIATE;
                    $$ = (Node *)n;
                }
+       ;
+
 
 /* ConstraintElem specifies constraint syntax which is not embedded into
  *  a column definition. ColConstraintElem specifies the embedded form.
@@ -1345,13 +1239,13 @@ TableConstraint:  CONSTRAINT name ConstraintElem
                        case T_Constraint:
                            {
                                Constraint *n = (Constraint *)$3;
-                               if (n != NULL) n->name = $2;
+                               n->name = $2;
                            }
                            break;
                        case T_FkConstraint:
                            {
                                FkConstraint *n = (FkConstraint *)$3;
-                               if (n != NULL) n->constr_name = $2;
+                               n->constr_name = $2;
                            }
                            break;
                        default:
@@ -1382,14 +1276,18 @@ ConstraintElem:  CHECK '(' a_expr ')'
                    n->keys = $3;
                    $$ = (Node *)n;
                }
-       | PrimaryKey '(' columnList ')'
+       | PRIMARY KEY '(' columnList ')'
                {
-                   Constraint *n = (Constraint *)$1;
-                   n->keys = $3;
+                   Constraint *n = makeNode(Constraint);
+                   n->contype = CONSTR_PRIMARY;
+                   n->name = NULL;
+                   n->raw_expr = NULL;
+                   n->cooked_expr = NULL;
+                   n->keys = $4;
                    $$ = (Node *)n;
                }
        | FOREIGN KEY '(' columnList ')' REFERENCES ColId opt_column_list
-               key_match key_actions ConstraintAttribute
+               key_match key_actions ConstraintAttributeSpec
                {
                    FkConstraint *n = makeNode(FkConstraint);
                    n->constr_name      = NULL;
@@ -1402,20 +1300,6 @@ ConstraintElem:  CHECK '(' a_expr ')'
                    n->initdeferred     = ($11 & 2) != 0;
                    $$ = (Node *)n;
                }
-       | FOREIGN KEY '(' columnList ')' REFERENCES ColId opt_column_list
-               key_match key_actions
-               {
-                   FkConstraint *n = makeNode(FkConstraint);
-                   n->constr_name      = NULL;
-                   n->pktable_name     = $7;
-                   n->fk_attrs         = $4;
-                   n->pk_attrs         = $8;
-                   n->match_type       = $9;
-                   n->actions          = $10;
-                   n->deferrable       = false;
-                   n->initdeferred     = false;
-                   $$ = (Node *)n;
-               }
        ;
 
 key_match:  MATCH FULL
@@ -1645,7 +1529,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 ')'
                {
                    CreateTrigStmt *n = makeNode(CreateTrigStmt);
@@ -1740,37 +1624,41 @@ OptConstrFromTable:         /* Empty */
                }
        ;
 
-ConstraintAttribute:  DeferrabilityClause
+ConstraintAttributeSpec:  ConstraintDeferrabilitySpec
+           { $$ = $1; }
+       | ConstraintDeferrabilitySpec ConstraintTimeSpec
            {
-               $$ = $1;
+               if ($1 == 0 && $2 != 0)
+                   elog(ERROR, "INITIALLY DEFERRED constraint must be DEFERRABLE");
+               $$ = $1 | $2;
            }
-       | TimeClause
+       | ConstraintTimeSpec
            {
                if ($1 != 0)
                    $$ = 3;
                else
                    $$ = 0;
            }
-       | DeferrabilityClause TimeClause
-           {
-               if ($1 == 0 && $2 != 0)
-                   elog(ERROR, "INITIALLY DEFERRED constraint must be DEFERRABLE");
-               $$ = $1 | $2;
-           }
-       | TimeClause DeferrabilityClause
+       | ConstraintTimeSpec ConstraintDeferrabilitySpec
            {
                if ($2 == 0 && $1 != 0)
                    elog(ERROR, "INITIALLY DEFERRED constraint must be DEFERRABLE");
                $$ = $1 | $2;
            }
+       | /* Empty */
+           { $$ = 0; }
        ;
 
-DeferrabilityClause:  DEFERRABLE               { $$ = 1; }
-       | NOT DEFERRABLE                        { $$ = 0; }
+ConstraintDeferrabilitySpec: NOT DEFERRABLE
+           { $$ = 0; }
+       | DEFERRABLE
+           { $$ = 1; }
        ;
 
-TimeClause:  INITIALLY IMMEDIATE               { $$ = 0; }
-       | INITIALLY DEFERRED                    { $$ = 2; }
+ConstraintTimeSpec: INITIALLY IMMEDIATE
+           { $$ = 0; }
+       | INITIALLY DEFERRED
+           { $$ = 2; }
        ;
 
 
@@ -3395,8 +3283,39 @@ SubSelect:   SELECT opt_distinct target_list
        ;
 
        /* easy way to return two values. Can someone improve this?  bjm */
-result:  INTO OptTemp opt_table relation_name  { $$ = lcons(makeInteger($2), (List *)$4); }
-       | /*EMPTY*/                             { $$ = lcons(makeInteger(false), NIL); }
+result:  INTO OptTempTableName         { $$ = $2; }
+       | /*EMPTY*/                     { $$ = lcons(makeInteger(false), NIL); }
+       ;
+
+/*
+ * 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
+               { $$ = lcons(makeInteger(TRUE), (List *) $3); }
+           | TEMP opt_table relation_name
+               { $$ = lcons(makeInteger(TRUE), (List *) $3); }
+           | LOCAL TEMPORARY opt_table relation_name
+               { $$ = lcons(makeInteger(TRUE), (List *) $4); }
+           | LOCAL TEMP opt_table relation_name
+               { $$ = lcons(makeInteger(TRUE), (List *) $4); }
+           | GLOBAL TEMPORARY opt_table relation_name
+               {
+                   elog(ERROR, "GLOBAL TEMPORARY TABLE is not currently supported");
+                   $$ = lcons(makeInteger(TRUE), (List *) $4);
+               }
+           | GLOBAL TEMP opt_table relation_name
+               {
+                   elog(ERROR, "GLOBAL TEMPORARY TABLE is not currently supported");
+                   $$ = lcons(makeInteger(TRUE), (List *) $4);
+               }
+           | TABLE relation_name
+               { $$ = lcons(makeInteger(FALSE), (List *) $2); }
+           | relation_name
+               { $$ = lcons(makeInteger(FALSE), (List *) $1); }
        ;
 
 opt_table:  TABLE                              { $$ = TRUE; }
@@ -5274,7 +5193,6 @@ ColId:  IDENT                         { $$ = $1; }
        | CREATEUSER                    { $$ = "createuser"; }
        | CYCLE                         { $$ = "cycle"; }
        | DATABASE                      { $$ = "database"; }
-       | DEFERRABLE                    { $$ = "deferrable"; }
        | DEFERRED                      { $$ = "deferred"; }
        | DELIMITERS                    { $$ = "delimiters"; }
        | DOUBLE                        { $$ = "double"; }
@@ -5288,7 +5206,6 @@ ColId:  IDENT                         { $$ = $1; }
        | INCREMENT                     { $$ = "increment"; }
        | INDEX                         { $$ = "index"; }
        | INHERITS                      { $$ = "inherits"; }
-       | INITIALLY                     { $$ = "initially"; }
        | INSENSITIVE                   { $$ = "insensitive"; }
        | INSTEAD                       { $$ = "instead"; }
        | INTERVAL                      { $$ = "interval"; }
@@ -5335,6 +5252,8 @@ ColId:  IDENT                         { $$ = $1; }
        | STDIN                         { $$ = "stdin"; }
        | STDOUT                        { $$ = "stdout"; }
        | SYSID                         { $$ = "sysid"; }
+       | TEMP                          { $$ = "temp"; }
+       | TEMPORARY                     { $$ = "temporary"; }
        | TIME                          { $$ = "time"; }
        | TIMESTAMP                     { $$ = "timestamp"; }
        | TIMEZONE_HOUR                 { $$ = "timezone_hour"; }
@@ -5371,6 +5290,7 @@ ColLabel:  ColId                      { $$ = $1; }
        | CURRENT_USER                  { $$ = "current_user"; }
        | DEC                           { $$ = "dec"; }
        | DECIMAL                       { $$ = "decimal"; }
+       | DEFERRABLE                    { $$ = "deferrable"; }
        | DO                            { $$ = "do"; }
        | ELSE                          { $$ = "else"; }
        | END_TRANS                     { $$ = "end"; }
@@ -5381,6 +5301,7 @@ ColLabel:  ColId                      { $$ = $1; }
        | FOREIGN                       { $$ = "foreign"; }
        | GLOBAL                        { $$ = "global"; }
        | GROUP                         { $$ = "group"; }
+       | INITIALLY                     { $$ = "initially"; }
        | LISTEN                        { $$ = "listen"; }
        | LOAD                          { $$ = "load"; }
        | LOCAL                         { $$ = "local"; }
index d98398aadecd895c55b3856ba015cc6bdac27795..e90b42c01e8e9edd1bff43135c1b625ed36e4ebc 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.100 2000/02/18 09:29:44 inoue Exp $
+ * $Id: parsenodes.h,v 1.101 2000/03/01 05:18:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -152,32 +152,51 @@ typedef struct CreateStmt
    List       *tableElts;      /* column definitions (list of ColumnDef) */
    List       *inhRelnames;    /* relations to inherit from (list of
                                 * T_String Values) */
-   List       *constraints;    /* list of constraints (Constraint nodes) */
+   List       *constraints;    /* constraints (list of Constraint and
+                                * FkConstraint nodes) */
 } CreateStmt;
 
-typedef enum ConstrType            /* types of constraints */
-{
-   CONSTR_NULL, CONSTR_NOTNULL, CONSTR_DEFAULT, CONSTR_CHECK,
-   CONSTR_PRIMARY, CONSTR_UNIQUE
-} ConstrType;
-
-/*
+/* ----------
+ * Definitions for plain (non-FOREIGN KEY) constraints in CreateStmt
+ *
+ * XXX probably these ought to be unified with FkConstraints at some point?
+ *
  * For constraints that use expressions (CONSTR_DEFAULT, CONSTR_CHECK)
  * we may have the expression in either "raw" form (an untransformed
  * parse tree) or "cooked" form (the nodeToString representation of
  * an executable expression tree), depending on how this Constraint
  * node was created (by parsing, or by inheritance from an existing
  * relation).  We should never have both in the same node!
+ *
+ * Constraint attributes (DEFERRABLE etc) are initially represented as
+ * separate Constraint nodes for simplicity of parsing.  analyze.c makes
+ * a pass through the constraints list to attach the info to the appropriate
+ * FkConstraint node (and, perhaps, someday to other kinds of constraints).
+ * ----------
  */
 
+typedef enum ConstrType            /* types of constraints */
+{
+   CONSTR_NULL,                /* not SQL92, but a lot of people expect it */
+   CONSTR_NOTNULL,
+   CONSTR_DEFAULT,
+   CONSTR_CHECK,
+   CONSTR_PRIMARY,
+   CONSTR_UNIQUE,
+   CONSTR_ATTR_DEFERRABLE,     /* attributes for previous constraint node */
+   CONSTR_ATTR_NOT_DEFERRABLE,
+   CONSTR_ATTR_DEFERRED,
+   CONSTR_ATTR_IMMEDIATE
+} ConstrType;
+
 typedef struct Constraint
 {
    NodeTag     type;
    ConstrType  contype;
-   char       *name;           /* name */
-   Node       *raw_expr;       /* untransformed parse tree */
-   char       *cooked_expr;    /* nodeToString representation */
-   List       *keys;           /* list of primary keys */
+   char       *name;           /* name, or NULL if unnamed */
+   Node       *raw_expr;       /* expr, as untransformed parse tree */
+   char       *cooked_expr;    /* expr, as nodeToString representation */
+   List       *keys;           /* list of primary keys (or unique columns) */
 } Constraint;