aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/analyze.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r--src/backend/parser/analyze.c147
1 files changed, 131 insertions, 16 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a78a0571fe4..612481deabc 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -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)
}
}
}
-