66 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
77 * Portions Copyright (c) 1994, Regents of the University of California
88 *
9- *$Id: analyze.c,v 1.138 2000/02/29 12:28:25 wieck Exp $
9+ *$Id: analyze.c,v 1.139 2000/03/01 05:18:20 tgl Exp $
1010 *
1111 *-------------------------------------------------------------------------
1212 */
@@ -44,6 +44,7 @@ static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt);
4444
4545static void transformForUpdate (Query * qry ,List * forUpdate );
4646static void transformFkeyGetPrimaryKey (FkConstraint * fkconstraint );
47+ static void transformConstraintAttrs (List * constraintList );
4748static void transformColumnType (ParseState * pstate ,ColumnDef * column );
4849
4950/* kluge to return extra info from transformCreateStmt() */
@@ -589,6 +590,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
589590IndexStmt * index ,
590591* pkey = NULL ;
591592IndexElem * iparam ;
593+ bool saw_nullable ;
592594
593595q = makeNode (Query );
594596q -> commandType = CMD_UTILITY ;
@@ -621,6 +623,12 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
621623FuncCall * funccallnode ;
622624CreateSeqStmt * sequence ;
623625
626+ /*
627+ * Create appropriate constraints for SERIAL. We do this
628+ * in full, rather than shortcutting, so that we will
629+ * detect any conflicting constraints the user wrote
630+ * (like a different DEFAULT).
631+ */
624632sname = makeObjectName (stmt -> relname ,column -> colname ,
625633"seq" );
626634/*
@@ -644,27 +652,37 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
644652constraint -> raw_expr = (Node * )funccallnode ;
645653constraint -> cooked_expr = NULL ;
646654constraint -> keys = NULL ;
647-
648- column -> constraints = lappend ( column -> constraints , constraint );
655+ column -> constraints = lappend ( column -> constraints ,
656+ constraint );
649657
650658constraint = makeNode (Constraint );
651659constraint -> contype = CONSTR_UNIQUE ;
652660constraint -> name = makeObjectName (stmt -> relname ,
653661column -> colname ,
654662"key" );
655- column -> constraints = lappend (column -> constraints ,constraint );
663+ column -> constraints = lappend (column -> constraints ,
664+ constraint );
665+
666+ constraint = makeNode (Constraint );
667+ constraint -> contype = CONSTR_NOTNULL ;
668+ column -> constraints = lappend (column -> constraints ,
669+ constraint );
656670
657671sequence = makeNode (CreateSeqStmt );
658672sequence -> seqname = pstrdup (sname );
659673sequence -> options = NIL ;
660674
661675elog (NOTICE ,"CREATE TABLE will create implicit sequence '%s' for SERIAL column '%s.%s'" ,
662- sequence -> seqname ,stmt -> relname ,column -> colname );
676+ sequence -> seqname ,stmt -> relname ,column -> colname );
663677
664678blist = lcons (sequence ,NIL );
665679}
666680
667681/* Process column constraints, if any... */
682+ transformConstraintAttrs (column -> constraints );
683+
684+ saw_nullable = false;
685+
668686foreach (clist ,column -> constraints )
669687{
670688constraint = lfirst (clist );
@@ -676,7 +694,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
676694 * to be processed later.
677695 * ----------
678696 */
679- if (nodeTag (constraint ) == T_FkConstraint )
697+ if (IsA (constraint , FkConstraint ) )
680698{
681699Ident * id = makeNode (Ident );
682700id -> name = column -> colname ;
@@ -693,23 +711,19 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
693711switch (constraint -> contype )
694712{
695713case CONSTR_NULL :
696-
697- /*
698- * We should mark this explicitly, so we
699- * can tell if NULL and NOT NULL are both
700- * specified
701- */
702- if (column -> is_not_null )
714+ if (saw_nullable && column -> is_not_null )
703715elog (ERROR ,"CREATE TABLE/(NOT) NULL conflicting declaration"
704716" for '%s.%s'" ,stmt -> relname ,column -> colname );
705717column -> is_not_null = FALSE;
718+ saw_nullable = true;
706719break ;
707720
708721case CONSTR_NOTNULL :
709- if (column -> is_not_null )
710- elog (ERROR ,"CREATE TABLE/NOT NULLalready specified "
722+ if (saw_nullable && ! column -> is_not_null )
723+ elog (ERROR ,"CREATE TABLE/( NOT) NULLconflicting declaration "
711724" for '%s.%s'" ,stmt -> relname ,column -> colname );
712725column -> is_not_null = TRUE;
726+ saw_nullable = true;
713727break ;
714728
715729case CONSTR_DEFAULT :
@@ -742,6 +756,13 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
742756constraints = lappend (constraints ,constraint );
743757break ;
744758
759+ case CONSTR_ATTR_DEFERRABLE :
760+ case CONSTR_ATTR_NOT_DEFERRABLE :
761+ case CONSTR_ATTR_DEFERRED :
762+ case CONSTR_ATTR_IMMEDIATE :
763+ /* transformConstraintAttrs took care of these */
764+ break ;
765+
745766default :
746767elog (ERROR ,"parser: unrecognized constraint (internal error)" );
747768break ;
@@ -767,10 +788,16 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
767788constraints = lappend (constraints ,constraint );
768789break ;
769790
791+ case CONSTR_NULL :
770792case CONSTR_NOTNULL :
771793case CONSTR_DEFAULT :
794+ case CONSTR_ATTR_DEFERRABLE :
795+ case CONSTR_ATTR_NOT_DEFERRABLE :
796+ case CONSTR_ATTR_DEFERRED :
797+ case CONSTR_ATTR_IMMEDIATE :
772798elog (ERROR ,"parser: illegal context for constraint (internal error)" );
773799break ;
800+
774801default :
775802elog (ERROR ,"parser: unrecognized constraint (internal error)" );
776803break ;
@@ -1999,6 +2026,95 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint)
19992026heap_close (pkrel ,AccessShareLock );
20002027}
20012028
2029+ /*
2030+ * Preprocess a list of column constraint clauses
2031+ * to attach constraint attributes to their primary constraint nodes
2032+ * and detect inconsistent/misplaced constraint attributes.
2033+ *
2034+ * NOTE: currently, attributes are only supported for FOREIGN KEY primary
2035+ * constraints, but someday they ought to be supported for other constraints.
2036+ */
2037+ static void
2038+ transformConstraintAttrs (List * constraintList )
2039+ {
2040+ Node * lastprimarynode = NULL ;
2041+ bool saw_deferrability = false;
2042+ bool saw_initially = false;
2043+ List * clist ;
2044+
2045+ foreach (clist ,constraintList )
2046+ {
2047+ Node * node = lfirst (clist );
2048+
2049+ if (!IsA (node ,Constraint ))
2050+ {
2051+ lastprimarynode = node ;
2052+ /* reset flags for new primary node */
2053+ saw_deferrability = false;
2054+ saw_initially = false;
2055+ }
2056+ else
2057+ {
2058+ Constraint * con = (Constraint * )node ;
2059+
2060+ switch (con -> contype )
2061+ {
2062+ case CONSTR_ATTR_DEFERRABLE :
2063+ if (lastprimarynode == NULL ||
2064+ !IsA (lastprimarynode ,FkConstraint ))
2065+ elog (ERROR ,"Misplaced DEFERRABLE clause" );
2066+ if (saw_deferrability )
2067+ elog (ERROR ,"Multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed" );
2068+ saw_deferrability = true;
2069+ ((FkConstraint * )lastprimarynode )-> deferrable = true;
2070+ break ;
2071+ case CONSTR_ATTR_NOT_DEFERRABLE :
2072+ if (lastprimarynode == NULL ||
2073+ !IsA (lastprimarynode ,FkConstraint ))
2074+ elog (ERROR ,"Misplaced NOT DEFERRABLE clause" );
2075+ if (saw_deferrability )
2076+ elog (ERROR ,"Multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed" );
2077+ saw_deferrability = true;
2078+ ((FkConstraint * )lastprimarynode )-> deferrable = false;
2079+ if (saw_initially &&
2080+ ((FkConstraint * )lastprimarynode )-> initdeferred )
2081+ elog (ERROR ,"INITIALLY DEFERRED constraint must be DEFERRABLE" );
2082+ break ;
2083+ case CONSTR_ATTR_DEFERRED :
2084+ if (lastprimarynode == NULL ||
2085+ !IsA (lastprimarynode ,FkConstraint ))
2086+ elog (ERROR ,"Misplaced INITIALLY DEFERRED clause" );
2087+ if (saw_initially )
2088+ elog (ERROR ,"Multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed" );
2089+ saw_initially = true;
2090+ ((FkConstraint * )lastprimarynode )-> initdeferred = true;
2091+ /* If only INITIALLY DEFERRED appears, assume DEFERRABLE */
2092+ if (!saw_deferrability )
2093+ ((FkConstraint * )lastprimarynode )-> deferrable = true;
2094+ else if (! ((FkConstraint * )lastprimarynode )-> deferrable )
2095+ elog (ERROR ,"INITIALLY DEFERRED constraint must be DEFERRABLE" );
2096+ break ;
2097+ case CONSTR_ATTR_IMMEDIATE :
2098+ if (lastprimarynode == NULL ||
2099+ !IsA (lastprimarynode ,FkConstraint ))
2100+ elog (ERROR ,"Misplaced INITIALLY IMMEDIATE clause" );
2101+ if (saw_initially )
2102+ elog (ERROR ,"Multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed" );
2103+ saw_initially = true;
2104+ ((FkConstraint * )lastprimarynode )-> initdeferred = false;
2105+ break ;
2106+ default :
2107+ /* Otherwise it's not an attribute */
2108+ lastprimarynode = node ;
2109+ /* reset flags for new primary node */
2110+ saw_deferrability = false;
2111+ saw_initially = false;
2112+ break ;
2113+ }
2114+ }
2115+ }
2116+ }
2117+
20022118/*
20032119 * Special handling of type definition for a column
20042120 */
@@ -2027,4 +2143,3 @@ transformColumnType(ParseState *pstate, ColumnDef *column)
20272143}
20282144}
20292145}
2030-