*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 1820,1830 ****
  
    <para>
     The catalog <structname>pg_constraint</structname> stores check, primary
!    key, unique, foreign key, and exclusion constraints on tables.
     (Column constraints are not treated specially.  Every column constraint is
     equivalent to some table constraint.)
!    Not-null constraints are represented in the <structname>pg_attribute</>
!    catalog, not here.
    </para>
  
    <para>
--- 1820,1832 ----
  
    <para>
     The catalog <structname>pg_constraint</structname> stores check, primary
!    key, unique, foreign key, exclusion constraints and additional information
!    of not-null constraints on tables.
     (Column constraints are not treated specially.  Every column constraint is
     equivalent to some table constraint.)
!    Not-null constraints are primarily represented in the <structname>pg_attribute</>
!    catalog, but each attribute which has a not-null constraint in <structname>pg_attribute</>
!    has additional inheritance information stored in <structname>pg_constraint</>.
    </para>
  
    <para>
***************
*** 1873,1878 ****
--- 1875,1881 ----
        <entry>
          <literal>c</> = check constraint,
          <literal>f</> = foreign key constraint,
+         <literal>n</> = not-null constraint,
          <literal>p</> = primary key constraint,
          <literal>u</> = unique constraint,
          <literal>t</> = constraint trigger,
***************
*** 1994,2000 ****
        <entry><type>int2[]</type></entry>
        <entry><literal><link linkend="catalog-pg-attribute"><structname>pg_attribute</structname></link>.attnum</></entry>
        <entry>If a table constraint (including foreign keys, but not constraint
!        triggers), list of the constrained columns</entry>
       </row>
  
       <row>
--- 1997,2004 ----
        <entry><type>int2[]</type></entry>
        <entry><literal><link linkend="catalog-pg-attribute"><structname>pg_attribute</structname></link>.attnum</></entry>
        <entry>If a table constraint (including foreign keys, but not constraint
!        triggers), list of the constrained columns. This column is used to store
!        the referenced attribute number of a not-null constraint, too</entry>
       </row>
  
       <row>
***************
*** 2059,2064 ****
--- 2063,2077 ----
     index.)
    </para>
  
+   <para>
+    Not-null constraints are used to store inheritance information for each not-null
+    constraint attached to a column. They shouldn't be confused with check
+    constraints, since they are handled differently. If a column in <structname>pg_attribute</>
+    has its <structfield>attnotnull</> set, there must be a matching not-null constraint
+    in <structname>pg_constraint</> with the attribute number of the column stored in
+    <structfield>conkey</>. There is always only one attribute number stored at the same time.
+   </para>
+ 
    <note>
     <para>
      <structfield>consrc</structfield> is not updated when referenced objects
*** a/src/backend/access/common/tupdesc.c
--- b/src/backend/access/common/tupdesc.c
***************
*** 557,562 **** BuildDescForRelation(List *schema)
--- 557,563 ----
  	foreach(l, schema)
  	{
  		ColumnDef  *entry = lfirst(l);
+ 		bool		notnull;
  
  		/*
  		 * for each entry in the list, get the name and type information from
***************
*** 585,592 **** BuildDescForRelation(List *schema)
  			desc->attrs[attnum - 1]->attstorage = entry->storage;
  
  		/* Fill in additional stuff not handled by TupleDescInitEntry */
! 		desc->attrs[attnum - 1]->attnotnull = entry->is_not_null;
! 		has_not_null |= entry->is_not_null;
  		desc->attrs[attnum - 1]->attislocal = entry->is_local;
  		desc->attrs[attnum - 1]->attinhcount = entry->inhcount;
  	}
--- 586,594 ----
  			desc->attrs[attnum - 1]->attstorage = entry->storage;
  
  		/* Fill in additional stuff not handled by TupleDescInitEntry */
! 		notnull = entry->is_not_null | entry->is_primary_key;
! 		has_not_null |= notnull;
! 		desc->attrs[attnum - 1]->attnotnull = notnull;
  		desc->attrs[attnum - 1]->attislocal = entry->is_local;
  		desc->attrs[attnum - 1]->attinhcount = entry->inhcount;
  	}
*** a/src/backend/catalog/index.c
--- b/src/backend/catalog/index.c
***************
*** 174,181 **** relationHasPrimaryKey(Relation rel)
   * are simple column references (not expressions), and that all those
   * columns are marked NOT NULL.  If they aren't (which can only happen during
   * ALTER TABLE ADD CONSTRAINT, since the parser forces such columns to be
!  * created NOT NULL during CREATE TABLE), do an ALTER SET NOT NULL to mark
!  * them so --- or fail if they are not in fact nonnull.
   *
   * Caller had better have at least ShareLock on the table, else the not-null
   * checking isn't trustworthy.
--- 174,182 ----
   * are simple column references (not expressions), and that all those
   * columns are marked NOT NULL.  If they aren't (which can only happen during
   * ALTER TABLE ADD CONSTRAINT, since the parser forces such columns to be
!  * created NOT NULL during CREATE TABLE; or if they come from an inherited
!  * relation), do an ALTER SET NOT NULL to mark them so --- or fail if they are
!  * not in fact nonnull.
   *
   * Caller had better have at least ShareLock on the table, else the not-null
   * checking isn't trustworthy.
***************
*** 236,242 **** index_check_primary_key(Relation heapRel,
  			/* Add a subcommand to make this one NOT NULL */
  			AlterTableCmd *cmd = makeNode(AlterTableCmd);
  
! 			cmd->subtype = AT_SetNotNull;
  			cmd->name = pstrdup(NameStr(attform->attname));
  			cmds = lappend(cmds, cmd);
  		}
--- 237,243 ----
  			/* Add a subcommand to make this one NOT NULL */
  			AlterTableCmd *cmd = makeNode(AlterTableCmd);
  
! 			cmd->subtype = AT_SetAttNotNull;
  			cmd->name = pstrdup(NameStr(attform->attname));
  			cmds = lappend(cmds, cmd);
  		}
*** a/src/backend/catalog/pg_constraint.c
--- b/src/backend/catalog/pg_constraint.c
***************
*** 23,28 ****
--- 23,29 ----
  #include "catalog/pg_operator.h"
  #include "catalog/pg_type.h"
  #include "commands/defrem.h"
+ #include "commands/constraint.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/fmgroids.h"
***************
*** 522,527 **** ChooseConstraintName(const char *name1, const char *name2,
--- 523,773 ----
  }
  
  /*
+  * Create a NOT NULL pg_constraint entry for the given column of the
+  * given name.  If the name is NULL, a new one is generated.
+  */
+ void
+ CreateNotNullConstraint(Relation rel, AttrNumber attnum, char *constr_name,
+ 						int inhcount, bool islocal)
+ {
+ 	NotNullConstraint	constraint;
+ 
+ 	Assert(AttrNumberIsForUserDefinedAttr(attnum));
+ 
+ 	if (constr_name)
+ 		constraint.conname = pstrdup(constr_name);
+ 	else
+ 	{
+ 		TupleDesc	tupdesc = RelationGetDescr(rel);
+ 		char	   *attname = NameStr(tupdesc->attrs[attnum - 1]->attname);
+ 
+ 		constraint.conname =
+ 			ChooseConstraintName(RelationGetRelationName(rel),
+ 								 attname,
+ 								 "not_null", RelationGetNamespace(rel),
+ 								 NIL);
+ 	}
+ 	/* attname is not used here */
+ 	constraint.attnum = attnum;
+ 	constraint.is_local = islocal;
+ 	constraint.inhcount = islocal ? 0 : 1;
+ 
+ 	StoreNotNullConstraint(RelationGetRelid(rel), RelationGetNamespace(rel),
+ 						   &constraint);
+ 	pfree(constraint.conname);
+ }
+ 
+ /*
+  * Stores a NOT NULL constraint of a column into pg_constraint.
+  */
+ void
+ StoreNotNullConstraint(Oid relid, Oid nspid, NotNullConstraint *constraint)
+ {
+ 	/*
+ 	 * Store the constraint. Reflect conislocal and coninhcount to
+ 	 * match the same values as the attached column.
+ 	 */
+ 	CreateConstraintEntry(constraint->conname,
+ 						  nspid,
+ 						  CONSTRAINT_NOTNULL,
+ 						  false,
+ 						  false,
+ 						  true,
+ 						  relid,
+ 						  &(constraint->attnum),
+ 						  1,
+ 						  InvalidOid,
+ 						  InvalidOid,
+ 						  InvalidOid,
+ 						  NULL,
+ 						  InvalidOid,
+ 						  InvalidOid,
+ 						  InvalidOid,
+ 						  0,
+ 						  ' ',
+ 						  ' ',
+ 						  ' ',
+ 						  NULL,
+ 						  NULL,
+ 						  NULL,
+ 						  NULL,
+ 						  constraint->is_local,
+ 						  constraint->inhcount);
+ }
+ 
+ /*
+  * get_constraint_singlecol_attno
+  *
+  * Get the column number that this pg_constraint tuple is for.  Note that
+  * this can only be useful for NOT NULL constraints, because other types
+  * might have more than one key column, and this only returns the first one.
+  */
+ AttrNumber
+ get_constraint_singlecol_attno(HeapTuple tup)
+ {
+ 	Datum	arrayp;
+ 	bool	isnull;
+ 	Datum  *keyvals;
+ 	int		nelems;
+ 
+ 	arrayp = SysCacheGetAttr(CONSTROID, tup,
+ 							 Anum_pg_constraint_conkey,
+ 							 &isnull);
+ 	if (isnull)
+ 		elog(ERROR, "null conkey found");
+ 
+ 	deconstruct_array(DatumGetArrayTypeP(arrayp), INT2OID, 2, true,
+ 					  's', &keyvals, NULL, &nelems);
+ 	if (nelems != 1)
+ 		elog(ERROR, "constraint tuple has more than one key column");
+ 
+ 	return DatumGetInt16(keyvals[0]);
+ }
+ 
+ /*
+  * RelationFetchNotNullConstraint
+  *
+  * Get (a copy of) the HeapTuple representing a NOT NULL constraint for the
+  * given relation and column, or NULL if there's no such constraint.
+  */
+ HeapTuple
+ RelationFetchNotNullConstraint(Relation pg_constraint, Oid relid,
+ 							   AttrNumber attnum)
+ {
+ 	SysScanDesc	scan;
+ 	ScanKeyData	key[1];
+ 	HeapTuple	tup;
+ 	bool		found;
+ 
+ 	ScanKeyInit(&key[0],
+ 				Anum_pg_constraint_conrelid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(relid));
+ 
+ 	scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
+ 							  SnapshotNow, 1, key);
+ 
+ 	while (HeapTupleIsValid((tup = systable_getnext(scan))))
+ 	{
+ 		Form_pg_constraint	conform = (Form_pg_constraint) GETSTRUCT(tup);
+ 		AttrNumber		con_attno;
+ 
+ 		if (conform->contype != CONSTRAINT_NOTNULL)
+ 			continue;
+ 
+ 		con_attno = get_constraint_singlecol_attno(tup);
+ 
+ 		if (con_attno != attnum)
+ 			continue;
+ 
+ 		tup = heap_copytuple(tup);
+ 		found = true;
+ 		break;
+ 	}
+ 	if (!found)
+ 		tup = NULL;
+ 
+ 	systable_endscan(scan);
+ 
+ 	return tup;
+ }
+ 
+ /*
+  * GetRelationNotNullConstraints
+  *
+  * Return a list of not null constraints in a table, as a list of struct
+  * NotNullConstraint.
+  */
+ List *
+ GetRelationNotNullConstraints(Relation rel)
+ {
+ 	Relation		constr_rel;
+ 	ScanKeyData		key;
+ 	SysScanDesc		scan;
+ 	HeapTuple		constr_tup;
+ 	List		   *constraints = NIL;
+ 	TupleDesc		tupdesc = RelationGetDescr(rel);
+ 
+ 	constr_rel = heap_open(ConstraintRelationId, AccessShareLock);
+ 	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_tup = systable_getnext(scan)))
+ 	{
+ 		Form_pg_constraint	constr;
+ 		int					attnum;
+ 		Form_pg_attribute	attr;
+ 		NotNullConstraint  *constraint;
+ 
+ 		constr = (Form_pg_constraint) GETSTRUCT(constr_tup);
+ 
+ 		if (constr->contype != CONSTRAINT_NOTNULL)
+ 			continue;
+ 
+ 		attnum = get_constraint_singlecol_attno(constr_tup);
+ 		attr = tupdesc->attrs[attnum - 1];
+ 
+ 		constraint = (NotNullConstraint *) palloc(sizeof(NotNullConstraint));
+ 		constraint->conname = pstrdup(NameStr(constr->conname));
+ 		constraint->attname = pstrdup(NameStr(attr->attname));
+ 		constraint->attnum = attnum;
+ 		constraint->is_local = constr->conislocal;
+ 		constraint->inhcount = constr->coninhcount;
+ 
+ 		constraints = lappend(constraints, constraint);
+ 	}
+ 
+ 	systable_endscan(scan);
+ 	heap_close(constr_rel, AccessShareLock);
+ 
+ 	return constraints;
+ }
+ 
+ /*
+  * CreateOrAdjustNotNullConstraint
+  *
+  * Create a new NOT NULL constraint for the given relation and attnum, if
+  * none exists already; otherwise, adjust its properties by adding the given
+  * quantity to coninhcount, and tweak conislocal as follows: set to true if
+  * tweak_islocal is positive, to false if negative, or not at all if 0.
+  */
+ void
+ CreateOrAdjustNotNullConstraint(Relation rel, AttrNumber attnum, char *attname,
+ 								int addinhcount, int tweak_islocal)
+ {
+ 	HeapTuple	constrtup;
+ 	Relation	pg_con;
+ 
+ 	pg_con = heap_open(ConstraintRelationId, RowExclusiveLock);
+ 
+ 	constrtup = RelationFetchNotNullConstraint(pg_con, RelationGetRelid(rel),
+ 											   attnum);
+ 	if (constrtup)
+ 	{
+ 		HeapTuple	copy_con = heap_copytuple(constrtup);
+ 		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(copy_con);
+ 
+ 		conform->coninhcount += addinhcount;
+ 		if (tweak_islocal > 0)
+ 			conform->conislocal = true;
+ 		else if (tweak_islocal < 0)
+ 			conform->conislocal = false;
+ 
+ 		simple_heap_update(pg_con, &(copy_con->t_self), copy_con);
+ 		CatalogUpdateIndexes(pg_con, copy_con);
+ 	}
+ 	else
+ 	{
+ 		CreateNotNullConstraint(rel, attnum, NULL, 1, false);
+ 	}
+ 
+ 	heap_close(pg_con, RowExclusiveLock);
+ }
+ 
+ /*
   * Delete a single constraint record.
   */
  void
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 43,48 ****
--- 43,49 ----
  #include "catalog/toasting.h"
  #include "commands/cluster.h"
  #include "commands/comment.h"
+ #include "commands/constraint.h"
  #include "commands/defrem.h"
  #include "commands/sequence.h"
  #include "commands/tablecmds.h"
***************
*** 145,151 **** typedef struct AlteredTableInfo
  	/* Information saved by Phases 1/2 for Phase 3: */
  	List	   *constraints;	/* List of NewConstraint */
  	List	   *newvals;		/* List of NewColumnValue */
! 	bool		new_notnull;	/* T if we added new NOT NULL constraints */
  	bool		rewrite;		/* T if a rewrite is forced */
  	Oid			newTableSpace;	/* new tablespace; 0 means no change */
  	/* Objects to rebuild after completing ALTER TYPE operations */
--- 146,152 ----
  	/* Information saved by Phases 1/2 for Phase 3: */
  	List	   *constraints;	/* List of NewConstraint */
  	List	   *newvals;		/* List of NewColumnValue */
! 	bool		new_notnull;	/* T if we added new NOT NULL or PKs */
  	bool		rewrite;		/* T if a rewrite is forced */
  	Oid			newTableSpace;	/* new tablespace; 0 means no change */
  	/* Objects to rebuild after completing ALTER TYPE operations */
***************
*** 183,188 **** typedef struct NewColumnValue
--- 184,200 ----
  } NewColumnValue;
  
  /*
+  * Struct describing the constraint information to disinherit. Currently only
+  * CHECK and NOT NULL constraints use this struct.
+  */
+ typedef struct DisinheritConstraintInfo
+ {
+ 	ConstrType	contype;		/* constraint type */
+ 	AttrNumber	attnum;			/* if NOT NULL constraint, the attnum */
+ 	char	   *conname;		/* if CHECK constraint, the constraint name */
+ } DisinheritConstraintInfo;
+ 
+ /*
   * Error-reporting support for RemoveRelations
   */
  struct dropmsgstrings
***************
*** 244,254 **** static const struct dropmsgstrings dropmsgstringarray[] = {
  
  static void truncate_check_rel(Relation rel);
  static List *MergeAttributes(List *schema, List *supers, char relpersistence,
! 				List **supOids, List **supconstr, int *supOidCount);
  static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
  static bool change_varattnos_walker(Node *node, const AttrNumber *newattno);
! static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
! static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
  static void StoreCatalogInheritance(Oid relationId, List *supers);
  static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
  						 int16 seqNumber, Relation inhRelation);
--- 256,271 ----
  
  static void truncate_check_rel(Relation rel);
  static List *MergeAttributes(List *schema, List *supers, char relpersistence,
! 				char relkind, List **supOids, List **supconstr,
! 				List **nullconstr, int *supOidCount);
  static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
+ static void StoreNotNullConstraints(Oid relid, Oid nspid, List *constraints,
+ 						char *relname, TupleDesc tupdesc);
  static bool change_varattnos_walker(Node *node, const AttrNumber *newattno);
! static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel,
! 										List **child_attnums);
! static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel,
! 										 List *child_attnums);
  static void StoreCatalogInheritance(Oid relationId, List *supers);
  static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
  						 int16 seqNumber, Relation inhRelation);
***************
*** 303,311 **** static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
  static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
  static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
  			  AlterTableCmd *cmd, LOCKMODE lockmode);
! static void ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
! static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
! 				 const char *colName, LOCKMODE lockmode);
  static void ATExecColumnDefault(Relation rel, const char *colName,
  					Node *newDefault, LOCKMODE lockmode);
  static void ATPrepSetStatistics(Relation rel, const char *colName,
--- 320,332 ----
  static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
  static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
  			  AlterTableCmd *cmd, LOCKMODE lockmode);
! static bool ATExecDropNotNull(Relation rel, const char *colName,
! 				  bool recurse, bool recursing, LOCKMODE lockmode);
! static bool ATExecSetNotNull(List **wqueue, AlteredTableInfo *tab,
! 				 Relation rel, const char *colName, bool recurse,
! 				 bool recursing, LOCKMODE lockmode);
! static void ATExecSetAttNotNull(List **wqueue, AlteredTableInfo *tab,
! 					Relation rel, const char *colName, LOCKMODE lockmode);
  static void ATExecColumnDefault(Relation rel, const char *colName,
  					Node *newDefault, LOCKMODE lockmode);
  static void ATPrepSetStatistics(Relation rel, const char *colName,
***************
*** 371,377 **** static void ATExecGenericOptions(Relation rel, List *options);
  static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
  				   ForkNumber forkNum, char relpersistence);
  static const char *storage_name(char c);
! 
  
  /* ----------------------------------------------------------------
   *		DefineRelation
--- 392,398 ----
  static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
  				   ForkNumber forkNum, char relpersistence);
  static const char *storage_name(char c);
! static Constraint *locate_notnull_constraint(ColumnDef *def);
  
  /* ----------------------------------------------------------------
   *		DefineRelation
***************
*** 402,407 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
--- 423,429 ----
  	TupleDesc	descriptor;
  	List	   *inheritOids;
  	List	   *old_constraints;
+ 	List	   *null_constraints;
  	bool		localHasOids;
  	int			parentOidCount;
  	List	   *rawDefaults;
***************
*** 503,510 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
  	 * inherited attributes.
  	 */
  	schema = MergeAttributes(schema, stmt->inhRelations,
! 							 stmt->relation->relpersistence,
! 							 &inheritOids, &old_constraints, &parentOidCount);
  
  	/*
  	 * Create a tuple descriptor from the relation schema.	Note that this
--- 525,533 ----
  	 * inherited attributes.
  	 */
  	schema = MergeAttributes(schema, stmt->inhRelations,
! 							 stmt->relation->relpersistence, relkind,
! 							 &inheritOids, &old_constraints, &null_constraints,
! 							 &parentOidCount);
  
  	/*
  	 * Create a tuple descriptor from the relation schema.	Note that this
***************
*** 601,606 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
--- 624,633 ----
  	/* Store inheritance information for new rel. */
  	StoreCatalogInheritance(relationId, inheritOids);
  
+ 	/* Store NOT NULL constraints */
+ 	StoreNotNullConstraints(relationId, namespaceId, null_constraints,
+ 							relname, descriptor);
+ 
  	/*
  	 * We must bump the command counter to make the newly-created relation
  	 * tuple visible for opening.
***************
*** 638,643 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
--- 665,717 ----
  }
  
  /*
+  * StoreNotNullConstraints
+  *		Subroutine for DefineRelation
+  *
+  * Store a bunch of NOT NULL constraints into pg_constraint, automatically
+  * generating names for those that don't have them.  Also, take care to resolve
+  * attnums from attnames using the passed tupledesc for those attribute whose
+  * numbers could not be resolved earlier.
+  */
+ static void
+ StoreNotNullConstraints(Oid relid, Oid nspid, List *constraints, char *relname,
+ 						TupleDesc tupdesc)
+ {
+ 	ListCell	*cell;
+ 
+ 	foreach(cell, constraints)
+ 	{
+ 		NotNullConstraint *constraint = lfirst(cell);
+ 
+ 		if (constraint->attnum == InvalidAttrNumber)
+ 		{
+ 			int		i;
+ 
+ 			/*
+ 			 * XXX how can we avoid looping over the tupdesc for each
+ 			 * constraint?  Is this worth optimizing?
+ 			 */
+ 			for (i = 0; i < tupdesc->natts; i++)
+ 			{
+ 				if (strcmp(NameStr(tupdesc->attrs[i]->attname),
+ 						   constraint->attname) == 0)
+ 				{
+ 					constraint->attnum = i + 1;
+ 					break;
+ 				}
+ 			}
+ 		}
+ 
+ 		if (constraint->conname == NULL)
+ 			constraint->conname =
+ 				ChooseConstraintName(relname, constraint->attname,
+ 									 "not_null", nspid, NIL);
+ 
+ 		StoreNotNullConstraint(relid, nspid, constraint);
+ 	}
+ }
+ 
+ /*
   * Emit the right error or warning message for a "DROP" command issued on a
   * non-existent relation
   */
***************
*** 1193,1203 **** storage_name(char c)
   *		of ColumnDef's.) It is destructively changed.
   * 'supers' is a list of names (as RangeVar nodes) of parent relations.
   * 'relpersistence' is a persistence type of the table.
   *
   * Output arguments:
   * 'supOids' receives a list of the OIDs of the parent relations.
!  * 'supconstr' receives a list of constraints belonging to the parents,
   *		updated as necessary to be valid for the child.
   * 'supOidCount' is set to the number of parents that have OID columns.
   *
   * Return value:
--- 1267,1282 ----
   *		of ColumnDef's.) It is destructively changed.
   * 'supers' is a list of names (as RangeVar nodes) of parent relations.
   * 'relpersistence' is a persistence type of the table.
+  * 'relkind' is the relkind of the table.
   *
   * Output arguments:
   * 'supOids' receives a list of the OIDs of the parent relations.
!  * 'supconstr' receives a list of CHECK constraints belonging to the parents,
   *		updated as necessary to be valid for the child.
+  * 'nullconstr' receives a list of NOT NULL constraints, both the parents'
+  *      and locally defined by the child definition. NB - only filled if
+  *      it's a RELKIND_RELATION (i.e. plain table).  Otherwise, not nulls
+  *      are only recorded in the is_not_null flag of each column
   * 'supOidCount' is set to the number of parents that have OID columns.
   *
   * Return value:
***************
*** 1243,1259 **** storage_name(char c)
   *----------
   */
  static List *
! MergeAttributes(List *schema, List *supers, char relpersistence,
! 				List **supOids, List **supconstr, int *supOidCount)
  {
  	ListCell   *entry;
  	List	   *inhSchema = NIL;
  	List	   *parentOids = NIL;
  	List	   *constraints = NIL;
  	int			parentsWithOids = 0;
  	bool		have_bogus_defaults = false;
  	int			child_attno;
  	static Node bogus_marker = {0};		/* marks conflicting defaults */
  
  	/*
  	 * Check for and reject tables with too many columns. We perform this
--- 1322,1341 ----
   *----------
   */
  static List *
! MergeAttributes(List *schema, List *supers, char relpersistence, char relkind,
! 				List **supOids, List **supconstr, List **nullconstr,
! 				int *supOidCount)
  {
  	ListCell   *entry;
  	List	   *inhSchema = NIL;
  	List	   *parentOids = NIL;
  	List	   *constraints = NIL;
+ 	List	   *notnullconstrs = NIL;
  	int			parentsWithOids = 0;
  	bool		have_bogus_defaults = false;
  	int			child_attno;
  	static Node bogus_marker = {0};		/* marks conflicting defaults */
+ 	bool		collect_notnulls = relkind == RELKIND_RELATION;
  
  	/*
  	 * Check for and reject tables with too many columns. We perform this
***************
*** 1311,1316 **** MergeAttributes(List *schema, List *supers, char relpersistence,
--- 1393,1399 ----
  					 * merge the column options into the column from the type
  					 */
  					coldef->is_not_null = restdef->is_not_null;
+ 					coldef->is_primary_key = restdef->is_primary_key;
  					coldef->raw_default = restdef->raw_default;
  					coldef->cooked_default = restdef->cooked_default;
  					coldef->constraints = restdef->constraints;
***************
*** 1342,1347 **** MergeAttributes(List *schema, List *supers, char relpersistence,
--- 1425,1432 ----
  		TupleConstr *constr;
  		AttrNumber *newattno;
  		AttrNumber	parent_attno;
+ 		List	   *parent_nns;
+ 		ListCell   *cell;
  
  		/*
  		 * A self-exclusive lock is needed here.  If two backends attempt to
***************
*** 1358,1365 **** MergeAttributes(List *schema, List *supers, char relpersistence,
  					 errmsg("inherited relation \"%s\" is not a table",
  							parent->relname)));
  		/* Permanent rels cannot inherit from temporary ones */
! 		if (relpersistence != RELPERSISTENCE_TEMP
! 			&& RelationUsesTempNamespace(relation))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("cannot inherit from temporary relation \"%s\"",
--- 1443,1450 ----
  					 errmsg("inherited relation \"%s\" is not a table",
  							parent->relname)));
  		/* Permanent rels cannot inherit from temporary ones */
! 		if (relpersistence != RELPERSISTENCE_TEMP &&
! 			RelationUsesTempNamespace(relation))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("cannot inherit from temporary relation \"%s\"",
***************
*** 1387,1395 **** MergeAttributes(List *schema, List *supers, char relpersistence,
  		if (relation->rd_rel->relhasoids)
  			parentsWithOids++;
  
! 		tupleDesc = RelationGetDescr(relation);
  		constr = tupleDesc->constr;
  
  		/*
  		 * newattno[] will contain the child-table attribute numbers for the
  		 * attributes of this parent table.  (They are not the same for
--- 1472,1508 ----
  		if (relation->rd_rel->relhasoids)
  			parentsWithOids++;
  
! 		/*
! 		 * We want to inherit NOT NULL constraints, but not primary keys.
! 		 * Since attnotnull flags in pg_attribute stores both, we want to keep
! 		 * the attnotnull flag only on those columns that have it from NOT NULL
! 		 * constraints.  To do this, we create a copy of the table's descriptor
! 		 * and scribble on it by resetting all the attnotnull bits to false,
! 		 * and the setting them true for columns that appear in a NOT NULL
! 		 * constraint.
! 		 *
! 		 * Note: we cannot use CreateTupleDescCopy here, because it'd lose the
! 		 * atthasdef bits, as well as constraints, both of which we need below.
! 		 */
! 		tupleDesc = CreateTupleDescCopyConstr(RelationGetDescr(relation));
  		constr = tupleDesc->constr;
  
+ 		if (collect_notnulls)
+ 		{
+ 			parent_nns = GetRelationNotNullConstraints(relation);
+ 
+ 			for (parent_attno = 1; parent_attno <= tupleDesc->natts;
+ 				 parent_attno++)
+ 				tupleDesc->attrs[parent_attno - 1]->attnotnull = false;
+ 
+ 			foreach (cell, parent_nns)
+ 			{
+ 				NotNullConstraint *constr = lfirst(cell);
+ 
+ 				tupleDesc->attrs[constr->attnum - 1]->attnotnull = true;
+ 			}
+ 		}
+ 
  		/*
  		 * newattno[] will contain the child-table attribute numbers for the
  		 * attributes of this parent table.  (They are not the same for
***************
*** 1429,1434 **** MergeAttributes(List *schema, List *supers, char relpersistence,
--- 1542,1548 ----
  				Oid			defTypeId;
  				int32		deftypmod;
  				Oid			defCollId;
+ 				NotNullConstraint *nnconstr;
  
  				/*
  				 * Yes, try to merge the two column definitions. They must
***************
*** 1471,1477 **** MergeAttributes(List *schema, List *supers, char relpersistence,
  									   storage_name(attribute->attstorage))));
  
  				def->inhcount++;
! 				/* Merge of NOT NULL constraints = OR 'em together */
  				def->is_not_null |= attribute->attnotnull;
  				/* Default and other constraints are handled below */
  				newattno[parent_attno - 1] = exist_attno;
--- 1585,1634 ----
  									   storage_name(attribute->attstorage))));
  
  				def->inhcount++;
! 
! 				/*
! 				 * Merge NOT NULL constraints: create a new NotNullConstraint
! 				 * if it wasn't NOT NULL in the previous parents; bump inhcount
! 				 * if it was.
! 				 */
! 				if (collect_notnulls)
! 				{
! 					if (def->is_not_null && attribute->attnotnull)
! 					{
! 						bool	found = false;
! 						ListCell *cell;
! 
! 						foreach (cell, notnullconstrs)
! 						{
! 							nnconstr = lfirst(cell);
! 							if (strcmp(nnconstr->attname, def->colname) == 0)
! 							{
! 								found = true;
! 								break;
! 							}
! 						}
! 
! 						nnconstr->inhcount++;
! 
! 						if (!found)
! 							elog(ERROR, "missing Constraint for NOT NULL");
! 					}
! 					else if (attribute->attnotnull)
! 					{
! 						NotNullConstraint *nnconstr;
! 
! 						nnconstr = (NotNullConstraint *)
! 							palloc(sizeof(NotNullConstraint));
! 						nnconstr->conname = NULL;	/* figure out later */
! 						nnconstr->attname = pstrdup(def->colname);
! 						nnconstr->attnum = exist_attno;
! 						nnconstr->is_local = false;
! 						nnconstr->inhcount = 1;
! 						notnullconstrs = lappend(notnullconstrs, nnconstr);
! 					}
! 				}
! 
! 				/* also OR the NOT NULL bits in the ColDef */
  				def->is_not_null |= attribute->attnotnull;
  				/* Default and other constraints are handled below */
  				newattno[parent_attno - 1] = exist_attno;
***************
*** 1495,1502 **** MergeAttributes(List *schema, List *supers, char relpersistence,
  				def->collClause = NULL;
  				def->collOid = attribute->attcollation;
  				def->constraints = NIL;
- 				inhSchema = lappend(inhSchema, def);
  				newattno[parent_attno - 1] = ++child_attno;
  			}
  
  			/*
--- 1652,1673 ----
  				def->collClause = NULL;
  				def->collOid = attribute->attcollation;
  				def->constraints = NIL;
  				newattno[parent_attno - 1] = ++child_attno;
+ 
+ 				if (def->is_not_null && collect_notnulls)
+ 				{
+ 					NotNullConstraint *nnconstr;
+ 
+ 					nnconstr = (NotNullConstraint *) palloc(sizeof(NotNullConstraint));
+ 					nnconstr->conname = NULL;	/* figure out later */
+ 					nnconstr->attname = pstrdup(def->colname);
+ 					nnconstr->attnum = child_attno;
+ 					nnconstr->is_local = false;
+ 					nnconstr->inhcount = 1;
+ 					notnullconstrs = lappend(notnullconstrs, nnconstr);
+ 				}
+ 
+ 				inhSchema = lappend(inhSchema, def);
  			}
  
  			/*
***************
*** 1580,1585 **** MergeAttributes(List *schema, List *supers, char relpersistence,
--- 1751,1791 ----
  			}
  		}
  
+ 		/*
+ 		 * We also have to adjust the NOT NULL constraints that we added to the
+ 		 * child's list, by filling in the parent's name for the constraint if
+ 		 * appropriate.
+ 		 */
+ 		if (list_length(parent_nns) > 0)
+ 		{
+ 			ListCell   *childcell;
+ 
+ 			foreach (childcell, notnullconstrs)
+ 			{
+ 				NotNullConstraint *childconstr = lfirst(childcell);
+ 				ListCell   *cell;
+ 
+ 				/* name already claimed by a previous parent */
+ 				if (childconstr->conname != NULL)
+ 					continue;
+ 
+ 				foreach (cell, parent_nns)
+ 				{
+ 					NotNullConstraint *constr = lfirst(cell);
+ 
+ 					if (strcmp(childconstr->attname, constr->attname) == 0)
+ 					{
+ 						childconstr->conname = pstrdup(constr->conname);
+ 						break;
+ 					}
+ 				}
+ 
+ 				Assert(childconstr->conname != NULL);
+ 			}
+ 
+ 			/* XXX free the parent's list memory */
+ 		}
+ 
  		pfree(newattno);
  
  		/*
***************
*** 1592,1601 **** MergeAttributes(List *schema, List *supers, char relpersistence,
  
  	/*
  	 * If we had no inherited attributes, the result schema is just the
! 	 * explicitly declared columns.  Otherwise, we need to merge the declared
! 	 * columns into the inherited schema list.
  	 */
! 	if (inhSchema != NIL)
  	{
  		foreach(entry, schema)
  		{
--- 1798,1836 ----
  
  	/*
  	 * If we had no inherited attributes, the result schema is just the
! 	 * explicitly declared columns; we only need to walk it to generate NOT
! 	 * NULL constraint nodes.  If there are inherited attributes, we need to
! 	 * merge the declared columns into the inherited schema list.
  	 */
! 	if (inhSchema == NIL)
! 	{
! 		if (collect_notnulls)
! 		{
! 			foreach(entry, schema)
! 			{
! 				ColumnDef  *newdef = lfirst(entry);
! 
! 				if (newdef->is_not_null)
! 				{
! 					NotNullConstraint	*constr;
! 					Constraint		*constraint;
! 
! 					constraint = locate_notnull_constraint(newdef);
! 
! 					constr = (NotNullConstraint *)
! 						palloc(sizeof(NotNullConstraint));
! 					constr->conname = constraint->conname ?
! 						pstrdup(constraint->conname) : NULL;
! 					constr->attname = pstrdup(newdef->colname);
! 					constr->attnum = InvalidAttrNumber;
! 					constr->is_local = true;
! 					constr->inhcount = 0;
! 					notnullconstrs = lappend(notnullconstrs, constr);
! 				}
! 			}
! 		}
! 	}
! 	else
  	{
  		foreach(entry, schema)
  		{
***************
*** 1660,1667 **** MergeAttributes(List *schema, List *supers, char relpersistence,
  
  				/* Mark the column as locally defined */
  				def->is_local = true;
! 				/* Merge of NOT NULL constraints = OR 'em together */
  				def->is_not_null |= newdef->is_not_null;
  				/* If new def has a default, override previous default */
  				if (newdef->raw_default != NULL)
  				{
--- 1895,1973 ----
  
  				/* Mark the column as locally defined */
  				def->is_local = true;
! 
! 				/*
! 				 * Handle NOT NULL constraints.  We have two cases on which
! 				 * we need to do something: (1) if parents and child define the
! 				 * column as not null, there's already a NotNullConstraint node
! 				 * and we only need to flip the is_local bit.  We keep the
! 				 * child's name for the constraint, if defined; otherwise we
! 				 * use whatever was already there.  Finally, (2) if the child
! 				 * defines the column as not null but the parents didn't, then
! 				 * create a new NotNull node.
! 				 *
! 				 * The other two possibilities are (a) the parents define the
! 				 * column as not null, but the child doesn't; we don't need to
! 				 * do anything here because the constraint node is already in
! 				 * the list, and there's no need to tweak it.  Finally, (b)
! 				 * neither parents nor child define the column as not null.
! 				 */
! 				if (collect_notnulls)
! 				{
! 					if (def->is_not_null && newdef->is_not_null)	/* case 1 */
! 					{
! 						ListCell *cell;
! 						NotNullConstraint *nnconstr;
! 						Constraint	*constraint;
! 						bool		found;
! 
! 						constraint = locate_notnull_constraint(newdef);
! 
! 						/* locate the existing node */
! 						found = false;
! 						foreach (cell, notnullconstrs)
! 						{
! 							nnconstr = lfirst(cell);
! 
! 							if (strcmp(nnconstr->attname, newdef->colname) == 0)
! 							{
! 								found = true;
! 								break;
! 							}
! 						}
! 						if (!found)
! 							elog(ERROR, "missing NotNullConstraint node");
! 
! 						if (constraint->conname != NULL)
! 						{
! 							pfree(nnconstr->conname);
! 							nnconstr->conname = pstrdup(constraint->conname);
! 						}
! 						nnconstr->is_local = true;
! 
! 						/* This node is already in the NOT NULL list */
! 					}
! 					else if (!def->is_not_null && newdef->is_not_null)
! 					{
! 						Constraint			*constraint;
! 						NotNullConstraint	*constr;
! 
! 						constraint = locate_notnull_constraint(newdef);
! 						constr = (NotNullConstraint *)
! 							palloc(sizeof(NotNullConstraint));
! 						constr->conname = constraint->conname ?
! 							pstrdup(constraint->conname) : NULL;
! 						constr->attname = pstrdup(newdef->colname);
! 						constr->attnum = exist_attno;
! 						constr->is_local = true;
! 						constr->inhcount = 0;
! 						notnullconstrs = lappend(notnullconstrs, constr);
! 					}
! 				}
! 
! 				/* ... as for the columnDef list, just OR the bits together */
  				def->is_not_null |= newdef->is_not_null;
+ 
  				/* If new def has a default, override previous default */
  				if (newdef->raw_default != NULL)
  				{
***************
*** 1672,1680 **** MergeAttributes(List *schema, List *supers, char relpersistence,
  			else
  			{
  				/*
! 				 * No, attach new column to result schema
  				 */
  				inhSchema = lappend(inhSchema, newdef);
  			}
  		}
  
--- 1978,2004 ----
  			else
  			{
  				/*
! 				 * No, attach new column to result schema and handle NOT NULL
  				 */
  				inhSchema = lappend(inhSchema, newdef);
+ 
+ 				if (newdef->is_not_null && collect_notnulls)
+ 				{
+ 					NotNullConstraint	*constr;
+ 					Constraint		*constraint;
+ 
+ 					constraint = locate_notnull_constraint(newdef);
+ 
+ 					constr = (NotNullConstraint *)
+ 						palloc(sizeof(NotNullConstraint));
+ 					constr->conname = constraint->conname ?
+ 						pstrdup(constraint->conname) : NULL;
+ 					constr->attname = pstrdup(newdef->colname);
+ 					constr->attnum = InvalidAttrNumber;
+ 					constr->is_local = true;
+ 					constr->inhcount = 0;
+ 					notnullconstrs = lappend(notnullconstrs, constr);
+ 				}
  			}
  		}
  
***************
*** 1712,1717 **** MergeAttributes(List *schema, List *supers, char relpersistence,
--- 2036,2042 ----
  
  	*supOids = parentOids;
  	*supconstr = constraints;
+ 	*nullconstr = notnullconstrs;
  	*supOidCount = parentsWithOids;
  	return schema;
  }
***************
*** 1760,1765 **** MergeCheckConstraint(List *constraints, char *name, Node *expr)
--- 2085,2119 ----
  	return false;
  }
  
+ /*
+  * Find the Constraint node related to the NOT NULL marking for a column
+  */
+ static Constraint *
+ locate_notnull_constraint(ColumnDef *def)
+ {
+ 	Constraint *constraint;
+ 	bool		found = false;
+ 	ListCell   *cell;
+ 
+ 	foreach (cell, def->constraints)
+ 	{
+ 		constraint = lfirst(cell);
+ 
+ 		if (constraint->contype == CONSTR_NOTNULL)
+ 		{
+ 			found = true;
+ 			break;
+ 		}
+ 	}
+ 	/* sanity check */
+ 	if (!found)
+ 	{
+ 		constraint = NULL;		/* keep compiler quiet */
+ 		elog(ERROR, "missing Constraint node for NOT NULL constraint");
+ 	}
+ 
+ 	return constraint;
+ }
  
  /*
   * Replace varattno values in an expression tree according to the given
***************
*** 2636,2641 **** AlterTableGetLockLevel(List *cmds)
--- 2990,2996 ----
  			case AT_SetTableSpace:		/* must rewrite heap */
  			case AT_DropNotNull:		/* may change some SQL plans */
  			case AT_SetNotNull:
+ 			case AT_SetAttNotNull:
  			case AT_GenericOptions:
  				cmd_lockmode = AccessExclusiveLock;
  				break;
***************
*** 2844,2856 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
  			break;
  		case AT_DropNotNull:	/* ALTER COLUMN DROP NOT NULL */
  			ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
! 			ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
  			/* No command-specific prep needed */
  			pass = AT_PASS_DROP;
  			break;
  		case AT_SetNotNull:		/* ALTER COLUMN SET NOT NULL */
  			ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
! 			ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
  			/* No command-specific prep needed */
  			pass = AT_PASS_ADD_CONSTR;
  			break;
--- 3199,3219 ----
  			break;
  		case AT_DropNotNull:	/* ALTER COLUMN DROP NOT NULL */
  			ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
! 			if (recurse)
! 				cmd->subtype = AT_DropNotNullRecurse;
  			/* No command-specific prep needed */
  			pass = AT_PASS_DROP;
  			break;
+ 		case AT_SetAttNotNull:	/* used by DefineIndex in PRIMARY KEY case */
+ 			ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
+ 			/* This command never recurses */
+ 			/* No command-specific prep needed */
+ 			pass = AT_PASS_ADD_CONSTR;
+ 			break;
  		case AT_SetNotNull:		/* ALTER COLUMN SET NOT NULL */
  			ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
! 			if (recurse)
! 				cmd->subtype = AT_SetNotNullRecurse;
  			/* No command-specific prep needed */
  			pass = AT_PASS_ADD_CONSTR;
  			break;
***************
*** 3096,3105 **** ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
  			ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
  			break;
  		case AT_DropNotNull:	/* ALTER COLUMN DROP NOT NULL */
! 			ATExecDropNotNull(rel, cmd->name, lockmode);
  			break;
  		case AT_SetNotNull:		/* ALTER COLUMN SET NOT NULL */
! 			ATExecSetNotNull(tab, rel, cmd->name, lockmode);
  			break;
  		case AT_SetStatistics:	/* ALTER COLUMN SET STATISTICS */
  			ATExecSetStatistics(rel, cmd->name, cmd->def, lockmode);
--- 3459,3481 ----
  			ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
  			break;
  		case AT_DropNotNull:	/* ALTER COLUMN DROP NOT NULL */
! 			ATExecDropNotNull(rel, cmd->name, false, false, lockmode);
! 			break;
! 		case AT_DropNotNullRecurse:		/* ALTER COLUMN DROP NOT NULL with
! 										 * recursion */
! 			ATExecDropNotNull(rel, cmd->name, true, false, lockmode);
  			break;
  		case AT_SetNotNull:		/* ALTER COLUMN SET NOT NULL */
! 			ATExecSetNotNull(wqueue, tab, rel, cmd->name, false,
! 							 false, lockmode);
! 			break;
! 		case AT_SetNotNullRecurse:		/* ALTER COLUMN SET NOT NULL with
! 										 * recursion */
! 			ATExecSetNotNull(wqueue, tab, rel, cmd->name, true,
! 							 false, lockmode);
! 			break;
! 		case AT_SetAttNotNull:
! 			ATExecSetAttNotNull(wqueue, tab, rel, cmd->name, lockmode);
  			break;
  		case AT_SetStatistics:	/* ALTER COLUMN SET STATISTICS */
  			ATExecSetStatistics(rel, cmd->name, cmd->def, lockmode);
***************
*** 3547,3555 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
  	{
  		/*
  		 * If we are rebuilding the tuples OR if we added any new NOT NULL
! 		 * constraints, check all not-null constraints.  This is a bit of
! 		 * overkill but it minimizes risk of bugs, and heap_attisnull is a
! 		 * pretty cheap test anyway.
  		 */
  		for (i = 0; i < newTupDesc->natts; i++)
  		{
--- 3923,3931 ----
  	{
  		/*
  		 * If we are rebuilding the tuples OR if we added any new NOT NULL
! 		 * or primary key constraints, check all not-null constraints.  This is
! 		 * a bit of overkill but it minimizes risk of bugs, and heap_attisnull
! 		 * is a pretty cheap test anyway.
  		 */
  		for (i = 0; i < newTupDesc->natts; i++)
  		{
***************
*** 3687,3694 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
  				if (heap_attisnull(tuple, attn + 1))
  					ereport(ERROR,
  							(errcode(ERRCODE_NOT_NULL_VIOLATION),
! 							 errmsg("column \"%s\" contains null values",
! 								NameStr(newTupDesc->attrs[attn]->attname))));
  			}
  
  			foreach(l, tab->constraints)
--- 4063,4071 ----
  				if (heap_attisnull(tuple, attn + 1))
  					ereport(ERROR,
  							(errcode(ERRCODE_NOT_NULL_VIOLATION),
! 							 errmsg("column \"%s\" of relation \"%s\" contains null values",
! 									NameStr(newTupDesc->attrs[attn]->attname),
! 									RelationGetRelationName(oldrel))));
  			}
  
  			foreach(l, tab->constraints)
***************
*** 4192,4197 **** ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
--- 4569,4575 ----
  	Oid			collOid;
  	Form_pg_type tform;
  	Expr	   *defval;
+ 	bool        need_final_cci;
  	List	   *children;
  	ListCell   *child;
  
***************
*** 4250,4255 **** ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
--- 4628,4644 ----
  			simple_heap_update(attrdesc, &tuple->t_self, tuple);
  			CatalogUpdateIndexes(attrdesc, tuple);
  
+ 			/*
+ 			 * If the new column is marked NOT NULL, we need to increment
+ 			 * inhcount to an existing NOT NULL constraint in the child, or
+ 			 * create a new one if it doesn't exist.  Skip this for the OID
+ 			 * column.
+ 			 */
+ 			if (colDef->is_not_null && !isOid)
+ 				CreateOrAdjustNotNullConstraint(rel, childatt->attnum,
+ 												NameStr(childatt->attname),
+ 												1, 0);
+ 
  			heap_freetuple(tuple);
  
  			/* Inform the user about the merge */
***************
*** 4317,4323 **** ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
  	attribute.attndims = list_length(colDef->typeName->arrayBounds);
  	attribute.attstorage = tform->typstorage;
  	attribute.attalign = tform->typalign;
! 	attribute.attnotnull = colDef->is_not_null;
  	attribute.atthasdef = false;
  	attribute.attisdropped = false;
  	attribute.attislocal = colDef->is_local;
--- 4706,4712 ----
  	attribute.attndims = list_length(colDef->typeName->arrayBounds);
  	attribute.attstorage = tform->typstorage;
  	attribute.attalign = tform->typalign;
! 	attribute.attnotnull = colDef->is_not_null | colDef->is_primary_key;
  	attribute.atthasdef = false;
  	attribute.attisdropped = false;
  	attribute.attislocal = colDef->is_local;
***************
*** 4355,4360 **** ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
--- 4744,4751 ----
  	/* Make the attribute's catalog entry visible */
  	CommandCounterIncrement();
  
+ 	need_final_cci = false;
+ 
  	/*
  	 * Store the DEFAULT, if any, in the catalogs
  	 */
***************
*** 4377,4387 **** ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
  		 */
  		AddRelationNewConstraints(rel, list_make1(rawEnt), NIL, false, true);
  
! 		/* Make the additional catalog changes visible */
! 		CommandCounterIncrement();
  	}
  
  	/*
  	 * Tell Phase 3 to fill in the default expression, if there is one.
  	 *
  	 * If there is no default, Phase 3 doesn't have to do anything, because
--- 4768,4793 ----
  		 */
  		AddRelationNewConstraints(rel, list_make1(rawEnt), NIL, false, true);
  
! 		need_final_cci = true;
! 	}
! 
! 	/* Finally, store the NOT NULL constraint, if necessary, except for OID */
! 	if (colDef->is_not_null && !isOid)
! 	{
! 		Constraint	*constraint = locate_notnull_constraint(colDef);
! 
! 		CreateNotNullConstraint(rel, attribute.attnum, constraint->conname,
! 								colDef->inhcount, colDef->is_local);
! 		need_final_cci = true;
  	}
  
  	/*
+ 	 * Make new defaults and NOT NULL constraints visible.
+ 	 */
+ 	if (need_final_cci)
+ 		CommandCounterIncrement();
+ 
+ 	/*
  	 * Tell Phase 3 to fill in the default expression, if there is one.
  	 *
  	 * If there is no default, Phase 3 doesn't have to do anything, because
***************
*** 4449,4460 **** ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
  		}
  
  		/*
! 		 * If the new column is NOT NULL, tell Phase 3 it needs to test that.
! 		 * (Note we don't do this for an OID column.  OID will be marked not
! 		 * null, but since it's filled specially, there's no need to test
! 		 * anything.)
  		 */
! 		tab->new_notnull |= colDef->is_not_null;
  	}
  
  	/*
--- 4855,4866 ----
  		}
  
  		/*
! 		 * If the new column is either NOT NULL or primary key, tell Phase 3 it
! 		 * needs to test that.  (Note we don't do this for an OID column.  OID
! 		 * will be marked not null, but since it's filled specially, there's no
! 		 * need to test anything.)
  		 */
! 		tab->new_notnull |= (colDef->is_not_null | colDef->is_primary_key);
  	}
  
  	/*
***************
*** 4585,4606 **** ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOC
  
  /*
   * ALTER TABLE ALTER COLUMN DROP NOT NULL
   */
! static void
! ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
  {
  	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,
--- 4991,5257 ----
  
  /*
   * ALTER TABLE ALTER COLUMN DROP NOT NULL
+  *
+  * For inheritance children, the consequences are nonobvious: if we're asked
+  * to recurse, we have to recurse to delete the constraint from them too.  If
+  * we're asked not to recurse, we have to recurse anyway, but instead of
+  * deleting we need to decrement the coninhcount for the constraint, and set
+  * conislocal if inhcount goes to zero.
   */
! static bool
! ATExecDropNotNull(Relation rel, const char *colName,
! 				  bool recurse, bool recursing, LOCKMODE lockmode)
! {
! 	List	   *children;
! 	ListCell   *child;
! 	Oid			relid;
! 	HeapTuple   attrtup;
! 	HeapTuple   constrtup;
! 	Relation    pg_attr;
! 	Relation    pg_con;
! 	Form_pg_attribute attrform;
! 	Form_pg_constraint constrform;
! 	bool		do_drop;
! 	bool		retval = false;
! 
! 	/* At top level, permission check was done in ATPrepCmd, else do it */
! 	if (recursing)
! 		ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
! 
! 	relid = RelationGetRelid(rel);
! 	children = find_inheritance_children(relid, lockmode);
! 
! 	/* lookup the pg_attribute row */
! 	pg_attr = heap_open(AttributeRelationId, RowExclusiveLock);
! 	attrtup = SearchSysCacheCopyAttName(relid, colName);
! 
! 	if (!HeapTupleIsValid(attrtup))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_UNDEFINED_COLUMN),
! 				 errmsg("column \"%s\" of relation \"%s\" does not exist",
! 						colName, RelationGetRelationName(rel))));
! 
! 	attrform = (Form_pg_attribute) GETSTRUCT(attrtup);
! 
! 	/* Prevent them from altering a system attribute */
! 	if (attrform->attnum <= 0)
! 		ereport(ERROR,
! 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("cannot alter system column \"%s\"", colName)));
! 
! 	/* Easy case: if attnotnull is not set, there's nothing to do */
! 	if (!attrform->attnotnull)
! 	{
! 		heap_freetuple(attrtup);
! 		heap_close(pg_attr, RowExclusiveLock);
! 		return false;
! 	}
! 
! 	/*
! 	 * Look up the pg_constraint row.  If there's none, then the attnotnull
! 	 * mark is due to a primary key, and we must not touch it.
! 	 *
! 	 * XXX We could, but do not, attempt to locate the pg_constraint row for
! 	 * the primary key, to ensure that what our message says is true.
! 	 */
! 	pg_con = heap_open(ConstraintRelationId, RowExclusiveLock);
! 	constrtup = RelationFetchNotNullConstraint(pg_con, relid, attrform->attnum);
! 	if (!constrtup)
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
! 				 errmsg("column \"%s\" is in a primary key", colName)));
! 
! 	constrform = (Form_pg_constraint) GETSTRUCT(constrtup);
! 	
! 	/*
! 	 * Finally, figure out if we're to actually drop the constraint or not.  If
! 	 * this is the relation for which we were directly called, we need to
! 	 * forbid dropping the constraint if it's inherited; they need to drop it
! 	 * from the parent instead.
! 	 *
! 	 * On the other hand, if we're recursing, we actually drop the constraint
! 	 * only if it's not locally defined and there are no other parents forcing
! 	 * this constraint, *and* we're asked to recurse.  Otherwise, just update
! 	 * the pg_constraint tuple: decrement coninhcount in all cases, and if not
! 	 * asked to recurse we need to set conislocal to true in case it goes to
! 	 * zero.  Otherwise (i.e. inhcount is zero and we're asked to recurse), do
! 	 * drop the contraint.
! 	 */
! 	do_drop = false;
! 	if (!recursing)
! 	{
! 		if (constrform->coninhcount != 0)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
! 					 errmsg("cannot drop inherited NOT NULL constraint \"%s\", relation \"%s\"",
! 							NameStr(constrform->conname), RelationGetRelationName(rel))));
! 		do_drop = true;
! 	}
! 	else
! 	{
! 		bool	do_update = false;
! 
! 		if (--constrform->coninhcount == 0 && !constrform->conislocal)
! 		{
! 			if (!recurse && recursing)
! 			{
! 				constrform->conislocal = true;
! 				do_update = true;
! 			}
! 			else
! 				do_drop = true;
! 		}
! 		else
! 			do_update = true;
! 
! 		if (do_update)
! 		{
! 			Assert(!do_drop);
! 			simple_heap_update(pg_con, &(constrtup->t_self), constrtup);
! 			CatalogUpdateIndexes(pg_con, constrtup);
! 
! 			retval = true;
! 		}
! 	}
! 
! 	/*
! 	 * Okay, delete the constraint.  Note that we mustn't update pg_attribute
! 	 * if there's a PK involving the column.
! 	 */
! 	if (do_drop)
! 	{
! 		ObjectAddress	conobj;
! 		HeapTuple	pktup;
! 		bool		ispk;
! 		SysScanDesc	scan;
! 		ScanKeyData	key[1];
! 
! 		conobj.classId = ConstraintRelationId;
! 		conobj.objectId = HeapTupleGetOid(constrtup);
! 		conobj.objectSubId = 0;
! 
! 		performDeletion(&conobj, DROP_CASCADE);
! 
! 		ScanKeyInit(&key[0],
! 					Anum_pg_constraint_conrelid,
! 					BTEqualStrategyNumber, F_OIDEQ,
! 					ObjectIdGetDatum(relid));
! 
! 		scan = systable_beginscan(pg_con, ConstraintRelidIndexId,
! 								  true, SnapshotNow, 1, key);
! 		ispk = false;
! 		while (HeapTupleIsValid(pktup = systable_getnext(scan)))
! 		{
! 			Form_pg_constraint	pkform;
! 			Datum	arrayp;
! 			Datum  *keyvals;
! 			int		nelems;
! 			bool	isnull;
! 			int		i;
! 
! 			pkform = (Form_pg_constraint) GETSTRUCT(pktup);
! 			if (pkform->contype != CONSTRAINT_PRIMARY)
! 				continue;
! 
! 			arrayp = SysCacheGetAttr(CONSTROID, pktup,
! 									 Anum_pg_constraint_conkey,
! 									 &isnull);
! 			if (isnull)
! 				elog(ERROR, "null conkey found");
! 
! 			deconstruct_array(DatumGetArrayTypeP(arrayp), INT2OID, 2, true,
! 							  's', &keyvals, NULL, &nelems);
! 			for (i = 0; i < nelems; i++)
! 			{
! 				if (DatumGetInt16(keyvals[i]) == attrform->attnum)
! 				{
! 					ispk = true;
! 					break;
! 				}
! 			}
! 			if (ispk)
! 				break;
! 		}
! 		systable_endscan(scan);
! 
! 		/* it's not PK -- reset attnotnull */
! 		if (!ispk)
! 		{
! 			attrform->attnotnull = false;
! 			simple_heap_update(pg_attr, &(attrtup->t_self), attrtup);
! 			CatalogUpdateIndexes(pg_attr, attrtup);
! 		}
! 
! 		/*
! 		 * Perform work on all children, if any.  Note that if a children is
! 		 * altered during recursion, we need a CommandCounterIncrement, as in
! 		 * ATExecSetNotNull; see comments therein.  (This is only necessary in
! 		 * case we update pg_constraint, though; if we do a delete there, or in
! 		 * case we update pg_attribute, there's no need for this.)
! 		 */
! 		foreach(child, children)
! 		{
! 			Oid childrelid = lfirst_oid(child);
! 			Relation childrel;
! 
! 			Assert(!recursing || recurse);
! 
! 			/* find_inheritance_children already got lock */
! 			childrel = heap_open(childrelid, NoLock);
! 			CheckTableNotInUse(childrel, "ALTER TABLE");
! 
! 			if (ATExecDropNotNull(childrel, colName, recurse, true, lockmode))
! 				CommandCounterIncrement();
! 
! 			heap_close(childrel, NoLock);
! 		}
! 	}
! 
! 	heap_close(pg_con, RowExclusiveLock);
! 	heap_close(pg_attr, RowExclusiveLock);
! 
! 	return retval;
! }
! 
! /*
!  * ALTER TABLE ALTER COLUMN SET NOT NULL
!  *
!  * Recursively add not null constraints to the given relation and column name.
!  * If the constraint is inherited, walk down the inheritance tree to ensure the
!  * inheritance counter for each not null constraint is adjusted accordingly.
!  */
! static bool
! ATExecSetNotNull(List **wqueue, AlteredTableInfo *tab, Relation rel,
! 				 const char *colName, bool recurse, bool recursing,
! 				 LOCKMODE lockmode)
  {
  	HeapTuple	tuple;
  	AttrNumber	attnum;
  	Relation	attr_rel;
! 	Oid			relid;
! 	List       *children;
! 	ListCell   *cell;
! 	Relation	pg_con;
! 	HeapTuple	constrtup;
! 	Form_pg_attribute	attrtup;
! 
! 	/* At top level, permission check was done in ATPrepCmd, else do it */
! 	if (recursing)
! 		ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
  
  	/*
! 	 * We only recurse one level at a time, because if we merge a constraint
! 	 * with an existing one in a child, we have to stop recursing down that
! 	 * hierarchy path.
  	 */
+ 	children = find_inheritance_children(RelationGetRelid(rel),
+ 										 lockmode);
+ 
+ 	/* lookup the attribute */
  	attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
  
! 	relid = RelationGetRelid(rel);
! 	tuple = SearchSysCacheCopyAttName(relid, colName);
  
  	if (!HeapTupleIsValid(tuple))
  		ereport(ERROR,
***************
*** 4608,4696 **** ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
  				 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;
  
! 		indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
! 		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)
  		{
! 			/*
! 			 * Loop over each attribute in the primary key and see if it
! 			 * matches the to-be-altered attribute
! 			 */
! 			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);
  }
  
  /*
!  * ALTER TABLE ALTER COLUMN SET NOT NULL
   */
  static void
! ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
  				 const char *colName, LOCKMODE lockmode)
  {
  	HeapTuple	tuple;
  	AttrNumber	attnum;
- 	Relation	attr_rel;
  
! 	/*
! 	 * lookup the attribute
! 	 */
  	attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
  
! 	tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
  
  	if (!HeapTupleIsValid(tuple))
  		ereport(ERROR,
--- 5259,5414 ----
  				 errmsg("column \"%s\" of relation \"%s\" does not exist",
  						colName, RelationGetRelationName(rel))));
  
! 	attrtup = (Form_pg_attribute) GETSTRUCT(tuple);
! 	attnum = attrtup->attnum;
  
  	/* Prevent them from altering a system attribute */
  	if (attnum <= 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("cannot alter system column \"%s\" of relation \"%s\"",
! 						colName, RelationGetRelationName(rel))));
  
  	/*
! 	 * If we are told not to recurse, there had better not be any child
! 	 * tables; else the addition would put them out of step.
  	 */
+ 	if (children && !recurse)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ 				 errmsg("NOT NULL constraint must be added to child tables too")));
  
! 	pg_con = heap_open(ConstraintRelationId, RowExclusiveLock);
! 	constrtup = RelationFetchNotNullConstraint(pg_con, relid, attnum);
  
! 	/*
! 	 * At the top level of recursion, an existing constraint means we only need
! 	 * to set conislocal (if not set already).  If we're already recursing,
! 	 * however, we need to adjust attinhcount.  In both cases we stop
! 	 * recursing, because the children must already have this constraint.
! 	 */
! 	if (constrtup)
  	{
! 		Form_pg_constraint	constrform;
! 		bool			touched = false;
  
! 		constrform = (Form_pg_constraint) GETSTRUCT(constrtup);
  
! 		if (!recursing)
  		{
! 			if (!constrform->conislocal)
  			{
! 				constrform->conislocal = true;
! 				touched = true;
  			}
  		}
+ 		else
+ 		{
+ 			constrform->coninhcount++;
+ 			touched = true;
+ 		}
  
! 		if (touched)
! 		{
! 			simple_heap_update(pg_con, &(constrtup->t_self), constrtup); 
! 			CatalogUpdateIndexes(pg_con, constrtup);
! 		}
! 
! 		heap_close(pg_con, RowExclusiveLock);
! 		heap_close(attr_rel, RowExclusiveLock);
! 
! 		return touched;
  	}
  
! 	/*
! 	 * By here, we've established that the column does not already have a not
! 	 * null constraint, so we need to add one and recurse to ensure that our
! 	 * children have it too.
! 	 */
! 	CreateNotNullConstraint(rel, attnum, NULL,
! 							recursing ? 1 : 0,
! 							recursing ? false : true);
  
  	/*
! 	 * And set attnotnull in pg_attribute.  Note that it might already be set,
! 	 * if the column belongs to a primary key.
  	 */
! 	if (!attrtup->attnotnull)
  	{
! 		HeapTuple	copy_attr = heap_copytuple(tuple);
! 		Form_pg_attribute attrform = (Form_pg_attribute) GETSTRUCT(copy_attr);
  
! 		attrform->attnotnull = true;
  
! 		simple_heap_update(attr_rel, &(copy_attr->t_self), copy_attr);
! 		CatalogUpdateIndexes(attr_rel, copy_attr);
  	}
  
+ 	/* Tell Phase 3 it needs to test the constraint */
+ 	tab->new_notnull = true;
+ 
  	heap_close(attr_rel, RowExclusiveLock);
+ 	heap_close(pg_con, RowExclusiveLock);
+ 
+ 	/*
+ 	 * Recurse.  Note that if a child is altered during recursion, we need
+ 	 * a CommandCounterIncrement in case that table is also a direct child
+ 	 * of our parent.  (Skipping that would mean that we would try to update
+ 	 * its pg_constraint and pg_attribute rows twice without an intervening
+ 	 * CCI, leading to "tuple updated by self").  Maybe there's a way to avoid
+ 	 * this overhead by building a table of coninhcount increments we need to
+ 	 * apply to each relation before actually applying them, but it doesn't
+ 	 * seem worth the trouble (and it doesn't help with pg_attribute updates
+ 	 * anyway.)
+ 	 */
+ 	foreach(cell, children)
+ 	{
+ 		Oid			childrelid = lfirst_oid(cell);
+ 		Relation	childrel;
+ 		AlteredTableInfo *childtab;
+ 
+ 		/* find_inheritance_children already got lock */
+ 		childrel = heap_open(childrelid, NoLock);
+ 		CheckTableNotInUse(childrel, "ALTER TABLE");
+ 
+ 		/* get work queue entry for child relation */
+ 		childtab = ATGetQueueEntry(wqueue, childrel);
+ 
+ 		/* perform the actual work */
+ 		if (ATExecSetNotNull(wqueue, childtab, childrel,
+ 							 colName, recurse, true,
+ 							 lockmode))
+ 			CommandCounterIncrement();
+ 
+ 		heap_close(childrel, NoLock);
+ 	}
+ 
+ 	return true;
  }
  
  /*
!  * ATExecSetAttNotNull
!  *
!  * This is not SQL-accessible directly -- it's only used by DefineIndex to
!  * ensure that pg_attribute.attnotnull is set for PRIMARY KEY columns.  The
!  * differences with plain SET NOT NULL is that we don't create pg_constraint
!  * entries, and we do not recurse either.
   */
  static void
! ATExecSetAttNotNull(List **wqueue, AlteredTableInfo *tab, Relation rel,
  				 const char *colName, LOCKMODE lockmode)
  {
+ 	Relation	attr_rel;
+ 	Oid			relid;
  	HeapTuple	tuple;
+ 	Form_pg_attribute	attrtup;
  	AttrNumber	attnum;
  
! 	/* lookup the attribute */
  	attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
  
! 	relid = RelationGetRelid(rel);
! 	tuple = SearchSysCacheCopyAttName(relid, colName);
  
  	if (!HeapTupleIsValid(tuple))
  		ereport(ERROR,
***************
*** 4698,4731 **** ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
  				 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)));
  
  	/*
! 	 * 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);
  
! 		/* keep the system catalog indexes current */
! 		CatalogUpdateIndexes(attr_rel, tuple);
! 
! 		/* Tell Phase 3 it needs to test the constraint */
! 		tab->new_notnull = true;
  	}
  
  	heap_close(attr_rel, RowExclusiveLock);
  }
  
  /*
   * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
   */
--- 5416,5453 ----
  				 errmsg("column \"%s\" of relation \"%s\" does not exist",
  						colName, RelationGetRelationName(rel))));
  
! 	attrtup = (Form_pg_attribute) GETSTRUCT(tuple);
! 	attnum = attrtup->attnum;
  
  	/* Prevent them from altering a system attribute */
  	if (attnum <= 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("cannot alter system column \"%s\" of relation \"%s\"",
! 						colName, RelationGetRelationName(rel))));
  
  	/*
! 	 * And set attnotnull in pg_attribute.  Note that it might already be set,
! 	 * if the column has a NOT NULL constraint already.
  	 */
! 	if (!attrtup->attnotnull)
  	{
! 		HeapTuple	copy_attr = heap_copytuple(tuple);
! 		Form_pg_attribute attrform = (Form_pg_attribute) GETSTRUCT(copy_attr);
  
! 		attrform->attnotnull = true;
  
! 		simple_heap_update(attr_rel, &(copy_attr->t_self), copy_attr);
! 		CatalogUpdateIndexes(attr_rel, copy_attr);
  	}
  
+ 	/* Tell Phase 3 it needs to test the constraint */
+ 	tab->new_notnull = true;
+ 
  	heap_close(attr_rel, RowExclusiveLock);
  }
  
+ 
  /*
   * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
   */
***************
*** 6583,6588 **** createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
--- 7305,7392 ----
  }
  
  /*
+  * After dropping a primary key, we need to adjust the attnotnull flags for all
+  * columns that do not have a NOT NULL constraint.
+  *
+  * rel is the involved relation, "tuple" is the pg_constraint tuple for the
+  * dropped primary key.  pg_con is pg_constraint, suitably locked.
+  */
+ static void
+ fixup_attnotnull(Relation rel, Relation pg_con, HeapTuple pktuple)
+ {
+ 	Relation	pg_attr;
+ 	HeapTuple	contuple;
+ 	SysScanDesc	scan;
+ 	ScanKeyData	key[2];
+ 	List	   *notnulls = NIL;
+ 	List	   *nullables = NIL;
+ 	ListCell   *cell;
+ 	Datum		arrayp;
+ 	bool		isnull;
+ 	Datum	   *keyvals;
+ 	int			nelems;
+ 	int			i;
+ 	HeapTuple	attrtup;
+ 
+ 	pg_attr = heap_open(AttributeRelationId, RowExclusiveLock);
+ 
+ 	ScanKeyInit(&key[0],
+ 				Anum_pg_constraint_conrelid,
+ 				BTEqualStrategyNumber,
+ 				F_OIDEQ,
+ 				ObjectIdGetDatum(RelationGetRelid(rel)));
+ 
+ 	scan = systable_beginscan(pg_con, ConstraintRelidIndexId, true,
+ 							  SnapshotNow, 1, key);
+ 	while (HeapTupleIsValid(contuple = systable_getnext(scan)))
+ 	{
+ 		Form_pg_constraint	conForm = (Form_pg_constraint) GETSTRUCT(contuple);
+ 		AttrNumber		attno;
+ 
+ 		if (conForm->contype != CONSTRAINT_NOTNULL)
+ 			continue;
+ 
+ 		attno = get_constraint_singlecol_attno(contuple);
+ 		notnulls = lappend_int(notnulls, attno);
+ 	}
+ 	systable_endscan(scan);
+ 
+ 	arrayp = SysCacheGetAttr(CONSTROID, pktuple,
+ 							 Anum_pg_constraint_conkey,
+ 							 &isnull);
+ 	if (isnull)
+ 		elog(ERROR, "null conkey found");
+ 
+ 	deconstruct_array(DatumGetArrayTypeP(arrayp), INT2OID, 2, true,
+ 					  's', &keyvals, NULL, &nelems);
+ 	
+ 	/* this could be made a lot faster if necessary ... */
+ 	for (i = 0; i < nelems; i++)
+ 		if (!list_member_int(notnulls, DatumGetInt16(keyvals[i])))
+ 			nullables = lappend_int(nullables, DatumGetInt16(keyvals[i]));
+ 
+ 	foreach(cell, nullables)
+ 	{
+ 		AttrNumber		attr = (AttrNumber) lfirst_int(cell);
+ 		Form_pg_attribute	attrform;
+ 
+ 		attrtup = SearchSysCacheCopy2(ATTNUM,
+ 									  ObjectIdGetDatum(RelationGetRelid(rel)),
+ 									  Int16GetDatum(attr));
+ 		if (!HeapTupleIsValid(attrtup))
+ 			elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ 				 attr, RelationGetRelid(rel));
+ 		attrform = (Form_pg_attribute) GETSTRUCT(attrtup);
+ 		Assert(attrform->attnotnull == true);
+ 		attrform->attnotnull = false;
+ 		simple_heap_update(pg_attr, &attrtup->t_self, attrtup);
+ 		CatalogUpdateIndexes(pg_attr, attrtup);
+ 	}
+ 
+ 	heap_close(pg_attr, RowExclusiveLock);
+ }
+ 
+ /*
   * ALTER TABLE DROP CONSTRAINT
   *
   * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
***************
*** 6601,6607 **** ATExecDropConstraint(Relation rel, const char *constrName,
  	ScanKeyData key;
  	HeapTuple	tuple;
  	bool		found = false;
! 	bool		is_check_constraint = false;
  
  	/* At top level, permission check was done in ATPrepCmd, else do it */
  	if (recursing)
--- 7405,7411 ----
  	ScanKeyData key;
  	HeapTuple	tuple;
  	bool		found = false;
! 	bool		is_inheritable_constraint = false;
  
  	/* At top level, permission check was done in ATPrepCmd, else do it */
  	if (recursing)
***************
*** 6625,6630 **** ATExecDropConstraint(Relation rel, const char *constrName,
--- 7429,7438 ----
  
  		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;
  
***************
*** 6635,6643 **** ATExecDropConstraint(Relation rel, const char *constrName,
  					 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;
  
  		/*
  		 * Perform the actual constraint deletion
--- 7443,7454 ----
  					 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
  							constrName, RelationGetRelationName(rel))));
  
! 		/*
! 		 * Remember if we see an inheritable constraint.  We only need to
! 		 * handle CHECK constraints, because we rejected NOT NULL ones above.
! 		 */
  		if (con->contype == CONSTRAINT_CHECK)
! 			is_inheritable_constraint = true;
  
  		/*
  		 * Perform the actual constraint deletion
***************
*** 6648,6653 **** ATExecDropConstraint(Relation rel, const char *constrName,
--- 7459,7471 ----
  
  		performDeletion(&conobj, behavior);
  
+ 		/*
+ 		 * If we dropped the primary key, we need to reset the attnotnull
+ 		 * flag for all columns that don't have a NOT NULL constraint.
+ 		 */
+ 		if (con->contype == CONSTRAINT_PRIMARY)
+ 			fixup_attnotnull(rel, conrel, tuple);
+ 
  		found = true;
  	}
  
***************
*** 6677,6683 **** ATExecDropConstraint(Relation rel, const char *constrName,
  	 * routines, we have to do this one level of recursion at a time; we can't
  	 * use find_all_inheritors to do it in one pass.
  	 */
! 	if (is_check_constraint)
  		children = find_inheritance_children(RelationGetRelid(rel), lockmode);
  	else
  		children = NIL;
--- 7495,7501 ----
  	 * routines, we have to do this one level of recursion at a time; we can't
  	 * use find_all_inheritors to do it in one pass.
  	 */
! 	if (is_inheritable_constraint)
  		children = find_inheritance_children(RelationGetRelid(rel), lockmode);
  	else
  		children = NIL;
***************
*** 6706,6712 **** ATExecDropConstraint(Relation rel, const char *constrName,
  
  			con = (Form_pg_constraint) GETSTRUCT(tuple);
  
! 			/* Right now only CHECK constraints can be inherited */
  			if (con->contype != CONSTRAINT_CHECK)
  				continue;
  
--- 7524,7534 ----
  
  			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;
  
***************
*** 7501,7506 **** ATPostAlterTypeParse(char *cmd, List **wqueue, LOCKMODE lockmode)
--- 8323,8329 ----
  								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);
***************
*** 8266,8271 **** ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
--- 9089,9095 ----
  	HeapTuple	inheritsTuple;
  	int32		inhseqno;
  	List	   *children;
+ 	List       *child_attnums;
  
  	/*
  	 * A self-exclusive lock is needed here.  See the similar case in
***************
*** 8353,8362 **** ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
  						RelationGetRelationName(parent_rel))));
  
  	/* Match up the columns and bump attinhcount as needed */
! 	MergeAttributesIntoExisting(child_rel, parent_rel);
  
  	/* Match up the constraints and bump coninhcount as needed */
! 	MergeConstraintsIntoExisting(child_rel, parent_rel);
  
  	/*
  	 * OK, it looks valid.	Make the catalog entries that show inheritance.
--- 9177,9187 ----
  						RelationGetRelationName(parent_rel))));
  
  	/* Match up the columns and bump attinhcount as needed */
! 	child_attnums = NIL;
! 	MergeAttributesIntoExisting(child_rel, parent_rel, &child_attnums);
  
  	/* Match up the constraints and bump coninhcount as needed */
! 	MergeConstraintsIntoExisting(child_rel, parent_rel, child_attnums);
  
  	/*
  	 * OK, it looks valid.	Make the catalog entries that show inheritance.
***************
*** 8432,8438 **** constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
   * the child must be as well. Defaults are not compared, however.
   */
  static void
! MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
  {
  	Relation	attrrel;
  	AttrNumber	parent_attno;
--- 9257,9264 ----
   * the child must be as well. Defaults are not compared, however.
   */
  static void
! MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel,
! 							List **attnums_list)
  {
  	Relation	attrrel;
  	AttrNumber	parent_attno;
***************
*** 8488,8493 **** MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
--- 9314,9324 ----
  					   attributeName)));
  
  			/*
+ 			 * Record the child's inherited attnum in attnums_list.
+ 			 */
+ 			*attnums_list = lappend_int(*attnums_list, childatt->attnum);
+ 
+ 			/*
  			 * OK, bump the child column's inheritance count.  (If we fail
  			 * later on, this change will just roll back.)
  			 */
***************
*** 8526,8532 **** MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
   * a problem though. Even 100 constraints ought not be the end of the world.
   */
  static void
! MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
  {
  	Relation	catalog_relation;
  	TupleDesc	tuple_desc;
--- 9357,9364 ----
   * a problem though. Even 100 constraints ought not be the end of the world.
   */
  static void
! MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel,
! 							 List *child_attnums)
  {
  	Relation	catalog_relation;
  	TupleDesc	tuple_desc;
***************
*** 8553,8559 **** MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
  		HeapTuple	child_tuple;
  		bool		found = false;
  
! 		if (parent_con->contype != CONSTRAINT_CHECK)
  			continue;
  
  		/* Search for a child constraint matching this one */
--- 9385,9392 ----
  		HeapTuple	child_tuple;
  		bool		found = false;
  
! 		if (parent_con->contype != CONSTRAINT_CHECK
! 			&& parent_con->contype != CONSTRAINT_NOTNULL)
  			continue;
  
  		/* Search for a child constraint matching this one */
***************
*** 8569,8587 **** MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
  			Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
  			HeapTuple	child_copy;
  
! 			if (child_con->contype != CONSTRAINT_CHECK)
! 				continue;
! 
! 			if (strcmp(NameStr(parent_con->conname),
! 					   NameStr(child_con->conname)) != 0)
! 				continue;
! 
! 			if (!constraints_equivalent(parent_tuple, child_tuple, tuple_desc))
! 				ereport(ERROR,
! 						(errcode(ERRCODE_DATATYPE_MISMATCH),
! 						 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
! 								RelationGetRelationName(child_rel),
! 								NameStr(parent_con->conname))));
  
  			/*
  			 * OK, bump the child constraint's inheritance count.  (If we fail
--- 9402,9448 ----
  			Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
  			HeapTuple	child_copy;
  
! 			switch(child_con->contype)
! 			{
! 				case CONSTRAINT_CHECK:
! 				{
! 					if (strcmp(NameStr(parent_con->conname),
! 							   NameStr(child_con->conname)) != 0)
! 						continue;
! 
! 					if (!constraints_equivalent(parent_tuple, child_tuple, tuple_desc))
! 						ereport(ERROR,
! 								(errcode(ERRCODE_DATATYPE_MISMATCH),
! 								 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
! 										RelationGetRelationName(child_rel),
! 										NameStr(parent_con->conname))));
! 					break;
! 				}
! 				case CONSTRAINT_NOTNULL:
! 				{
! 					Datum     arrayp;
! 					Datum    *vals;
! 					int       nelems;
! 					bool      isnull;
! 
! 					if (child_attnums == NIL)
! 						continue;
! 
! 					arrayp = SysCacheGetAttr(CONSTROID, child_tuple,
! 											 Anum_pg_constraint_conkey, &isnull);
! 					/* should not happen */
! 					Assert(!isnull);
! 					deconstruct_array(DatumGetArrayTypeP(arrayp), INT2OID, 2, true,
! 									  's', &vals, NULL, &nelems);
! 					Assert(nelems > 0);
! 
! 					if (!list_member_int(child_attnums, DatumGetInt16(vals[0])))
! 						continue;
! 					break;
! 				}
! 				default:
! 					continue;
! 			}
  
  			/*
  			 * OK, bump the child constraint's inheritance count.  (If we fail
***************
*** 8624,8631 **** MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
   * surprise. But at least we'll never surprise by dropping columns someone
   * isn't expecting to be dropped which would actually mean data loss.
   *
!  * coninhcount and conislocal for inherited constraints are adjusted in
!  * exactly the same way.
   */
  static void
  ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
--- 9485,9492 ----
   * surprise. But at least we'll never surprise by dropping columns someone
   * isn't expecting to be dropped which would actually mean data loss.
   *
!  * coninhcount and conislocal for inherited CHECK and NOT NULL constraints
!  * are adjusted in exactly the same way.
   */
  static void
  ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
***************
*** 8636,8643 **** ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
  	ScanKeyData key[3];
  	HeapTuple	inheritsTuple,
  				attributeTuple,
! 				constraintTuple;
! 	List	   *connames;
  	bool		found = false;
  
  	/*
--- 9497,9504 ----
  	ScanKeyData key[3];
  	HeapTuple	inheritsTuple,
  				attributeTuple,
! 		        constraintTuple;
! 	List	   *constraints;
  	bool		found = false;
  
  	/*
***************
*** 8687,8692 **** ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
--- 9548,9555 ----
  						RelationGetRelationName(parent_rel),
  						RelationGetRelationName(rel))));
  
+ 	constraints = NIL;
+ 
  	/*
  	 * Search through child columns looking for ones matching parent rel
  	 */
***************
*** 8700,8705 **** ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
--- 9563,9569 ----
  	while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
  	{
  		Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
+ 		HeapTuple         parentattr_tuple;
  
  		/* Ignore if dropped or not inherited */
  		if (att->attisdropped)
***************
*** 8707,8714 **** ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
  		if (att->attinhcount <= 0)
  			continue;
  
! 		if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
! 										NameStr(att->attname)))
  		{
  			/* Decrement inhcount and possibly set islocal to true */
  			HeapTuple	copyTuple = heap_copytuple(attributeTuple);
--- 9571,9580 ----
  		if (att->attinhcount <= 0)
  			continue;
  
! 		parentattr_tuple = SearchSysCacheCopyAttName(RelationGetRelid(parent_rel),
! 													 NameStr(att->attname));
! 
! 		if (HeapTupleIsValid(parentattr_tuple))
  		{
  			/* Decrement inhcount and possibly set islocal to true */
  			HeapTuple	copyTuple = heap_copytuple(attributeTuple);
***************
*** 8721,8736 **** ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
  			simple_heap_update(catalogRelation, &copyTuple->t_self, copyTuple);
  			CatalogUpdateIndexes(catalogRelation, copyTuple);
  			heap_freetuple(copyTuple);
  		}
  	}
  	systable_endscan(scan);
  	heap_close(catalogRelation, RowExclusiveLock);
  
  	/*
! 	 * Likewise, find inherited check constraints and disinherit them. To do
! 	 * this, we first need a list of the names of the parent's check
! 	 * constraints.  (We cheat a bit by only checking for name matches,
! 	 * assuming that the expressions will match.)
  	 */
  	catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
  	ScanKeyInit(&key[0],
--- 9587,9604 ----
  			simple_heap_update(catalogRelation, &copyTuple->t_self, copyTuple);
  			CatalogUpdateIndexes(catalogRelation, copyTuple);
  			heap_freetuple(copyTuple);
+ 			heap_freetuple(parentattr_tuple);
  		}
  	}
  	systable_endscan(scan);
  	heap_close(catalogRelation, RowExclusiveLock);
  
  	/*
! 	 * Likewise, find inherited check and NOT NULL constraints and disinherit
! 	 * them. To do this, we first need a list of the names of the parent's
! 	 * check and NOT NULL constraints.  (For check constraints, we cheat a bit
! 	 * by only checking for name matches, assuming that the expressions will
! 	 * match.)
  	 */
  	catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
  	ScanKeyInit(&key[0],
***************
*** 8740,8753 **** ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
  	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
  							  true, SnapshotNow, 1, key);
  
- 	connames = NIL;
- 
  	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
  	{
! 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
  
  		if (con->contype == CONSTRAINT_CHECK)
! 			connames = lappend(connames, pstrdup(NameStr(con->conname)));
  	}
  
  	systable_endscan(scan);
--- 9608,9636 ----
  	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
  							  true, SnapshotNow, 1, key);
  
  	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
  	{
! 		Form_pg_constraint con = (Form_pg_constraint)
! 			GETSTRUCT(constraintTuple);
  
  		if (con->contype == CONSTRAINT_CHECK)
! 		{
! 			DisinheritConstraintInfo *inhInfo = (DisinheritConstraintInfo *)
! 				palloc(sizeof(DisinheritConstraintInfo));
! 			inhInfo->contype = CONSTRAINT_CHECK;
! 			inhInfo->conname = pstrdup(NameStr(con->conname));
! 			inhInfo->attnum  = 0;
! 			constraints = lappend(constraints, inhInfo);
! 		}
! 		else if (con->contype == CONSTRAINT_NOTNULL)
! 		{
! 			DisinheritConstraintInfo *inhInfo = (DisinheritConstraintInfo *)
! 				palloc(sizeof(DisinheritConstraintInfo));
! 			inhInfo->contype = CONSTRAINT_NOTNULL;
! 			inhInfo->conname = NULL;
! 			inhInfo->attnum = get_constraint_singlecol_attno(constraintTuple);
! 			constraints = lappend(constraints, inhInfo);
! 		}
  	}
  
  	systable_endscan(scan);
***************
*** 8762,8778 **** ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
  
  	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
  	{
! 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
  		bool		match;
  		ListCell   *lc;
  
! 		if (con->contype != CONSTRAINT_CHECK)
  			continue;
  
  		match = false;
! 		foreach(lc, connames)
  		{
! 			if (strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
  			{
  				match = true;
  				break;
--- 9645,9685 ----
  
  	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
  	{
! 		Form_pg_constraint con;
  		bool		match;
  		ListCell   *lc;
  
! 		con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
! 
! 		if (con->contype != CONSTRAINT_CHECK &&
! 			con->contype != CONSTRAINT_NOTNULL)
  			continue;
  
+ 		/*
+ 		 * We scan through all constraints of the current child relation,
+ 		 * checking for CONSTRAINT_CHECK and CONSTRAINT_NOTNULL occurrences.
+ 		 *
+ 		 * CHECK constraints are assumed to be named equally to in the
+ 		 * relation's ancestor, while NOT NULL constraints always are attached
+ 		 * to a specific column. Check for attnum and adjust the inheritance
+ 		 * information accordingly.
+ 		 */
+ 
  		match = false;
! 		foreach(lc, constraints)
  		{
! 			DisinheritConstraintInfo *inhInfo = (DisinheritConstraintInfo *) lfirst(lc);
! 
! 			if (inhInfo->contype != con->contype)
! 				continue;
! 			if (con->contype == CONSTRAINT_CHECK &&
! 				strcmp(NameStr(con->conname), inhInfo->conname) == 0)
! 			{
! 				match = true;
! 				break;
! 			}
! 			if (con->contype == CONSTRAINT_NOTNULL &&
! 				(get_constraint_singlecol_attno(constraintTuple) == inhInfo->attnum))
  			{
  				match = true;
  				break;
***************
*** 8783,8794 **** ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
  		{
  			/* Decrement inhcount and possibly set islocal to true */
  			HeapTuple	copyTuple = heap_copytuple(constraintTuple);
! 			Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
  
  			if (copy_con->coninhcount <= 0)		/* shouldn't happen */
  				elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
  					 RelationGetRelid(rel), NameStr(copy_con->conname));
  
  			copy_con->coninhcount--;
  			if (copy_con->coninhcount == 0)
  				copy_con->conislocal = true;
--- 9690,9705 ----
  		{
  			/* Decrement inhcount and possibly set islocal to true */
  			HeapTuple	copyTuple = heap_copytuple(constraintTuple);
! 			Form_pg_constraint copy_con = (Form_pg_constraint)
! 				GETSTRUCT(copyTuple);
  
  			if (copy_con->coninhcount <= 0)		/* shouldn't happen */
  				elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
  					 RelationGetRelid(rel), NameStr(copy_con->conname));
  
+ 			elog(DEBUG1, "disinherit not null constraint \"%s\" relation \"%s\"",
+ 				 NameStr(copy_con->conname), RelationGetRelationName(rel));
+ 
  			copy_con->coninhcount--;
  			if (copy_con->coninhcount == 0)
  				copy_con->conislocal = true;
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 95,101 **** static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
  					Oid baseTypeOid,
  					int typMod, Constraint *constr,
  					char *domainName);
! 
  
  /*
   * DefineType
--- 95,105 ----
  					Oid baseTypeOid,
  					int typMod, Constraint *constr,
  					char *domainName);
! static void domainAddNotNull(Oid domainOid, Oid domainNamespace,
! 							 Constraint *constr, char *domainName);
! static void
! addOrRemoveDomainNotNull(Oid domainOid, Oid domainNamespace,
! 						 char * domainName, bool is_new);
  
  /*
   * DefineType
***************
*** 781,786 **** DefineDomain(CreateDomainStmt *stmt)
--- 785,791 ----
  	Form_pg_type baseType;
  	int32		basetypeMod;
  	Oid			baseColl;
+ 	bool        saw_notnull   = false;
  
  	/* Convert list of names to a name and namespace */
  	domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
***************
*** 973,980 **** DefineDomain(CreateDomainStmt *stmt)
--- 978,999 ----
  					ereport(ERROR,
  							(errcode(ERRCODE_SYNTAX_ERROR),
  						   errmsg("conflicting NULL/NOT NULL constraints")));
+ 
+ 				if (saw_notnull)
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_SYNTAX_ERROR),
+ 							 errmsg("multiple definition for NOT NULL constraint")));
+ 
+ 				/*
+ 				 * We are going to create a pg_constraint entry for this
+ 				 * constraint to record an explicit named NOT NULL constraint.
+ 				 * We do this even in case it wasn't named explicitly. Since
+ 				 * we need the domains' OID, this is done later.
+ 				 */
+ 
  				typNotNull = true;
  				nullDefined = true;
+ 				saw_notnull = true;
  				break;
  
  			case CONSTR_NULL:
***************
*** 1080,1089 **** DefineDomain(CreateDomainStmt *stmt)
  	{
  		Constraint *constr = lfirst(listptr);
  
! 		/* it must be a Constraint, per check above */
  
  		switch (constr->contype)
  		{
  			case CONSTR_CHECK:
  				domainAddConstraint(domainoid, domainNamespace,
  									basetypeoid, basetypeMod,
--- 1099,1112 ----
  	{
  		Constraint *constr = lfirst(listptr);
  
! 		/* it must be a CHECK or NOT NULL Constraint, per check above */
  
  		switch (constr->contype)
  		{
+ 			case CONSTR_NOTNULL:
+ 				domainAddNotNull(domainoid, domainNamespace,
+ 								 constr, domainName);
+ 				break;
  			case CONSTR_CHECK:
  				domainAddConstraint(domainoid, domainNamespace,
  									basetypeoid, basetypeMod,
***************
*** 1857,1868 **** AlterDomainNotNull(List *names, bool notNull)
--- 1880,1978 ----
  
  	CatalogUpdateIndexes(typrel, tup);
  
+ 	/*
+ 	 * Depending on wether we are setting the domain to NOT NULL or NULL, we
+ 	 * need to update pg_constraint as well.
+ 	 */
+ 	addOrRemoveDomainNotNull(domainoid, typTup->typnamespace,
+ 							 NameStr(typTup->typname), typTup->typnotnull);
+ 
  	/* Clean up */
  	heap_freetuple(tup);
  	heap_close(typrel, RowExclusiveLock);
  }
  
  /*
+  * Internal magic for pg_constraint and ALTER DOMAIN ... SET|DROP NOT NULL.
+  */
+ static void
+ addOrRemoveDomainNotNull(Oid domainOid, Oid domainNamespace,
+ 						 char * domainName, bool is_new)
+ {
+ 	if (is_new)
+ 	{
+ 		/*
+ 		 * Caller wants a new CONSTRAINT_NOTNULL tuple
+ 		 * for the given domain OID.
+ 		 */
+ 		Constraint *constr;
+ 		constr = (Constraint *) palloc(sizeof(Constraint));
+ 
+ 		/*
+ 		 * ALTER DOMAIN ... SET NOT NULL doesn't have an infrastructure
+ 		 * to specify a name for the new constraint, so always generate
+ 		 * a new one.
+ 		 */
+ 		constr->conname = NULL;
+ 		constr->contype = CONSTRAINT_NOTNULL;
+ 
+ 		/*
+ 		 * Finally do the creation.
+ 		 */
+ 		domainAddNotNull(domainOid, domainNamespace,
+ 						 constr, domainName);
+ 	}
+ 	else
+ 	{
+ 		/* Existing constraint tuple should be dropped */
+ 
+ 		Relation conrel;
+ 		HeapTuple contup;
+ 		SysScanDesc conscan;
+ 		ScanKeyData key[1];
+ 
+ 		/* Grab an appropriate lock on the pg_constraint relation */
+ 		conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
+ 
+ 		/* Use the index to scan only constraints of the target relation */
+ 		ScanKeyInit(&key[0],
+ 					Anum_pg_constraint_contypid,
+ 					BTEqualStrategyNumber, F_OIDEQ,
+ 					ObjectIdGetDatum(domainOid));
+ 
+ 		conscan = systable_beginscan(conrel, ConstraintTypidIndexId, true,
+ 									 SnapshotNow, 1, key);
+ 
+ 		while ((contup = systable_getnext(conscan)) != NULL)
+ 		{
+ 			Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
+ 
+ 			/*
+ 			 * Perform deletion over pg_depend
+ 			 */
+ 			if (con->contype == CONSTRAINT_NOTNULL)
+ 			{
+ 				ObjectAddress conobj;
+ 
+ 				conobj.classId = ConstraintRelationId;
+ 				conobj.objectId = HeapTupleGetOid(contup);
+ 				conobj.objectSubId = 0;
+ 
+ 				performDeletion(&conobj, DEPENDENCY_AUTO);
+ 
+ 				/*
+ 				 * Only one tuple expected, break.
+ 				 */
+ 				break;
+ 			}
+ 		}
+ 
+ 		systable_endscan(conscan);
+ 		heap_close(conrel, NoLock);
+ 	}
+ }
+ 
+ /*
   * AlterDomainDropConstraint
   *
   * Implements the ALTER DOMAIN DROP CONSTRAINT statement
***************
*** 2400,2405 **** checkDomainOwner(HeapTuple tup)
--- 2510,2576 ----
  }
  
  /*
+  * domainAddNotNull - add a NOT NULL constraint to
+  * pg_constraint.
+  */
+ void
+ domainAddNotNull(Oid domainOid, Oid domainNamespace,
+ 				 Constraint *constr, char *domainName)
+ {
+ 	/*
+ 	 * Validate constraint name or assign a new one
+ 	 * if not specified already.
+ 	 */
+ 	if (constr->conname)
+ 	{
+ 		if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
+ 								 domainOid,
+ 								 domainNamespace,
+ 								 constr->conname))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 					 errmsg("not null constraint \"%s\" for domain \"%s\" already exists",
+ 							constr->conname, domainName)));
+ 	}
+ 	else
+ 		constr->conname = ChooseConstraintName(domainName,
+ 											   NULL,
+ 											   "not_null",
+ 											   domainNamespace,
+ 											   NIL);
+ 
+ 	/*
+ 	 * Store the not null constraint in pg_constraint
+ 	 */
+ 	CreateConstraintEntry(constr->conname,		/* Constraint Name */
+ 						  domainNamespace,		/* namespace */
+ 						  CONSTRAINT_NOTNULL,		/* Constraint Type */
+ 						  false,	/* Is Deferrable */
+ 						  false,	/* Is Deferred */
+ 						  true,		/* Is Validated */
+ 						  InvalidOid,	/* not a relation constraint */
+ 						  NULL,
+ 						  0,
+ 						  domainOid,	/* domain constraint */
+ 						  InvalidOid,	/* no associated index */
+ 						  InvalidOid,	/* Foreign key fields */
+ 						  NULL,
+ 						  NULL,
+ 						  NULL,
+ 						  NULL,
+ 						  0,
+ 						  ' ',
+ 						  ' ',
+ 						  ' ',
+ 						  NULL, /* not an exclusion constraint */
+ 						  NULL, /* Tree form of check constraint */
+ 						  NULL,	/* Binary form of check constraint */
+ 						  NULL,	/* Source form of check constraint */
+ 						  true, /* is local */
+ 						  0);	/* inhcount */
+ }
+ 
+ /*
   * domainAddConstraint - code shared between CREATE and ALTER DOMAIN
   */
  static char *
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 2305,2310 **** _copyColumnDef(ColumnDef *from)
--- 2305,2311 ----
  	COPY_SCALAR_FIELD(inhcount);
  	COPY_SCALAR_FIELD(is_local);
  	COPY_SCALAR_FIELD(is_not_null);
+ 	COPY_SCALAR_FIELD(is_primary_key);
  	COPY_SCALAR_FIELD(is_from_type);
  	COPY_SCALAR_FIELD(storage);
  	COPY_NODE_FIELD(raw_default);
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 2235,2240 **** _equalColumnDef(ColumnDef *a, ColumnDef *b)
--- 2235,2241 ----
  	COMPARE_SCALAR_FIELD(inhcount);
  	COMPARE_SCALAR_FIELD(is_local);
  	COMPARE_SCALAR_FIELD(is_not_null);
+ 	COMPARE_SCALAR_FIELD(is_primary_key);
  	COMPARE_SCALAR_FIELD(is_from_type);
  	COMPARE_SCALAR_FIELD(storage);
  	COMPARE_NODE_FIELD(raw_default);
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 2094,2099 **** _outColumnDef(StringInfo str, ColumnDef *node)
--- 2094,2100 ----
  	WRITE_INT_FIELD(inhcount);
  	WRITE_BOOL_FIELD(is_local);
  	WRITE_BOOL_FIELD(is_not_null);
+ 	WRITE_BOOL_FIELD(is_primary_key);
  	WRITE_BOOL_FIELD(is_from_type);
  	WRITE_INT_FIELD(storage);
  	WRITE_NODE_FIELD(raw_default);
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 2505,2510 **** columnDef:	ColId Typename ColQualList
--- 2505,2511 ----
  					n->inhcount = 0;
  					n->is_local = true;
  					n->is_not_null = false;
+ 					n->is_primary_key = false;
  					n->is_from_type = false;
  					n->storage = 0;
  					n->raw_default = NULL;
***************
*** 2524,2529 **** columnOptions:	ColId WITH OPTIONS ColQualList
--- 2525,2531 ----
  					n->inhcount = 0;
  					n->is_local = true;
  					n->is_not_null = false;
+ 					n->is_primary_key = false;
  					n->is_from_type = false;
  					n->storage = 0;
  					n->raw_default = NULL;
***************
*** 3045,3050 **** CreateAsElement:
--- 3047,3053 ----
  					n->inhcount = 0;
  					n->is_local = true;
  					n->is_not_null = false;
+ 					n->is_primary_key = false;
  					n->is_from_type = false;
  					n->storage = 0;
  					n->raw_default = NULL;
***************
*** 9168,9173 **** TableFuncElement:	ColId Typename opt_collate_clause
--- 9171,9177 ----
  					n->inhcount = 0;
  					n->is_local = true;
  					n->is_not_null = false;
+ 					n->is_primary_key = false;
  					n->is_from_type = false;
  					n->storage = 0;
  					n->raw_default = NULL;
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
***************
*** 65,71 ****
  
  
  /* State shared by transformCreateStmt and its subroutines */
! typedef struct
  {
  	ParseState *pstate;			/* overall parser state */
  	const char *stmtType;		/* "CREATE [FOREIGN] TABLE" or "ALTER TABLE" */
--- 65,71 ----
  
  
  /* State shared by transformCreateStmt and its subroutines */
! typedef struct CreateStmtContext
  {
  	ParseState *pstate;			/* overall parser state */
  	const char *stmtType;		/* "CREATE [FOREIGN] TABLE" or "ALTER TABLE" */
***************
*** 87,93 **** typedef struct
  } CreateStmtContext;
  
  /* State shared by transformCreateSchemaStmt and its subroutines */
! typedef struct
  {
  	const char *stmtType;		/* "CREATE SCHEMA" or "ALTER SCHEMA" */
  	char	   *schemaname;		/* name of schema */
--- 87,93 ----
  } CreateStmtContext;
  
  /* State shared by transformCreateSchemaStmt and its subroutines */
! typedef struct CreateSchemaStmtContext
  {
  	const char *stmtType;		/* "CREATE SCHEMA" or "ALTER SCHEMA" */
  	char	   *schemaname;		/* name of schema */
***************
*** 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;
  
--- 491,497 ----
  									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;
  
--- 503,509 ----
  									column->colname, cxt->relation->relname),
  							 parser_errposition(cxt->pstate,
  												constraint->location)));
! 				column->is_not_null = true;
  				saw_nullable = true;
  				break;
  
***************
*** 678,683 **** transformInhRelation(CreateStmtContext *cxt, InhRelation *inhRelation)
--- 678,697 ----
  		def->constraints = NIL;
  
  		/*
+ 		 * FIXME strictly speaking, we need to fetch pg_constraint rows so that
+ 		 * we don't create spurious NOT NULL constraints for the primary key
+ 		 * columns.
+ 		 */
+ 		if (attribute->attnotnull)
+ 		{
+ 			Constraint *constraint = makeNode(Constraint);
+ 
+ 			constraint->contype = CONSTR_NOTNULL;
+ 			constraint->location = -1;
+ 			def->constraints = lappend(def->constraints, constraint);
+ 		}
+ 
+ 		/*
  		 * Add to column list
  		 */
  		cxt->columns = lappend(cxt->columns, def);
***************
*** 1644,1650 **** transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
  		{
  			/* found column in the new table; force it to be NOT NULL */
  			if (constraint->contype == CONSTR_PRIMARY)
! 				column->is_not_null = TRUE;
  		}
  		else if (SystemAttributeByName(key, cxt->hasoids) != NULL)
  		{
--- 1658,1664 ----
  		{
  			/* found column in the new table; force it to be NOT NULL */
  			if (constraint->contype == CONSTR_PRIMARY)
! 				column->is_primary_key = true;
  		}
  		else if (SystemAttributeByName(key, cxt->hasoids) != NULL)
  		{
***************
*** 2300,2305 **** transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
--- 2314,2321 ----
  			case AT_AddColumnToView:
  				{
  					ColumnDef  *def = (ColumnDef *) cmd->def;
+ 					List       *constraints = NIL;
+ 					ListCell   *cell;
  
  					Assert(IsA(def, ColumnDef));
  					transformColumnDefinition(&cxt, def);
***************
*** 2312,2322 **** transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
  						skipValidation = false;
  
  					/*
! 					 * All constraints are processed in other ways. Remove the
! 					 * original list
  					 */
! 					def->constraints = NIL;
  
  					newcmds = lappend(newcmds, cmd);
  					break;
  				}
--- 2328,2346 ----
  						skipValidation = false;
  
  					/*
! 					 * All constraints are processed in other ways, except NOT
! 					 * NULL.  Filter down the list so that only CONSTR_NOTNULL
! 					 * nodes remain.
  					 */
! 					foreach(cell, def->constraints)
! 					{
! 						Constraint *constraint = (Constraint *) lfirst(cell);
! 
! 						if (constraint->contype == CONSTR_NOTNULL)
! 							constraints = lappend(constraints, constraint);
! 					}
  
+ 					def->constraints = constraints;
  					newcmds = lappend(newcmds, cmd);
  					break;
  				}
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 1107,1119 **** pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
  
  	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;
--- 1107,1157 ----
  
  	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);
+ 
+ 			if (conForm->conrelid != InvalidOid)
+ 			{
+ 				/* Should not happen */
+ 				if (isnull)
+ 					elog(ERROR, "null conkey for constraint %u",
+ 						 constraintId);
+ 
+ 				/* relation constraint */
+ 				decompile_column_index_array(val, conForm->conrelid, &buf);
+ 				appendStringInfo(&buf, " SET NOT NULL");
+ 			}
+ 			else
+ 			{
+ 				/* Domain constraint */
+ 			}
+ 
+ 			break;
+ 		}
  		case CONSTRAINT_FOREIGN:
  			{
  				Datum		val;
*** a/src/bin/initdb/initdb.c
--- b/src/bin/initdb/initdb.c
***************
*** 165,170 **** static void setup_config(void);
--- 165,171 ----
  static void bootstrap_template1(void);
  static void setup_auth(void);
  static void get_set_pwd(void);
+ static void setup_notnull_constraints(void);
  static void setup_depend(void);
  static void setup_sysviews(void);
  static void setup_description(void);
***************
*** 1326,1331 **** get_set_pwd(void)
--- 1327,1372 ----
  	check_ok();
  }
  
+ static void
+ setup_notnull_constraints(void)
+ {
+ 	PG_CMD_DECL;
+ 	const char **line;
+ 	static const char *notnull_constraints_setup[] = {
+ 		/*
+ 		 * Create pg_constraint entries for all NOT NULL columns created so
+ 		 * far.  The name chosen should resemble the rules in
+ 		 * ChooseConstraintName.
+ 		 */
+ 		"INSERT INTO pg_constraint"
+ 		"     SELECT format('%s_%s_notnull', relname, attname), relnamespace,"
+ 		"            'n', 'f', 'f', 't', pg_class.oid, 0, 0, 0, ' ', ' ', ' ',"
+ 		"            't', 0, ARRAY[attnum], null, null, null, null, null,"
+ 		"            null, null"
+ 		"       FROM pg_attribute att JOIN pg_class"
+ 		"            ON (att.attrelid = pg_class.oid)"
+ 		"       WHERE attnotnull AND attnum > 0;\n",
+ 		NULL
+ 	};
+ 
+ 	fputs(_("initializing constraints ... "), stdout);
+ 	fflush(stdout);
+ 
+ 	snprintf(cmd, sizeof(cmd),
+ 			 "\"%s\" %s template1 >%s",
+ 			 backend_exec, backend_options,
+ 			 DEVNULL);
+ 
+ 	PG_CMD_OPEN;
+ 
+ 	for (line = notnull_constraints_setup; *line != NULL; line++)
+ 		PG_CMD_PUTS(*line);
+ 
+ 	PG_CMD_CLOSE;
+ 
+ 	check_ok();
+ }
+ 
  /*
   * set up pg_depend
   */
***************
*** 3269,3274 **** main(int argc, char *argv[])
--- 3310,3317 ----
  	if (pwprompt || pwfilename)
  		get_set_pwd();
  
+ 	setup_notnull_constraints();
+ 
  	setup_depend();
  
  	setup_sysviews();
*** a/src/include/catalog/pg_attribute.h
--- b/src/include/catalog/pg_attribute.h
***************
*** 127,133 **** CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
  	 */
  	char		attalign;
  
! 	/* This flag represents the "NOT NULL" constraint */
  	bool		attnotnull;
  
  	/* Has DEFAULT value or not */
--- 127,136 ----
  	 */
  	char		attalign;
  
! 	/*
! 	 * Whether the column is nullable.  This might be from a NOT NULL
! 	 * constraint or a primary key.
! 	 */
  	bool		attnotnull;
  
  	/* Has DEFAULT value or not */
*** a/src/include/catalog/pg_constraint.h
--- b/src/include/catalog/pg_constraint.h
***************
*** 177,182 **** typedef FormData_pg_constraint *Form_pg_constraint;
--- 177,183 ----
  
  /* Valid values for contype */
  #define CONSTRAINT_CHECK			'c'
+ #define CONSTRAINT_NOTNULL			'n'
  #define CONSTRAINT_FOREIGN			'f'
  #define CONSTRAINT_PRIMARY			'p'
  #define CONSTRAINT_UNIQUE			'u'
*** /dev/null
--- b/src/include/commands/constraint.h
***************
*** 0 ****
--- 1,40 ----
+ /*-------------------------------------------------------------------------
+  * constraint.h
+  * 		Definitions for using POSTGRES constraint code
+  *
+  * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/commands/constraint.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef CONSTRAINT_H
+ #define CONSTRAINT_H
+ 
+ /*
+  * Used for add/drop operations of NOT NULL constraints
+  */
+ typedef struct NotNullConstraint
+ {
+ 	char	   *conname;
+ 	char	   *attname;
+ 	AttrNumber	attnum;
+ 	bool		is_local;
+ 	int			inhcount;
+ } NotNullConstraint;
+ 
+ extern void StoreNotNullConstraint(Oid relid, Oid nspid,
+ 					   NotNullConstraint *constraint);
+ extern HeapTuple RelationFetchNotNullConstraint(Relation pg_constraint,
+ 							   Oid relid, AttrNumber attnum);
+ extern List *GetRelationNotNullConstraints(Relation rel);
+ extern void CreateOrAdjustNotNullConstraint(Relation rel, AttrNumber attnum,
+ 								char *attname, int addinhcount,
+ 								int tweak_islocal);
+ extern AttrNumber get_constraint_singlecol_attno(HeapTuple tup);
+ 
+ extern void CreateNotNullConstraint(Relation rel, AttrNumber attnum,
+ 						char *constr_name, int inhcount, bool islocal);
+ 
+ #endif /* CONSTRAINT_H */
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 493,498 **** typedef struct ColumnDef
--- 493,499 ----
  	int			inhcount;		/* number of times column is inherited */
  	bool		is_local;		/* column has local (non-inherited) def'n */
  	bool		is_not_null;	/* NOT NULL constraint specified? */
+ 	bool		is_primary_key;	/* Does it belong to a PK? */
  	bool		is_from_type;	/* column definition came from table type */
  	char		storage;		/* attstorage setting, or 0 for default */
  	Node	   *raw_default;	/* default value (untransformed parse tree) */
***************
*** 1178,1184 **** typedef enum AlterTableType
--- 1179,1188 ----
  	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_SetAttNotNull,			/* internal (used by DefineIndex) */
  	AT_SetStatistics,			/* alter column set statistics */
  	AT_SetOptions,				/* alter column set ( options ) */
  	AT_ResetOptions,			/* alter column reset ( options ) */
*** a/src/test/regress/expected/alter_table.out
--- b/src/test/regress/expected/alter_table.out
***************
*** 620,626 **** create table atacc1 ( test int );
  insert into atacc1 (test) values (NULL);
  -- add a primary key (fails)
  alter table atacc1 add constraint atacc_test1 primary key (test);
! ERROR:  column "test" contains null values
  insert into atacc1 (test) values (3);
  drop table atacc1;
  -- let's do one where the primary key constraint fails
--- 620,626 ----
  insert into atacc1 (test) values (NULL);
  -- add a primary key (fails)
  alter table atacc1 add constraint atacc_test1 primary key (test);
! ERROR:  column "test" of relation "atacc1" contains null values
  insert into atacc1 (test) values (3);
  drop table atacc1;
  -- let's do one where the primary key constraint fails
***************
*** 637,643 **** insert into atacc1 (test) values (0);
  -- add a primary key column without a default (fails).
  alter table atacc1 add column test2 int primary key;
  NOTICE:  ALTER TABLE / ADD PRIMARY KEY will create implicit index "atacc1_pkey" for table "atacc1"
! ERROR:  column "test2" contains null values
  -- now add a primary key column with a default (succeeds).
  alter table atacc1 add column test2 int default 0 primary key;
  NOTICE:  ALTER TABLE / ADD PRIMARY KEY will create implicit index "atacc1_pkey" for table "atacc1"
--- 637,643 ----
  -- add a primary key column without a default (fails).
  alter table atacc1 add column test2 int primary key;
  NOTICE:  ALTER TABLE / ADD PRIMARY KEY will create implicit index "atacc1_pkey" for table "atacc1"
! ERROR:  column "test2" of relation "atacc1" contains null values
  -- now add a primary key column with a default (succeeds).
  alter table atacc1 add column test2 int default 0 primary key;
  NOTICE:  ALTER TABLE / ADD PRIMARY KEY will create implicit index "atacc1_pkey" for table "atacc1"
***************
*** 695,706 **** create table atacc1 (test int not null) with oids;
  alter table atacc1 add constraint "atacc1_pkey" primary key (test);
  NOTICE:  ALTER TABLE / ADD PRIMARY KEY will create implicit index "atacc1_pkey" for table "atacc1"
  alter table atacc1 alter column test drop not null;
- ERROR:  column "test" is in a primary key
  alter table atacc1 drop constraint "atacc1_pkey";
  alter table atacc1 alter column test drop not null;
  insert into atacc1 values (null);
  alter table atacc1 alter test set not null;
! ERROR:  column "test" contains null values
  delete from atacc1;
  alter table atacc1 alter test set not null;
  -- try altering a non-existent column, should fail
--- 695,705 ----
  alter table atacc1 add constraint "atacc1_pkey" primary key (test);
  NOTICE:  ALTER TABLE / ADD PRIMARY KEY will create implicit index "atacc1_pkey" for table "atacc1"
  alter table atacc1 alter column test drop not null;
  alter table atacc1 drop constraint "atacc1_pkey";
  alter table atacc1 alter column test drop not null;
  insert into atacc1 values (null);
  alter table atacc1 alter test set not null;
! ERROR:  column "test" of relation "atacc1" contains null values
  delete from atacc1;
  alter table atacc1 alter test set not null;
  -- try altering a non-existent column, should fail
***************
*** 710,716 **** alter table atacc1 alter bar drop not null;
  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
--- 709,715 ----
  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"
  -- try creating a view and altering that, should fail
***************
*** 733,745 **** alter table parent alter a drop not null;
  insert into parent values (NULL);
  insert into child (a, b) values (NULL, 'foo');
  alter table only parent alter a set not null;
! ERROR:  column "a" contains null values
  alter table child alter a set not null;
! ERROR:  column "a" contains null values
  delete from parent;
  alter table only parent alter a set not null;
  insert into parent values (NULL);
- ERROR:  null value in column "a" violates not-null constraint
  alter table child alter a set not null;
  insert into child (a, b) values (NULL, 'foo');
  ERROR:  null value in column "a" violates not-null constraint
--- 732,744 ----
  insert into parent values (NULL);
  insert into child (a, b) values (NULL, 'foo');
  alter table only parent alter a set not null;
! ERROR:  NOT NULL constraint must be added to child tables too
  alter table child alter a set not null;
! ERROR:  column "a" of relation "child" contains null values
  delete from parent;
  alter table only parent alter a set not null;
+ ERROR:  NOT NULL constraint must be added to child tables too
  insert into parent values (NULL);
  alter table child alter a set not null;
  insert into child (a, b) values (NULL, 'foo');
  ERROR:  null value in column "a" violates not-null constraint
*** a/src/test/regress/expected/cluster.out
--- b/src/test/regress/expected/cluster.out
***************
*** 251,261 **** ERROR:  insert or update on table "clstr_tst" violates foreign key constraint "c
  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
*** a/src/test/regress/expected/domain.out
--- b/src/test/regress/expected/domain.out
***************
*** 305,310 **** drop domain dnotnulltest cascade;
--- 305,327 ----
  NOTICE:  drop cascades to 2 other objects
  DETAIL:  drop cascades to table domnotnull column col1
  drop cascades to table domnotnull column col2
+ create domain dnotnulltest integer constraint dnn not null;
+ select conname, contype, contypid::regtype from pg_constraint c
+    where contype = 'n' and contypid <> 0 order by 1;
+     conname     | contype |   contypid   
+ ----------------+---------+--------------
+  ddef5_not_null | n       | ddef5
+  dnn            | n       | dnotnulltest
+ (2 rows)
+ 
+ drop domain dnotnulltest;
+ select conname, contype, contypid::regtype from pg_constraint
+    where contype = 'n' and contypid <> 0 order by 1;
+     conname     | contype | contypid 
+ ----------------+---------+----------
+  ddef5_not_null | n       | ddef5
+ (1 row)
+ 
  -- Test ALTER DOMAIN .. DEFAULT ..
  create table domdeftest (col1 ddef1);
  insert into domdeftest default values;
*** a/src/test/regress/expected/inherit.out
--- b/src/test/regress/expected/inherit.out
***************
*** 990,996 **** NOTICE:  merging multiple inherited definitions of column "a"
                 Table "public.t13_inh"
   Column | Type | Modifiers | Storage  | Description 
  --------+------+-----------+----------+-------------
!  a      | text | not null  | main     | 
   b      | text |           | extended | 
   c      | text |           | external | 
  Check constraints:
--- 990,996 ----
                 Table "public.t13_inh"
   Column | Type | Modifiers | Storage  | Description 
  --------+------+-----------+----------+-------------
!  a      | text |           | main     | 
   b      | text |           | extended | 
   c      | text |           | external | 
  Check constraints:
***************
*** 1006,1012 **** NOTICE:  merging column "a" with inherited definition
                Table "public.t13_like"
   Column | Type | Modifiers | Storage  | Description 
  --------+------+-----------+----------+-------------
!  a      | text | not null  | main     | A3
   b      | text |           | extended | 
   c      | text |           | external | C
  Check constraints:
--- 1006,1012 ----
                Table "public.t13_like"
   Column | Type | Modifiers | Storage  | Description 
  --------+------+-----------+----------+-------------
!  a      | text |           | main     | A3
   b      | text |           | extended | 
   c      | text |           | external | C
  Check constraints:
***************
*** 1242,1244 **** NOTICE:  drop cascades to 3 other objects
--- 1242,1546 ----
  DETAIL:  drop cascades to table matest1
  drop cascades to table matest2
  drop cascades to table matest3
+ --
+ -- Test inheritance of NOT NULL constraints
+ --
+ create table pp1 (f1 int);
+ create table cc1 (f2 text, f3 int) inherits (pp1);
+ \d cc1
+       Table "public.cc1"
+  Column |  Type   | Modifiers 
+ --------+---------+-----------
+  f1     | integer | 
+  f2     | text    | 
+  f3     | integer | 
+ Inherits: pp1
+ 
+ create table cc2(f4 float) inherits(pp1,cc1);
+ NOTICE:  merging multiple inherited definitions of column "f1"
+ \d cc2
+           Table "public.cc2"
+  Column |       Type       | Modifiers 
+ --------+------------------+-----------
+  f1     | integer          | 
+  f2     | text             | 
+  f3     | integer          | 
+  f4     | double precision | 
+ Inherits: pp1,
+           cc1
+ 
+ -- named NOT NULL constraint
+ alter table cc1 add column a2 int constraint nn not null;
+ \d cc1
+       Table "public.cc1"
+  Column |  Type   | Modifiers 
+ --------+---------+-----------
+  f1     | integer | 
+  f2     | text    | 
+  f3     | integer | 
+  a2     | integer | not null
+ Inherits: pp1
+ Number of child tables: 1 (Use \d+ to list them.)
+ 
+ \d cc2
+           Table "public.cc2"
+  Column |       Type       | Modifiers 
+ --------+------------------+-----------
+  f1     | integer          | 
+  f2     | text             | 
+  f3     | integer          | 
+  f4     | double precision | 
+  a2     | integer          | not null
+ Inherits: pp1,
+           cc1
+ 
+ alter table pp1 alter column f1 set not null;
+ \d pp1
+       Table "public.pp1"
+  Column |  Type   | Modifiers 
+ --------+---------+-----------
+  f1     | integer | not null
+ Number of child tables: 2 (Use \d+ to list them.)
+ 
+ \d cc1
+       Table "public.cc1"
+  Column |  Type   | Modifiers 
+ --------+---------+-----------
+  f1     | integer | not null
+  f2     | text    | 
+  f3     | integer | 
+  a2     | integer | not null
+ Inherits: pp1
+ Number of child tables: 1 (Use \d+ to list them.)
+ 
+ \d cc2
+           Table "public.cc2"
+  Column |       Type       | Modifiers 
+ --------+------------------+-----------
+  f1     | integer          | not null
+  f2     | text             | 
+  f3     | integer          | 
+  f4     | double precision | 
+  a2     | integer          | not null
+ Inherits: pp1,
+           cc1
+ 
+ -- have a look at pg_constraint
+ select conrelid::regclass, conname, contype, coninhcount, conislocal from pg_constraint where contype = 'n' and conrelid::regclass in ('pp1', 'cc1', 'cc2') order by 2;
+  conrelid |     conname     | contype | coninhcount | conislocal 
+ ----------+-----------------+---------+-------------+------------
+  cc1      | cc1_f1_not_null | n       |           1 | f
+  cc2      | cc2_f1_not_null | n       |           2 | f
+  cc1      | nn              | n       |           0 | t
+  cc2      | nn              | n       |           1 | f
+  pp1      | pp1_f1_not_null | n       |           0 | t
+ (5 rows)
+ 
+ -- remove constraint from cc2, should fail
+ alter table cc2 alter column a2 drop not null;
+ ERROR:  cannot drop inherited NOT NULL constraint "nn", relation "cc2"
+ -- remove constraint cc1, should succeed
+ alter table cc1 alter column a2 drop not null;
+ -- have a look at pg_constraint
+ select conrelid::regclass, conname, contype, coninhcount, conislocal from pg_constraint where contype = 'n' and conrelid::regclass in ('pp1', 'cc1', 'cc2') order by 2;
+  conrelid |     conname     | contype | coninhcount | conislocal 
+ ----------+-----------------+---------+-------------+------------
+  cc1      | cc1_f1_not_null | n       |           1 | f
+  cc2      | cc2_f1_not_null | n       |           2 | f
+  pp1      | pp1_f1_not_null | n       |           0 | t
+ (3 rows)
+ 
+ -- same for cc2
+ alter table cc2 alter column f1 drop not null;
+ ERROR:  cannot drop inherited NOT NULL constraint "cc2_f1_not_null", relation "cc2"
+ -- remove from cc1, should fail again
+ alter table cc1 alter column f1 drop not null;
+ ERROR:  cannot drop inherited NOT NULL constraint "cc1_f1_not_null", relation "cc1"
+ -- remove from pp1, should succeed
+ alter table pp1 alter column f1 drop not null;
+ -- have a look at pg_constraint
+ select conrelid::regclass, conname, contype, coninhcount, conislocal from pg_constraint where contype = 'n' and conrelid::regclass in ('pp1', 'cc1', 'cc2') order by 2;
+  conrelid | conname | contype | coninhcount | conislocal 
+ ----------+---------+---------+-------------+------------
+ (0 rows)
+ 
+ drop table pp1 cascade;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to table cc1
+ drop cascades to table cc2
+ -- have a look at pg_constraint, everything should be clean now
+ select conrelid::regclass, conname, contype, coninhcount, conislocal from pg_constraint where contype = 'n' and conrelid::regclass in ('pp1', 'cc1', 'cc2') order by 2;
+ ERROR:  relation "pp1" does not exist
+ LINE 1: ...nt where contype = 'n' and conrelid::regclass in ('pp1', 'cc...
+                                                              ^
+ --
+ -- test inherit/deinherit
+ --
+ create table parent(f1 int);
+ create table child1(f1 int not null);
+ create table child2(f1 int);
+ -- child1 should have not null constraint
+ alter table child1 inherit parent;
+ -- should fail, missing NOT NULL constraint
+ alter table child2 inherit child1;
+ ERROR:  column "f1" in child table must be marked NOT NULL
+ alter table child2 alter column f1 set not null;
+ alter table child2 inherit child1;
+ -- add NOT NULL constraint recursively
+ alter table parent alter column f1 set not null;
+ \d parent
+     Table "public.parent"
+  Column |  Type   | Modifiers 
+ --------+---------+-----------
+  f1     | integer | not null
+ Number of child tables: 1 (Use \d+ to list them.)
+ 
+ \d child1
+     Table "public.child1"
+  Column |  Type   | Modifiers 
+ --------+---------+-----------
+  f1     | integer | not null
+ Inherits: parent
+ Number of child tables: 1 (Use \d+ to list them.)
+ 
+ \d child2
+     Table "public.child2"
+  Column |  Type   | Modifiers 
+ --------+---------+-----------
+  f1     | integer | not null
+ Inherits: child1
+ 
+ select conrelid::regclass, conname, contype, coninhcount, conislocal from pg_constraint where contype = 'n' and conrelid::regclass in ('parent', 'child1', 'child2') order by 2;
+  conrelid |      conname       | contype | coninhcount | conislocal 
+ ----------+--------------------+---------+-------------+------------
+  child1   | child1_f1_not_null | n       |           1 | t
+  child2   | child2_f1_not_null | n       |           1 | t
+  parent   | parent_f1_not_null | n       |           0 | t
+ (3 rows)
+ 
+ --
+ -- test deinherit procedure
+ --
+ -- deinherit child1
+ alter table child1 no inherit parent;
+ \d parent
+     Table "public.parent"
+  Column |  Type   | Modifiers 
+ --------+---------+-----------
+  f1     | integer | not null
+ 
+ \d child1
+     Table "public.child1"
+  Column |  Type   | Modifiers 
+ --------+---------+-----------
+  f1     | integer | not null
+ Number of child tables: 1 (Use \d+ to list them.)
+ 
+ \d child2
+     Table "public.child2"
+  Column |  Type   | Modifiers 
+ --------+---------+-----------
+  f1     | integer | not null
+ Inherits: child1
+ 
+ select conrelid::regclass, conname, contype, coninhcount, conislocal from pg_constraint where contype = 'n' and conrelid::regclass in ('parent', 'child1', 'child2') order by 2;
+  conrelid |      conname       | contype | coninhcount | conislocal 
+ ----------+--------------------+---------+-------------+------------
+  child1   | child1_f1_not_null | n       |           0 | t
+  child2   | child2_f1_not_null | n       |           1 | t
+  parent   | parent_f1_not_null | n       |           0 | t
+ (3 rows)
+ 
+ -- test inhcount of child2, should fail
+ alter table child2 alter f1 drop not null;
+ ERROR:  cannot drop inherited NOT NULL constraint "child2_f1_not_null", relation "child2"
+ -- should succeed
+ drop table parent;
+ drop table child1 cascade;
+ NOTICE:  drop cascades to table child2
+ --
+ -- test multi inheritance tree
+ --
+ create table parent(f1 int not null);
+ create table c1() inherits(parent);
+ create table c2() inherits(parent);
+ create table d1() inherits(c1, c2);
+ NOTICE:  merging multiple inherited definitions of column "f1"
+ -- show constraint info
+ select conrelid::regclass, conname, contype, coninhcount, conislocal from pg_constraint where contype = 'n' and conrelid::regclass in ('parent', 'c1', 'c2', 'd1') order by 2;
+  conrelid |      conname       | contype | coninhcount | conislocal 
+ ----------+--------------------+---------+-------------+------------
+  parent   | parent_f1_not_null | n       |           0 | t
+  c1       | parent_f1_not_null | n       |           1 | f
+  c2       | parent_f1_not_null | n       |           1 | f
+  d1       | parent_f1_not_null | n       |           2 | f
+ (4 rows)
+ 
+ drop table parent cascade;
+ NOTICE:  drop cascades to 3 other objects
+ DETAIL:  drop cascades to table c1
+ drop cascades to table c2
+ drop cascades to table d1
+ -- test child table with inherited columns and
+ -- with explicitely specified not null constraints
+ create table parent1(f1 int);
+ create table parent2(f2 text);
+ create table child(f1 int not null, f2 text not null) inherits(parent1, parent2);
+ NOTICE:  merging column "f1" with inherited definition
+ NOTICE:  merging column "f2" with inherited definition
+ -- show constraint info
+ select conrelid::regclass, conname, contype, coninhcount, conislocal, conkey from pg_constraint where contype = 'n' and conrelid::regclass in ('parent1', 'parent2', 'child') order by 2;
+  conrelid |      conname      | contype | coninhcount | conislocal | conkey 
+ ----------+-------------------+---------+-------------+------------+--------
+  child    | child_f1_not_null | n       |           0 | t          | {1}
+  child    | child_f2_not_null | n       |           0 | t          | {2}
+ (2 rows)
+ 
+ -- also drops child table
+ drop table parent1 cascade;
+ NOTICE:  drop cascades to table child
+ drop table parent2;
+ -- test multi layer inheritance tree
+ create table p1(f1 int not null);
+ create table p2(f1 int not null);
+ create table p3(f2 int);
+ create table p4(f1 int not null, f3 text not null);
+ create table c1() inherits(p1, p2, p3, p4);
+ NOTICE:  merging multiple inherited definitions of column "f1"
+ NOTICE:  merging multiple inherited definitions of column "f1"
+ -- constraint on f1 should have three parents
+ select conrelid::regclass, conname, contype, coninhcount, conislocal, conkey from pg_constraint where contype = 'n' and conrelid::regclass in ('p1', 'p2', 'p3', 'p4', 'c1') order by 1, 2;
+  conrelid |    conname     | contype | coninhcount | conislocal | conkey 
+ ----------+----------------+---------+-------------+------------+--------
+  p1       | p1_f1_not_null | n       |           0 | t          | {1}
+  p2       | p2_f1_not_null | n       |           0 | t          | {1}
+  p4       | p4_f1_not_null | n       |           0 | t          | {1}
+  p4       | p4_f3_not_null | n       |           0 | t          | {2}
+  c1       | p1_f1_not_null | n       |           3 | f          | {1}
+  c1       | p4_f3_not_null | n       |           1 | f          | {3}
+ (6 rows)
+ 
+ create table d1(a int not null, f1 int) inherits(p3, c1);
+ NOTICE:  merging multiple inherited definitions of column "f2"
+ NOTICE:  merging column "f1" with inherited definition
+ select conrelid::regclass, conname, contype, coninhcount, conislocal, conkey from pg_constraint where contype = 'n' and conrelid::regclass in ('p1', 'p2', 'p3', 'p4', 'c1', 'd1') order by 1, 2;
+  conrelid |    conname     | contype | coninhcount | conislocal | conkey 
+ ----------+----------------+---------+-------------+------------+--------
+  p1       | p1_f1_not_null | n       |           0 | t          | {1}
+  p2       | p2_f1_not_null | n       |           0 | t          | {1}
+  p4       | p4_f1_not_null | n       |           0 | t          | {1}
+  p4       | p4_f3_not_null | n       |           0 | t          | {2}
+  c1       | p1_f1_not_null | n       |           3 | f          | {1}
+  c1       | p4_f3_not_null | n       |           1 | f          | {3}
+  d1       | d1_a_not_null  | n       |           0 | t          | {4}
+  d1       | p1_f1_not_null | n       |           1 | f          | {2}
+  d1       | p4_f3_not_null | n       |           1 | f          | {3}
+ (9 rows)
+ 
+ drop table p1 cascade;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to table c1
+ drop cascades to table d1
+ drop table p2;
+ drop table p3;
+ drop table p4;
*** a/src/test/regress/sql/domain.sql
--- b/src/test/regress/sql/domain.sql
***************
*** 224,229 **** update domnotnull set col1 = null;
--- 224,239 ----
  
  drop domain dnotnulltest cascade;
  
+ create domain dnotnulltest integer constraint dnn not null;
+ 
+ select conname, contype, contypid::regtype from pg_constraint c
+    where contype = 'n' and contypid <> 0 order by 1;
+ 
+ drop domain dnotnulltest;
+ 
+ select conname, contype, contypid::regtype from pg_constraint
+    where contype = 'n' and contypid <> 0 order by 1;
+ 
  -- Test ALTER DOMAIN .. DEFAULT ..
  create table domdeftest (col1 ddef1);
  
*** a/src/test/regress/sql/inherit.sql
--- b/src/test/regress/sql/inherit.sql
***************
*** 406,408 **** select * from matest0 order by 1-id;
--- 406,545 ----
  reset enable_seqscan;
  
  drop table matest0 cascade;
+ 
+ --
+ -- Test inheritance of NOT NULL constraints
+ --
+ create table pp1 (f1 int);
+ create table cc1 (f2 text, f3 int) inherits (pp1);
+ \d cc1
+ create table cc2(f4 float) inherits(pp1,cc1);
+ \d cc2
+ 
+ -- named NOT NULL constraint
+ alter table cc1 add column a2 int constraint nn not null;
+ \d cc1
+ \d cc2
+ alter table pp1 alter column f1 set not null;
+ \d pp1
+ \d cc1
+ \d cc2
+ 
+ -- have a look at pg_constraint
+ select conrelid::regclass, conname, contype, coninhcount, conislocal from pg_constraint where contype = 'n' and conrelid::regclass in ('pp1', 'cc1', 'cc2') order by 2;
+ 
+ -- remove constraint from cc2, should fail
+ alter table cc2 alter column a2 drop not null;
+ 
+ -- remove constraint cc1, should succeed
+ alter table cc1 alter column a2 drop not null;
+ 
+ -- have a look at pg_constraint
+ select conrelid::regclass, conname, contype, coninhcount, conislocal from pg_constraint where contype = 'n' and conrelid::regclass in ('pp1', 'cc1', 'cc2') order by 2;
+ 
+ -- same for cc2
+ alter table cc2 alter column f1 drop not null;
+ 
+ -- remove from cc1, should fail again
+ alter table cc1 alter column f1 drop not null;
+ 
+ -- remove from pp1, should succeed
+ alter table pp1 alter column f1 drop not null;
+ 
+ -- have a look at pg_constraint
+ select conrelid::regclass, conname, contype, coninhcount, conislocal from pg_constraint where contype = 'n' and conrelid::regclass in ('pp1', 'cc1', 'cc2') order by 2;
+ 
+ drop table pp1 cascade;
+ 
+ -- have a look at pg_constraint, everything should be clean now
+ select conrelid::regclass, conname, contype, coninhcount, conislocal from pg_constraint where contype = 'n' and conrelid::regclass in ('pp1', 'cc1', 'cc2') order by 2;
+ 
+ --
+ -- test inherit/deinherit
+ --
+ create table parent(f1 int);
+ create table child1(f1 int not null);
+ create table child2(f1 int);
+ 
+ -- child1 should have not null constraint
+ alter table child1 inherit parent;
+ 
+ -- should fail, missing NOT NULL constraint
+ alter table child2 inherit child1;
+ 
+ alter table child2 alter column f1 set not null;
+ alter table child2 inherit child1;
+ 
+ -- add NOT NULL constraint recursively
+ alter table parent alter column f1 set not null;
+ 
+ \d parent
+ \d child1
+ \d child2
+ 
+ select conrelid::regclass, conname, contype, coninhcount, conislocal from pg_constraint where contype = 'n' and conrelid::regclass in ('parent', 'child1', 'child2') order by 2;
+ 
+ --
+ -- test deinherit procedure
+ --
+ 
+ -- deinherit child1
+ alter table child1 no inherit parent;
+ \d parent
+ \d child1
+ \d child2
+ select conrelid::regclass, conname, contype, coninhcount, conislocal from pg_constraint where contype = 'n' and conrelid::regclass in ('parent', 'child1', 'child2') order by 2;
+ 
+ -- test inhcount of child2, should fail
+ alter table child2 alter f1 drop not null;
+ 
+ -- should succeed
+ 
+ drop table parent;
+ drop table child1 cascade;
+ 
+ --
+ -- test multi inheritance tree
+ --
+ create table parent(f1 int not null);
+ create table c1() inherits(parent);
+ create table c2() inherits(parent);
+ create table d1() inherits(c1, c2);
+ 
+ -- show constraint info
+ select conrelid::regclass, conname, contype, coninhcount, conislocal from pg_constraint where contype = 'n' and conrelid::regclass in ('parent', 'c1', 'c2', 'd1') order by 2;
+ 
+ drop table parent cascade;
+ 
+ -- test child table with inherited columns and
+ -- with explicitely specified not null constraints
+ create table parent1(f1 int);
+ create table parent2(f2 text);
+ create table child(f1 int not null, f2 text not null) inherits(parent1, parent2);
+ 
+ -- show constraint info
+ select conrelid::regclass, conname, contype, coninhcount, conislocal, conkey from pg_constraint where contype = 'n' and conrelid::regclass in ('parent1', 'parent2', 'child') order by 2;
+ 
+ -- also drops child table
+ drop table parent1 cascade;
+ drop table parent2;
+ 
+ -- test multi layer inheritance tree
+ create table p1(f1 int not null);
+ create table p2(f1 int not null);
+ create table p3(f2 int);
+ create table p4(f1 int not null, f3 text not null);
+ 
+ create table c1() inherits(p1, p2, p3, p4);
+ 
+ -- constraint on f1 should have three parents
+ select conrelid::regclass, conname, contype, coninhcount, conislocal, conkey from pg_constraint where contype = 'n' and conrelid::regclass in ('p1', 'p2', 'p3', 'p4', 'c1') order by 1, 2;
+ 
+ create table d1(a int not null, f1 int) inherits(p3, c1);
+ 
+ select conrelid::regclass, conname, contype, coninhcount, conislocal, conkey from pg_constraint where contype = 'n' and conrelid::regclass in ('p1', 'p2', 'p3', 'p4', 'c1', 'd1') order by 1, 2;
+ 
+ drop table p1 cascade;
+ drop table p2;
+ drop table p3;
+ drop table p4;
