*** 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 <name> ALTER [COLUMN] <colname> 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 <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 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? */
