aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_utilcmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_utilcmd.c')
-rw-r--r--src/backend/parser/parse_utilcmd.c206
1 files changed, 151 insertions, 55 deletions
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index b0f6fe4fa61..15d8f3a1a0b 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -83,6 +83,7 @@ typedef struct
List *ckconstraints; /* CHECK constraints */
List *fkconstraints; /* FOREIGN KEY constraints */
List *ixconstraints; /* index-creating constraints */
+ List *nnconstraints; /* NOT NULL constraints */
List *likeclauses; /* LIKE clauses that need post-processing */
List *extstats; /* cloned extended statistics */
List *blist; /* "before list" of things to do before
@@ -244,6 +245,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt.ckconstraints = NIL;
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
+ cxt.nnconstraints = NIL;
cxt.likeclauses = NIL;
cxt.extstats = NIL;
cxt.blist = NIL;
@@ -348,6 +350,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
*/
stmt->tableElts = cxt.columns;
stmt->constraints = cxt.ckconstraints;
+ stmt->nnconstraints = cxt.nnconstraints;
result = lappend(cxt.blist, stmt);
result = list_concat(result, cxt.alist);
@@ -537,6 +540,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
bool saw_default;
bool saw_identity;
bool saw_generated;
+ bool need_notnull = false;
ListCell *clist;
cxt->columns = lappend(cxt->columns, column);
@@ -634,10 +638,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
constraint->cooked_expr = NULL;
column->constraints = lappend(column->constraints, constraint);
- constraint = makeNode(Constraint);
- constraint->contype = CONSTR_NOTNULL;
- constraint->location = -1;
- column->constraints = lappend(column->constraints, constraint);
+ /* have a NOT NULL constraint added later */
+ need_notnull = true;
}
/* Process column constraints, if any... */
@@ -655,7 +657,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
switch (constraint->contype)
{
case CONSTR_NULL:
- if (saw_nullable && column->is_not_null)
+ if ((saw_nullable && column->is_not_null) || need_notnull)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
@@ -667,15 +669,58 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
break;
case CONSTR_NOTNULL:
- if (saw_nullable && !column->is_not_null)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
- column->colname, cxt->relation->relname),
- parser_errposition(cxt->pstate,
- constraint->location)));
- column->is_not_null = true;
- saw_nullable = true;
+
+ /*
+ * For NOT NULL declarations, we need to mark the column as
+ * not nullable, and set things up to have a CHECK constraint
+ * created. Also, duplicate NOT NULL declarations are not
+ * allowed.
+ */
+ if (saw_nullable)
+ {
+ if (!column->is_not_null)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
+ column->colname, cxt->relation->relname),
+ parser_errposition(cxt->pstate,
+ constraint->location)));
+ else
+ ereport(ERROR,
+ errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("redundant NOT NULL declarations for column \"%s\" of table \"%s\"",
+ column->colname, cxt->relation->relname),
+ parser_errposition(cxt->pstate,
+ constraint->location));
+ }
+
+ /*
+ * If this is the first time we see this column being marked
+ * not null, keep track to later add a NOT NULL constraint.
+ */
+ if (!column->is_not_null)
+ {
+ Constraint *notnull;
+
+ column->is_not_null = true;
+ saw_nullable = true;
+
+ notnull = makeNode(Constraint);
+ notnull->contype = CONSTR_NOTNULL;
+ notnull->conname = constraint->conname;
+ notnull->deferrable = false;
+ notnull->initdeferred = false;
+ notnull->location = -1;
+ notnull->colname = column->colname;
+ notnull->skip_validation = false;
+ notnull->initially_valid = true;
+
+ cxt->nnconstraints = lappend(cxt->nnconstraints, notnull);
+
+ /* Don't need this anymore, if we had it */
+ need_notnull = false;
+ }
+
break;
case CONSTR_DEFAULT:
@@ -725,16 +770,19 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
column->identity = constraint->generated_when;
saw_identity = true;
- /* An identity column is implicitly NOT NULL */
- if (saw_nullable && !column->is_not_null)
+ /*
+ * Identity columns are always NOT NULL, but we may have a
+ * constraint already.
+ */
+ if (!saw_nullable)
+ need_notnull = true;
+ else if (!column->is_not_null)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
column->colname, cxt->relation->relname),
parser_errposition(cxt->pstate,
constraint->location)));
- column->is_not_null = true;
- saw_nullable = true;
break;
}
@@ -758,6 +806,11 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
case CONSTR_CHECK:
cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
+
+ /*
+ * XXX If the user says CHECK (IS NOT NULL), should we turn
+ * that into a regular NOT NULL constraint?
+ */
break;
case CONSTR_PRIMARY:
@@ -841,6 +894,29 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
}
/*
+ * If we need a NOT NULL constraint for SERIAL or IDENTITY, and one was
+ * not explicitly specified, add one now.
+ */
+ if (need_notnull && !(saw_nullable && column->is_not_null))
+ {
+ Constraint *notnull;
+
+ column->is_not_null = true;
+
+ notnull = makeNode(Constraint);
+ notnull->contype = CONSTR_NOTNULL;
+ notnull->conname = NULL;
+ notnull->deferrable = false;
+ notnull->initdeferred = false;
+ notnull->location = -1;
+ notnull->colname = column->colname;
+ notnull->skip_validation = false;
+ notnull->initially_valid = true;
+
+ cxt->nnconstraints = lappend(cxt->nnconstraints, notnull);
+ }
+
+ /*
* If needed, generate ALTER FOREIGN TABLE ALTER COLUMN statement to add
* per-column foreign data wrapper options to this column after creation.
*/
@@ -915,6 +991,10 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
break;
+ case CONSTR_NOTNULL:
+ cxt->nnconstraints = lappend(cxt->nnconstraints, constraint);
+ break;
+
case CONSTR_FOREIGN:
if (cxt->isforeign)
ereport(ERROR,
@@ -926,7 +1006,6 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
break;
case CONSTR_NULL:
- case CONSTR_NOTNULL:
case CONSTR_DEFAULT:
case CONSTR_ATTR_DEFERRABLE:
case CONSTR_ATTR_NOT_DEFERRABLE:
@@ -962,6 +1041,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
AclResult aclresult;
char *comment;
ParseCallbackState pcbstate;
+ bool process_notnull_constraints;
setup_parser_errposition_callback(&pcbstate, cxt->pstate,
table_like_clause->relation->location);
@@ -1043,6 +1123,8 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
def->inhcount = 0;
def->is_local = true;
def->is_not_null = attribute->attnotnull;
+ if (attribute->attnotnull)
+ process_notnull_constraints = true;
def->is_from_type = false;
def->storage = 0;
def->raw_default = NULL;
@@ -1124,14 +1206,19 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
* we don't yet know what column numbers the copied columns will have in
* the finished table. If any of those options are specified, add the
* LIKE clause to cxt->likeclauses so that expandTableLikeClause will be
- * called after we do know that. Also, remember the relation OID so that
+ * called after we do know that; in addition, do that if there are any NOT
+ * NULL constraints, because those must be propagated even if not
+ * explicitly requested.
+ *
+ * In order for this to work, we remember the relation OID so that
* expandTableLikeClause is certain to open the same table.
*/
- if (table_like_clause->options &
+ if ((table_like_clause->options &
(CREATE_TABLE_LIKE_DEFAULTS |
CREATE_TABLE_LIKE_GENERATED |
CREATE_TABLE_LIKE_CONSTRAINTS |
- CREATE_TABLE_LIKE_INDEXES))
+ CREATE_TABLE_LIKE_INDEXES)) ||
+ process_notnull_constraints)
{
table_like_clause->relationOid = RelationGetRelid(relation);
cxt->likeclauses = lappend(cxt->likeclauses, table_like_clause);
@@ -1203,6 +1290,7 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause)
TupleConstr *constr;
AttrMap *attmap;
char *comment;
+ ListCell *lc;
/*
* Open the relation referenced by the LIKE clause. We should still have
@@ -1383,6 +1471,20 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause)
}
/*
+ * Copy NOT NULL constraints, too (these do not require any option to have
+ * been given).
+ */
+ foreach(lc, RelationGetNotNullConstraints(relation, false))
+ {
+ AlterTableCmd *atsubcmd;
+
+ atsubcmd = makeNode(AlterTableCmd);
+ atsubcmd->subtype = AT_AddConstraint;
+ atsubcmd->def = (Node *) lfirst_node(Constraint, lc);
+ atsubcmds = lappend(atsubcmds, atsubcmd);
+ }
+
+ /*
* If we generated any ALTER TABLE actions above, wrap them into a single
* ALTER TABLE command. Stick it at the front of the result, so it runs
* before any CommentStmts we made above.
@@ -2059,10 +2161,12 @@ transformIndexConstraints(CreateStmtContext *cxt)
ListCell *lc;
/*
- * Run through the constraints that need to generate an index. For PRIMARY
- * KEY, mark each column as NOT NULL and create an index. For UNIQUE or
- * EXCLUDE, create an index as for PRIMARY KEY, but do not insist on NOT
- * NULL.
+ * Run through the constraints that need to generate an index, and do so.
+ *
+ * For PRIMARY KEY, in addition we set each column's attnotnull flag true.
+ * We do not create a separate CHECK (IS NOT NULL) constraint, as that
+ * would be redundant: the PRIMARY KEY constraint itself fulfills that
+ * role. Other constraint types don't need any NOT NULL markings.
*/
foreach(lc, cxt->ixconstraints)
{
@@ -2136,9 +2240,7 @@ transformIndexConstraints(CreateStmtContext *cxt)
}
/*
- * Now append all the IndexStmts to cxt->alist. If we generated an ALTER
- * TABLE SET NOT NULL statement to support a primary key, it's already in
- * cxt->alist.
+ * Now append all the IndexStmts to cxt->alist.
*/
cxt->alist = list_concat(cxt->alist, finalindexlist);
}
@@ -2146,12 +2248,10 @@ transformIndexConstraints(CreateStmtContext *cxt)
/*
* transformIndexConstraint
* Transform one UNIQUE, PRIMARY KEY, or EXCLUDE constraint for
- * transformIndexConstraints.
+ * transformIndexConstraints. An IndexStmt is returned.
*
- * We return an IndexStmt. For a PRIMARY KEY constraint, we additionally
- * produce NOT NULL constraints, either by marking ColumnDefs in cxt->columns
- * as is_not_null or by adding an ALTER TABLE SET NOT NULL command to
- * cxt->alist.
+ * For a PRIMARY KEY constraint, we additionally force the columns to be
+ * marked as NOT NULL, without producing a CHECK (IS NOT NULL) constraint.
*/
static IndexStmt *
transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
@@ -2417,7 +2517,6 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
{
char *key = strVal(lfirst(lc));
bool found = false;
- bool forced_not_null = false;
ColumnDef *column = NULL;
ListCell *columns;
IndexElem *iparam;
@@ -2438,13 +2537,14 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
* column is defined in the new table. For PRIMARY KEY, we
* can apply the NOT NULL constraint cheaply here ... unless
* the column is marked is_from_type, in which case marking it
- * here would be ineffective (see MergeAttributes).
+ * here would be ineffective (see MergeAttributes). Note that
+ * this isn't effective in ALTER TABLE either, unless the
+ * column is being added in the same command.
*/
if (constraint->contype == CONSTR_PRIMARY &&
!column->is_from_type)
{
column->is_not_null = true;
- forced_not_null = true;
}
}
else if (SystemAttributeByName(key) != NULL)
@@ -2487,14 +2587,6 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
if (strcmp(key, inhname) == 0)
{
found = true;
-
- /*
- * It's tempting to set forced_not_null if the
- * parent column is already NOT NULL, but that
- * seems unsafe because the column's NOT NULL
- * marking might disappear between now and
- * execution. Do the runtime check to be safe.
- */
break;
}
}
@@ -2548,15 +2640,11 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
index->indexParams = lappend(index->indexParams, iparam);
- /*
- * For a primary-key column, also create an item for ALTER TABLE
- * SET NOT NULL if we couldn't ensure it via is_not_null above.
- */
- if (constraint->contype == CONSTR_PRIMARY && !forced_not_null)
+ if (constraint->contype == CONSTR_PRIMARY)
{
AlterTableCmd *notnullcmd = makeNode(AlterTableCmd);
- notnullcmd->subtype = AT_SetNotNull;
+ notnullcmd->subtype = AT_SetAttNotNull;
notnullcmd->name = pstrdup(key);
notnullcmds = lappend(notnullcmds, notnullcmd);
}
@@ -3328,6 +3416,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
cxt.isalter = true;
cxt.columns = NIL;
cxt.ckconstraints = NIL;
+ cxt.nnconstraints = NIL;
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.likeclauses = NIL;
@@ -3571,8 +3660,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
/*
* We assume here that cxt.alist contains only IndexStmts and possibly
- * ALTER TABLE SET NOT NULL statements generated from primary key
- * constraints. We absorb the subcommands of the latter directly.
+ * AT_SetAttNotNull statements generated from primary key constraints.
+ * We absorb the subcommands of the latter directly.
*/
if (IsA(istmt, IndexStmt))
{
@@ -3600,14 +3689,21 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
{
newcmd = makeNode(AlterTableCmd);
newcmd->subtype = AT_AddConstraint;
- newcmd->def = (Node *) lfirst(l);
+ newcmd->def = (Node *) lfirst_node(Constraint, l);
newcmds = lappend(newcmds, newcmd);
}
foreach(l, cxt.fkconstraints)
{
newcmd = makeNode(AlterTableCmd);
newcmd->subtype = AT_AddConstraint;
- newcmd->def = (Node *) lfirst(l);
+ newcmd->def = (Node *) lfirst_node(Constraint, l);
+ newcmds = lappend(newcmds, newcmd);
+ }
+ foreach(l, cxt.nnconstraints)
+ {
+ newcmd = makeNode(AlterTableCmd);
+ newcmd->subtype = AT_AddConstraint;
+ newcmd->def = (Node *) lfirst_node(Constraint, l);
newcmds = lappend(newcmds, newcmd);
}