*** a/doc/src/sgml/ref/alter_table.sgml
--- b/doc/src/sgml/ref/alter_table.sgml
***************
*** 37,42 **** ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
--- 37,43 ----
      ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> [ SET DATA ] TYPE <replaceable class="PARAMETER">data_type</replaceable> [ COLLATE <replaceable class="PARAMETER">collation</replaceable> ] [ USING <replaceable class="PARAMETER">expression</replaceable> ]
      ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET DEFAULT <replaceable class="PARAMETER">expression</replaceable>
      ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> DROP DEFAULT
+     ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> ADD <replaceable class="PARAMETER">column_constraint</replaceable>
      ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET | DROP } NOT NULL
      ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STATISTICS <replaceable class="PARAMETER">integer</replaceable>
      ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET ( <replaceable class="PARAMETER">attribute_option</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
***************
*** 145,150 **** ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
--- 146,164 ----
     </varlistentry>
  
     <varlistentry>
+     <term><literal>ADD</literal> <replaceable>column_constraint [, ...]</replaceable></term>
+     <listitem>
+      <para>
+       This form adds new column constraints, using the same syntax as
+       <xref linkend="SQL-CREATETABLE">.  Note: while it is possible to specify
+       names for <literal>NOT NULL</literal> constraints this way, they are
+       currently discarded silently.
+      </note>
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
      <term><literal>SET</literal>/<literal>DROP NOT NULL</literal></term>
      <listitem>
       <para>
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 1680,1685 **** alter_table_cmd:
--- 1680,1700 ----
  					n->def = $4;
  					$$ = (Node *)n;
  				}
+ 			/* ALTER TABLE <name> ALTER [COLUMN] <colname> ADD CONSTRAINT ... */
+ 			| ALTER opt_column ColId ADD_P ColQualList
+ 				{
+ 					AlterTableCmd *n = makeNode(AlterTableCmd);
+ 					ColumnDef *col = makeNode(ColumnDef);
+ 					n->subtype = AT_AddConstraint;
+ 					col->colname = $3;
+ 					SplitColQualList($5, &col->constraints, &col->collClause, yyscanner);
+ 					if (col->collClause != NULL)
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 								 errmsg("COLLATE clause not allowed in ALTER TABLE / ALTER COLUMN")));
+ 					n->def = (Node *) col;
+ 					$$ = (Node *)n;
+ 				}
  			/* ALTER TABLE <name> ALTER [COLUMN] <colname> 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 transformColumnConstraints(CreateStmtContext *cxt,
+ 						   ColumnDef *column);
  static void transformConstraintAttrs(CreateStmtContext *cxt,
  						 List *constraintList);
  static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column);
***************
*** 212,217 **** transformCreateStmt(CreateStmt *stmt, const char *queryString)
--- 215,221 ----
  	cxt.ckconstraints = NIL;
  	cxt.fkconstraints = NIL;
  	cxt.ixconstraints = NIL;
+ 	cxt.nnconstraints = NIL;
  	cxt.inh_indexes = NIL;
  	cxt.blist = NIL;
  	cxt.alist = NIL;
***************
*** 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);
  
--- 297,302 ----
***************
*** 360,365 **** transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
--- 360,366 ----
  		CreateSeqStmt *seqstmt;
  		AlterSeqStmt *altseqstmt;
  		List	   *attnamelist;
+ 		Constraint *constraint;
  
  		/*
  		 * Determine namespace and name to use for the sequence.
***************
*** 471,476 **** transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
--- 472,494 ----
  	}
  
  	/* Process column constraints, if any... */
+ 	transformColumnConstraints(cxt, column);
+ }
+ 
+ /*
+  * transformColumnConstraints -
+  * 		Transform constraints as found on a column definition.
+  *
+  * in addition to transformColumnDefinition, this is used by ALTER TABLE
+  * ADD CONSTRAINT.
+  */
+ static void
+ transformColumnConstraints(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)
--- 496,502 ----
  
  	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;
  
--- 509,515 ----
  									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;
  
--- 521,529 ----
  									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;
  
***************
*** 2280,2285 **** transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
--- 2300,2306 ----
  	cxt.ckconstraints = NIL;
  	cxt.fkconstraints = NIL;
  	cxt.ixconstraints = NIL;
+ 	cxt.nnconstraints = NIL;
  	cxt.inh_indexes = NIL;
  	cxt.blist = NIL;
  	cxt.alist = NIL;
***************
*** 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)
  	{
--- 2308,2315 ----
  
  	/*
  	 * 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)
  	{
***************
*** 2325,2331 **** transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
  				/*
  				 * The original AddConstraint cmd node doesn't go to newcmds
  				 */
! 				if (IsA(cmd->def, Constraint))
  				{
  					transformTableConstraint(&cxt, (Constraint *) cmd->def);
  					if (((Constraint *) cmd->def)->contype == CONSTR_FOREIGN)
--- 2346,2362 ----
  				/*
  				 * The original AddConstraint cmd node doesn't go to newcmds
  				 */
! 				if (IsA(cmd->def, ColumnDef))
! 				{
! 					transformColumnConstraints(&cxt, (ColumnDef *) cmd->def);
! 					if (((ColumnDef *) cmd->def)->raw_default)
! 						ereport(ERROR,
! 								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 								 errmsg("DEFAULT clause not allowed in ALTER TABLE / ALTER COLUMN")));
! 					if (list_length(cxt.fkconstraints) > 0)
! 						skipValidation = false;
! 				}
! 				else if (IsA(cmd->def, Constraint))
  				{
  					transformTableConstraint(&cxt, (Constraint *) cmd->def);
  					if (((Constraint *) cmd->def)->contype == CONSTR_FOREIGN)
***************
*** 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);
--- 2415,2421 ----
  	}
  	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)
--- 2430,2442 ----
  		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
***************
*** 1545,1550 **** typedef struct Constraint
--- 1545,1553 ----
  	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? */
*** a/src/test/regress/expected/alter_table.out
--- b/src/test/regress/expected/alter_table.out
***************
*** 667,672 **** insert into atacc1 (test,test2) values (4,5);
--- 667,706 ----
  insert into atacc1 (test,test2) values (5,4);
  insert into atacc1 (test,test2) values (5,5);
  drop table atacc1;
+ -- try the column constraint variant
+ create table atacc0 (test int, test2 int);
+ alter table atacc0 alter column test add constraint pk primary key;
+ NOTICE:  ALTER TABLE / ADD PRIMARY KEY will create implicit index "pk" for table "atacc0"
+ alter table atacc0 alter column test2 add constraint uq unique;
+ NOTICE:  ALTER TABLE / ADD UNIQUE will create implicit index "uq" for table "atacc0"
+ insert into atacc0 values (1, 1), (4, 2);
+ create table atacc1 (test int, test2 int);
+ alter table atacc1 alter column test add constraint test_fk references atacc0 initially deferred deferrable
+ 	check (test <> 4) constraint fnn not null;
+ insert into atacc1 values (1, 0);
+ insert into atacc1 values (2, 0);
+ ERROR:  insert or update on table "atacc1" violates foreign key constraint "test_fk"
+ DETAIL:  Key (test)=(2) is not present in table "atacc0".
+ begin;
+ insert into atacc1 values (2, 0);
+ insert into atacc0 values (2, 3);
+ end;
+ insert into atacc1 values (4, 0);
+ ERROR:  new row for relation "atacc1" violates check constraint "atacc1_test_check"
+ insert into atacc1 values (null, 0);
+ ERROR:  null value in column "test" violates not-null constraint
+ alter table atacc1 alter column test2 add constraint test2_fk references atacc0 (test2);
+ ERROR:  insert or update on table "atacc1" violates foreign key constraint "test2_fk"
+ DETAIL:  Key (test2)=(0) is not present in table "atacc0".
+ insert into atacc0 values (5, 0);
+ alter table atacc1 alter column test2 add constraint test2_fk references atacc0 (test2);
+ insert into atacc1 values (1, 3);
+ -- these are disallowed
+ alter table atacc1 alter column test2 add default 4;
+ ERROR:  DEFAULT clause not allowed in ALTER TABLE / ALTER COLUMN
+ alter table atacc1 alter column test2 add constraint test3_fk references atacc0 (test2) collate C;
+ ERROR:  COLLATE clause not allowed in ALTER TABLE / ALTER COLUMN
+ drop table atacc1, atacc0;
  -- lets do some naming tests
  create table atacc1 (test int, test2 int, primary key(test));
  NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "atacc1_pkey" for table "atacc1"
*** a/src/test/regress/sql/alter_table.sql
--- b/src/test/regress/sql/alter_table.sql
***************
*** 599,604 **** insert into atacc1 (test,test2) values (5,4);
--- 599,629 ----
  insert into atacc1 (test,test2) values (5,5);
  drop table atacc1;
  
+ -- try the column constraint variant
+ create table atacc0 (test int, test2 int);
+ alter table atacc0 alter column test add constraint pk primary key;
+ alter table atacc0 alter column test2 add constraint uq unique;
+ insert into atacc0 values (1, 1), (4, 2);
+ create table atacc1 (test int, test2 int);
+ alter table atacc1 alter column test add constraint test_fk references atacc0 initially deferred deferrable
+ 	check (test <> 4) constraint fnn not null;
+ insert into atacc1 values (1, 0);
+ insert into atacc1 values (2, 0);
+ begin;
+ insert into atacc1 values (2, 0);
+ insert into atacc0 values (2, 3);
+ end;
+ insert into atacc1 values (4, 0);
+ insert into atacc1 values (null, 0);
+ alter table atacc1 alter column test2 add constraint test2_fk references atacc0 (test2);
+ insert into atacc0 values (5, 0);
+ alter table atacc1 alter column test2 add constraint test2_fk references atacc0 (test2);
+ insert into atacc1 values (1, 3);
+ -- these are disallowed
+ alter table atacc1 alter column test2 add default 4;
+ alter table atacc1 alter column test2 add constraint test3_fk references atacc0 (test2) collate C;
+ drop table atacc1, atacc0;
+ 
  -- lets do some naming tests
  create table atacc1 (test int, test2 int, primary key(test));
  -- only first should succeed
