OSDN Git Service

Simplify parsing of column constraints by treating constraint attributes
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 1 Mar 2000 05:18:20 +0000 (05:18 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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 a78a057..612481d 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;
@@ -2000,6 +2027,95 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint)
 }
 
 /*
+ * 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
  */
 static void
@@ -2027,4 +2143,3 @@ transformColumnType(ParseState *pstate, ColumnDef *column)
                }
        }
 }
-
index b39f2cf..79bd88f 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 <chr>            operation, TriggerOneEvent
 
 %type <list>   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 <node>   func_return
 %type <boolean>        set_opt
 
-%type <boolean>        TriggerForOpt, TriggerForType, OptTemp, OptTempType, OptTempScope
+%type <boolean>        TriggerForOpt, TriggerForType, OptTemp
 
 %type <list>   for_update_clause, update_list
 %type <boolean>        opt_all
@@ -263,15 +263,12 @@ static void doNegateFloat(Value *v);
 %type <str>            TypeId
 
 %type <node>   TableConstraint
-%type <list>   ColQualList, ColQualifier
-%type <list>   ColQualListWithNull
-%type <node>   ColConstraint, ColConstraintElem, PrimaryKey, NotNull
-%type <node>   DefaultClause, DefaultExpr
-%type <node>   ColConstraintWithNull, ColConstraintElemWithNull
+%type <list>   ColQualList
+%type <node>   ColConstraint, ColConstraintElem, ConstraintAttr
 %type <ival>   key_actions, key_delete, key_update, key_reference
 %type <str>            key_match
-%type <ival>   ConstraintAttribute, DeferrabilityClause,
-                               TimeClause
+%type <ival>   ConstraintAttributeSpec, ConstraintDeferrabilitySpec,
+                               ConstraintTimeSpec
 
 %type <list>   constraints_set_list
 %type <list>   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 d98398a..e90b42c 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;