*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 1081,1088 ****
        <entry><type>bool</type></entry>
        <entry></entry>
        <entry>
!        This represents a not-null constraint.  It is possible to
!        change this column to enable or disable the constraint.
        </entry>
       </row>
  
--- 1081,1090 ----
        <entry><type>bool</type></entry>
        <entry></entry>
        <entry>
!        This represents a non nullable column.  This may be either because
!        of a not null constraint, or because of a primary key. 
!        The <structname>pg_constraint</structname> catalog contains the complete
!        information; this column should be considered just a cache.
        </entry>
       </row>
  
***************
*** 1825,1835 ****
  
    <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>
--- 1827,1836 ----
  
    <para>
     The catalog <structname>pg_constraint</structname> stores check, primary
!    key, unique, foreign key, exclusion, and not null constraints
!    on tables.
     (Column constraints are not treated specially.  Every column constraint is
     equivalent to some table constraint.)
    </para>
  
    <para>
***************
*** 1838,1844 ****
    </para>
  
    <para>
!    Check constraints on domains are stored here, too.
    </para>
  
    <table>
--- 1839,1845 ----
    </para>
  
    <para>
!    Check and not null constraints on domains are stored here, too.
    </para>
  
    <table>
***************
*** 1878,1883 ****
--- 1879,1885 ----
        <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,
***************
*** 1999,2005 ****
        <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>
--- 2001,2008 ----
        <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>
*** 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/information_schema.sql
--- b/src/backend/catalog/information_schema.sql
***************
*** 427,450 **** CREATE VIEW check_constraints AS
             LEFT OUTER JOIN pg_class c ON (c.oid = con.conrelid)
             LEFT OUTER JOIN pg_type t ON (t.oid = con.contypid)
      WHERE pg_has_role(coalesce(c.relowner, t.typowner), 'USAGE')
!       AND con.contype = 'c'
! 
!     UNION
!     -- not-null constraints
! 
!     SELECT CAST(current_database() AS sql_identifier) AS constraint_catalog,
!            CAST(n.nspname AS sql_identifier) AS constraint_schema,
!            CAST(CAST(n.oid AS text) || '_' || CAST(r.oid AS text) || '_' || CAST(a.attnum AS text) || '_not_null' AS sql_identifier) AS constraint_name, -- XXX
!            CAST(a.attname || ' IS NOT NULL' AS character_data)
!              AS check_clause
!     FROM pg_namespace n, pg_class r, pg_attribute a
!     WHERE n.oid = r.relnamespace
!       AND r.oid = a.attrelid
!       AND a.attnum > 0
!       AND NOT a.attisdropped
!       AND a.attnotnull
!       AND r.relkind = 'r'
!       AND pg_has_role(r.relowner, 'USAGE');
  
  GRANT SELECT ON check_constraints TO PUBLIC;
  
--- 427,433 ----
             LEFT OUTER JOIN pg_class c ON (c.oid = con.conrelid)
             LEFT OUTER JOIN pg_type t ON (t.oid = con.contypid)
      WHERE pg_has_role(coalesce(c.relowner, t.typowner), 'USAGE')
!       AND con.contype = 'c';
  
  GRANT SELECT ON check_constraints TO PUBLIC;
  
***************
*** 1704,1710 **** CREATE VIEW table_constraints AS
               CASE c.contype WHEN 'c' THEN 'CHECK'
                              WHEN 'f' THEN 'FOREIGN KEY'
                              WHEN 'p' THEN 'PRIMARY KEY'
!                             WHEN 'u' THEN 'UNIQUE' END
               AS character_data) AS constraint_type,
             CAST(CASE WHEN c.condeferrable THEN 'YES' ELSE 'NO' END AS yes_or_no)
               AS is_deferrable,
--- 1687,1694 ----
               CASE c.contype WHEN 'c' THEN 'CHECK'
                              WHEN 'f' THEN 'FOREIGN KEY'
                              WHEN 'p' THEN 'PRIMARY KEY'
!                             WHEN 'u' THEN 'UNIQUE'
! 							WHEN 'n' THEN 'NOT NULL' END
               AS character_data) AS constraint_type,
             CAST(CASE WHEN c.condeferrable THEN 'YES' ELSE 'NO' END AS yes_or_no)
               AS is_deferrable,
***************
*** 1724,1760 **** CREATE VIEW table_constraints AS
            AND (pg_has_role(r.relowner, 'USAGE')
                 -- SELECT privilege omitted, per SQL standard
                 OR has_table_privilege(r.oid, 'INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER')
!                OR has_any_column_privilege(r.oid, 'INSERT, UPDATE, REFERENCES') )
! 
!     UNION ALL
! 
!     -- not-null constraints
! 
!     SELECT CAST(current_database() AS sql_identifier) AS constraint_catalog,
!            CAST(nr.nspname AS sql_identifier) AS constraint_schema,
!            CAST(CAST(nr.oid AS text) || '_' || CAST(r.oid AS text) || '_' || CAST(a.attnum AS text) || '_not_null' AS sql_identifier) AS constraint_name, -- XXX
!            CAST(current_database() AS sql_identifier) AS table_catalog,
!            CAST(nr.nspname AS sql_identifier) AS table_schema,
!            CAST(r.relname AS sql_identifier) AS table_name,
!            CAST('CHECK' AS character_data) AS constraint_type,
!            CAST('NO' AS yes_or_no) AS is_deferrable,
!            CAST('NO' AS yes_or_no) AS initially_deferred
! 
!     FROM pg_namespace nr,
!          pg_class r,
!          pg_attribute a
! 
!     WHERE nr.oid = r.relnamespace
!           AND r.oid = a.attrelid
!           AND a.attnotnull
!           AND a.attnum > 0
!           AND NOT a.attisdropped
!           AND r.relkind = 'r'
!           AND (NOT pg_is_other_temp_schema(nr.oid))
!           AND (pg_has_role(r.relowner, 'USAGE')
!                -- SELECT privilege omitted, per SQL standard
!                OR has_table_privilege(r.oid, 'INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER')
!                OR has_any_column_privilege(r.oid, 'INSERT, UPDATE, REFERENCES') );
  
  GRANT SELECT ON table_constraints TO PUBLIC;
  
--- 1708,1714 ----
            AND (pg_has_role(r.relowner, 'USAGE')
                 -- SELECT privilege omitted, per SQL standard
                 OR has_table_privilege(r.oid, 'INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER')
!                OR has_any_column_privilege(r.oid, 'INSERT, UPDATE, REFERENCES') ) ;
  
  GRANT SELECT ON table_constraints TO PUBLIC;
  
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 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 */
--- 145,151 ----
  	/* 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 */
***************
*** 169,174 **** typedef struct NewConstraint
--- 169,186 ----
  } NewConstraint;
  
  /*
+  * Struct used to describe add/drop operations of NOT NULL constraints
+  */
+ typedef struct NotNullConstraint
+ {
+ 	char	   *conname;
+ 	char	   *attname;
+ 	AttrNumber	attnum;
+ 	bool		is_local;
+ 	int			inhcount;
+ } NotNullConstraint;
+ 
+ /*
   * Struct describing one new column value that needs to be computed during
   * Phase 3 copy (this could be either a new column with a non-null default, or
   * a column that we're changing the type of).  Columns without such an entry
***************
*** 183,188 **** typedef struct NewColumnValue
--- 195,211 ----
  } 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);
--- 267,282 ----
  
  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);
***************
*** 301,311 **** static void ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
  				bool recurse, bool recursing, LOCKMODE lockmode);
  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,
--- 329,354 ----
  				bool recurse, bool recursing, LOCKMODE lockmode);
  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 StoreNotNullConstraint(Oid relid, Oid nspid,
+ 					   NotNullConstraint *constraint);
+ static HeapTuple RelationFetchNotNullConstraint(Relation pg_constraint,
+ 							   Oid relid, AttrNumber attnum);
+ static List *GetRelationNotNullConstraints(Relation rel);
+ static void CreateOrAdjustNotNullConstraint(Relation rel, AttrNumber attnum,
+ 								char *attname, int addinhcount,
+ 								int tweak_islocal);
+ static AttrNumber get_constraint_singlecol_attno(HeapTuple tup);
+ static void CreateNotNullConstraint(Relation rel, AttrNumber attnum,
+ 						char *constr_name, int inhcount, bool islocal);
  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,
***************
*** 373,379 **** 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
--- 416,422 ----
  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
***************
*** 404,409 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
--- 447,453 ----
  	TupleDesc	descriptor;
  	List	   *inheritOids;
  	List	   *old_constraints;
+ 	List	   *null_constraints;
  	bool		localHasOids;
  	int			parentOidCount;
  	List	   *rawDefaults;
***************
*** 505,512 **** 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
--- 549,557 ----
  	 * 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
***************
*** 603,608 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
--- 648,657 ----
  	/* 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.
***************
*** 640,645 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
--- 689,741 ----
  }
  
  /*
+  * 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
   */
***************
*** 1202,1212 **** 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:
--- 1298,1313 ----
   *		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:
***************
*** 1252,1268 **** 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
--- 1353,1372 ----
   *----------
   */
  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
***************
*** 1320,1325 **** MergeAttributes(List *schema, List *supers, char relpersistence,
--- 1424,1430 ----
  					 * 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;
***************
*** 1351,1356 **** MergeAttributes(List *schema, List *supers, char relpersistence,
--- 1456,1463 ----
  		TupleConstr *constr;
  		AttrNumber *newattno;
  		AttrNumber	parent_attno;
+ 		List	   *parent_nns;
+ 		ListCell   *cell;
  
  		/*
  		 * A self-exclusive lock is needed here.  If two backends attempt to
***************
*** 1367,1374 **** 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\"",
--- 1474,1481 ----
  					 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\"",
***************
*** 1396,1404 **** 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
--- 1503,1539 ----
  		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
***************
*** 1438,1443 **** MergeAttributes(List *schema, List *supers, char relpersistence,
--- 1573,1579 ----
  				Oid			defTypeId;
  				int32		deftypmod;
  				Oid			defCollId;
+ 				NotNullConstraint *nnconstr;
  
  				/*
  				 * Yes, try to merge the two column definitions. They must
***************
*** 1480,1486 **** 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;
--- 1616,1665 ----
  									   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;
***************
*** 1504,1511 **** 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;
  			}
  
  			/*
--- 1683,1704 ----
  				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);
  			}
  
  			/*
***************
*** 1589,1594 **** MergeAttributes(List *schema, List *supers, char relpersistence,
--- 1782,1822 ----
  			}
  		}
  
+ 		/*
+ 		 * 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);
  
  		/*
***************
*** 1601,1610 **** 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)
  		{
--- 1829,1867 ----
  
  	/*
  	 * 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)
  		{
***************
*** 1669,1676 **** 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)
  				{
--- 1926,2004 ----
  
  				/* 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)
  				{
***************
*** 1681,1689 **** MergeAttributes(List *schema, List *supers, char relpersistence,
  			else
  			{
  				/*
! 				 * No, attach new column to result schema
  				 */
  				inhSchema = lappend(inhSchema, newdef);
  			}
  		}
  
--- 2009,2035 ----
  			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);
+ 				}
  			}
  		}
  
***************
*** 1721,1726 **** MergeAttributes(List *schema, List *supers, char relpersistence,
--- 2067,2073 ----
  
  	*supOids = parentOids;
  	*supconstr = constraints;
+ 	*nullconstr = notnullconstrs;
  	*supOidCount = parentsWithOids;
  	return schema;
  }
***************
*** 1769,1774 **** MergeCheckConstraint(List *constraints, char *name, Node *expr)
--- 2116,2150 ----
  	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
***************
*** 2647,2652 **** AlterTableGetLockLevel(List *cmds)
--- 3023,3029 ----
  			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;
***************
*** 2855,2867 **** 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;
--- 3232,3252 ----
  			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;
***************
*** 3107,3116 **** 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);
--- 3492,3514 ----
  			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);
***************
*** 3558,3566 **** 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++)
  		{
--- 3956,3964 ----
  	{
  		/*
  		 * 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++)
  		{
***************
*** 3698,3705 **** 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)
--- 4096,4104 ----
  				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)
***************
*** 4203,4208 **** ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
--- 4602,4608 ----
  	Oid			collOid;
  	Form_pg_type tform;
  	Expr	   *defval;
+ 	bool        need_final_cci;
  	List	   *children;
  	ListCell   *child;
  
***************
*** 4261,4266 **** ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
--- 4661,4677 ----
  			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 */
***************
*** 4328,4334 **** 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;
--- 4739,4745 ----
  	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;
***************
*** 4366,4371 **** ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
--- 4777,4784 ----
  	/* Make the attribute's catalog entry visible */
  	CommandCounterIncrement();
  
+ 	need_final_cci = false;
+ 
  	/*
  	 * Store the DEFAULT, if any, in the catalogs
  	 */
***************
*** 4388,4398 **** 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
--- 4801,4826 ----
  		 */
  		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
***************
*** 4460,4471 **** 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;
  	}
  
  	/*
--- 4888,4899 ----
  		}
  
  		/*
! 		 * 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);
  	}
  
  	/*
***************
*** 4567,4707 **** add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
  }
  
  /*
!  * ALTER TABLE SET WITH OIDS
!  *
!  * Basically this is an ADD COLUMN for the special OID column.	We have
!  * to cons up a ColumnDef node because the ADD COLUMN code needs one.
   */
! static void
! ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOCKMODE lockmode)
  {
! 	/* If we're recursing to a child table, the ColumnDef is already set up */
! 	if (cmd->def == NULL)
  	{
! 		ColumnDef  *cdef = makeNode(ColumnDef);
  
! 		cdef->colname = pstrdup("oid");
! 		cdef->typeName = makeTypeNameFromOid(OIDOID, -1);
! 		cdef->inhcount = 0;
! 		cdef->is_local = true;
! 		cdef->is_not_null = true;
! 		cdef->storage = 0;
! 		cmd->def = (Node *) cdef;
  	}
! 	ATPrepAddColumn(wqueue, rel, recurse, false, cmd, lockmode);
  
! 	if (recurse)
! 		cmd->subtype = AT_AddOidsRecurse;
  }
  
  /*
!  * 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,
! 				(errcode(ERRCODE_UNDEFINED_COLUMN),
! 				 errmsg("column \"%s\" of relation \"%s\" does not exist",
! 						colName, RelationGetRelationName(rel))));
  
! 	attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
  
! 	/* Prevent them from altering a system attribute */
! 	if (attnum <= 0)
! 		ereport(ERROR,
! 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("cannot alter system column \"%s\"",
! 						colName)));
  
! 	/*
! 	 * Check that the attribute is not in a primary key
! 	 */
  
! 	/* Loop over all indexes on the relation */
! 	indexoidlist = RelationGetIndexList(rel);
  
! 	foreach(indexoidscan, indexoidlist)
  	{
! 		Oid			indexoid = lfirst_oid(indexoidscan);
! 		HeapTuple	indexTuple;
! 		Form_pg_index indexStruct;
! 		int			i;
  
! 		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,
--- 4995,5536 ----
  }
  
  /*
!  * 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);
! }
! 
! 
! /*
!  * ALTER TABLE SET WITH OIDS
!  *
!  * Basically this is an ADD COLUMN for the special OID column.	We have
!  * to cons up a ColumnDef node because the ADD COLUMN code needs one.
   */
  static void
! ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOCKMODE lockmode)
! {
! 	/* If we're recursing to a child table, the ColumnDef is already set up */
! 	if (cmd->def == NULL)
! 	{
! 		ColumnDef  *cdef = makeNode(ColumnDef);
! 
! 		cdef->colname = pstrdup("oid");
! 		cdef->typeName = makeTypeNameFromOid(OIDOID, -1);
! 		cdef->inhcount = 0;
! 		cdef->is_local = true;
! 		cdef->is_not_null = true;
! 		cdef->storage = 0;
! 		cmd->def = (Node *) cdef;
! 	}
! 	ATPrepAddColumn(wqueue, rel, recurse, false, cmd, lockmode);
! 
! 	if (recurse)
! 		cmd->subtype = AT_AddOidsRecurse;
! }
! 
! /*
!  * 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,
***************
*** 4709,4742 **** 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
   */
--- 5538,5732 ----
  				 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,
! 				(errcode(ERRCODE_UNDEFINED_COLUMN),
! 				 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
   */
***************
*** 6609,6614 **** createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
--- 7599,7686 ----
  }
  
  /*
+  * 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.
***************
*** 6627,6633 **** 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)
--- 7699,7705 ----
  	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)
***************
*** 6651,6656 **** ATExecDropConstraint(Relation rel, const char *constrName,
--- 7723,7732 ----
  
  		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;
  
***************
*** 6661,6669 **** 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
--- 7737,7748 ----
  					 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
***************
*** 6674,6679 **** ATExecDropConstraint(Relation rel, const char *constrName,
--- 7753,7765 ----
  
  		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;
  	}
  
***************
*** 6703,6709 **** 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;
--- 7789,7795 ----
  	 * 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;
***************
*** 6732,6738 **** 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;
  
--- 7818,7828 ----
  
  			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;
  
***************
*** 7540,7545 **** ATPostAlterTypeParse(Oid oldId, char *cmd,
--- 8630,8636 ----
  								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);
***************
*** 8325,8330 **** ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
--- 9416,9422 ----
  	HeapTuple	inheritsTuple;
  	int32		inhseqno;
  	List	   *children;
+ 	List       *child_attnums;
  
  	/*
  	 * A self-exclusive lock is needed here.  See the similar case in
***************
*** 8412,8421 **** 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.
--- 9504,9514 ----
  						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.
***************
*** 8491,8497 **** 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;
--- 9584,9591 ----
   * 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;
***************
*** 8547,8552 **** MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
--- 9641,9651 ----
  					   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.)
  			 */
***************
*** 8585,8591 **** 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;
--- 9684,9691 ----
   * 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;
***************
*** 8612,8618 **** 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 */
--- 9712,9719 ----
  		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 */
***************
*** 8628,8646 **** 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
--- 9729,9775 ----
  			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
***************
*** 8683,8690 **** 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)
--- 9812,9819 ----
   * 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)
***************
*** 8695,8702 **** ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
  	ScanKeyData key[3];
  	HeapTuple	inheritsTuple,
  				attributeTuple,
! 				constraintTuple;
! 	List	   *connames;
  	bool		found = false;
  
  	/*
--- 9824,9831 ----
  	ScanKeyData key[3];
  	HeapTuple	inheritsTuple,
  				attributeTuple,
! 		        constraintTuple;
! 	List	   *constraints;
  	bool		found = false;
  
  	/*
***************
*** 8746,8751 **** ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
--- 9875,9882 ----
  						RelationGetRelationName(parent_rel),
  						RelationGetRelationName(rel))));
  
+ 	constraints = NIL;
+ 
  	/*
  	 * Search through child columns looking for ones matching parent rel
  	 */
***************
*** 8759,8764 **** ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
--- 9890,9896 ----
  	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)
***************
*** 8766,8773 **** 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);
--- 9898,9907 ----
  		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);
***************
*** 8780,8795 **** 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],
--- 9914,9931 ----
  			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],
***************
*** 8799,8812 **** 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);
--- 9935,9963 ----
  	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);
***************
*** 8821,8837 **** 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;
--- 9972,10012 ----
  
  	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;
***************
*** 8842,8853 **** 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;
--- 10017,10032 ----
  		{
  			/* 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,103 ----
  					Oid baseTypeOid,
  					int typMod, Constraint *constr,
  					char *domainName);
! static void domainAddNotNull(Oid domainOid, Oid domainNamespace,
! 				 Constraint *constr, char *domainName);
! static void domainDropNotNull(Oid domainOid);
  
  /*
   * DefineType
***************
*** 969,978 **** DefineDomain(CreateDomainStmt *stmt)
  				break;
  
  			case CONSTR_NOTNULL:
! 				if (nullDefined && !typNotNull)
! 					ereport(ERROR,
! 							(errcode(ERRCODE_SYNTAX_ERROR),
! 						   errmsg("conflicting NULL/NOT NULL constraints")));
  				typNotNull = true;
  				nullDefined = true;
  				break;
--- 971,995 ----
  				break;
  
  			case CONSTR_NOTNULL:
! 				if (nullDefined) 
! 				{
! 					if (!typNotNull)
! 						ereport(ERROR,
! 								(errcode(ERRCODE_SYNTAX_ERROR),
! 								 errmsg("conflicting NULL/NOT NULL constraints")));
! 					else
! 						ereport(ERROR,
! 								(errcode(ERRCODE_SYNTAX_ERROR),
! 								 errmsg("multiple definitions 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 domain's OID, this is done later.
! 				 */
! 
  				typNotNull = true;
  				nullDefined = true;
  				break;
***************
*** 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,
--- 1097,1110 ----
  	{
  		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)
--- 1878,1959 ----
  
  	CatalogUpdateIndexes(typrel, tup);
  
+ 	/* also update pg_constraint */
+ 	if (typTup->typnotnull)
+ 	{
+ 		Constraint constr;
+ 
+ 		/*
+ 		 * 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.contype = CONSTR_NOTNULL;
+ 		constr.conname = NULL;
+ 
+ 		domainAddNotNull(domainoid, typTup->typnamespace,
+ 						 &constr, NameStr(typTup->typname));
+ 	}
+ 	else
+ 		domainDropNotNull(domainoid);
+ 
  	/* Clean up */
  	heap_freetuple(tup);
  	heap_close(typrel, RowExclusiveLock);
  }
  
  /*
+  * Subroutine for AlterDomainNotNull
+  *		Delete the pg_constraint entry for a domain's NOT NULL constraint
+  */
+ static void
+ domainDropNotNull(Oid domainOid)
+ {
+ 	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 (HeapTupleIsValid(contup = systable_getnext(conscan)))
+ 	{
+ 		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, DROP_RESTRICT);
+ 
+ 			/*
+ 			 * we only expect a single tuple, but keep looping just in case.
+ 			 */
+ 		}
+ 	}
+ 
+ 	systable_endscan(conscan);
+ 	heap_close(conrel, RowExclusiveLock);
+ }
+ 
+ /*
   * AlterDomainDropConstraint
   *
   * Implements the ALTER DOMAIN DROP CONSTRAINT statement
***************
*** 2400,2405 **** checkDomainOwner(HeapTuple tup)
--- 2491,2558 ----
  }
  
  /*
+  * domainAddNotNull
+  *		add a NOT NULL constraint to pg_constraint for a domain.
+  */
+ void
+ domainAddNotNull(Oid domainOid, Oid domainNamespace, Constraint *constr,
+ 				 char *domainName)
+ {
+ 	Assert(constr->contype == CONSTR_NOTNULL);
+ 
+ 	/*
+ 	 * 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,
+ 						  domainNamespace,
+ 						  CONSTRAINT_NOTNULL,
+ 						  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
***************
*** 2236,2241 **** _equalColumnDef(ColumnDef *a, ColumnDef *b)
--- 2236,2242 ----
  	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
***************
*** 2095,2100 **** _outColumnDef(StringInfo str, ColumnDef *node)
--- 2095,2101 ----
  	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;
***************
*** 9171,9176 **** TableFuncElement:	ColId Typename opt_collate_clause
--- 9174,9180 ----
  					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
***************
*** 1110,1122 **** 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;
--- 1110,1160 ----
  
  	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/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
***************
*** 439,444 **** flagInhAttrs(TableInfo *tblinfo, int numTables)
--- 439,449 ----
  					tbinfo->inhNotNull[j] = false;
  				}
  
+ 				/*
+ 				 * Clear it if NOT NULL is known to have a local definition */
+ 				if (tbinfo->localnotnull[j])
+ 					tbinfo->inhNotNull[j] = false;
+ 
  				/* Clear it if attr has local definition */
  				if (tbinfo->attislocal[j])
  					tbinfo->inhAttrs[j] = false;
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 1767,1773 **** dumpDatabase(Archive *AH)
  						  "datcollate, datctype, datfrozenxid, "
  						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
  					  "shobj_description(oid, 'pg_database') AS description "
- 
  						  "FROM pg_database "
  						  "WHERE datname = ",
  						  username_subquery);
--- 1767,1772 ----
***************
*** 1781,1787 **** dumpDatabase(Archive *AH)
  					   "NULL AS datcollate, NULL AS datctype, datfrozenxid, "
  						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
  					  "shobj_description(oid, 'pg_database') AS description "
- 
  						  "FROM pg_database "
  						  "WHERE datname = ",
  						  username_subquery);
--- 1780,1785 ----
***************
*** 4827,4832 **** getDomainConstraints(TypeInfo *tyinfo)
--- 4825,4831 ----
  	int			i_tableoid,
  				i_oid,
  				i_conname,
+ 				i_contype,
  				i_consrc;
  	int			ntups;
  
***************
*** 4843,4856 **** getDomainConstraints(TypeInfo *tyinfo)
  	query = createPQExpBuffer();
  
  	if (g_fout->remoteVersion >= 70400)
! 		appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
  						  "pg_catalog.pg_get_constraintdef(oid) AS consrc "
  						  "FROM pg_catalog.pg_constraint "
  						  "WHERE contypid = '%u'::pg_catalog.oid "
  						  "ORDER BY conname",
  						  tyinfo->dobj.catId.oid);
  	else
! 		appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
  						  "'CHECK (' || consrc || ')' AS consrc "
  						  "FROM pg_catalog.pg_constraint "
  						  "WHERE contypid = '%u'::pg_catalog.oid "
--- 4842,4855 ----
  	query = createPQExpBuffer();
  
  	if (g_fout->remoteVersion >= 70400)
! 		appendPQExpBuffer(query, "SELECT tableoid, oid, conname, contype, "
  						  "pg_catalog.pg_get_constraintdef(oid) AS consrc "
  						  "FROM pg_catalog.pg_constraint "
  						  "WHERE contypid = '%u'::pg_catalog.oid "
  						  "ORDER BY conname",
  						  tyinfo->dobj.catId.oid);
  	else
! 		appendPQExpBuffer(query, "SELECT tableoid, oid, conname, 'c' as contype, "
  						  "'CHECK (' || consrc || ')' AS consrc "
  						  "FROM pg_catalog.pg_constraint "
  						  "WHERE contypid = '%u'::pg_catalog.oid "
***************
*** 4865,4870 **** getDomainConstraints(TypeInfo *tyinfo)
--- 4864,4870 ----
  	i_tableoid = PQfnumber(res, "tableoid");
  	i_oid = PQfnumber(res, "oid");
  	i_conname = PQfnumber(res, "conname");
+ 	i_contype = PQfnumber(res, "contype");
  	i_consrc = PQfnumber(res, "consrc");
  
  	constrinfo = (ConstraintInfo *) malloc(ntups * sizeof(ConstraintInfo));
***************
*** 4882,4888 **** getDomainConstraints(TypeInfo *tyinfo)
  		constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
  		constrinfo[i].contable = NULL;
  		constrinfo[i].condomain = tyinfo;
! 		constrinfo[i].contype = 'c';
  		constrinfo[i].condef = strdup(PQgetvalue(res, i, i_consrc));
  		constrinfo[i].confrelid = InvalidOid;
  		constrinfo[i].conindex = 0;
--- 4882,4888 ----
  		constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
  		constrinfo[i].contable = NULL;
  		constrinfo[i].condomain = tyinfo;
! 		constrinfo[i].contype = *PQgetvalue(res, i, i_contype);
  		constrinfo[i].condef = strdup(PQgetvalue(res, i, i_consrc));
  		constrinfo[i].confrelid = InvalidOid;
  		constrinfo[i].conindex = 0;
***************
*** 5581,5586 **** getTableAttrs(TableInfo *tblinfo, int numTables)
--- 5581,5587 ----
  	for (i = 0; i < numTables; i++)
  	{
  		TableInfo  *tbinfo = &tblinfo[i];
+ 		int			notnulls;
  
  		/* Don't bother to collect info for sequences */
  		if (tbinfo->relkind == RELKIND_SEQUENCE)
***************
*** 5739,5744 **** getTableAttrs(TableInfo *tblinfo, int numTables)
--- 5740,5747 ----
  		tbinfo->attalign = (char *) malloc(ntups * sizeof(char));
  		tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool));
  		tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool));
+ 		tbinfo->namenotnull = (char **) malloc(ntups * sizeof(char *));
+ 		tbinfo->localnotnull = (bool *) malloc(ntups * sizeof(bool));
  		tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *));
  		tbinfo->attoptions = (char **) malloc(ntups * sizeof(char *));
  		tbinfo->attcollation = (Oid *) malloc(ntups * sizeof(Oid));
***************
*** 5746,5751 **** getTableAttrs(TableInfo *tblinfo, int numTables)
--- 5749,5755 ----
  		tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool));
  		tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool));
  		hasdefaults = false;
+ 		notnulls = 0;
  
  		for (j = 0; j < ntups; j++)
  		{
***************
*** 5766,5771 **** getTableAttrs(TableInfo *tblinfo, int numTables)
--- 5770,5780 ----
  			tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign));
  			tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't');
  			tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
+ 			tbinfo->namenotnull[j] = NULL;		/* fix below */
+ 			tbinfo->localnotnull[j] = false;	/* fix below */
+ 			/* not null info is dumped separately only in 9.2 up */
+ 			if (tbinfo->notnull[j] && g_fout->remoteVersion >= 90200)
+ 				notnulls++;
  			tbinfo->attoptions[j] = strdup(PQgetvalue(res, j, i_attoptions));
  			tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation));
  			tbinfo->attrdefs[j] = NULL; /* fix below */
***************
*** 6020,6025 **** getTableAttrs(TableInfo *tblinfo, int numTables)
--- 6029,6074 ----
  			}
  			PQclear(res);
  		}
+ 
+ 		/*
+ 		 * Get info about not null constraints
+ 		 */
+ 		if (notnulls > 0)
+ 		{
+ 			if (g_verbose)
+ 				write_msg(NULL, "finding NOT NULL constraint names for table \"%s\"\n",
+ 						  tbinfo->dobj.name);
+ 
+ 			resetPQExpBuffer(q);
+ 			appendPQExpBuffer(q, "SELECT conkey[1], conname, conislocal "
+ 							  "FROM pg_catalog.pg_constraint "
+ 							  "WHERE conrelid = '%u'::pg_catalog.oid "
+ 							  "   AND contype = 'n' "
+ 							  "ORDER BY 1",
+ 							  tbinfo->dobj.catId.oid);
+ 			res = PQexec(g_conn, q->data);
+ 			check_sql_result(res, g_conn, q->data, PGRES_TUPLES_OK);
+ 
+ 			if (PQntuples(res) != notnulls)
+ 			{
+ 				write_msg(NULL, ngettext("expected %d not null constraint on table \"%s\" but found %d\n",
+ 										 "expected %d not null constraints on table \"%s\" but found %d\n",
+ 										 notnulls),
+ 						  notnulls, tbinfo->dobj.name, PQntuples(res));
+ 				write_msg(NULL, "(The system catalogs might be corrupted.)\n");
+ 				exit_nicely();
+ 			}
+ 
+ 			for (j = 0; j < notnulls; j++)
+ 			{
+ 				int		colnum = atoi(PQgetvalue(res, j, 0));
+ 
+ 				tbinfo->namenotnull[colnum - 1] = strdup(PQgetvalue(res, j, 1));
+ 				tbinfo->localnotnull[colnum - 1] = PQgetvalue(res, j, 2)[0] == 't';
+ 			}
+ 
+ 			PQclear(res);
+ 		}
  	}
  
  	destroyPQExpBuffer(q);
***************
*** 7805,7811 **** dumpDomain(Archive *fout, TypeInfo *tyinfo)
  	selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
  
  	/* Fetch domain specific details */
! 	if (g_fout->remoteVersion >= 90100)
  	{
  		/* typcollation is new in 9.1 */
  		appendPQExpBuffer(query, "SELECT t.typnotnull, "
--- 7854,7875 ----
  	selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
  
  	/* Fetch domain specific details */
! 	if (g_fout->remoteVersion >= 90200)
! 	{
! 		/* NOT NULL is handled as a constraint in 9.2 */
! 		appendPQExpBuffer(query, "SELECT 'f' AS typnotnull, "
! 			"pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
! 						  "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
! 						  "t.typdefault, "
! 						  "CASE WHEN t.typcollation <> u.typcollation "
! 						  "THEN t.typcollation ELSE 0 END AS typcollation "
! 						  "FROM pg_catalog.pg_type t "
! 				 "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
! 						  "WHERE t.oid = '%u'::pg_catalog.oid",
! 						  tyinfo->dobj.catId.oid);
! 
! 	}
! 	else if (g_fout->remoteVersion >= 90100)
  	{
  		/* typcollation is new in 9.1 */
  		appendPQExpBuffer(query, "SELECT t.typnotnull, "
***************
*** 7904,7911 **** dumpDomain(Archive *fout, TypeInfo *tyinfo)
  		ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
  
  		if (!domcheck->separate)
! 			appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
! 							  fmtId(domcheck->dobj.name), domcheck->condef);
  	}
  
  	appendPQExpBuffer(q, ";\n");
--- 7968,7981 ----
  		ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
  
  		if (!domcheck->separate)
! 		{
! 			if (domcheck->contype == 'c')
! 				appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
! 								  fmtId(domcheck->dobj.name), domcheck->condef);
! 			else
! 				appendPQExpBuffer(q, "\n\tCONSTRAINT %s NOT NULL",
! 								  fmtId(domcheck->dobj.name));
! 		}
  	}
  
  	appendPQExpBuffer(q, ";\n");
***************
*** 12202,12208 **** dumpTableSchema(Archive *fout, TableInfo *tbinfo)
--- 12272,12282 ----
  									  tbinfo->attrdefs[j]->adef_expr);
  
  				if (has_notnull)
+ 				{
+ 					if (tbinfo->namenotnull[j])
+ 						appendPQExpBuffer(q, " CONSTRAINT %s", tbinfo->namenotnull[j]);
  					appendPQExpBuffer(q, " NOT NULL");
+ 				}
  			}
  		}
  
***************
*** 12888,12893 **** dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
--- 12962,13005 ----
  						 NULL, NULL);
  		}
  	}
+ 	else if (coninfo->contype == 'n' && tbinfo == NULL)
+ 	{
+ 		/* NOT NULL constraint on a domain */
+ 		TypeInfo   *tyinfo = coninfo->condomain;
+ 
+ 		/* Ignore if not to be dumped separately */
+ 		if (coninfo->separate)
+ 		{
+ 			/*
+ 			 * FIXME this code path is apparently unused.  Is there any use
+ 			 * for separate NOT NULL constraints?
+ 			 */
+ 			appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
+ 							  fmtId(tyinfo->dobj.name));
+ 			/* XXX we lose the constraint name */
+ 			appendPQExpBuffer(q, "    SET NOT NULL;\n");
+ 
+ 			/*
+ 			 * DROP must be fully qualified in case same name appears in
+ 			 * pg_catalog
+ 			 */
+ 			appendPQExpBuffer(delq, "ALTER DOMAIN %s.",
+ 							  fmtId(tyinfo->dobj.namespace->dobj.name));
+ 			appendPQExpBuffer(delq, "%s ",
+ 							  fmtId(tyinfo->dobj.name));
+ 			appendPQExpBuffer(delq, "DROP NOT NULL;\n");
+ 
+ 			ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
+ 						 coninfo->dobj.name,
+ 						 tyinfo->dobj.namespace->dobj.name,
+ 						 NULL,
+ 						 tyinfo->rolname, false,
+ 						 "NOT NULL CONSTRAINT", SECTION_POST_DATA,
+ 						 q->data, delq->data, NULL,
+ 						 coninfo->dobj.dependencies, coninfo->dobj.nDeps,
+ 						 NULL, NULL);
+ 		}
+ 	}
  	else
  	{
  		write_msg(NULL, "unrecognized constraint type: %c\n", coninfo->contype);
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
***************
*** 282,287 **** typedef struct _tableInfo
--- 282,289 ----
  	 * were inherited.
  	 */
  	bool	   *notnull;		/* Not null constraints on attributes */
+ 	bool	   *localnotnull;	/* whether NOT NULL constrs have local defs */
+ 	char	  **namenotnull;	/* names of the NOT NULL constraints */
  	struct _attrDefInfo **attrdefs;		/* DEFAULT expressions */
  	bool	   *inhAttrs;		/* true if each attribute is inherited */
  	bool	   *inhAttrDef;		/* true if attr's default is inherited */
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
***************
*** 753,759 **** repairTableAttrDefMultiLoop(DumpableObject *tableobj,
  }
  
  /*
!  * CHECK constraints on domains work just like those on tables ...
   */
  static void
  repairDomainConstraintLoop(DumpableObject *domainobj,
--- 753,759 ----
  }
  
  /*
!  * CHECK and NOT NULL constraints on domains work just like those on tables ...
   */
  static void
  repairDomainConstraintLoop(DumpableObject *domainobj,
***************
*** 928,938 **** repairDependencyLoop(DumpableObject **loop,
  		}
  	}
  
! 	/* Domain and CHECK constraint */
  	if (nLoop == 2 &&
  		loop[0]->objType == DO_TYPE &&
  		loop[1]->objType == DO_CONSTRAINT &&
! 		((ConstraintInfo *) loop[1])->contype == 'c' &&
  		((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
  	{
  		repairDomainConstraintLoop(loop[0], loop[1]);
--- 928,939 ----
  		}
  	}
  
! 	/* Domain and CHECK or NOT NULL constraint */
  	if (nLoop == 2 &&
  		loop[0]->objType == DO_TYPE &&
  		loop[1]->objType == DO_CONSTRAINT &&
! 		(((ConstraintInfo *) loop[1])->contype == 'c' ||
! 		 ((ConstraintInfo *) loop[1])->contype == 'n') &&
  		((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
  	{
  		repairDomainConstraintLoop(loop[0], loop[1]);
***************
*** 941,954 **** repairDependencyLoop(DumpableObject **loop,
  	if (nLoop == 2 &&
  		loop[1]->objType == DO_TYPE &&
  		loop[0]->objType == DO_CONSTRAINT &&
! 		((ConstraintInfo *) loop[0])->contype == 'c' &&
  		((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
  	{
  		repairDomainConstraintLoop(loop[1], loop[0]);
  		return;
  	}
  
! 	/* Indirect loop involving domain and CHECK constraint */
  	if (nLoop > 2)
  	{
  		for (i = 0; i < nLoop; i++)
--- 942,956 ----
  	if (nLoop == 2 &&
  		loop[1]->objType == DO_TYPE &&
  		loop[0]->objType == DO_CONSTRAINT &&
! 		(((ConstraintInfo *) loop[0])->contype == 'c' ||
! 		 ((ConstraintInfo *) loop[0])->contype == 'n') &&
  		((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
  	{
  		repairDomainConstraintLoop(loop[1], loop[0]);
  		return;
  	}
  
! 	/* Indirect loop involving domain and CHECK or NOT NULL constraint */
  	if (nLoop > 2)
  	{
  		for (i = 0; i < nLoop; i++)
***************
*** 958,964 **** repairDependencyLoop(DumpableObject **loop,
  				for (j = 0; j < nLoop; j++)
  				{
  					if (loop[j]->objType == DO_CONSTRAINT &&
! 						((ConstraintInfo *) loop[j])->contype == 'c' &&
  						((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
  					{
  						repairDomainConstraintMultiLoop(loop[i], loop[j]);
--- 960,967 ----
  				for (j = 0; j < nLoop; j++)
  				{
  					if (loop[j]->objType == DO_CONSTRAINT &&
! 						(((ConstraintInfo *) loop[j])->contype == 'c' ||
! 						 ((ConstraintInfo *) loop[j])->contype == 'n') &&
  						((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
  					{
  						repairDomainConstraintMultiLoop(loop[i], loop[j]);
*** 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'
*** 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;
