diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index d848ef0..d55fa59 100644
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
*************** StoreAttrDefault(Relation rel, AttrNumbe
*** 1692,1697 ****
--- 1692,1737 ----
  }
  
  /*
+  * Stores a NOT NULL constraint of a column into pg_constraint.
+  */
+ void
+ StoreColumnNotNullConstraint(Relation rel, CookedConstraint *cooked)
+ {
+ 
+ 	/*
+ 	 * Store the constraint. Reflect conislocal and coninhcount to
+ 	 * match the same values as the attached column.
+ 	 */
+ 	CreateConstraintEntry(cooked->name,
+ 						  RelationGetNamespace(rel),
+ 						  CONSTRAINT_NOTNULL,
+ 						  false,
+ 						  false,
+ 						  RelationGetRelid(rel),
+ 						  &(cooked->attnum),
+ 						  1,
+ 						  InvalidOid,
+ 						  InvalidOid,
+ 						  InvalidOid,
+ 						  NULL,
+ 						  InvalidOid,
+ 						  InvalidOid,
+ 						  InvalidOid,
+ 						  0,
+ 						  ' ',
+ 						  ' ',
+ 						  ' ',
+ 						  NULL,
+ 						  NULL,
+ 						  NULL,
+ 						  NULL,
+ 						  cooked->is_local,
+ 						  cooked->inhcount
+ 		);
+ 
+ }
+ 
+ /*
   * Store a check-constraint expression for the given relation.
   *
   * Caller is responsible for updating the count of constraints
*************** StoreConstraints(Relation rel, List *coo
*** 1815,1820 ****
--- 1855,1863 ----
  
  		switch (con->contype)
  		{
+ 			case CONSTR_NOTNULL:
+ 				StoreColumnNotNullConstraint(rel, con);
+ 				break;
  			case CONSTR_DEFAULT:
  				StoreAttrDefault(rel, con->attnum, con->expr);
  				break;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 9b5ce65..62defe2 100644
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
*************** static void ATExecAddColumn(AlteredTable
*** 275,281 ****
  static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
  static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
  			  AlterTableCmd *cmd);
! static void ATExecDropNotNull(Relation rel, const char *colName);
  static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
  				 const char *colName);
  static void ATExecColumnDefault(Relation rel, const char *colName,
--- 275,282 ----
  static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
  static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
  			  AlterTableCmd *cmd);
! static void ATExecDropNotNull(Relation rel, const char *colName, DropBehavior behavior,
! 							  bool recurse, bool recursing);
  static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
  				 const char *colName);
  static void ATExecColumnDefault(Relation rel, const char *colName,
*************** static void ATExecDropInherit(Relation r
*** 332,338 ****
  static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
  				   ForkNumber forkNum, bool istemp);
  static const char *storage_name(char c);
! 
  
  /* ----------------------------------------------------------------
   *		DefineRelation
--- 333,346 ----
  static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
  				   ForkNumber forkNum, bool istemp);
  static const char *storage_name(char c);
! static bool ATExecDropNotNullInternal(Relation rel, HeapTuple constrtup,
! 									  DropBehavior behavior,
! 									  bool recurse, bool recursing);
! static bool
! CheckNotNullOnAttributeName(Relation rel, const char *colname, 
! 							AttrNumber *attnum);
! static void
! DropNotNullOnAttributeNum(Relation rel, AttrNumber attnum, bool lock);
  
  /* ----------------------------------------------------------------
   *		DefineRelation
*************** DefineRelation(CreateStmt *stmt, char re
*** 495,500 ****
--- 503,526 ----
  
  		attnum++;
  
+ 		if (colDef->is_not_null) {
+ 			/*
+ 			 * Adjust NOT NULL constraint of this column
+ 			 * to hold new attnum and inheritance information
+ 			 */
+ 			CookedConstraint *cooked;
+ 
+ 			cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
+ 			cooked->contype = CONSTR_NOTNULL;
+ 			cooked->name = ChooseConstraintName(relname, NameStr(descriptor->attrs[attnum - 1]->attname),
+ 												"not_null", namespaceId, NIL);
+ 			cooked->attnum = attnum;
+ 			cooked->expr = NULL;
+ 			cooked->is_local = colDef->is_local;
+ 			cooked->inhcount = colDef->inhcount;
+ 			cookedDefaults = lappend(cookedDefaults, cooked);
+ 		}
+ 
  		if (colDef->raw_default != NULL)
  		{
  			RawColumnDefault *rawEnt;
*************** ATPrepCmd(List **wqueue, Relation rel, A
*** 2469,2475 ****
  			break;
  		case AT_DropNotNull:	/* ALTER COLUMN DROP NOT NULL */
  			ATSimplePermissions(rel, false);
! 			ATSimpleRecursion(wqueue, rel, cmd, recurse);
  			/* No command-specific prep needed */
  			pass = AT_PASS_DROP;
  			break;
--- 2495,2502 ----
  			break;
  		case AT_DropNotNull:	/* ALTER COLUMN DROP NOT NULL */
  			ATSimplePermissions(rel, false);
! 			if (recurse)
! 				cmd->subtype = AT_DropNotNullRecurse;
  			/* No command-specific prep needed */
  			pass = AT_PASS_DROP;
  			break;
*************** ATExecCmd(List **wqueue, AlteredTableInf
*** 2696,2702 ****
  			ATExecColumnDefault(rel, cmd->name, cmd->def);
  			break;
  		case AT_DropNotNull:	/* ALTER COLUMN DROP NOT NULL */
! 			ATExecDropNotNull(rel, cmd->name);
  			break;
  		case AT_SetNotNull:		/* ALTER COLUMN SET NOT NULL */
  			ATExecSetNotNull(tab, rel, cmd->name);
--- 2723,2734 ----
  			ATExecColumnDefault(rel, cmd->name, cmd->def);
  			break;
  		case AT_DropNotNull:	/* ALTER COLUMN DROP NOT NULL */
! 			ATExecDropNotNull(rel, cmd->name, cmd->behavior, 
! 							  false, false);
! 			break;
! 		case AT_DropNotNullRecurse: /* ALTER COLUMN DROP NOT NULL with recursion */
! 			ATExecDropNotNull(rel, cmd->name, cmd->behavior, 
! 							  true, false);
  			break;
  		case AT_SetNotNull:		/* ALTER COLUMN SET NOT NULL */
  			ATExecSetNotNull(tab, rel, cmd->name);
*************** ATPrepAddOids(List **wqueue, Relation re
*** 3909,3955 ****
   * ALTER TABLE ALTER COLUMN DROP NOT NULL
   */
  static void
! ATExecDropNotNull(Relation rel, const char *colName)
  {
! 	HeapTuple	tuple;
  	AttrNumber	attnum;
! 	Relation	attr_rel;
! 	List	   *indexoidlist;
! 	ListCell   *indexoidscan;
  
  	/*
! 	 * lookup the attribute
  	 */
! 	attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
  
! 	tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
  
! 	if (!HeapTupleIsValid(tuple))
  		ereport(ERROR,
  				(errcode(ERRCODE_UNDEFINED_COLUMN),
  				 errmsg("column \"%s\" of relation \"%s\" does not exist",
! 						colName, RelationGetRelationName(rel))));
  
! 	attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
  
  	/* Prevent them from altering a system attribute */
! 	if (attnum <= 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("cannot alter system column \"%s\"",
! 						colName)));
  
  	/*
! 	 * Check that the attribute is not in a primary key
  	 */
  
  	/* Loop over all indexes on the relation */
  	indexoidlist = RelationGetIndexList(rel);
! 
  	foreach(indexoidscan, indexoidlist)
  	{
! 		Oid			indexoid = lfirst_oid(indexoidscan);
! 		HeapTuple	indexTuple;
  		Form_pg_index indexStruct;
  		int			i;
  
--- 3941,4288 ----
   * ALTER TABLE ALTER COLUMN DROP NOT NULL
   */
  static void
! ATExecDropNotNull(Relation rel, const char *colName, DropBehavior behavior,
! 				  bool recurse, bool recursing)
  {
! 	List	   *children;
! 	ListCell   *child;
! 	HeapTuple   constr_tuple;
  	AttrNumber	attnum;
! 	Relation    constr_rel;
! 	SysScanDesc scan;
! 	ScanKeyData key;
! 	bool        found;
! 
! 	if (recursing)
! 		ATSimplePermissions(rel, false);
  
  	/*
! 	 * Lookup the attribute. This also checks for a dropped column
! 	 * or any attempts to alter a system column. Returns false in case
! 	 * no further work needs to be done (e.g. no NOT NULL present).
  	 */
! 	if (!CheckNotNullOnAttributeName(rel, colName, &attnum))
! 		return;
  
! 	/*
! 	 * Scan through our constraint records, looking for the matching
! 	 * NOT NULL constraint.
! 	 */
! 	constr_rel = heap_open(ConstraintRelationId, RowExclusiveLock);
! 	ScanKeyInit(&key,
! 				Anum_pg_constraint_conrelid,
! 				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(rel)));
! 	scan = systable_beginscan(constr_rel, ConstraintRelidIndexId,
! 							  true, SnapshotNow, 1, &key);
! 	
! 	while(HeapTupleIsValid(constr_tuple = systable_getnext(scan)))
! 	{
! 		Form_pg_constraint constrStruct;
! 		Datum              arrayp;
! 		Datum             *keyvals;
! 		int                nelems;
! 		bool               isnull;
  
! 		constrStruct = (Form_pg_constraint) GETSTRUCT(constr_tuple);
! 
! 		if (constrStruct->contype != CONSTRAINT_NOTNULL)
! 			continue;
! 
! 		/*
! 		 * Check wether this constraint is attached to the given column.
! 		 * ATExecSetNotNull() ensures that only one NOT NULL constraint tuple
! 		 * lives per attribute.
! 		 */
! 		arrayp = SysCacheGetAttr(CONSTROID, constr_tuple, Anum_pg_constraint_conkey, &isnull);
! 
! 		/* should not happen */
! 		Assert(!isnull);
! 
! 		deconstruct_array(DatumGetArrayTypeP(arrayp), INT2OID, 2, true, 
! 						  's', &keyvals, NULL, &nelems);
! 
! 		/* Is this NOT NULL constraint attached to the current tuple? */
! 		if (DatumGetInt16(keyvals[0]) != attnum) 
! 			continue;
! 
! 		/*
! 		 * Check if the attribute is inherited from another relation or
! 		 * part of a primary key.
! 		 * If true, error out, otherwise perform the deletion.
! 		 * Note: ATExecDropNotNullInternal() performs a CCI, if required.
! 		 */
! 		if ((found = ATExecDropNotNullInternal(rel,
! 											   constr_tuple,
! 											   behavior,
! 											   recurse, recursing)))
! 		{			
! 			/*
! 			 * Okay, actually remove the NOT NULL from pg_attribute. 
! 			 * CheckNotNullOnAttributeName() already holds a lock on pg_attribute, so
! 			 * override an additional lock here.
! 			 */
! 			DropNotNullOnAttributeNum(rel, attnum, false);
! 			break;
! 		}
! 	}
! 
! 	systable_endscan(scan);
! 
! 	if (found)
! 	{
! 		/*
! 		 * Propagate to children as appropriate.
! 		 */
! 		children = find_inheritance_children(RelationGetRelid(rel),
! 											 AccessExclusiveLock);
! 	}
! 	else
! 	{
! 		ereport(ERROR,
! 				(errcode(ERRCODE_UNDEFINED_OBJECT),
! 				 errmsg("column \"%s\" of relation \"%s\" has no NOT NULL constraint",
! 						colName,
! 						RelationGetRelationName(rel))));
! 	}
! 
! 	foreach(child, children)
! 	{
! 		Oid childrelid = lfirst_oid(child);
! 		Relation childrel;
! 
! 		childrel = heap_open(childrelid, NoLock);
! 		CheckTableNotInUse(childrel, "ALTER TABLE");
! 		
! 		ScanKeyInit(&key,
! 					Anum_pg_constraint_conrelid,
! 					BTEqualStrategyNumber, F_OIDEQ,
! 					ObjectIdGetDatum(childrelid));
! 		scan = systable_beginscan(constr_rel, ConstraintRelidIndexId,
! 								  true, SnapshotNow, 1, &key);
! 
! 		while (HeapTupleIsValid(constr_tuple = systable_getnext(scan)))
! 		{
! 			HeapTuple copy_tuple;
! 			Form_pg_constraint constrStruct;
! 			Datum              arrayp;
! 			Datum             *keyvals;
! 			int                nelems;
! 			bool               isnull;
! 			
! 			constrStruct = (Form_pg_constraint) GETSTRUCT(constr_tuple);
! 			
! 			if (constrStruct->contype != CONSTRAINT_NOTNULL)
! 				continue;
! 			
! 			/*
! 			 * Check wether this constraint is attached to the given column.
! 			 * ATExecSetNotNull() ensures that only one NOT NULL constraint tuple
! 			 * lives per attribute.
! 			 */
! 			arrayp = SysCacheGetAttr(CONSTROID, constr_tuple, Anum_pg_constraint_conkey, &isnull);
! 			
! 			/* should not happen */
! 			Assert(!isnull);
! 			
! 			deconstruct_array(DatumGetArrayTypeP(arrayp), INT2OID, 2, true, 
! 							  's', &keyvals, NULL, &nelems);
! 			
! 			/* Is this NOT NULL constraint attached to the current tuple? */
! 			if (DatumGetInt16(keyvals[0]) != attnum) 
! 				continue;
! 
! 			found = true;
! 
! 			if (constrStruct->coninhcount <= 0) /* shouldn't happen */
! 				elog(ERROR, "relation %u has non-inherited NOT NULL constraint \"%s\"",
! 					 childrelid, NameStr(constrStruct->conname));
! 			
! 			copy_tuple = heap_copytuple(constr_tuple);
! 			constrStruct = (Form_pg_constraint) GETSTRUCT(copy_tuple);
! 
! 			if (recurse)
! 			{
! 				/*
! 				 * If the child constraint has other definition sources, just
! 				 * decrement its inheritance count; if not, recurse to delete
! 				 * it.
! 				 */
! 				if (constrStruct->coninhcount == 1 && !constrStruct->conislocal)
! 				{
! 					/* Time to delete this child constraint, too */
! 					ATExecDropNotNull(childrel, colName, behavior,
! 									  true, true);
! 				}
! 				else
! 				{
! 					/* Child constraint must survive my deletion */
! 					constrStruct->coninhcount--;
! 					simple_heap_update(constr_rel, &copy_tuple->t_self, copy_tuple);
! 					CatalogUpdateIndexes(constr_rel, copy_tuple);
! 
! 					/* Make update visible */
! 					CommandCounterIncrement();
! 				}
! 			}
! 			else
! 			{
! 				/*
! 				 * If we were told to drop ONLY in this table (no recursion),
! 				 * we need to mark the inheritors' constraints as locally
! 				 * defined rather than inherited.
! 				 */
! 				constrStruct->coninhcount--;
! 				constrStruct->conislocal = true;
! 
! 				simple_heap_update(constr_rel, &copy_tuple->t_self, copy_tuple);
! 				CatalogUpdateIndexes(constr_rel, copy_tuple);
! 
! 				/* Make update visible */
! 				CommandCounterIncrement();
! 			}
! 
! 			heap_freetuple(copy_tuple);
! 
! 		}
! 
! 		systable_endscan(scan);
! 		heap_close(childrel, NoLock);
! 
! 	}
! 
! 	heap_close(constr_rel, NoLock);
! 	
! }
! 
! /*
!  * Does the leg work on dropping a NOT NULL constraint
!  * identified by the given constrtup tuple.
!  *
!  * Note: The caller is responsible to pass a valid
!  * CONSTRAINT_NOTNULL tuple.
!  */
! static bool
! ATExecDropNotNullInternal(Relation rel, HeapTuple constrtup,
! 						  DropBehavior behavior,
! 						  bool recurse, bool recursing)
! {
! 	bool               found;
! 	Form_pg_constraint constrStruct;
! 	ObjectAddress      conobj;
! 
! 	found = false;
! 
! 	if (HeapTupleIsValid(constrtup))
! 	{
! 		
! 		constrStruct = (Form_pg_constraint) GETSTRUCT(constrtup);
! 
! 		/*
! 		 * Be paranoid, caller is responsible to pass a valid HeapTuple.
! 		 */
! 		Assert(constrStruct->contype == CONSTRAINT_NOTNULL);
! 		
! 		elog(DEBUG1, "ATExecDropNotNullInternal, relation %s",
! 			 RelationGetRelationName(rel));
! 		
! 		/* It is okay to drop this constraint when recursing */
! 		if ((constrStruct->coninhcount > 0) && (!recursing))
! 		{
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
! 					 errmsg("cannot drop inherited NOT NULL constraint \"%s\", relation \"%s\"",
! 							NameStr(constrStruct->conname), RelationGetRelationName(rel))));
! 		} 
! 
! 		/*
! 		 * Time to drop the constraint now.
! 		 */
! 		conobj.classId = ConstraintRelationId;
! 		conobj.objectId = HeapTupleGetOid(constrtup);
! 		conobj.objectSubId = 0;
! 
! 		performDeletion(&conobj, behavior);
! 
! 		/*
! 		 * Make sure changes are visible
! 		 */
! 		CommandCounterIncrement();
! 		found = true;
! 			
! 	}
! 
! 	return found;
! }
! 
! /*
!  * Checks wether the given attribute name has a NOT NULL constraint attached
!  * which can be dropped.
!  *
!  * Any attempts to drop a NOT NULL constraint on a system
!  * column or non-existing column will throw an error. Returns true
!  * in case the caller is allowed to proceed, false if no NOT NULL flag
!  * was set on the attribute.
!  */
! static bool
! CheckNotNullOnAttributeName(Relation rel, const char *colname, AttrNumber *attnum)
! {
! 	Relation  attr_rel;
! 	HeapTuple attr_tuple;
! 	Form_pg_attribute attr_struct;
! 	bool              has_not_null;
! 	List	         *indexoidlist;
! 	ListCell         *indexoidscan;
! 
! 	has_not_null = false;
! 
! 	attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
! 	attr_tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colname);
! 
! 	if (!HeapTupleIsValid(attr_tuple))
  		ereport(ERROR,
  				(errcode(ERRCODE_UNDEFINED_COLUMN),
  				 errmsg("column \"%s\" of relation \"%s\" does not exist",
! 						colname, 
! 						RelationGetRelationName(rel))));
  
! 	/* attribute exists */
! 	attr_struct = (Form_pg_attribute) GETSTRUCT(attr_tuple);
  
  	/* Prevent them from altering a system attribute */
! 	if (attr_struct->attnum <= 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("cannot alter system column \"%s\" of relation \"%s\"",
! 						colname,
! 						RelationGetRelationName(rel))));
  
  	/*
! 	 * Check for dropped attributes
  	 */
+ 	if (attr_struct->attisdropped)
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("attempt to drop NOT NULL constraint on dropped column \"%s\" of relation \"%s\"",
+ 						colname,
+ 						RelationGetRelationName(rel))));		
+ 	}
  
+ 	if (attnum)
+ 		*attnum = attr_struct->attnum;
+ 
+ 	/*
+ 	 * Check that the attribute is not in a primary key
+ 	 */
+ 	
  	/* Loop over all indexes on the relation */
  	indexoidlist = RelationGetIndexList(rel);
! 	
  	foreach(indexoidscan, indexoidlist)
  	{
! 		Oid			  indexoid = lfirst_oid(indexoidscan);
! 		HeapTuple	  indexTuple;
  		Form_pg_index indexStruct;
  		int			i;
  
*************** ATExecDropNotNull(Relation rel, const ch
*** 3957,3963 ****
  		if (!HeapTupleIsValid(indexTuple))
  			elog(ERROR, "cache lookup failed for index %u", indexoid);
  		indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
! 
  		/* If the index is not a primary key, skip the check */
  		if (indexStruct->indisprimary)
  		{
--- 4290,4296 ----
  		if (!HeapTupleIsValid(indexTuple))
  			elog(ERROR, "cache lookup failed for index %u", indexoid);
  		indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
! 		
  		/* If the index is not a primary key, skip the check */
  		if (indexStruct->indisprimary)
  		{
*************** ATExecDropNotNull(Relation rel, const ch
*** 3967,3999 ****
  			 */
  			for (i = 0; i < indexStruct->indnatts; i++)
  			{
! 				if (indexStruct->indkey.values[i] == attnum)
  					ereport(ERROR,
  							(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
  							 errmsg("column \"%s\" is in a primary key",
! 									colName)));
  			}
  		}
! 
  		ReleaseSysCache(indexTuple);
  	}
  
! 	list_free(indexoidlist);
! 
! 	/*
! 	 * Okay, actually perform the catalog change ... if needed
  	 */
! 	if (((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
! 	{
! 		((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = FALSE;
  
! 		simple_heap_update(attr_rel, &tuple->t_self, tuple);
  
! 		/* keep the system catalog indexes current */
! 		CatalogUpdateIndexes(attr_rel, tuple);
! 	}
  
- 	heap_close(attr_rel, RowExclusiveLock);
  }
  
  /*
--- 4300,4362 ----
  			 */
  			for (i = 0; i < indexStruct->indnatts; i++)
  			{
! 				if (indexStruct->indkey.values[i] == attr_struct->attnum)
  					ereport(ERROR,
  							(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
  							 errmsg("column \"%s\" is in a primary key",
! 									colname)));
  			}
  		}
! 		
  		ReleaseSysCache(indexTuple);
  	}
  
! 	/* 
! 	 * In case no NOT NULL constraint was set on the
! 	 * column, give up and tell the caller we haven't done
! 	 * anything.
  	 */
! 	has_not_null = ((!attr_struct->attnotnull) ? false : true);
  
! 	heap_freetuple(attr_tuple);
! 	heap_close(attr_rel, NoLock);
! 	return has_not_null;
! }
  
! /*
!  * Drops a NOT NULL constraint from the given attribute number of the
!  * specified relation.
!  */
! static void
! DropNotNullOnAttributeNum(Relation rel, AttrNumber attnum, bool lock)
! {
! 	Relation attr_rel;
! 	HeapTuple attr_tuple;
! 	Form_pg_attribute attr_struct;
! 
! 	if (lock)
! 		attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
! 	else
! 		attr_rel = heap_open(AttributeRelationId, NoLock);
! 
! 	attr_tuple = SearchSysCacheCopy(ATTNUM,
! 									ObjectIdGetDatum(RelationGetRelid(rel)),
! 									Int16GetDatum(attnum),
! 									0, 0);
! 
! 	attr_struct = (Form_pg_attribute) GETSTRUCT(attr_tuple);
! 
! 	/* not expected, so be paranoid */
! 	Assert(attr_struct->attnotnull);
! 
! 	((Form_pg_attribute) GETSTRUCT(attr_tuple))->attnotnull = FALSE;
! 	simple_heap_update(attr_rel, &attr_tuple->t_self, attr_tuple);
! 
! 	/* keep the system catalog indexes current */
! 	CatalogUpdateIndexes(attr_rel, attr_tuple);
! 
! 	heap_close(attr_rel, NoLock);
  
  }
  
  /*
*************** ATExecSetNotNull(AlteredTableInfo *tab, 
*** 4026,4039 ****
  	if (attnum <= 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("cannot alter system column \"%s\"",
! 						colName)));
  
  	/*
  	 * Okay, actually perform the catalog change ... if needed
  	 */
  	if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
  	{
  		((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = TRUE;
  
  		simple_heap_update(attr_rel, &tuple->t_self, tuple);
--- 4389,4430 ----
  	if (attnum <= 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("cannot alter system column \"%s\" of relation \"%s\"",
! 						colName, RelationGetRelationName(rel))));
  
  	/*
  	 * Okay, actually perform the catalog change ... if needed
  	 */
  	if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
  	{
+ 		/*
+ 		 * Setting attnotnull to TRUE requires to create a 
+ 		 * constraint tuple in pg_constraint as well. We do this
+ 		 * to record wether this constraint was added locally or
+ 		 * inherited by some other attribute. We also record the
+ 		 * attribute number to determine which column this
+ 		 * constraint belongs to.
+ 		 */
+ 		CookedConstraint *cooked = palloc(sizeof(CookedConstraint));
+ 		cooked->contype = CONSTR_NOTNULL;
+ 		cooked->expr = NULL;
+ 		cooked->name = ChooseConstraintName(RelationGetRelationName(rel), 
+ 											NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname),
+ 											"not_null", RelationGetNamespace(rel), NIL);
+ 		cooked->attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
+ 		cooked->is_local = ((Form_pg_attribute) GETSTRUCT(tuple))->attislocal;
+ 		cooked->inhcount = ((Form_pg_attribute) GETSTRUCT(tuple))->attinhcount;
+ 		StoreColumnNotNullConstraint(rel, cooked);
+ 
+ 		/*
+ 		 * Make changes visible
+ 		 */
+ 		CommandCounterIncrement();
+ 
+ 		/*
+ 		 * Finally record in pg_attribute that there is a NOT NULL
+ 		 * constraint.
+ 		 */
  		((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = TRUE;
  
  		simple_heap_update(attr_rel, &tuple->t_self, tuple);
*************** ATExecDropConstraint(Relation rel, const
*** 5610,5615 ****
--- 6001,6010 ----
  
  		con = (Form_pg_constraint) GETSTRUCT(tuple);
  
+ 		/* NOT NULL is handled by ALTER TABLE ... DROP/SET NOT NULL */
+ 		if (con->contype == CONSTRAINT_NOTNULL)
+ 			continue;
+ 
  		if (strcmp(NameStr(con->conname), constrName) != 0)
  			continue;
  
*************** ATExecDropConstraint(Relation rel, const
*** 5620,5626 ****
  					 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
  							constrName, RelationGetRelationName(rel))));
  
! 		/* Right now only CHECK constraints can be inherited */
  		if (con->contype == CONSTRAINT_CHECK)
  			is_check_constraint = true;
  
--- 6015,6023 ----
  					 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
  							constrName, RelationGetRelationName(rel))));
  
! 		/* This is a CHECK constraint, remember that we found one
! 		 * and proceed to drop it
! 		 */
  		if (con->contype == CONSTRAINT_CHECK)
  			is_check_constraint = true;
  
*************** ATExecDropConstraint(Relation rel, const
*** 5692,5698 ****
  
  			con = (Form_pg_constraint) GETSTRUCT(tuple);
  
! 			/* Right now only CHECK constraints can be inherited */
  			if (con->contype != CONSTRAINT_CHECK)
  				continue;
  
--- 6089,6098 ----
  
  			con = (Form_pg_constraint) GETSTRUCT(tuple);
  
! 			/* Besides CHECK constraint we support inheritance of
! 			 * NOT NULL constraints, too. Ignore them here, since they
! 			 * are handled by ALTER TABLE...DROP/SET NOT NULL
! 			 */
  			if (con->contype != CONSTRAINT_CHECK)
  				continue;
  
*************** ATPostAlterTypeParse(char *cmd, List **w
*** 6380,6385 ****
--- 6780,6786 ----
  								tab->subcmds[AT_PASS_OLD_INDEX] =
  									lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
  								break;
+ 							case AT_SetNotNull:
  							case AT_AddConstraint:
  								tab->subcmds[AT_PASS_OLD_CONSTR] =
  									lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 90d5c76..b9410ee 100644
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
*************** transformColumnDefinition(ParseState *ps
*** 453,458 ****
--- 453,459 ----
  							 parser_errposition(pstate,
  												constraint->location)));
  				column->is_not_null = TRUE;
+ 				cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
  				saw_nullable = true;
  				break;
  
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index d16f1c4..58e1884 100644
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
*************** pg_get_constraintdef_worker(Oid constrai
*** 1051,1063 ****
  
  	if (fullCommand && OidIsValid(conForm->conrelid))
  	{
! 		appendStringInfo(&buf, "ALTER TABLE ONLY %s ADD CONSTRAINT %s ",
! 						 generate_relation_name(conForm->conrelid, NIL),
! 						 quote_identifier(NameStr(conForm->conname)));
  	}
  
  	switch (conForm->contype)
  	{
  		case CONSTRAINT_FOREIGN:
  			{
  				Datum		val;
--- 1051,1092 ----
  
  	if (fullCommand && OidIsValid(conForm->conrelid))
  	{
! 		/* XXX: TODO, not sure how to handle this right now? */
! 		if (conForm->contype == CONSTRAINT_NOTNULL)
! 		{
! 			appendStringInfo(&buf, "ALTER TABLE ONLY %s ALTER COLUMN ",
! 							 generate_relation_name(conForm->conrelid, NIL));
! 		}
! 		else
! 		{
! 			appendStringInfo(&buf, "ALTER TABLE ONLY %s ADD CONSTRAINT %s ",
! 							 generate_relation_name(conForm->conrelid, NIL),
! 							 quote_identifier(NameStr(conForm->conname)));
! 		}
  	}
  
  	switch (conForm->contype)
  	{
+ 		case CONSTRAINT_NOTNULL:
+ 		    {
+ 				Datum val;
+ 				bool  isnull;
+ 				
+ 				/* XXX: TODO, not sure how to handle this right now? */
+ 				
+ 				/* Fetch referenced column OID */
+ 				val = SysCacheGetAttr(CONSTROID, tup,
+ 									  Anum_pg_constraint_conkey, &isnull);
+ 				
+ 				/* Should not happen */
+ 				if (isnull)
+ 					elog(ERROR, "null conkey for constraint %u",
+ 						 constraintId);
+ 
+ 				decompile_column_index_array(val, conForm->conrelid, &buf);
+ 				appendStringInfo(&buf, " SET NOT NULL");
+ 				break;
+ 		    }
  		case CONSTRAINT_FOREIGN:
  			{
  				Datum		val;
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 8292273..8eb04e3 100644
*** a/src/include/catalog/heap.h
--- b/src/include/catalog/heap.h
*************** extern List *AddRelationNewConstraints(R
*** 90,96 ****
  						  bool is_local);
  
  extern void StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr);
! 
  extern Node *cookDefault(ParseState *pstate,
  			Node *raw_default,
  			Oid atttypid,
--- 90,96 ----
  						  bool is_local);
  
  extern void StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr);
! extern void StoreColumnNotNullConstraint(Relation rel, CookedConstraint *cooked);
  extern Node *cookDefault(ParseState *pstate,
  			Node *raw_default,
  			Oid atttypid,
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index c3dfcb0..99d672f 100644
*** a/src/include/catalog/pg_constraint.h
--- b/src/include/catalog/pg_constraint.h
*************** typedef FormData_pg_constraint *Form_pg_
*** 175,180 ****
--- 175,181 ----
  
  /* Valid values for contype */
  #define CONSTRAINT_CHECK			'c'
+ #define CONSTRAINT_NOTNULL          'n'
  #define CONSTRAINT_FOREIGN			'f'
  #define CONSTRAINT_PRIMARY			'p'
  #define CONSTRAINT_UNIQUE			'u'
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b591073..a98ba53 100644
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef enum AlterTableType
*** 1113,1119 ****
--- 1113,1121 ----
  	AT_AddColumnToView,			/* implicitly via CREATE OR REPLACE VIEW */
  	AT_ColumnDefault,			/* alter column default */
  	AT_DropNotNull,				/* alter column drop not null */
+ 	AT_DropNotNullRecurse,      /* internal to commands/tablecmds.c */
  	AT_SetNotNull,				/* alter column set not null */
+ 	AT_SetNotNullRecurse,       /* internal to commands/tablecmds.c */
  	AT_SetStatistics,			/* alter column set statistics */
  	AT_SetOptions,				/* alter column set ( options ) */
  	AT_ResetOptions,			/* alter column reset ( options ) */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 5aff44f..9d31275 100644
*** a/src/test/regress/expected/alter_table.out
--- b/src/test/regress/expected/alter_table.out
*************** alter table atacc1 alter bar drop not nu
*** 593,601 ****
  ERROR:  column "bar" of relation "atacc1" does not exist
  -- try altering the oid column, should fail
  alter table atacc1 alter oid set not null;
! ERROR:  cannot alter system column "oid"
  alter table atacc1 alter oid drop not null;
! ERROR:  cannot alter system column "oid"
  -- try creating a view and altering that, should fail
  create view myview as select * from atacc1;
  alter table myview alter column test drop not null;
--- 593,601 ----
  ERROR:  column "bar" of relation "atacc1" does not exist
  -- try altering the oid column, should fail
  alter table atacc1 alter oid set not null;
! ERROR:  cannot alter system column "oid" of relation "atacc1"
  alter table atacc1 alter oid drop not null;
! ERROR:  cannot alter system column "oid" of relation "atacc1"
  -- try creating a view and altering that, should fail
  create view myview as select * from atacc1;
  alter table myview alter column test drop not null;
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 96bd816..6d0e3e1 100644
*** a/src/test/regress/expected/cluster.out
--- b/src/test/regress/expected/cluster.out
*************** ERROR:  insert or update on table "clstr
*** 251,261 ****
  DETAIL:  Key (b)=(1111) is not present in table "clstr_tst_s".
  SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass
  ORDER BY 1;
!     conname     
! ----------------
   clstr_tst_con
   clstr_tst_pkey
! (2 rows)
  
  SELECT relname, relkind,
      EXISTS(SELECT 1 FROM pg_class WHERE oid = c.reltoastrelid) AS hastoast
--- 251,262 ----
  DETAIL:  Key (b)=(1111) is not present in table "clstr_tst_s".
  SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass
  ORDER BY 1;
!        conname        
! ----------------------
!  clstr_tst_a_not_null
   clstr_tst_con
   clstr_tst_pkey
! (3 rows)
  
  SELECT relname, relkind,
      EXISTS(SELECT 1 FROM pg_class WHERE oid = c.reltoastrelid) AS hastoast
