Commit ab3dc664 authored by Tom Lane's avatar Tom Lane

Simplify parsing of column constraints by treating constraint attributes

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.
parent ea8cadbf
......@@ -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)
}
}
}
This diff is collapsed.
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment