*** a/src/backend/commands/tablecmds.c --- b/src/backend/commands/tablecmds.c *************** *** 2655,2660 **** AlterTableGetLockLevel(List *cmds) --- 2655,2661 ---- * These subcommands affect write operations only. */ case AT_ColumnDefault: + case AT_ColumnConstraint: case AT_ProcessedConstraint: /* becomes AT_AddConstraint */ case AT_AddConstraintRecurse: /* becomes AT_AddConstraint */ case AT_EnableTrig: *************** *** 2853,2858 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, --- 2854,2865 ---- /* No command-specific prep needed */ pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP; break; + case AT_ColumnConstraint: /* ALTER COLUMN ADD CONSTRAINT */ + /* should have been discarded by transformAlterTableStmt */ + pass = AT_PASS_ADD_CONSTR; /* keep compiler quiet */ + elog(ERROR, "unrecognized alter table type: %d", + (int) cmd->subtype); + break; case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); *************** *** 3106,3111 **** ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, --- 3113,3123 ---- case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */ ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode); break; + case AT_ColumnConstraint: /* ALTER COLUMN ADD CONSTRAINT */ + /* should have been discarded by transformAlterTableStmt */ + elog(ERROR, "unrecognized alter table type: %d", + (int) cmd->subtype); + break; case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ ATExecDropNotNull(rel, cmd->name, lockmode); break; *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** *** 1680,1685 **** alter_table_cmd: --- 1680,1699 ---- n->def = $4; $$ = (Node *)n; } + /* ALTER TABLE ALTER [COLUMN] ADD CONSTRAINT ... */ + | ALTER opt_column ColId ADD_P ColQualList + { + AlterTableCmd *n = makeNode(AlterTableCmd); + CollateClause *collate; + n->subtype = AT_ColumnConstraint; + n->name = $3; + SplitColQualList($5, (List **) &n->def, &collate, yyscanner); + if (collate != NULL) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("COLLATE clause not allowed in ALTER TABLE / ALTER COLUMN"))); + $$ = (Node *)n; + } /* ALTER TABLE ALTER [COLUMN] DROP NOT NULL */ | ALTER opt_column ColId DROP NOT NULL_P { *** a/src/backend/parser/parse_utilcmd.c --- b/src/backend/parser/parse_utilcmd.c *************** *** 78,83 **** typedef struct --- 78,84 ---- List *ckconstraints; /* CHECK constraints */ List *fkconstraints; /* FOREIGN KEY constraints */ List *ixconstraints; /* index-creating constraints */ + List *nnconstraints; /* NOT NULL constraints */ List *inh_indexes; /* cloned indexes from INCLUDING INDEXES */ List *blist; /* "before list" of things to do before * creating the table */ *************** *** 120,125 **** static IndexStmt *transformIndexConstraint(Constraint *constraint, --- 121,128 ---- static void transformFKConstraints(CreateStmtContext *cxt, bool skipValidation, bool isAddConstraint); + static void transformConstraintItems(CreateStmtContext *cxt, + ColumnDef *column); static void transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList); static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column); *************** *** 293,302 **** static void transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) { bool is_serial; - bool saw_nullable; - bool saw_default; - Constraint *constraint; - ListCell *clist; cxt->columns = lappend(cxt->columns, column); --- 296,301 ---- *************** *** 360,365 **** transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) --- 359,365 ---- CreateSeqStmt *seqstmt; AlterSeqStmt *altseqstmt; List *attnamelist; + Constraint *constraint; /* * Determine namespace and name to use for the sequence. *************** *** 471,476 **** transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) --- 471,493 ---- } /* Process column constraints, if any... */ + transformConstraintItems(cxt, column); + } + + /* + * transformConstraintItems - + * Transform constraints as found on a column definition. + * + * in addition to transformColumnDefinition, this is used by ALTER TABLE + * ADD CONSTRAINT. + */ + static void + transformConstraintItems(CreateStmtContext *cxt, ColumnDef *column) + { + bool saw_nullable; + bool saw_default; + ListCell *clist; + transformConstraintAttrs(cxt, column->constraints); saw_nullable = false; *************** *** 478,484 **** transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) foreach(clist, column->constraints) { ! constraint = lfirst(clist); Assert(IsA(constraint, Constraint)); switch (constraint->contype) --- 495,501 ---- foreach(clist, column->constraints) { ! Constraint *constraint = lfirst(clist); Assert(IsA(constraint, Constraint)); switch (constraint->contype) *************** *** 491,497 **** transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) column->colname, cxt->relation->relname), parser_errposition(cxt->pstate, constraint->location))); ! column->is_not_null = FALSE; saw_nullable = true; break; --- 508,514 ---- column->colname, cxt->relation->relname), parser_errposition(cxt->pstate, constraint->location))); ! column->is_not_null = false; saw_nullable = true; break; *************** *** 503,509 **** transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) column->colname, cxt->relation->relname), parser_errposition(cxt->pstate, constraint->location))); ! column->is_not_null = TRUE; saw_nullable = true; break; --- 520,528 ---- column->colname, cxt->relation->relname), parser_errposition(cxt->pstate, constraint->location))); ! constraint->colname = column->colname; ! cxt->nnconstraints = lappend(cxt->nnconstraints, constraint); ! column->is_not_null = true; saw_nullable = true; break; *************** *** 2287,2294 **** transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) /* * The only subtypes that currently require parse transformation handling ! * are ADD COLUMN and ADD CONSTRAINT. These largely re-use code from ! * CREATE TABLE. */ foreach(lcmd, stmt->cmds) { --- 2306,2313 ---- /* * The only subtypes that currently require parse transformation handling ! * are ADD COLUMN, ADD CONSTRAINT and ALTER COLUMN ADD CONSTRAINT. These ! * largely re-use code from CREATE TABLE. */ foreach(lcmd, stmt->cmds) { *************** *** 2346,2351 **** transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) --- 2365,2384 ---- newcmds = lappend(newcmds, cmd); break; + case AT_ColumnConstraint: + /* + * The original ColumnConstraint node doesn't go to newcmds + */ + { + ColumnDef *phony = makeNode(ColumnDef); + phony->colname = cmd->name; + phony->constraints = (List *) cmd->def; + transformConstraintItems(&cxt, phony); + if (phony->raw_default) + ereport(ERROR, + (errmsg("DEFAULT clause not supported in ALTER TABLE / ALTER COLUMN"))); + } + break; default: newcmds = lappend(newcmds, cmd); break; *************** *** 2384,2390 **** transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) } cxt.alist = NIL; ! /* Append any CHECK or FK constraints to the commands list */ foreach(l, cxt.ckconstraints) { newcmd = makeNode(AlterTableCmd); --- 2417,2423 ---- } cxt.alist = NIL; ! /* Append any CHECK, NOT NULL or FK constraints to the commands list */ foreach(l, cxt.ckconstraints) { newcmd = makeNode(AlterTableCmd); *************** *** 2399,2404 **** transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) --- 2432,2444 ---- newcmd->def = (Node *) lfirst(l); newcmds = lappend(newcmds, newcmd); } + foreach(l, cxt.nnconstraints) + { + newcmd = makeNode(AlterTableCmd); + newcmd->subtype = AT_SetNotNull; + newcmd->name = ((Constraint *) lfirst(l))->colname; + newcmds = lappend(newcmds, newcmd); + } /* Close rel but keep lock */ relation_close(rel, NoLock); *** a/src/include/nodes/parsenodes.h --- b/src/include/nodes/parsenodes.h *************** *** 1177,1182 **** typedef enum AlterTableType --- 1177,1183 ---- AT_AddColumnRecurse, /* internal to commands/tablecmds.c */ AT_AddColumnToView, /* implicitly via CREATE OR REPLACE VIEW */ AT_ColumnDefault, /* alter column default */ + AT_ColumnConstraint, /* alter column add constraint */ AT_DropNotNull, /* alter column drop not null */ AT_SetNotNull, /* alter column set not null */ AT_SetStatistics, /* alter column set statistics */ *************** *** 1545,1550 **** typedef struct Constraint --- 1546,1554 ---- char fk_upd_action; /* ON UPDATE action */ char fk_del_action; /* ON DELETE action */ + /* Fields used for a NOT NULL constraints: */ + char *colname; /* column name */ + /* Fields used for constraints that allow a NOT VALID specification */ bool skip_validation; /* skip validation of existing rows? */ bool initially_valid; /* mark the new constraint as valid? */