[badalex@gmail.com: Re: [BUGS] Problem identifying constraints which should not be inherited]

Started by Alvaro Herreraalmost 18 years ago15 messages
#1Alvaro Herrera
alvherre@commandprompt.com
1 attachment(s)

Forwarding this message so that it gets archived.

--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

Attachments:

inherited_constraint.patchapplication/octet-stream; name=inherited_constraint.patchDownload
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 87a4d73..477b958 100644
*** a/src/backend/catalog/heap.c
--- /bsrc/backend/catalog/heap.c
*************** static Oid AddNewRelationType(const char
*** 74,80 ****
  				   char new_rel_kind,
  				   Oid new_array_type);
  static void RelationRemoveInheritance(Oid relid);
! static void StoreRelCheck(Relation rel, char *ccname, char *ccbin);
  static void StoreConstraints(Relation rel, TupleDesc tupdesc);
  static void SetRelationNumChecks(Relation rel, int numchecks);
  static List *insert_ordered_unique_oid(List *list, Oid datum);
--- 74,80 ----
  				   char new_rel_kind,
  				   Oid new_array_type);
  static void RelationRemoveInheritance(Oid relid);
! static void StoreRelCheck(Relation rel, char *ccname, char *ccbin, const bool is_local, const int inhcount);
  static void StoreConstraints(Relation rel, TupleDesc tupdesc);
  static void SetRelationNumChecks(Relation rel, int numchecks);
  static List *insert_ordered_unique_oid(List *list, Oid datum);
*************** StoreAttrDefault(Relation rel, AttrNumbe
*** 1530,1536 ****
   * in the pg_class entry for the relation.
   */
  static void
! StoreRelCheck(Relation rel, char *ccname, char *ccbin)
  {
  	Node	   *expr;
  	char	   *ccsrc;
--- 1530,1536 ----
   * in the pg_class entry for the relation.
   */
  static void
! StoreRelCheck(Relation rel, char *ccname, char *ccbin, const bool is_local, const int inhcount)
  {
  	Node	   *expr;
  	char	   *ccsrc;
*************** StoreRelCheck(Relation rel, char *ccname
*** 1607,1613 ****
  						  InvalidOid,	/* no associated index */
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc);		/* Source form check constraint */
  
  	pfree(ccsrc);
  }
--- 1607,1615 ----
  						  InvalidOid,	/* no associated index */
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc, /* Source form check constraint */
! 						  is_local, /* islocal */
! 						  inhcount); /* coninhcount */
  
  	pfree(ccsrc);
  }
*************** StoreConstraints(Relation rel, TupleDesc
*** 1642,1648 ****
  
  	for (i = 0; i < constr->num_check; i++)
  		StoreRelCheck(rel, constr->check[i].ccname,
! 					  constr->check[i].ccbin);
  
  	if (constr->num_check > 0)
  		SetRelationNumChecks(rel, constr->num_check);
--- 1644,1652 ----
  
  	for (i = 0; i < constr->num_check; i++)
  		StoreRelCheck(rel, constr->check[i].ccname,
! 					  constr->check[i].ccbin,
! 					  constr->check[i].is_local,
! 					  constr->check[i].inhcount);
  
  	if (constr->num_check > 0)
  		SetRelationNumChecks(rel, constr->num_check);
*************** AddRelationRawConstraints(Relation rel,
*** 1793,1798 ****
--- 1797,1842 ----
  					(errcode(ERRCODE_GROUPING_ERROR),
  			   errmsg("cannot use aggregate function in check constraint")));
  
+ 		/* are we adding the constraint to a recursion child? */
+ 		/* XXX: this does not really work.... needs to be higher up _or_ we need to filter out dups
+ 		 * because right now if one does create table ( ... constraint check_b) inherits(junk);
+ 		 * we increase our inhcount to 2 for check_b (assuming junk has a check_b constraint)
+ 		 * plus we never check the consrc/conbin and make sure the match */
+ 		if (cdef->name != NULL)
+ 		{
+ 			HeapTuple tuple;
+ 			Form_pg_constraint con;
+ 			Relation conrel;
+ 
+ 			tuple = SearchSysCacheCopy(CONSTRNAME,
+ 								   ObjectIdGetDatum(RelationGetRelid(rel)),
+ 								   CStringGetDatum(cdef->name),
+ 								   0, 0);
+ 
+ 			/*
+ 			 * if we had the constraint, increment the inhcount
+ 			 * otherwise we just continue on adding the constraint
+ 			 */
+ 			if (HeapTupleIsValid(tuple))
+ 			{
+ 				con = (Form_pg_constraint) GETSTRUCT(tuple);
+ 				con->coninhcount++;
+ 
+ 				conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
+ 				simple_heap_update(conrel, &tuple->t_self, tuple);
+ 				CatalogUpdateIndexes(conrel, tuple);
+ 				heap_freetuple(tuple);
+ 				heap_close(conrel, RowExclusiveLock);
+ 
+ 				ereport(NOTICE,
+ 						(errmsg("merging constraint \"%s\" for child \"%s\"",
+ 							cdef->name, RelationGetRelationName(rel))));
+ 
+ 				/* all done, go onto the next constraint */
+ 				continue;
+ 			}
+ 		}
+ 
  		/*
  		 * Check name uniqueness, or generate a name if none was given.
  		 */
*************** AddRelationRawConstraints(Relation rel,
*** 1810,1815 ****
--- 1854,1860 ----
  						(errcode(ERRCODE_DUPLICATE_OBJECT),
  				errmsg("constraint \"%s\" for relation \"%s\" already exists",
  					   ccname, RelationGetRelationName(rel))));
+ 
  			/* Check against other new constraints */
  			/* Needed because we don't do CommandCounterIncrement in loop */
  			foreach(cell2, checknames)
*************** AddRelationRawConstraints(Relation rel,
*** 1862,1868 ****
  		/*
  		 * OK, store it.
  		 */
! 		StoreRelCheck(rel, ccname, nodeToString(expr));
  
  		numchecks++;
  
--- 1907,1913 ----
  		/*
  		 * OK, store it.
  		 */
! 		StoreRelCheck(rel, ccname, nodeToString(expr), cdef->is_local, cdef->inhcount);
  
  		numchecks++;
  
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index fd45460..7d97777 100644
*** a/src/backend/catalog/index.c
--- /bsrc/backend/catalog/index.c
*************** index_create(Oid heapRelationId,
*** 716,722 ****
  										   InvalidOid,	/* no associated index */
  										   NULL,		/* no check constraint */
  										   NULL,
! 										   NULL);
  
  			referenced.classId = ConstraintRelationId;
  			referenced.objectId = conOid;
--- 716,724 ----
  										   InvalidOid,	/* no associated index */
  										   NULL,		/* no check constraint */
  										   NULL,
! 										   NULL,
! 										   true, /* islocal */
! 										   0); /* inhcount */
  
  			referenced.classId = ConstraintRelationId;
  			referenced.objectId = conOid;
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 073047b..b1c4a80 100644
*** a/src/backend/catalog/pg_constraint.c
--- /bsrc/backend/catalog/pg_constraint.c
***************
*** 28,34 ****
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
  
- 
  /*
   * CreateConstraintEntry
   *	Create a constraint table entry.
--- 28,33 ----
*************** CreateConstraintEntry(const char *constr
*** 59,65 ****
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc)
  {
  	Relation	conDesc;
  	Oid			conOid;
--- 58,66 ----
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc,
! 					  const bool conIsLocal,
! 					  const int conInhCount)
  {
  	Relation	conDesc;
  	Oid			conOid;
*************** CreateConstraintEntry(const char *constr
*** 170,175 ****
--- 171,179 ----
  	else
  		nulls[Anum_pg_constraint_conffeqop - 1] = 'n';
  
+ 	values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
+ 	values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
+ 
  	/*
  	 * initialize the binary form of the check constraint.
  	 */
*************** AlterConstraintNamespaces(Oid ownerId, O
*** 695,697 ****
--- 699,744 ----
  
  	heap_close(conRel, RowExclusiveLock);
  }
+ 
+ /*
+  * Obtain the source-text form of the constraint expression for a check
+  * constraint, given its pg_constraint tuple
+  */
+ static char *
+ decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
+ {
+ 	Form_pg_constraint con;
+ 	bool		isnull;
+ 	Datum		attr;
+ 	Datum		expr;
+ 
+ 	con = (Form_pg_constraint) GETSTRUCT(contup);
+ 	attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
+ 	if (isnull)
+ 		elog(ERROR, "null conbin for constraint %u", HeapTupleGetOid(contup));
+ 
+ 	expr = DirectFunctionCall2(pg_get_expr, attr,
+ 							   ObjectIdGetDatum(con->conrelid));
+ 	return DatumGetCString(DirectFunctionCall1(textout, expr));
+ }
+ 
+ /*
+  * See if tow constraints are "functionally" equivalent, but not necessarily equal
+  */
+ bool
+ ConstraintIsEquiv(const HeapTuple a, const HeapTuple b, const TupleDesc tupleDesc)
+ {
+ 	const Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
+ 	const Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
+ 
+ 	if (acon->condeferrable != bcon->condeferrable ||
+ 		acon->condeferred != bcon->condeferred ||
+ 			strcmp(decompile_conbin(a, tupleDesc),
+ 				   decompile_conbin(b, tupleDesc)) != 0)
+ 	{
+ 		return 0;
+ 	}else
+ 	{
+ 		return 1;
+ 	}
+ }
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 886b792..78925c8 100644
*** a/src/backend/commands/tablecmds.c
--- /bsrc/backend/commands/tablecmds.c
*************** static void ATExecDropColumn(Relation re
*** 229,242 ****
  				 bool recurse, bool recursing);
  static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
  			   IndexStmt *stmt, bool is_rebuild);
  static void ATExecAddConstraint(AlteredTableInfo *tab, Relation rel,
  					Node *newConstraint);
  static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
  						  FkConstraint *fkconstraint);
- static void ATPrepDropConstraint(List **wqueue, Relation rel,
- 					 bool recurse, AlterTableCmd *cmd);
  static void ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior, bool quiet);
  static void ATPrepAlterColumnType(List **wqueue,
  					  AlteredTableInfo *tab, Relation rel,
  					  bool recurse, bool recursing,
--- 229,243 ----
  				 bool recurse, bool recursing);
  static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
  			   IndexStmt *stmt, bool is_rebuild);
+ static void ATPrepAddConstraint(List **wqueue, Relation rel, bool recurse,
+ 				AlterTableCmd *cmd);
  static void ATExecAddConstraint(AlteredTableInfo *tab, Relation rel,
  					Node *newConstraint);
  static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
  						  FkConstraint *fkconstraint);
  static void ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior, 
! 					 bool recurse, bool recursing);
  static void ATPrepAlterColumnType(List **wqueue,
  					  AlteredTableInfo *tab, Relation rel,
  					  bool recurse, bool recursing,
*************** static void ATExecAddInherit(Relation re
*** 261,267 ****
  static void ATExecDropInherit(Relation rel, RangeVar *parent);
  static void copy_relation_data(Relation rel, SMgrRelation dst);
  
- 
  /* ----------------------------------------------------------------
   *		DefineRelation
   *				Creates a new relation.
--- 262,267 ----
*************** DefineRelation(CreateStmt *stmt, char re
*** 387,392 ****
--- 387,393 ----
  		check = (ConstrCheck *)
  			palloc((list_length(old_constraints) +
  					list_length(stmt->constraints)) * sizeof(ConstrCheck));
+ 
  		/* deal with constraints from MergeAttributes */
  		foreach(listptr, old_constraints)
  		{
*************** DefineRelation(CreateStmt *stmt, char re
*** 400,405 ****
--- 401,407 ----
  		 * parse_utilcmd.c might have passed some precooked constraints too,
  		 * due to LIKE tab INCLUDING CONSTRAINTS
  		 */
+ 
  		foreach(listptr, stmt->constraints)
  		{
  			Constraint *cdef = (Constraint *) lfirst(listptr);
*************** DefineRelation(CreateStmt *stmt, char re
*** 407,421 ****
  			if (cdef->contype == CONSTR_CHECK && cdef->cooked_expr != NULL)
  				add_nonduplicate_constraint(cdef, check, &ncheck);
  		}
  		/* if we found any, insert 'em into the descriptor */
  		if (ncheck > 0)
  		{
  			if (descriptor->constr == NULL)
  			{
! 				descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr));
! 				descriptor->constr->defval = NULL;
! 				descriptor->constr->num_defval = 0;
! 				descriptor->constr->has_not_null = false;
  			}
  			descriptor->constr->num_check = ncheck;
  			descriptor->constr->check = check;
--- 409,424 ----
  			if (cdef->contype == CONSTR_CHECK && cdef->cooked_expr != NULL)
  				add_nonduplicate_constraint(cdef, check, &ncheck);
  		}
+ 
  		/* if we found any, insert 'em into the descriptor */
  		if (ncheck > 0)
  		{
  			if (descriptor->constr == NULL)
  			{
! 					descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr));
! 					descriptor->constr->defval = NULL;
! 					descriptor->constr->num_defval = 0;
! 					descriptor->constr->has_not_null = false;
  			}
  			descriptor->constr->num_check = ncheck;
  			descriptor->constr->check = check;
*************** MergeAttributes(List *schema, List *supe
*** 991,996 ****
--- 994,1001 ----
  				Constraint *cdef = makeNode(Constraint);
  				Node	   *expr;
  
+ 				cdef->is_local = false;
+ 				cdef->inhcount = check[i].inhcount + 1;
  				cdef->contype = CONSTR_CHECK;
  				cdef->name = pstrdup(check[i].ccname);
  				cdef->raw_expr = NULL;
*************** static void
*** 1126,1141 ****
  add_nonduplicate_constraint(Constraint *cdef, ConstrCheck *check, int *ncheck)
  {
  	int			i;
- 
  	/* Should only see precooked constraints here */
  	Assert(cdef->contype == CONSTR_CHECK);
  	Assert(cdef->name != NULL);
  	Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL);
  
  	for (i = 0; i < *ncheck; i++)
  	{
  		if (strcmp(check[i].ccname, cdef->name) != 0)
  			continue;
  		if (strcmp(check[i].ccbin, cdef->cooked_expr) == 0)
  			return;				/* duplicate constraint, so ignore it */
  		ereport(ERROR,
--- 1131,1148 ----
  add_nonduplicate_constraint(Constraint *cdef, ConstrCheck *check, int *ncheck)
  {
  	int			i;
  	/* Should only see precooked constraints here */
  	Assert(cdef->contype == CONSTR_CHECK);
  	Assert(cdef->name != NULL);
+ 
+ 
  	Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL);
  
  	for (i = 0; i < *ncheck; i++)
  	{
  		if (strcmp(check[i].ccname, cdef->name) != 0)
  			continue;
+ 
  		if (strcmp(check[i].ccbin, cdef->cooked_expr) == 0)
  			return;				/* duplicate constraint, so ignore it */
  		ereport(ERROR,
*************** add_nonduplicate_constraint(Constraint *
*** 1146,1151 ****
--- 1153,1160 ----
  	/* No match on name, so add it to array */
  	check[*ncheck].ccname = cdef->name;
  	check[*ncheck].ccbin = pstrdup(cdef->cooked_expr);
+ 	check[*ncheck].inhcount = cdef->inhcount;
+ 	check[*ncheck].is_local = cdef->is_local;
  	(*ncheck)++;
  }
  
*************** ATPrepCmd(List **wqueue, Relation rel, A
*** 1949,1975 ****
  			break;
  		case AT_AddConstraint:	/* ADD CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 
! 			/*
! 			 * Currently we recurse only for CHECK constraints, never for
! 			 * foreign-key constraints.  UNIQUE/PKEY constraints won't be seen
! 			 * here.
! 			 */
! 			if (IsA(cmd->def, Constraint))
! 				ATSimpleRecursion(wqueue, rel, cmd, recurse);
! 			/* No command-specific prep needed */
  			pass = AT_PASS_ADD_CONSTR;
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 			/* Performs own recursion */
! 			ATPrepDropConstraint(wqueue, rel, recurse, cmd);
! 			pass = AT_PASS_DROP;
! 			break;
! 		case AT_DropConstraintQuietly:	/* DROP CONSTRAINT for child */
! 			ATSimplePermissions(rel, false);
! 			ATSimpleRecursion(wqueue, rel, cmd, recurse);
! 			/* No command-specific prep needed */
  			pass = AT_PASS_DROP;
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
--- 1958,1973 ----
  			break;
  		case AT_AddConstraint:	/* ADD CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 			ATPrepAddConstraint(wqueue, rel, recurse, cmd);
  			pass = AT_PASS_ADD_CONSTR;
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 			/* we follow AT_DropColumns example for better or for worse so
! 			 * Recursion occurs during execution phase
! 			 * No command-specific prep needed except saving recurse flag */
! 			if (recurse)
! 				cmd->subtype = AT_DropConstraintRecurse;
  			pass = AT_PASS_DROP;
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
*************** ATExecCmd(AlteredTableInfo *tab, Relatio
*** 2158,2167 ****
  			ATExecAddConstraint(tab, rel, cmd->def);
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, false);
  			break;
! 		case AT_DropConstraintQuietly:	/* DROP CONSTRAINT for child */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, true);
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
  			ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def);
--- 2156,2165 ----
  			ATExecAddConstraint(tab, rel, cmd->def);
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, false, false);
  			break;
! 		case AT_DropConstraintRecurse:	/* DROP CONSTRAINT for child */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, true, false);
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
  			ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def);
*************** ATExecAddIndex(AlteredTableInfo *tab, Re
*** 3836,3841 ****
--- 3834,3888 ----
   * ALTER TABLE ADD CONSTRAINT
   */
  static void
+ ATPrepAddConstraint(List **wqueue, Relation rel, bool recurse,
+ 				AlterTableCmd *cmd)
+ {
+ 	/*
+ 	 * Currently we recurse only for CHECK constraints, never for
+ 	 * foreign-key constraints.  UNIQUE/PKEY constraints won't be seen
+ 	 * here.
+ 	 */
+ 	if (!IsA(cmd->def, Constraint))
+ 	{
+ 		return;
+ 	}
+ 
+ 	/*
+ 	 * Recurse to add the column to child classes, if requested.
+ 	 *
+ 	 * We must recurse one level at a time, so that multiply-inheriting
+ 	 * children are visited the right number of times and end up with the
+ 	 * right coninhcount.
+ 	 */
+ 	if (recurse)
+ 	{
+ 		AlterTableCmd *childCmd = copyObject(cmd);
+ 		Constraint *constr = (Constraint *) childCmd->def;
+ 
+ 		/* child should see column as singly inherited */
+ 		constr->inhcount = 1;
+ 		constr->is_local = false;
+ 
+ 		ATOneLevelRecursion(wqueue, rel, childCmd);
+ 	}
+ 	else
+ 	{
+ 		/*
+ 		 * 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 (find_inheritance_children(RelationGetRelid(rel)) != NIL)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ 					 errmsg("constraint must be added to child tables too")));
+ 	}
+ }
+ 
+ 
+ /*
+  * ALTER TABLE ADD CONSTRAINT
+  */
+ static void
  ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint)
  {
  	switch (nodeTag(newConstraint))
*************** ATExecAddConstraint(AlteredTableInfo *ta
*** 3872,3877 ****
--- 3919,3926 ----
  								newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
  								newcon->name = ccon->name;
  								newcon->contype = ccon->contype;
+ 
+ 
  								/* ExecQual wants implicit-AND format */
  								newcon->qual = (Node *)
  									make_ands_implicit((Expr *) ccon->expr);
*************** ATAddForeignKeyConstraint(AlteredTableIn
*** 4195,4201 ****
  									  indexOid,
  									  NULL,		/* no check constraint */
  									  NULL,
! 									  NULL);
  
  	/*
  	 * Create the triggers that will enforce the constraint.
--- 4244,4252 ----
  									  indexOid,
  									  NULL,		/* no check constraint */
  									  NULL,
! 									  NULL,
! 									  true, /* islocal */
! 									  0); /* inhcount */
  
  	/*
  	 * Create the triggers that will enforce the constraint.
*************** createForeignKeyTriggers(Relation rel, F
*** 4715,4758 ****
   * ALTER TABLE DROP CONSTRAINT
   */
  static void
! ATPrepDropConstraint(List **wqueue, Relation rel,
! 					 bool recurse, AlterTableCmd *cmd)
  {
  	/*
! 	 * We don't want errors or noise from child tables, so we have to pass
! 	 * down a modified command.
  	 */
! 	if (recurse)
  	{
! 		AlterTableCmd *childCmd = copyObject(cmd);
  
! 		childCmd->subtype = AT_DropConstraintQuietly;
! 		ATSimpleRecursion(wqueue, rel, childCmd, recurse);
! 	}
! }
  
! static void
! ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior, bool quiet)
! {
! 	int			deleted;
  
! 	deleted = RemoveRelConstraints(rel, constrName, behavior);
  
! 	if (!quiet)
! 	{
! 		/* If zero constraints deleted, complain */
! 		if (deleted == 0)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_UNDEFINED_OBJECT),
! 					 errmsg("constraint \"%s\" does not exist",
! 							constrName)));
! 		/* Otherwise if more than one constraint deleted, notify */
! 		else if (deleted > 1)
! 			ereport(NOTICE,
! 					(errmsg("multiple constraints named \"%s\" were dropped",
! 							constrName)));
  	}
  }
  
  /*
--- 4766,4883 ----
   * ALTER TABLE DROP CONSTRAINT
   */
  static void
! ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior,
! 					 bool recurse, bool recursing)
  {
+ 	HeapTuple	tuple;
+ 	List	   *children;
+ 	Relation	conrel;
+ 	TupleDesc	tupdesc;
+ 	Oid relid = RelationGetRelid(rel);
+ 	Form_pg_constraint con;
+ 
+ 	conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
+ 	tupdesc = RelationGetDescr(conrel);
+ 
+ 	/* At top level, permission check was done in ATPrepCmd, else do it */
+ 	if (recursing)
+ 		ATSimplePermissions(rel, false);
+ 
+ 	tuple = SearchSysCache(CONSTRNAME,
+ 						   ObjectIdGetDatum(relid),
+ 						   CStringGetDatum(constrName),
+ 						   0, 0);
+ 
+ 	if (!HeapTupleIsValid(tuple))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_COLUMN),
+ 				 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
+ 						constrName, RelationGetRelationName(rel))));
+ 
+ 	con = (Form_pg_constraint) GETSTRUCT(tuple);
+ 
+ 	/* Don't drop inherited constraints */
+ 	if (con->coninhcount > 0 && !recursing)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ 				 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
+ 						constrName,
+ 						RelationGetRelationName(rel))));
+ 
+ 	ReleaseSysCache(tuple);
+ 
  	/*
! 	 * Propagate to children as appropriate.  Unlike most other ALTER
! 	 * 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.
  	 */
! 	children = find_inheritance_children(RelationGetRelid(rel));
! 
! 	if (children)
  	{
! 		ListCell   *child;
  
! 		foreach(child, children)
! 		{
! 			Oid			childrelid = lfirst_oid(child);
! 			Relation	childrel;
! 			Form_pg_constraint childcon;
  
! 			childrel = heap_open(childrelid, AccessExclusiveLock);
! 			CheckTableNotInUse(childrel, "ALTER TABLE");
  
! 			tuple = SearchSysCacheCopy(CONSTRNAME,
! 								   ObjectIdGetDatum(childrelid),
! 								   CStringGetDatum(constrName),
! 								   0, 0);
! 			if (!HeapTupleIsValid(tuple))
! 				elog(ERROR, "cache lookup failed for constraint \"%s\" of relation %u",
! 					 constrName, childrelid);
! 
! 			childcon = (Form_pg_constraint) GETSTRUCT(tuple);
! 
! 			if (childcon->coninhcount <= 0)
! 				elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
! 					 childrelid, constrName);
  
! 			if (recurse)
! 			{
! 				if (childcon->coninhcount == 1 && !childcon->conislocal)
! 				{
! 					ATExecDropConstraint(childrel, constrName, behavior, true, true);
! 				}
! 				else
! 				{
! 					childcon->coninhcount--;
! 
! 					simple_heap_update(conrel, &tuple->t_self, tuple);
! 					CatalogUpdateIndexes(conrel, tuple);
! 				}
! 			}
! 			else
! 			{
! 				/*
! 				 * If we were told to drop ONLY in this table (no recursion),
! 				 * we need to mark the inheritors' attribute as locally
! 				 * defined rather than inherited.
! 				 */
! 
! 				childcon->coninhcount--;
! 				childcon->conislocal = true;
! 
! 				simple_heap_update(conrel, &tuple->t_self, tuple);
! 				CatalogUpdateIndexes(conrel, tuple);
! 			}
! 
! 			heap_freetuple(tuple);
! 			heap_close(childrel, AccessExclusiveLock);
! 		}
  	}
+ 
+ 	RemoveRelConstraints(rel, constrName, behavior);
+ 
+ 	heap_close(conrel, RowExclusiveLock);
  }
  
  /*
*************** ATExecAddInherit(Relation child_rel, Ran
*** 6138,6165 ****
  }
  
  /*
-  * Obtain the source-text form of the constraint expression for a check
-  * constraint, given its pg_constraint tuple
-  */
- static char *
- decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
- {
- 	Form_pg_constraint con;
- 	bool		isnull;
- 	Datum		attr;
- 	Datum		expr;
- 
- 	con = (Form_pg_constraint) GETSTRUCT(contup);
- 	attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
- 	if (isnull)
- 		elog(ERROR, "null conbin for constraint %u", HeapTupleGetOid(contup));
- 
- 	expr = DirectFunctionCall2(pg_get_expr, attr,
- 							   ObjectIdGetDatum(con->conrelid));
- 	return DatumGetCString(DirectFunctionCall1(textout, expr));
- }
- 
- /*
   * Check columns in child table match up with columns in parent, and increment
   * their attinhcount.
   *
--- 6263,6268 ----
*************** MergeAttributesIntoExisting(Relation chi
*** 6253,6261 ****
   * make it possible to ensure no records are mistakenly inserted into the
   * master in partitioned tables rather than the appropriate child.
   *
-  * XXX This is O(N^2) which may be an issue with tables with hundreds of
-  * constraints. As long as tables have more like 10 constraints it shouldn't be
-  * a problem though. Even 100 constraints ought not be the end of the world.
   */
  static void
  MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
--- 6356,6361 ----
*************** MergeConstraintsIntoExisting(Relation ch
*** 6265,6350 ****
  	SysScanDesc scan;
  	ScanKeyData key;
  	HeapTuple	constraintTuple;
- 	ListCell   *elem;
- 	List	   *constraints;
  
! 	/* First gather up the child's constraint definitions */
! 	catalogRelation = heap_open(ConstraintRelationId, AccessShareLock);
  	tupleDesc = RelationGetDescr(catalogRelation);
  
  	ScanKeyInit(&key,
  				Anum_pg_constraint_conrelid,
  				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(child_rel)));
  	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
  							  true, SnapshotNow, 1, &key);
  
- 	constraints = NIL;
- 	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
- 	{
- 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
- 
- 		if (con->contype != CONSTRAINT_CHECK)
- 			continue;
- 
- 		constraints = lappend(constraints, heap_copytuple(constraintTuple));
- 	}
- 
- 	systable_endscan(scan);
- 
- 	/* Then scan through the parent's constraints looking for matches */
- 	ScanKeyInit(&key,
- 				Anum_pg_constraint_conrelid,
- 				BTEqualStrategyNumber, F_OIDEQ,
- 				ObjectIdGetDatum(RelationGetRelid(parent_rel)));
- 	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId, true,
- 							  SnapshotNow, 1, &key);
- 
  	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
  	{
  		Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
- 		bool		found = false;
- 		Form_pg_constraint child_con = NULL;
- 		HeapTuple	child_contuple = NULL;
  
  		if (parent_con->contype != CONSTRAINT_CHECK)
  			continue;
  
! 		foreach(elem, constraints)
! 		{
! 			child_contuple = (HeapTuple) lfirst(elem);
! 			child_con = (Form_pg_constraint) GETSTRUCT(child_contuple);
! 			if (strcmp(NameStr(parent_con->conname),
! 					   NameStr(child_con->conname)) == 0)
! 			{
! 				found = true;
! 				break;
! 			}
! 		}
  
! 		if (!found)
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("child table is missing constraint \"%s\"",
  							NameStr(parent_con->conname))));
  
! 		if (parent_con->condeferrable != child_con->condeferrable ||
! 			parent_con->condeferred != child_con->condeferred ||
! 			strcmp(decompile_conbin(constraintTuple, tupleDesc),
! 				   decompile_conbin(child_contuple, tupleDesc)) != 0)
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("constraint definition for check constraint \"%s\" does not match",
  							NameStr(parent_con->conname))));
  
! 		/*
! 		 * TODO: add conislocal,coninhcount to constraints. This is where we
! 		 * would have to bump them just like attributes
! 		 */
  	}
  
  	systable_endscan(scan);
! 	heap_close(catalogRelation, AccessShareLock);
  }
  
  /*
--- 6365,6418 ----
  	SysScanDesc scan;
  	ScanKeyData key;
  	HeapTuple	constraintTuple;
  
! 	catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
  	tupleDesc = RelationGetDescr(catalogRelation);
  
  	ScanKeyInit(&key,
  				Anum_pg_constraint_conrelid,
  				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(parent_rel)));
  	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
  							  true, SnapshotNow, 1, &key);
  
  	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
  	{
+ 		HeapTuple childTuple;
+ 		Form_pg_constraint child_con;
  		Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
  
  		if (parent_con->contype != CONSTRAINT_CHECK)
  			continue;
  
! 		childTuple = SearchSysCacheCopy(CONSTRNAME,
! 									ObjectIdGetDatum(RelationGetRelid(child_rel)),
! 									CStringGetDatum(NameStr(parent_con->conname)),
! 									0, 0);
  
! 		if (!HeapTupleIsValid(childTuple))
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("child table is missing constraint \"%s\"",
  							NameStr(parent_con->conname))));
  
! 		child_con = (Form_pg_constraint) GETSTRUCT(childTuple);
! 
! 		if (!ConstraintIsEquiv(constraintTuple, childTuple, tupleDesc))
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("constraint definition for check constraint \"%s\" does not match",
  							NameStr(parent_con->conname))));
  
! 		/* increment coninhcount */
! 		child_con->coninhcount++;
! 		simple_heap_update(catalogRelation, &childTuple->t_self, childTuple);
! 		CatalogUpdateIndexes(catalogRelation, childTuple);
! 		heap_freetuple(childTuple);
  	}
  
  	systable_endscan(scan);
! 	heap_close(catalogRelation, RowExclusiveLock);
  }
  
  /*
*************** ATExecDropInherit(Relation rel, RangeVar
*** 6369,6376 ****
--- 6437,6446 ----
  	ScanKeyData key[3];
  	HeapTuple	inheritsTuple,
  				attributeTuple,
+ 				constraintTuple,
  				depTuple;
  	bool		found = false;
+ 	TupleDesc	tupdesc;
  
  	/*
  	 * AccessShareLock on the parent is probably enough, seeing that DROP
*************** ATExecDropInherit(Relation rel, RangeVar
*** 6408,6414 ****
  			break;
  		}
  	}
- 
  	systable_endscan(scan);
  	heap_close(catalogRelation, RowExclusiveLock);
  
--- 6478,6483 ----
*************** ATExecDropInherit(Relation rel, RangeVar
*** 6458,6463 ****
--- 6527,6567 ----
  	systable_endscan(scan);
  	heap_close(catalogRelation, RowExclusiveLock);
  
+ 	catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
+ 	ScanKeyInit(&key[0],
+ 				Anum_pg_constraint_conrelid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(RelationGetRelid(rel)));
+ 	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
+ 							  true, SnapshotNow, 1, key);
+ 
+ 	tupdesc = RelationGetDescr(catalogRelation);
+ 
+ 	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
+ 	{
+ 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
+ 
+ 		if (con->contype != CONSTRAINT_CHECK)
+ 			continue;
+ 
+ 		if (con->coninhcount > 0)
+ 		{
+ 			HeapTuple	copyTuple = heap_copytuple(constraintTuple);
+ 			Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
+ 
+ 			copy_con->coninhcount--;
+ 			if (copy_con->coninhcount == 0)
+ 				copy_con->conislocal = true;
+ 
+ 			simple_heap_update(catalogRelation, &copyTuple->t_self, copyTuple);
+ 			CatalogUpdateIndexes(catalogRelation, copyTuple);
+ 			heap_freetuple(copyTuple);
+ 		}
+ 	}
+ 
+ 	systable_endscan(scan);
+ 	heap_close(catalogRelation, RowExclusiveLock);
+ 
  	/*
  	 * Drop the dependency
  	 *
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index a1394d8..14c65be 100644
*** a/src/backend/commands/typecmds.c
--- /bsrc/backend/commands/typecmds.c
*************** domainAddConstraint(Oid domainOid, Oid d
*** 2206,2212 ****
  						  InvalidOid,
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc);		/* Source form check constraint */
  
  	/*
  	 * Return the compiled constraint expression so the calling routine can
--- 2206,2214 ----
  						  InvalidOid,
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc,	/* Source form check constraint */
! 						  true, /* is local */
! 						  0); /* inhcount */
  
  	/*
  	 * Return the compiled constraint expression so the calling routine can
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f7c1fd0..3026cf6 100644
*** a/src/backend/nodes/copyfuncs.c
--- /bsrc/backend/nodes/copyfuncs.c
*************** _copyConstraint(Constraint *from)
*** 1805,1810 ****
--- 1805,1812 ----
  	COPY_NODE_FIELD(keys);
  	COPY_NODE_FIELD(options);
  	COPY_STRING_FIELD(indexspace);
+ 	COPY_SCALAR_FIELD(inhcount);
+ 	COPY_SCALAR_FIELD(is_local);
  
  	return newnode;
  }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 3ff9691..9b64310 100644
*** a/src/backend/nodes/equalfuncs.c
--- /bsrc/backend/nodes/equalfuncs.c
*************** _equalConstraint(Constraint *a, Constrai
*** 1832,1837 ****
--- 1832,1839 ----
  	COMPARE_NODE_FIELD(keys);
  	COMPARE_NODE_FIELD(options);
  	COMPARE_STRING_FIELD(indexspace);
+ 	COMPARE_SCALAR_FIELD(inhcount);
+ 	COMPARE_SCALAR_FIELD(is_local);
  
  	return true;
  }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 2f5b1a9..c479b67 100644
*** a/src/backend/nodes/outfuncs.c
--- /bsrc/backend/nodes/outfuncs.c
*************** _outConstraint(StringInfo str, Constrain
*** 2026,2031 ****
--- 2026,2034 ----
  			appendStringInfo(str, "<unrecognized_constraint>");
  			break;
  	}
+ 
+ 	WRITE_INT_FIELD(inhcount);
+ 	WRITE_BOOL_FIELD(is_local);
  }
  
  static void
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 0205ef5..a502cb4 100644
*** a/src/backend/parser/gram.y
--- /bsrc/backend/parser/gram.y
*************** ColConstraintElem:
*** 2067,2072 ****
--- 2067,2074 ----
  					n->cooked_expr = NULL;
  					n->keys = NULL;
  					n->indexspace = NULL;
+ 					n->is_local = true;
+ 					n->inhcount = 0;
  					$$ = (Node *)n;
  				}
  			| DEFAULT b_expr
*************** ConstraintElem:
*** 2207,2212 ****
--- 2209,2216 ----
  					n->raw_expr = $3;
  					n->cooked_expr = NULL;
  					n->indexspace = NULL;
+ 					n->inhcount = 0;
+ 					n->is_local = true;
  					$$ = (Node *)n;
  				}
  			| UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index eec1e30..06bd3f8 100644
*** a/src/backend/parser/parse_utilcmd.c
--- /bsrc/backend/parser/parse_utilcmd.c
*************** transformColumnDefinition(ParseState *ps
*** 398,403 ****
--- 398,406 ----
  	{
  		constraint = lfirst(clist);
  
+ 		constraint->is_local = true;
+ 		constraint->inhcount = 0;
+ 
  		/*
  		 * If this column constraint is a FOREIGN KEY constraint, then we fill
  		 * in the current attribute's name and throw it into the list of FK
*************** transformInhRelation(ParseState *pstate,
*** 674,679 ****
--- 677,684 ----
  			n->cooked_expr = nodeToString(ccbin_node);
  			n->indexspace = NULL;
  			cxt->ckconstraints = lappend(cxt->ckconstraints, (Node *) n);
+ 			n->is_local = true;
+ 			n->inhcount = 0;
  		}
  	}
  
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 8cbf494..8436871 100644
*** a/src/backend/utils/cache/catcache.c
--- /bsrc/backend/utils/cache/catcache.c
*************** CatalogCacheInitializeCache(CatCache *ca
*** 961,967 ****
  		else
  		{
  			if (cache->cc_key[i] != ObjectIdAttributeNumber)
! 				elog(FATAL, "only sys attr supported in caches is OID");
  			keytype = OIDOID;
  		}
  
--- 961,967 ----
  		else
  		{
  			if (cache->cc_key[i] != ObjectIdAttributeNumber)
! 				elog(FATAL, "only sys attr supported in caches is OID for rel: %s", cache->cc_relname);
  			keytype = OIDOID;
  		}
  
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 7ff0e8a..789c6a4 100644
*** a/src/backend/utils/cache/syscache.c
--- /bsrc/backend/utils/cache/syscache.c
*************** static const struct cachedesc cacheinfo[
*** 305,310 ****
--- 305,322 ----
  		},
  		128
  	},
+ 	{ConstraintRelationId,		// CONSTRNAME
+ 		ConstraintRelidNameIndexId,
+ 		Anum_pg_constraint_conrelid,
+ 		2,
+ 		{
+ 			Anum_pg_constraint_conrelid,
+ 			Anum_pg_constraint_conname,
+ 			0,
+ 			0
+ 		},
+ 		256,
+ 	},
  	{ConstraintRelationId,		/* CONSTROID */
  		ConstraintOidIndexId,
  		0,
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index c5fb69a..aa683be 100644
*** a/src/include/access/tupdesc.h
--- /bsrc/include/access/tupdesc.h
*************** typedef struct attrDefault
*** 27,34 ****
  
  typedef struct constrCheck
  {
! 	char	   *ccname;
! 	char	   *ccbin;			/* nodeToString representation of expr */
  } ConstrCheck;
  
  /* This structure contains constraints of a tuple */
--- 27,36 ----
  
  typedef struct constrCheck
  {
! 	char	*ccname;
! 	char	*ccbin;			/* nodeToString representation of expr */
! 	bool	is_local;
! 	int		inhcount;
  } ConstrCheck;
  
  /* This structure contains constraints of a tuple */
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 7e5550a..846a48d 100644
*** a/src/include/catalog/catversion.h
--- /bsrc/include/catalog/catversion.h
***************
*** 53,58 ****
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	200711281
  
  #endif
--- 53,58 ----
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	20080305
  
  #endif
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index f28afc8..06b1c86 100644
*** a/src/include/catalog/heap.h
--- /bsrc/include/catalog/heap.h
*************** extern void InsertPgClassTuple(Relation 
*** 67,72 ****
--- 67,73 ----
  				   Oid new_rel_oid,
  				   Datum reloptions);
  
+ 
  extern List *AddRelationRawConstraints(Relation rel,
  						  List *rawColDefaults,
  						  List *rawConstraints);
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 7dc5215..d962930 100644
*** a/src/include/catalog/indexing.h
--- /bsrc/include/catalog/indexing.h
*************** DECLARE_INDEX(pg_constraint_contypid_ind
*** 121,126 ****
--- 121,128 ----
  #define ConstraintTypidIndexId	2666
  DECLARE_UNIQUE_INDEX(pg_constraint_oid_index, 2667, on pg_constraint using btree(oid oid_ops));
  #define ConstraintOidIndexId  2667
+ DECLARE_UNIQUE_INDEX(pg_constraint_relid_coname_index, 2700, on pg_constraint using btree(conrelid oid_ops, conname name_ops));
+ #define ConstraintRelidNameIndexId  2700
  
  DECLARE_UNIQUE_INDEX(pg_conversion_default_index, 2668, on pg_conversion using btree(connamespace oid_ops, conforencoding int4_ops, contoencoding int4_ops, oid oid_ops));
  #define ConversionDefaultIndexId  2668
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index ed71aa2..1ca0d6d 100644
*** a/src/include/catalog/pg_constraint.h
--- /bsrc/include/catalog/pg_constraint.h
***************
*** 19,24 ****
--- 19,26 ----
  #ifndef PG_CONSTRAINT_H
  #define PG_CONSTRAINT_H
  
+ #include "access/htup.h"
+ #include "access/tupdesc.h"
  #include "nodes/pg_list.h"
  
  /* ----------------
*************** CATALOG(pg_constraint,2606)
*** 77,82 ****
--- 79,90 ----
  	char		confdeltype;	/* foreign key's ON DELETE action */
  	char		confmatchtype;	/* foreign key's match type */
  
+ 	/* Has a local definition (hence, do not drop when coninhcount is 0) */
+ 	bool conislocal;
+ 
+ 	/* Number of times inherited from direct parent relation(s) */
+ 	int4 coninhcount;
+ 
  	/*
  	 * VARIABLE LENGTH FIELDS start here.  These fields may be NULL, too.
  	 */
*************** typedef FormData_pg_constraint *Form_pg_
*** 131,156 ****
   *		compiler constants for pg_constraint
   * ----------------
   */
! #define Natts_pg_constraint					18
! #define Anum_pg_constraint_conname			1
! #define Anum_pg_constraint_connamespace		2
! #define Anum_pg_constraint_contype			3
! #define Anum_pg_constraint_condeferrable	4
! #define Anum_pg_constraint_condeferred		5
! #define Anum_pg_constraint_conrelid			6
! #define Anum_pg_constraint_contypid			7
! #define Anum_pg_constraint_confrelid		8
! #define Anum_pg_constraint_confupdtype		9
! #define Anum_pg_constraint_confdeltype		10
! #define Anum_pg_constraint_confmatchtype	11
! #define Anum_pg_constraint_conkey			12
! #define Anum_pg_constraint_confkey			13
! #define Anum_pg_constraint_conpfeqop		14
! #define Anum_pg_constraint_conppeqop		15
! #define Anum_pg_constraint_conffeqop		16
! #define Anum_pg_constraint_conbin			17
! #define Anum_pg_constraint_consrc			18
! 
  
  /* Valid values for contype */
  #define CONSTRAINT_CHECK			'c'
--- 139,168 ----
   *		compiler constants for pg_constraint
   * ----------------
   */
! enum {
! 	Anum_pg_constraint_conname = 1,
! 	Anum_pg_constraint_connamespace,
! 	Anum_pg_constraint_contype,
! 	Anum_pg_constraint_condeferrable,
! 	Anum_pg_constraint_condeferred,
! 	Anum_pg_constraint_conrelid,
! 	Anum_pg_constraint_contypid,
! 	Anum_pg_constraint_confrelid,
! 	Anum_pg_constraint_confupdtype,
! 	Anum_pg_constraint_confdeltype,
! 	Anum_pg_constraint_confmatchtype,
! 	Anum_pg_constraint_conislocal,
! 	Anum_pg_constraint_coninhcount,
! 	Anum_pg_constraint_conkey,
! 	Anum_pg_constraint_confkey,
! 	Anum_pg_constraint_conpfeqop,
! 	Anum_pg_constraint_conppeqop,
! 	Anum_pg_constraint_conffeqop,
! 	Anum_pg_constraint_conbin,
! 	Anum_pg_constraint_consrc,
! 	Anum_pg_constraint_END
! };
! #define Natts_pg_constraint					Anum_pg_constraint_END-1
  
  /* Valid values for contype */
  #define CONSTRAINT_CHECK			'c'
*************** extern Oid CreateConstraintEntry(const c
*** 198,204 ****
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc);
  
  extern void RemoveConstraintById(Oid conId);
  extern void RenameConstraintById(Oid conId, const char *newname);
--- 210,218 ----
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc,
! 					  const bool conIsLocal,
! 					  const int conInhCout);
  
  extern void RemoveConstraintById(Oid conId);
  extern void RenameConstraintById(Oid conId, const char *newname);
*************** extern char *ChooseConstraintName(const 
*** 212,215 ****
--- 226,232 ----
  extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
  						  Oid newNspId, bool isType);
  
+ /* See if two constraints are "functionally" equivalent, but not necessarily equal */
+ bool ConstraintIsEquiv(const HeapTuple a, const HeapTuple b, const TupleDesc tupdesc);
+ 
  #endif   /* PG_CONSTRAINT_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 7b2716d..4f1240e 100644
*** a/src/include/nodes/parsenodes.h
--- /bsrc/include/nodes/parsenodes.h
*************** typedef enum AlterTableType
*** 894,900 ****
  	AT_ProcessedConstraint,		/* pre-processed add constraint (local in
  								 * parser/parse_utilcmd.c) */
  	AT_DropConstraint,			/* drop constraint */
! 	AT_DropConstraintQuietly,	/* drop constraint, no error/warning (local in
  								 * commands/tablecmds.c) */
  	AT_AlterColumnType,			/* alter column type */
  	AT_ChangeOwner,				/* change owner */
--- 894,900 ----
  	AT_ProcessedConstraint,		/* pre-processed add constraint (local in
  								 * parser/parse_utilcmd.c) */
  	AT_DropConstraint,			/* drop constraint */
! 	AT_DropConstraintRecurse,	/* drop constraint recursion (local in
  								 * commands/tablecmds.c) */
  	AT_AlterColumnType,			/* alter column type */
  	AT_ChangeOwner,				/* change owner */
*************** typedef struct Constraint
*** 1157,1162 ****
--- 1157,1164 ----
  	List	   *options;		/* options from WITH clause */
  	char	   *indexspace;		/* index tablespace for PKEY/UNIQUE
  								 * constraints; NULL for default */
+ 	bool is_local;
+ 	int inhcount;
  } Constraint;
  
  /* ----------
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 76248de..b99da72 100644
*** a/src/include/utils/syscache.h
--- /bsrc/include/utils/syscache.h
***************
*** 28,81 ****
   *		Keep them in alphabetical order.
   */
  
! #define AGGFNOID			0
! #define AMNAME				1
! #define AMOID				2
! #define AMOPOPID			3
! #define AMOPSTRATEGY		4
! #define AMPROCNUM			5
! #define ATTNAME				6
! #define ATTNUM				7
! #define AUTHMEMMEMROLE		8
! #define AUTHMEMROLEMEM		9
! #define AUTHNAME			10
! #define AUTHOID				11
! #define CASTSOURCETARGET	12
! #define CLAAMNAMENSP		13
! #define CLAOID				14
! #define CONDEFAULT			15
! #define CONNAMENSP			16
! #define CONSTROID			17
! #define CONVOID				18
! #define DATABASEOID			19
! #define ENUMOID				20
! #define ENUMTYPOIDNAME		21
! #define INDEXRELID			22
! #define LANGNAME			23
! #define LANGOID				24
! #define NAMESPACENAME		25
! #define NAMESPACEOID		26
! #define OPERNAMENSP			27
! #define OPEROID				28
! #define OPFAMILYAMNAMENSP	29
! #define OPFAMILYOID			30
! #define PROCNAMEARGSNSP		31
! #define PROCOID				32
! #define RELNAMENSP			33
! #define RELOID				34
! #define RULERELNAME			35
! #define STATRELATT			36
! #define TSCONFIGMAP			37
! #define TSCONFIGNAMENSP		38
! #define TSCONFIGOID			39
! #define TSDICTNAMENSP		40
! #define TSDICTOID			41
! #define TSPARSERNAMENSP		42
! #define TSPARSEROID			43
! #define TSTEMPLATENAMENSP	44
! #define TSTEMPLATEOID		45
! #define TYPENAMENSP			46
! #define TYPEOID				47
  
  extern void InitCatalogCache(void);
  extern void InitCatalogCachePhase2(void);
--- 28,84 ----
   *		Keep them in alphabetical order.
   */
  
! enum SysCacheIdent {
! 	AGGFNOID = 0,
! 	AMNAME,
! 	AMOID,
! 	AMOPOPID,
! 	AMOPSTRATEGY,
! 	AMPROCNUM,
! 	ATTNAME,
! 	ATTNUM,
! 	AUTHMEMMEMROLE,
! 	AUTHMEMROLEMEM,
! 	AUTHNAME,
! 	AUTHOID,
! 	CASTSOURCETARGET,
! 	CLAAMNAMENSP,
! 	CLAOID,
! 	CONDEFAULT,
! 	CONNAMENSP,
! 	CONSTRNAME,
! 	CONSTROID,
! 	CONVOID,
! 	DATABASEOID,
! 	ENUMOID,
! 	ENUMTYPOIDNAME,
! 	INDEXRELID,
! 	LANGNAME,
! 	LANGOID,
! 	NAMESPACENAME,
! 	NAMESPACEOID,
! 	OPERNAMENSP,
! 	OPEROID,
! 	OPFAMILYAMNAMENSP,
! 	OPFAMILYOID,
! 	PROCNAMEARGSNSP,
! 	PROCOID,
! 	RELNAMENSP,
! 	RELOID,
! 	RULERELNAME,
! 	STATRELATT,
! 	TSCONFIGMAP,
! 	TSCONFIGNAMENSP,
! 	TSCONFIGOID,
! 	TSDICTNAMENSP,
! 	TSDICTOID,
! 	TSPARSERNAMENSP,
! 	TSPARSEROID,
! 	TSTEMPLATENAMENSP,
! 	TSTEMPLATEOID,
! 	TYPENAMENSP,
! 	TYPEOID,
! };
  
  extern void InitCatalogCache(void);
  extern void InitCatalogCachePhase2(void);
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index cba489d..58ee537 100644
*** a/src/test/regress/expected/alter_table.out
--- /bsrc/test/regress/expected/alter_table.out
*************** create table atacc1 (test int);
*** 383,391 ****
  create table atacc2 (test2 int);
  create table atacc3 (test3 int) inherits (atacc1, atacc2);
  alter table only atacc2 add constraint foo check (test2>0);
  -- fail and then succeed on atacc2
  insert into atacc2 (test2) values (-3);
- ERROR:  new row for relation "atacc2" violates check constraint "foo"
  insert into atacc2 (test2) values (3);
  -- both succeed on atacc3
  insert into atacc3 (test2) values (-3);
--- 383,391 ----
  create table atacc2 (test2 int);
  create table atacc3 (test3 int) inherits (atacc1, atacc2);
  alter table only atacc2 add constraint foo check (test2>0);
+ ERROR:  constraint must be added to child tables too
  -- fail and then succeed on atacc2
  insert into atacc2 (test2) values (-3);
  insert into atacc2 (test2) values (3);
  -- both succeed on atacc3
  insert into atacc3 (test2) values (-3);
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index f81776f..52faae2 100644
*** a/src/test/regress/expected/inherit.out
--- /bsrc/test/regress/expected/inherit.out
*************** drop function p2text(p2);
*** 692,694 ****
--- 692,784 ----
  drop table c1;
  drop table p2;
  drop table p1;
+ CREATE TABLE ac (aa TEXT);
+ insert into ac (aa) values ('junk');
+ alter table ac add constraint ac_check check (aa is not null);
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in (select ac.tableoid from ac);
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+ (1 row)
+ 
+ CREATE TABLE bc (bb TEXT) INHERITS (ac);
+ insert into bc (aa) values ('junk');
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc limit 1));
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+  ac_check | c       | f          |           1 | (aa IS NOT NULL)
+ (2 rows)
+ 
+ insert into ac (aa) values (NULL);
+ ERROR:  new row for relation "ac" violates check constraint "ac_check"
+ insert into bc (aa) values (NULL);
+ ERROR:  new row for relation "bc" violates check constraint "ac_check"
+ alter table bc drop constraint ac_check;
+ ERROR:  cannot drop inherited constraint "ac_check" of relation "bc"
+ alter table ac drop constraint ac_check;
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc));
+  conname | contype | conislocal | coninhcount | consrc 
+ ---------+---------+------------+-------------+--------
+ (0 rows)
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc limit 1));
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+  ac_check | c       | f          |           1 | (aa IS NOT NULL)
+ (2 rows)
+ 
+ insert into ac (aa) values (NULL);
+ ERROR:  new row for relation "ac" violates check constraint "ac_check"
+ insert into bc (aa) values (NULL);
+ ERROR:  new row for relation "bc" violates check constraint "ac_check"
+ alter table bc drop constraint ac_check;
+ ERROR:  cannot drop inherited constraint "ac_check" of relation "bc"
+ alter table ac drop constraint ac_check;
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc));
+  conname | contype | conislocal | coninhcount | consrc 
+ ---------+---------+------------+-------------+--------
+ (0 rows)
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ alter table bc no inherit ac;
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc));
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+ (2 rows)
+ 
+ alter table bc drop constraint ac_check;
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc));
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+ (1 row)
+ 
+ alter table ac drop constraint ac_check;
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc));
+  conname | contype | conislocal | coninhcount | consrc 
+ ---------+---------+------------+-------------+--------
+ (0 rows)
+ 
+ drop table bc;
+ drop table ac;
+ create table ac (a int constraint check_a check (a <> 0));
+ create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac);
+ NOTICE:  merging column "a" with inherited definition
+ NOTICE:  merging constraint "check_a" with inherited definition
+ insert into ac (a) values (1);
+ insert into bc (a) values (1);
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc));
+  conname | contype | conislocal | coninhcount |  consrc  
+ ---------+---------+------------+-------------+----------
+  check_b | c       | t          |           0 | (b <> 0)
+  check_a | c       | t          |           0 | (a <> 0)
+  check_a | c       | f          |           1 | (a <> 0)
+ (3 rows)
+ 
+ drop table bc;
+ drop table ac;
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
index b0499a6..9831532 100644
*** a/src/test/regress/sql/inherit.sql
--- /bsrc/test/regress/sql/inherit.sql
*************** drop function p2text(p2);
*** 196,198 ****
--- 196,243 ----
  drop table c1;
  drop table p2;
  drop table p1;
+ 
+ CREATE TABLE ac (aa TEXT);
+ insert into ac (aa) values ('junk');
+ alter table ac add constraint ac_check check (aa is not null);
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in (select ac.tableoid from ac);
+ CREATE TABLE bc (bb TEXT) INHERITS (ac);
+ insert into bc (aa) values ('junk');
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc limit 1));
+ 
+ insert into ac (aa) values (NULL);
+ insert into bc (aa) values (NULL);
+ 
+ alter table bc drop constraint ac_check;
+ alter table ac drop constraint ac_check;
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc));
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc limit 1));
+ 
+ insert into ac (aa) values (NULL);
+ insert into bc (aa) values (NULL);
+ 
+ alter table bc drop constraint ac_check;
+ alter table ac drop constraint ac_check;
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc));
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ alter table bc no inherit ac;
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc));
+ alter table bc drop constraint ac_check;
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc));
+ alter table ac drop constraint ac_check;
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc));
+ 
+ drop table bc;
+ drop table ac;
+ 
+ create table ac (a int constraint check_a check (a <> 0));
+ create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac);
+ insert into ac (a) values (1);
+ insert into bc (a) values (1);
+ select conname, contype, conislocal, coninhcount, consrc from pg_constraint where conrelid in ((select ac.tableoid from ac limit 1), (select bc.tableoid from bc));
+ 
+ drop table bc;
+ drop table ac;
#2Alex Hunsaker
badalex@gmail.com
In reply to: Alvaro Herrera (#1)
1 attachment(s)
Re: [badalex@gmail.com: Re: [BUGS] Problem identifying constraints which should not be inherited]

(trimmed cc's)

Find attached inherited_constraint_v2.patch

Changes since v1:
-rebased against latest HEAD
-changed enum { Anum_pg_constraint_... } back into #define
Anum_pg_constraint_...
-remove whitespace damage I added
-fixed regression tests I added to be more robust
-fixed
create table ac (a int constraint check_a check (a <> 0));
create table bc (a int constraint check_a check (a <> 0)) inherits (ac);
so it properly works (removed crud I put into
AddRelationRawConstraints and created a proper fix in DefineRelation)

diffstat to head:
src/backend/catalog/heap.c | 15 +-
src/backend/catalog/index.c | 4 +-
src/backend/catalog/pg_constraint.c | 7 +-
src/backend/commands/tablecmds.c | 488 ++++++++++++++++++++++-------
src/backend/commands/typecmds.c | 4 +-
src/backend/nodes/copyfuncs.c | 2 +
src/backend/nodes/equalfuncs.c | 2 +
src/backend/nodes/outfuncs.c | 3 +
src/backend/parser/gram.y | 4 +
src/backend/parser/parse_utilcmd.c | 5 +
src/backend/utils/cache/catcache.c | 2 +-
src/backend/utils/cache/syscache.c | 12 +
src/include/access/tupdesc.h | 6 +-
src/include/catalog/catversion.h | 2 +-
src/include/catalog/indexing.h | 2 +
src/include/catalog/pg_constraint.h | 51 ++--
src/include/nodes/parsenodes.h | 4 +-
src/include/utils/syscache.h | 99 +++---
src/test/regress/expected/alter_table.out | 2 +-
src/test/regress/expected/inherit.out | 76 +++++
src/test/regress/sql/inherit.sql | 38 +++
21 files changed, 637 insertions(+), 191 deletions(-)

Attachments:

inherited_constraints_v2.patchapplication/octet-stream; name=inherited_constraints_v2.patchDownload
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 3553c3b..47d466c 100644
*** a/src/backend/catalog/heap.c
--- /bsrc/backend/catalog/heap.c
*************** static Oid AddNewRelationType(const char
*** 77,83 ****
  				   char new_rel_kind,
  				   Oid new_array_type);
  static void RelationRemoveInheritance(Oid relid);
! static void StoreRelCheck(Relation rel, char *ccname, char *ccbin);
  static void StoreConstraints(Relation rel, TupleDesc tupdesc);
  static void SetRelationNumChecks(Relation rel, int numchecks);
  static List *insert_ordered_unique_oid(List *list, Oid datum);
--- 77,83 ----
  				   char new_rel_kind,
  				   Oid new_array_type);
  static void RelationRemoveInheritance(Oid relid);
! static void StoreRelCheck(Relation rel, char *ccname, char *ccbin, const bool is_local, const int inhcount);
  static void StoreConstraints(Relation rel, TupleDesc tupdesc);
  static void SetRelationNumChecks(Relation rel, int numchecks);
  static List *insert_ordered_unique_oid(List *list, Oid datum);
*************** StoreAttrDefault(Relation rel, AttrNumbe
*** 1531,1537 ****
   * in the pg_class entry for the relation.
   */
  static void
! StoreRelCheck(Relation rel, char *ccname, char *ccbin)
  {
  	Node	   *expr;
  	char	   *ccsrc;
--- 1531,1537 ----
   * in the pg_class entry for the relation.
   */
  static void
! StoreRelCheck(Relation rel, char *ccname, char *ccbin, const bool is_local, const int inhcount)
  {
  	Node	   *expr;
  	char	   *ccsrc;
*************** StoreRelCheck(Relation rel, char *ccname
*** 1608,1614 ****
  						  InvalidOid,	/* no associated index */
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc);		/* Source form check constraint */
  
  	pfree(ccsrc);
  }
--- 1608,1616 ----
  						  InvalidOid,	/* no associated index */
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc, /* Source form check constraint */
! 						  is_local, /* islocal */
! 						  inhcount); /* coninhcount */
  
  	pfree(ccsrc);
  }
*************** StoreConstraints(Relation rel, TupleDesc
*** 1643,1649 ****
  
  	for (i = 0; i < constr->num_check; i++)
  		StoreRelCheck(rel, constr->check[i].ccname,
! 					  constr->check[i].ccbin);
  
  	if (constr->num_check > 0)
  		SetRelationNumChecks(rel, constr->num_check);
--- 1645,1653 ----
  
  	for (i = 0; i < constr->num_check; i++)
  		StoreRelCheck(rel, constr->check[i].ccname,
! 					  constr->check[i].ccbin,
! 					  constr->check[i].is_local,
! 					  constr->check[i].inhcount);
  
  	if (constr->num_check > 0)
  		SetRelationNumChecks(rel, constr->num_check);
*************** AddRelationRawConstraints(Relation rel,
*** 1811,1816 ****
--- 1815,1821 ----
  						(errcode(ERRCODE_DUPLICATE_OBJECT),
  				errmsg("constraint \"%s\" for relation \"%s\" already exists",
  					   ccname, RelationGetRelationName(rel))));
+ 
  			/* Check against other new constraints */
  			/* Needed because we don't do CommandCounterIncrement in loop */
  			foreach(cell2, checknames)
*************** AddRelationRawConstraints(Relation rel,
*** 1863,1869 ****
  		/*
  		 * OK, store it.
  		 */
! 		StoreRelCheck(rel, ccname, nodeToString(expr));
  
  		numchecks++;
  
--- 1868,1874 ----
  		/*
  		 * OK, store it.
  		 */
! 		StoreRelCheck(rel, ccname, nodeToString(expr), cdef->is_local, cdef->inhcount);
  
  		numchecks++;
  
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index f07d9ae..6db3084 100644
*** a/src/backend/catalog/index.c
--- /bsrc/backend/catalog/index.c
*************** index_create(Oid heapRelationId,
*** 716,722 ****
  										   InvalidOid,	/* no associated index */
  										   NULL,		/* no check constraint */
  										   NULL,
! 										   NULL);
  
  			referenced.classId = ConstraintRelationId;
  			referenced.objectId = conOid;
--- 716,724 ----
  										   InvalidOid,	/* no associated index */
  										   NULL,		/* no check constraint */
  										   NULL,
! 										   NULL,
! 										   true, /* islocal */
! 										   0); /* inhcount */
  
  			referenced.classId = ConstraintRelationId;
  			referenced.objectId = conOid;
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index d9b20fd..eafc143 100644
*** a/src/backend/catalog/pg_constraint.c
--- /bsrc/backend/catalog/pg_constraint.c
*************** CreateConstraintEntry(const char *constr
*** 60,66 ****
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc)
  {
  	Relation	conDesc;
  	Oid			conOid;
--- 60,68 ----
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc,
! 					  const bool conIsLocal,
! 					  const int conInhCount)
  {
  	Relation	conDesc;
  	Oid			conOid;
*************** CreateConstraintEntry(const char *constr
*** 171,176 ****
--- 173,181 ----
  	else
  		nulls[Anum_pg_constraint_conffeqop - 1] = 'n';
  
+ 	values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
+ 	values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
+ 
  	/*
  	 * initialize the binary form of the check constraint.
  	 */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 55c50f9..790b35a 100644
*** a/src/backend/commands/tablecmds.c
--- /bsrc/backend/commands/tablecmds.c
*************** static void truncate_check_rel(Relation 
*** 170,175 ****
--- 170,177 ----
  static List *MergeAttributes(List *schema, List *supers, bool istemp,
  				List **supOids, List **supconstr, int *supOidCount);
  static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
+ static void MergeInheritedConstraints(Relation rel, List *old_constraints,
+ 									List *new_constriants);
  static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
  static void add_nonduplicate_constraint(Constraint *cdef,
  							ConstrCheck *check, int *ncheck);
*************** static void ATExecDropColumn(Relation re
*** 232,245 ****
  				 bool recurse, bool recursing);
  static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
  			   IndexStmt *stmt, bool is_rebuild);
  static void ATExecAddConstraint(AlteredTableInfo *tab, Relation rel,
  					Node *newConstraint);
  static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
  						  FkConstraint *fkconstraint);
- static void ATPrepDropConstraint(List **wqueue, Relation rel,
- 					 bool recurse, AlterTableCmd *cmd);
  static void ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior, bool quiet);
  static void ATPrepAlterColumnType(List **wqueue,
  					  AlteredTableInfo *tab, Relation rel,
  					  bool recurse, bool recursing,
--- 234,248 ----
  				 bool recurse, bool recursing);
  static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
  			   IndexStmt *stmt, bool is_rebuild);
+ static void ATPrepAddConstraint(List **wqueue, Relation rel, bool recurse,
+ 				AlterTableCmd *cmd);
  static void ATExecAddConstraint(AlteredTableInfo *tab, Relation rel,
  					Node *newConstraint);
  static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
  						  FkConstraint *fkconstraint);
  static void ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior, 
! 					 bool recurse, bool recursing);
  static void ATPrepAlterColumnType(List **wqueue,
  					  AlteredTableInfo *tab, Relation rel,
  					  bool recurse, bool recursing,
*************** static void ATExecEnableDisableRule(Rela
*** 263,269 ****
  static void ATExecAddInherit(Relation rel, RangeVar *parent);
  static void ATExecDropInherit(Relation rel, RangeVar *parent);
  static void copy_relation_data(Relation rel, SMgrRelation dst);
! 
  
  /* ----------------------------------------------------------------
   *		DefineRelation
--- 266,276 ----
  static void ATExecAddInherit(Relation rel, RangeVar *parent);
  static void ATExecDropInherit(Relation rel, RangeVar *parent);
  static void copy_relation_data(Relation rel, SMgrRelation dst);
! static bool ConstraintIsEquiv(const HeapTuple a, const HeapTuple b,
! 							  const TupleDesc tupleDesc);
! static char *decompile_conbin(HeapTuple contup, TupleDesc tupdesc);
! static void change_opfuncid(Node *node, Oid opfuncid);
! static bool change_opfuncid_walker(Node *node, void *opfuncid);
  
  /* ----------------------------------------------------------------
   *		DefineRelation
*************** DefineRelation(CreateStmt *stmt, char re
*** 488,493 ****
--- 495,505 ----
  		}
  	}
  
+ 	if (old_constraints && stmt->constraints)
+ 	{
+ 		MergeInheritedConstraints(rel, old_constraints, stmt->constraints);
+ 	}
+ 
  	/*
  	 * Parse and add the defaults/constraints, if any.
  	 */
*************** MergeAttributes(List *schema, List *supe
*** 1053,1058 ****
--- 1065,1072 ----
  				Constraint *cdef = makeNode(Constraint);
  				Node	   *expr;
  
+ 				cdef->is_local = false;
+ 				cdef->inhcount = check[i].inhcount + 1;
  				cdef->contype = CONSTR_CHECK;
  				cdef->name = pstrdup(check[i].ccname);
  				cdef->raw_expr = NULL;
*************** MergeAttributes(List *schema, List *supe
*** 1175,1180 ****
--- 1189,1280 ----
  	return schema;
  }
  
+ /*
+  * Go through constraints in new constraints
+  * if we have a matching constraint in old_constraints and they have a
+  * matching definition, then remove the constraint from new_constraints
+  */
+ static void
+ MergeInheritedConstraints(Relation rel,
+ 						  List *old_constraints,
+ 						  List *new_constraints)
+ {
+ 	ListCell *cur_item;
+ 	ListCell *prev_item;
+ 	ListCell *listptr;
+ 
+ 	prev_item = NULL;
+ 	cur_item = list_head(new_constraints);
+ 
+ 	while (cur_item != NULL)
+ 	{
+ 		bool get_next = true;
+ 		Constraint *cdef = (Constraint *) lfirst(cur_item);
+ 
+ 		if (cdef->contype == CONSTR_CHECK &&
+ 			cdef->cooked_expr == NULL &&
+ 			cdef->name &&
+ 			cdef->raw_expr)
+ 		{
+ 			foreach(listptr, old_constraints)
+ 			{
+ 				Constraint *cdef1 = (Constraint *) lfirst(listptr);
+ 
+ 				if (cdef1->contype == CONSTR_CHECK &&
+ 					cdef1->name &&
+ 					strcmp(cdef->name, cdef1->name) == 0)
+ 				{
+ 					ParseState		*pstate;
+ 					RangeTblEntry	*rte;
+ 					Node			*expr;
+ 					char 			*cooked;
+ 
+ 					pstate = make_parsestate(NULL);
+ 					rte = addRangeTableEntryForRelation(pstate,
+ 														rel,
+ 														NULL,
+ 														false,
+ 														true);
+ 					addRTEtoQuery(pstate, rte, true, true, true);
+ 
+ 					expr = transformExpr(pstate, cdef->raw_expr);
+ 					expr = coerce_to_boolean(pstate, expr, "CHECK");
+ 					change_opfuncid(expr, 0);
+ 
+ 					cooked = nodeToString(expr);
+ 
+ 					if (strcmp(cooked, cdef1->cooked_expr) == 0)
+ 					{
+ 						ereport(NOTICE,
+ 							(errmsg("merging constraint \"%s\" with inherited definition",
+ 							cdef->name)));
+ 
+ 						list_delete_cell(new_constraints, cur_item, prev_item);
+ 						pfree(cdef);
+ 						get_next = false;
+ 						if(prev_item)
+ 							cur_item = lnext(prev_item);
+ 						else
+ 							cur_item = list_head(new_constraints);
+ 					}else
+ 					{
+ 						ereport(ERROR,
+ 								(errmsg("constraint \"%s\" does not match inherited definition",
+ 								cdef->name)));
+ 					}
+ 
+ 					break;
+ 				}
+ 			}
+ 		}
+ 
+ 		if(get_next)
+ 		{
+ 			prev_item = cur_item;
+ 			cur_item = lnext(prev_item);
+ 		}
+ 	}
+ }
  
  /*
   * In multiple-inheritance situations, it's possible to inherit
*************** add_nonduplicate_constraint(Constraint *
*** 1208,1213 ****
--- 1308,1315 ----
  	/* No match on name, so add it to array */
  	check[*ncheck].ccname = cdef->name;
  	check[*ncheck].ccbin = pstrdup(cdef->cooked_expr);
+ 	check[*ncheck].inhcount = cdef->inhcount;
+ 	check[*ncheck].is_local = cdef->is_local;
  	(*ncheck)++;
  }
  
*************** varattnos_map_schema(TupleDesc old, List
*** 1306,1311 ****
--- 1408,1445 ----
  	return attmap;
  }
  
+ /*
+  * change an opfuncid of a Node
+  *
+  * Note that the passed node tree is modified in-place!
+  */
+ static void
+ change_opfuncid(Node *node, Oid opfuncid)
+ {
+ 	Oid *o = palloc(sizeof(Oid));
+ 	*o = opfuncid;
+ 	(void) change_opfuncid_walker(node, (void*)o);
+ 	pfree(o);
+ }
+ 
+ static bool
+ change_opfuncid_walker(Node *node, void *opfuncid)
+ {
+ 	if (node == NULL)
+ 		return false;
+ 
+ 	if (IsA(node, OpExpr))
+ 	{
+ 		Oid		*o = (Oid*)opfuncid;
+ 		OpExpr	*op = (OpExpr *) node;
+ 
+ 		op->opfuncid = *o;
+ 		return false;
+ 	}
+ 	return expression_tree_walker(node, change_opfuncid_walker,
+ 								  opfuncid);
+ 
+ }
  
  /*
   * StoreCatalogInheritance
*************** ATPrepCmd(List **wqueue, Relation rel, A
*** 2038,2064 ****
  			break;
  		case AT_AddConstraint:	/* ADD CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 
! 			/*
! 			 * Currently we recurse only for CHECK constraints, never for
! 			 * foreign-key constraints.  UNIQUE/PKEY constraints won't be seen
! 			 * here.
! 			 */
! 			if (IsA(cmd->def, Constraint))
! 				ATSimpleRecursion(wqueue, rel, cmd, recurse);
! 			/* No command-specific prep needed */
  			pass = AT_PASS_ADD_CONSTR;
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 			/* Performs own recursion */
! 			ATPrepDropConstraint(wqueue, rel, recurse, cmd);
! 			pass = AT_PASS_DROP;
! 			break;
! 		case AT_DropConstraintQuietly:	/* DROP CONSTRAINT for child */
! 			ATSimplePermissions(rel, false);
! 			ATSimpleRecursion(wqueue, rel, cmd, recurse);
! 			/* No command-specific prep needed */
  			pass = AT_PASS_DROP;
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
--- 2172,2187 ----
  			break;
  		case AT_AddConstraint:	/* ADD CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 			ATPrepAddConstraint(wqueue, rel, recurse, cmd);
  			pass = AT_PASS_ADD_CONSTR;
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 			/* we follow AT_DropColumns example for better or for worse so
! 			 * Recursion occurs during execution phase
! 			 * No command-specific prep needed except saving recurse flag */
! 			if (recurse)
! 				cmd->subtype = AT_DropConstraintRecurse;
  			pass = AT_PASS_DROP;
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
*************** ATExecCmd(AlteredTableInfo *tab, Relatio
*** 2247,2256 ****
  			ATExecAddConstraint(tab, rel, cmd->def);
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, false);
  			break;
! 		case AT_DropConstraintQuietly:	/* DROP CONSTRAINT for child */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, true);
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
  			ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def);
--- 2370,2379 ----
  			ATExecAddConstraint(tab, rel, cmd->def);
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, false, false);
  			break;
! 		case AT_DropConstraintRecurse:	/* DROP CONSTRAINT for child */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, true, false);
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
  			ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def);
*************** ATExecAddIndex(AlteredTableInfo *tab, Re
*** 3925,3930 ****
--- 4048,4102 ----
   * ALTER TABLE ADD CONSTRAINT
   */
  static void
+ ATPrepAddConstraint(List **wqueue, Relation rel, bool recurse,
+ 				AlterTableCmd *cmd)
+ {
+ 	/*
+ 	 * Currently we recurse only for CHECK constraints, never for
+ 	 * foreign-key constraints.  UNIQUE/PKEY constraints won't be seen
+ 	 * here.
+ 	 */
+ 	if (!IsA(cmd->def, Constraint))
+ 	{
+ 		return;
+ 	}
+ 
+ 	/*
+ 	 * Recurse to add the column to child classes, if requested.
+ 	 *
+ 	 * We must recurse one level at a time, so that multiply-inheriting
+ 	 * children are visited the right number of times and end up with the
+ 	 * right coninhcount.
+ 	 */
+ 	if (recurse)
+ 	{
+ 		AlterTableCmd	*childCmd = copyObject(cmd);
+ 		Constraint		*constr = (Constraint *) childCmd->def;
+ 
+ 		/* child should see column as singly inherited */
+ 		constr->inhcount = 1;
+ 		constr->is_local = false;
+ 
+ 		ATOneLevelRecursion(wqueue, rel, childCmd);
+ 	}
+ 	else
+ 	{
+ 		/*
+ 		 * 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 (find_inheritance_children(RelationGetRelid(rel)) != NIL)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ 					 errmsg("constraint must be added to child tables too")));
+ 	}
+ }
+ 
+ 
+ /*
+  * ALTER TABLE ADD CONSTRAINT
+  */
+ static void
  ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint)
  {
  	switch (nodeTag(newConstraint))
*************** ATAddForeignKeyConstraint(AlteredTableIn
*** 4284,4290 ****
  									  indexOid,
  									  NULL,		/* no check constraint */
  									  NULL,
! 									  NULL);
  
  	/*
  	 * Create the triggers that will enforce the constraint.
--- 4456,4464 ----
  									  indexOid,
  									  NULL,		/* no check constraint */
  									  NULL,
! 									  NULL,
! 									  true, /* islocal */
! 									  0); /* inhcount */
  
  	/*
  	 * Create the triggers that will enforce the constraint.
*************** createForeignKeyTriggers(Relation rel, F
*** 4804,4847 ****
   * ALTER TABLE DROP CONSTRAINT
   */
  static void
! ATPrepDropConstraint(List **wqueue, Relation rel,
! 					 bool recurse, AlterTableCmd *cmd)
  {
  	/*
! 	 * We don't want errors or noise from child tables, so we have to pass
! 	 * down a modified command.
  	 */
! 	if (recurse)
  	{
! 		AlterTableCmd *childCmd = copyObject(cmd);
  
! 		childCmd->subtype = AT_DropConstraintQuietly;
! 		ATSimpleRecursion(wqueue, rel, childCmd, recurse);
! 	}
! }
  
! static void
! ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior, bool quiet)
! {
! 	int			deleted;
  
! 	deleted = RemoveRelConstraints(rel, constrName, behavior);
  
! 	if (!quiet)
! 	{
! 		/* If zero constraints deleted, complain */
! 		if (deleted == 0)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_UNDEFINED_OBJECT),
! 					 errmsg("constraint \"%s\" does not exist",
! 							constrName)));
! 		/* Otherwise if more than one constraint deleted, notify */
! 		else if (deleted > 1)
! 			ereport(NOTICE,
! 					(errmsg("multiple constraints named \"%s\" were dropped",
! 							constrName)));
  	}
  }
  
  /*
--- 4978,5096 ----
   * ALTER TABLE DROP CONSTRAINT
   */
  static void
! ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior,
! 					 bool recurse, bool recursing)
  {
+ 	HeapTuple	tuple;
+ 	List		*children;
+ 	Relation	conrel;
+ 	TupleDesc	tupdesc;
+ 	Oid		relid;
+ 	Form_pg_constraint con;
+ 
+ 	relid = RelationGetRelid(rel);
+ 	conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
+ 	tupdesc = RelationGetDescr(conrel);
+ 
+ 	/* At top level, permission check was done in ATPrepCmd, else do it */
+ 	if (recursing)
+ 		ATSimplePermissions(rel, false);
+ 
+ 	tuple = SearchSysCache(CONSTRNAME,
+ 						   ObjectIdGetDatum(relid),
+ 						   CStringGetDatum(constrName),
+ 						   0, 0);
+ 
+ 	if (!HeapTupleIsValid(tuple))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_COLUMN),
+ 				 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
+ 						constrName, RelationGetRelationName(rel))));
+ 
+ 	con = (Form_pg_constraint) GETSTRUCT(tuple);
+ 
+ 	/* Don't drop inherited constraints */
+ 	if (con->coninhcount > 0 && !recursing)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ 				 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
+ 						constrName,
+ 						RelationGetRelationName(rel))));
+ 
+ 	ReleaseSysCache(tuple);
+ 
  	/*
! 	 * Propagate to children as appropriate.  Unlike most other ALTER
! 	 * 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.
  	 */
! 	children = find_inheritance_children(RelationGetRelid(rel));
! 
! 	if (children)
  	{
! 		ListCell   *child;
  
! 		foreach(child, children)
! 		{
! 			Oid			childrelid = lfirst_oid(child);
! 			Relation	childrel;
! 			Form_pg_constraint childcon;
  
! 			childrel = heap_open(childrelid, AccessExclusiveLock);
! 			CheckTableNotInUse(childrel, "ALTER TABLE");
  
! 			tuple = SearchSysCacheCopy(CONSTRNAME,
! 								   ObjectIdGetDatum(childrelid),
! 								   CStringGetDatum(constrName),
! 								   0, 0);
! 			if (!HeapTupleIsValid(tuple))
! 				elog(ERROR, "cache lookup failed for constraint \"%s\" of relation %u",
! 					 constrName, childrelid);
! 
! 			childcon = (Form_pg_constraint) GETSTRUCT(tuple);
! 
! 			if (childcon->coninhcount <= 0)
! 				elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
! 					 childrelid, constrName);
  
! 			if (recurse)
! 			{
! 				if (childcon->coninhcount == 1 && !childcon->conislocal)
! 				{
! 					ATExecDropConstraint(childrel, constrName, behavior, true, true);
! 				}
! 				else
! 				{
! 					childcon->coninhcount--;
! 
! 					simple_heap_update(conrel, &tuple->t_self, tuple);
! 					CatalogUpdateIndexes(conrel, tuple);
! 				}
! 			}
! 			else
! 			{
! 				/*
! 				 * If we were told to drop ONLY in this table (no recursion),
! 				 * we need to mark the inheritors' attribute as locally
! 				 * defined rather than inherited.
! 				 */
! 
! 				childcon->coninhcount--;
! 				childcon->conislocal = true;
! 
! 				simple_heap_update(conrel, &tuple->t_self, tuple);
! 				CatalogUpdateIndexes(conrel, tuple);
! 			}
! 
! 			heap_freetuple(tuple);
! 			heap_close(childrel, AccessExclusiveLock);
! 		}
  	}
+ 
+ 	RemoveRelConstraints(rel, constrName, behavior);
+ 
+ 	heap_close(conrel, RowExclusiveLock);
  }
  
  /*
*************** decompile_conbin(HeapTuple contup, Tuple
*** 6249,6254 ****
--- 6498,6523 ----
  }
  
  /*
+  * See if two constraints are "functionally" equivalent, but not necessarily equal
+  */
+ static bool
+ ConstraintIsEquiv(const HeapTuple a, const HeapTuple b, const TupleDesc tupleDesc)
+ {
+ 	const Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
+ 	const Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
+ 
+ 	if (acon->condeferrable != bcon->condeferrable ||
+ 		acon->condeferred != bcon->condeferred ||
+ 			strcmp(decompile_conbin(a, tupleDesc),
+ 				   decompile_conbin(b, tupleDesc)) != 0)
+ 	{
+ 		return 0;
+ 	}else
+ 	{
+ 		return 1;
+ 	}
+ }
+ /*
   * Check columns in child table match up with columns in parent, and increment
   * their attinhcount.
   *
*************** MergeAttributesIntoExisting(Relation chi
*** 6342,6350 ****
   * make it possible to ensure no records are mistakenly inserted into the
   * master in partitioned tables rather than the appropriate child.
   *
-  * XXX This is O(N^2) which may be an issue with tables with hundreds of
-  * constraints. As long as tables have more like 10 constraints it shouldn't be
-  * a problem though. Even 100 constraints ought not be the end of the world.
   */
  static void
  MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
--- 6611,6616 ----
*************** MergeConstraintsIntoExisting(Relation ch
*** 6354,6439 ****
  	SysScanDesc scan;
  	ScanKeyData key;
  	HeapTuple	constraintTuple;
- 	ListCell   *elem;
- 	List	   *constraints;
  
! 	/* First gather up the child's constraint definitions */
! 	catalogRelation = heap_open(ConstraintRelationId, AccessShareLock);
  	tupleDesc = RelationGetDescr(catalogRelation);
  
  	ScanKeyInit(&key,
  				Anum_pg_constraint_conrelid,
  				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(child_rel)));
  	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
  							  true, SnapshotNow, 1, &key);
  
- 	constraints = NIL;
- 	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
- 	{
- 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
- 
- 		if (con->contype != CONSTRAINT_CHECK)
- 			continue;
- 
- 		constraints = lappend(constraints, heap_copytuple(constraintTuple));
- 	}
- 
- 	systable_endscan(scan);
- 
- 	/* Then scan through the parent's constraints looking for matches */
- 	ScanKeyInit(&key,
- 				Anum_pg_constraint_conrelid,
- 				BTEqualStrategyNumber, F_OIDEQ,
- 				ObjectIdGetDatum(RelationGetRelid(parent_rel)));
- 	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId, true,
- 							  SnapshotNow, 1, &key);
- 
  	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
  	{
  		Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
- 		bool		found = false;
- 		Form_pg_constraint child_con = NULL;
- 		HeapTuple	child_contuple = NULL;
  
  		if (parent_con->contype != CONSTRAINT_CHECK)
  			continue;
  
! 		foreach(elem, constraints)
! 		{
! 			child_contuple = (HeapTuple) lfirst(elem);
! 			child_con = (Form_pg_constraint) GETSTRUCT(child_contuple);
! 			if (strcmp(NameStr(parent_con->conname),
! 					   NameStr(child_con->conname)) == 0)
! 			{
! 				found = true;
! 				break;
! 			}
! 		}
  
! 		if (!found)
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("child table is missing constraint \"%s\"",
  							NameStr(parent_con->conname))));
  
! 		if (parent_con->condeferrable != child_con->condeferrable ||
! 			parent_con->condeferred != child_con->condeferred ||
! 			strcmp(decompile_conbin(constraintTuple, tupleDesc),
! 				   decompile_conbin(child_contuple, tupleDesc)) != 0)
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("constraint definition for check constraint \"%s\" does not match",
  							NameStr(parent_con->conname))));
  
! 		/*
! 		 * TODO: add conislocal,coninhcount to constraints. This is where we
! 		 * would have to bump them just like attributes
! 		 */
  	}
  
  	systable_endscan(scan);
! 	heap_close(catalogRelation, AccessShareLock);
  }
  
  /*
--- 6620,6673 ----
  	SysScanDesc scan;
  	ScanKeyData key;
  	HeapTuple	constraintTuple;
  
! 	catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
  	tupleDesc = RelationGetDescr(catalogRelation);
  
  	ScanKeyInit(&key,
  				Anum_pg_constraint_conrelid,
  				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(parent_rel)));
  	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
  							  true, SnapshotNow, 1, &key);
  
  	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
  	{
+ 		HeapTuple childTuple;
+ 		Form_pg_constraint child_con;
  		Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
  
  		if (parent_con->contype != CONSTRAINT_CHECK)
  			continue;
  
! 		childTuple = SearchSysCacheCopy(CONSTRNAME,
! 									ObjectIdGetDatum(RelationGetRelid(child_rel)),
! 									CStringGetDatum(NameStr(parent_con->conname)),
! 									0, 0);
  
! 		if (!HeapTupleIsValid(childTuple))
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("child table is missing constraint \"%s\"",
  							NameStr(parent_con->conname))));
  
! 		child_con = (Form_pg_constraint) GETSTRUCT(childTuple);
! 
! 		if (!ConstraintIsEquiv(constraintTuple, childTuple, tupleDesc))
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("constraint definition for check constraint \"%s\" does not match",
  							NameStr(parent_con->conname))));
  
! 		/* increment coninhcount */
! 		child_con->coninhcount++;
! 		simple_heap_update(catalogRelation, &childTuple->t_self, childTuple);
! 		CatalogUpdateIndexes(catalogRelation, childTuple);
! 		heap_freetuple(childTuple);
  	}
  
  	systable_endscan(scan);
! 	heap_close(catalogRelation, RowExclusiveLock);
  }
  
  /*
*************** ATExecDropInherit(Relation rel, RangeVar
*** 6458,6465 ****
--- 6692,6701 ----
  	ScanKeyData key[3];
  	HeapTuple	inheritsTuple,
  				attributeTuple,
+ 				constraintTuple,
  				depTuple;
  	bool		found = false;
+ 	TupleDesc	tupdesc;
  
  	/*
  	 * AccessShareLock on the parent is probably enough, seeing that DROP
*************** ATExecDropInherit(Relation rel, RangeVar
*** 6497,6503 ****
  			break;
  		}
  	}
- 
  	systable_endscan(scan);
  	heap_close(catalogRelation, RowExclusiveLock);
  
--- 6733,6738 ----
*************** ATExecDropInherit(Relation rel, RangeVar
*** 6547,6552 ****
--- 6782,6822 ----
  	systable_endscan(scan);
  	heap_close(catalogRelation, RowExclusiveLock);
  
+ 	catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
+ 	ScanKeyInit(&key[0],
+ 				Anum_pg_constraint_conrelid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(RelationGetRelid(rel)));
+ 	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
+ 							  true, SnapshotNow, 1, key);
+ 
+ 	tupdesc = RelationGetDescr(catalogRelation);
+ 
+ 	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
+ 	{
+ 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
+ 
+ 		if (con->contype != CONSTRAINT_CHECK)
+ 			continue;
+ 
+ 		if (con->coninhcount > 0)
+ 		{
+ 			HeapTuple	copyTuple = heap_copytuple(constraintTuple);
+ 			Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
+ 
+ 			copy_con->coninhcount--;
+ 			if (copy_con->coninhcount == 0)
+ 				copy_con->conislocal = true;
+ 
+ 			simple_heap_update(catalogRelation, &copyTuple->t_self, copyTuple);
+ 			CatalogUpdateIndexes(catalogRelation, copyTuple);
+ 			heap_freetuple(copyTuple);
+ 		}
+ 	}
+ 
+ 	systable_endscan(scan);
+ 	heap_close(catalogRelation, RowExclusiveLock);
+ 
  	/*
  	 * Drop the dependency
  	 *
*************** AtEOSubXact_on_commit_actions(bool isCom
*** 7050,7052 ****
--- 7320,7324 ----
  		}
  	}
  }
+ 
+ 
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 188fe43..123209d 100644
*** a/src/backend/commands/typecmds.c
--- /bsrc/backend/commands/typecmds.c
*************** domainAddConstraint(Oid domainOid, Oid d
*** 2206,2212 ****
  						  InvalidOid,
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc);		/* Source form check constraint */
  
  	/*
  	 * Return the compiled constraint expression so the calling routine can
--- 2206,2214 ----
  						  InvalidOid,
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc,	/* Source form check constraint */
! 						  true, /* is local */
! 						  0); /* inhcount */
  
  	/*
  	 * Return the compiled constraint expression so the calling routine can
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 77bba77..c7fc509 100644
*** a/src/backend/nodes/copyfuncs.c
--- /bsrc/backend/nodes/copyfuncs.c
*************** _copyConstraint(Constraint *from)
*** 1814,1819 ****
--- 1814,1821 ----
  	COPY_NODE_FIELD(keys);
  	COPY_NODE_FIELD(options);
  	COPY_STRING_FIELD(indexspace);
+ 	COPY_SCALAR_FIELD(inhcount);
+ 	COPY_SCALAR_FIELD(is_local);
  
  	return newnode;
  }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 7645e45..fe4ca20 100644
*** a/src/backend/nodes/equalfuncs.c
--- /bsrc/backend/nodes/equalfuncs.c
*************** _equalConstraint(Constraint *a, Constrai
*** 1839,1844 ****
--- 1839,1846 ----
  	COMPARE_NODE_FIELD(keys);
  	COMPARE_NODE_FIELD(options);
  	COMPARE_STRING_FIELD(indexspace);
+ 	COMPARE_SCALAR_FIELD(inhcount);
+ 	COMPARE_SCALAR_FIELD(is_local);
  
  	return true;
  }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 72ee2ff..450186c 100644
*** a/src/backend/nodes/outfuncs.c
--- /bsrc/backend/nodes/outfuncs.c
*************** _outConstraint(StringInfo str, Constrain
*** 2033,2038 ****
--- 2033,2041 ----
  			appendStringInfo(str, "<unrecognized_constraint>");
  			break;
  	}
+ 
+ 	WRITE_INT_FIELD(inhcount);
+ 	WRITE_BOOL_FIELD(is_local);
  }
  
  static void
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index efd50f6..a5f6932 100644
*** a/src/backend/parser/gram.y
--- /bsrc/backend/parser/gram.y
*************** ColConstraintElem:
*** 2068,2073 ****
--- 2068,2075 ----
  					n->cooked_expr = NULL;
  					n->keys = NULL;
  					n->indexspace = NULL;
+ 					n->is_local = true;
+ 					n->inhcount = 0;
  					$$ = (Node *)n;
  				}
  			| DEFAULT b_expr
*************** ConstraintElem:
*** 2208,2213 ****
--- 2210,2217 ----
  					n->raw_expr = $3;
  					n->cooked_expr = NULL;
  					n->indexspace = NULL;
+ 					n->inhcount = 0;
+ 					n->is_local = true;
  					$$ = (Node *)n;
  				}
  			| UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index f2087e4..f105d39 100644
*** a/src/backend/parser/parse_utilcmd.c
--- /bsrc/backend/parser/parse_utilcmd.c
*************** transformColumnDefinition(ParseState *ps
*** 409,414 ****
--- 409,417 ----
  	{
  		constraint = lfirst(clist);
  
+ 		constraint->is_local = true;
+ 		constraint->inhcount = 0;
+ 
  		/*
  		 * If this column constraint is a FOREIGN KEY constraint, then we fill
  		 * in the current attribute's name and throw it into the list of FK
*************** transformInhRelation(ParseState *pstate,
*** 685,690 ****
--- 688,695 ----
  			n->cooked_expr = nodeToString(ccbin_node);
  			n->indexspace = NULL;
  			cxt->ckconstraints = lappend(cxt->ckconstraints, (Node *) n);
+ 			n->is_local = true;
+ 			n->inhcount = 0;
  		}
  	}
  
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 3f3c67d..250d468 100644
*** a/src/backend/utils/cache/catcache.c
--- /bsrc/backend/utils/cache/catcache.c
*************** CatalogCacheInitializeCache(CatCache *ca
*** 962,968 ****
  		else
  		{
  			if (cache->cc_key[i] != ObjectIdAttributeNumber)
! 				elog(FATAL, "only sys attr supported in caches is OID");
  			keytype = OIDOID;
  		}
  
--- 962,968 ----
  		else
  		{
  			if (cache->cc_key[i] != ObjectIdAttributeNumber)
! 				elog(FATAL, "only sys attr supported in caches is OID for rel: %s", cache->cc_relname);
  			keytype = OIDOID;
  		}
  
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 7ff0e8a..789c6a4 100644
*** a/src/backend/utils/cache/syscache.c
--- /bsrc/backend/utils/cache/syscache.c
*************** static const struct cachedesc cacheinfo[
*** 305,310 ****
--- 305,322 ----
  		},
  		128
  	},
+ 	{ConstraintRelationId,		// CONSTRNAME
+ 		ConstraintRelidNameIndexId,
+ 		Anum_pg_constraint_conrelid,
+ 		2,
+ 		{
+ 			Anum_pg_constraint_conrelid,
+ 			Anum_pg_constraint_conname,
+ 			0,
+ 			0
+ 		},
+ 		256,
+ 	},
  	{ConstraintRelationId,		/* CONSTROID */
  		ConstraintOidIndexId,
  		0,
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index c5fb69a..aa683be 100644
*** a/src/include/access/tupdesc.h
--- /bsrc/include/access/tupdesc.h
*************** typedef struct attrDefault
*** 27,34 ****
  
  typedef struct constrCheck
  {
! 	char	   *ccname;
! 	char	   *ccbin;			/* nodeToString representation of expr */
  } ConstrCheck;
  
  /* This structure contains constraints of a tuple */
--- 27,36 ----
  
  typedef struct constrCheck
  {
! 	char	*ccname;
! 	char	*ccbin;			/* nodeToString representation of expr */
! 	bool	is_local;
! 	int		inhcount;
  } ConstrCheck;
  
  /* This structure contains constraints of a tuple */
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index b9f34f2..9b17a73 100644
*** a/src/include/catalog/catversion.h
--- /bsrc/include/catalog/catversion.h
***************
*** 53,58 ****
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	200803222
  
  #endif
--- 53,58 ----
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	200803290
  
  #endif
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 7dc5215..d962930 100644
*** a/src/include/catalog/indexing.h
--- /bsrc/include/catalog/indexing.h
*************** DECLARE_INDEX(pg_constraint_contypid_ind
*** 121,126 ****
--- 121,128 ----
  #define ConstraintTypidIndexId	2666
  DECLARE_UNIQUE_INDEX(pg_constraint_oid_index, 2667, on pg_constraint using btree(oid oid_ops));
  #define ConstraintOidIndexId  2667
+ DECLARE_UNIQUE_INDEX(pg_constraint_relid_coname_index, 2700, on pg_constraint using btree(conrelid oid_ops, conname name_ops));
+ #define ConstraintRelidNameIndexId  2700
  
  DECLARE_UNIQUE_INDEX(pg_conversion_default_index, 2668, on pg_conversion using btree(connamespace oid_ops, conforencoding int4_ops, contoencoding int4_ops, oid oid_ops));
  #define ConversionDefaultIndexId  2668
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index 501899d..9e4a7c9 100644
*** a/src/include/catalog/pg_constraint.h
--- /bsrc/include/catalog/pg_constraint.h
*************** CATALOG(pg_constraint,2606)
*** 71,76 ****
--- 71,82 ----
  	char		confdeltype;	/* foreign key's ON DELETE action */
  	char		confmatchtype;	/* foreign key's match type */
  
+ 	/* Has a local definition (hence, do not drop when coninhcount is 0) */
+ 	bool conislocal;
+ 
+ 	/* Number of times inherited from direct parent relation(s) */
+ 	int4 coninhcount;
+ 
  	/*
  	 * VARIABLE LENGTH FIELDS start here.  These fields may be NULL, too.
  	 */
*************** typedef FormData_pg_constraint *Form_pg_
*** 125,150 ****
   *		compiler constants for pg_constraint
   * ----------------
   */
! #define Natts_pg_constraint					18
! #define Anum_pg_constraint_conname			1
! #define Anum_pg_constraint_connamespace		2
! #define Anum_pg_constraint_contype			3
! #define Anum_pg_constraint_condeferrable	4
! #define Anum_pg_constraint_condeferred		5
! #define Anum_pg_constraint_conrelid			6
! #define Anum_pg_constraint_contypid			7
! #define Anum_pg_constraint_confrelid		8
! #define Anum_pg_constraint_confupdtype		9
! #define Anum_pg_constraint_confdeltype		10
! #define Anum_pg_constraint_confmatchtype	11
! #define Anum_pg_constraint_conkey			12
! #define Anum_pg_constraint_confkey			13
! #define Anum_pg_constraint_conpfeqop		14
! #define Anum_pg_constraint_conppeqop		15
! #define Anum_pg_constraint_conffeqop		16
! #define Anum_pg_constraint_conbin			17
! #define Anum_pg_constraint_consrc			18
! 
  
  /* Valid values for contype */
  #define CONSTRAINT_CHECK			'c'
--- 131,157 ----
   *		compiler constants for pg_constraint
   * ----------------
   */
! #define Natts_pg_constraint					20
! #define	Anum_pg_constraint_conname			1
! #define	Anum_pg_constraint_connamespace		2
! #define	Anum_pg_constraint_contype			3
! #define	Anum_pg_constraint_condeferrable	4
! #define	Anum_pg_constraint_condeferred		5
! #define	Anum_pg_constraint_conrelid			6
! #define	Anum_pg_constraint_contypid			7
! #define	Anum_pg_constraint_confrelid		8
! #define	Anum_pg_constraint_confupdtype		9
! #define	Anum_pg_constraint_confdeltype		10
! #define	Anum_pg_constraint_confmatchtype	11
! #define	Anum_pg_constraint_conislocal		12
! #define	Anum_pg_constraint_coninhcount		13
! #define	Anum_pg_constraint_conkey			14
! #define	Anum_pg_constraint_confkey			15
! #define	Anum_pg_constraint_conpfeqop		16
! #define	Anum_pg_constraint_conppeqop		17
! #define	Anum_pg_constraint_conffeqop		18
! #define	Anum_pg_constraint_conbin			19
! #define	Anum_pg_constraint_consrc			20
  
  /* Valid values for contype */
  #define CONSTRAINT_CHECK			'c'
*************** extern Oid CreateConstraintEntry(const c
*** 192,198 ****
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc);
  
  extern void RemoveConstraintById(Oid conId);
  extern void RenameConstraintById(Oid conId, const char *newname);
--- 199,207 ----
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc,
! 					  const bool conIsLocal,
! 					  const int conInhCout);
  
  extern void RemoveConstraintById(Oid conId);
  extern void RenameConstraintById(Oid conId, const char *newname);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f786f18..e5c1d65 100644
*** a/src/include/nodes/parsenodes.h
--- /bsrc/include/nodes/parsenodes.h
*************** typedef enum AlterTableType
*** 902,908 ****
  	AT_ProcessedConstraint,		/* pre-processed add constraint (local in
  								 * parser/parse_utilcmd.c) */
  	AT_DropConstraint,			/* drop constraint */
! 	AT_DropConstraintQuietly,	/* drop constraint, no error/warning (local in
  								 * commands/tablecmds.c) */
  	AT_AlterColumnType,			/* alter column type */
  	AT_ChangeOwner,				/* change owner */
--- 902,908 ----
  	AT_ProcessedConstraint,		/* pre-processed add constraint (local in
  								 * parser/parse_utilcmd.c) */
  	AT_DropConstraint,			/* drop constraint */
! 	AT_DropConstraintRecurse,	/* drop constraint recursion (local in
  								 * commands/tablecmds.c) */
  	AT_AlterColumnType,			/* alter column type */
  	AT_ChangeOwner,				/* change owner */
*************** typedef struct Constraint
*** 1165,1170 ****
--- 1165,1172 ----
  	List	   *options;		/* options from WITH clause */
  	char	   *indexspace;		/* index tablespace for PKEY/UNIQUE
  								 * constraints; NULL for default */
+ 	bool is_local;
+ 	int inhcount;
  } Constraint;
  
  /* ----------
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 76248de..b99da72 100644
*** a/src/include/utils/syscache.h
--- /bsrc/include/utils/syscache.h
***************
*** 28,81 ****
   *		Keep them in alphabetical order.
   */
  
! #define AGGFNOID			0
! #define AMNAME				1
! #define AMOID				2
! #define AMOPOPID			3
! #define AMOPSTRATEGY		4
! #define AMPROCNUM			5
! #define ATTNAME				6
! #define ATTNUM				7
! #define AUTHMEMMEMROLE		8
! #define AUTHMEMROLEMEM		9
! #define AUTHNAME			10
! #define AUTHOID				11
! #define CASTSOURCETARGET	12
! #define CLAAMNAMENSP		13
! #define CLAOID				14
! #define CONDEFAULT			15
! #define CONNAMENSP			16
! #define CONSTROID			17
! #define CONVOID				18
! #define DATABASEOID			19
! #define ENUMOID				20
! #define ENUMTYPOIDNAME		21
! #define INDEXRELID			22
! #define LANGNAME			23
! #define LANGOID				24
! #define NAMESPACENAME		25
! #define NAMESPACEOID		26
! #define OPERNAMENSP			27
! #define OPEROID				28
! #define OPFAMILYAMNAMENSP	29
! #define OPFAMILYOID			30
! #define PROCNAMEARGSNSP		31
! #define PROCOID				32
! #define RELNAMENSP			33
! #define RELOID				34
! #define RULERELNAME			35
! #define STATRELATT			36
! #define TSCONFIGMAP			37
! #define TSCONFIGNAMENSP		38
! #define TSCONFIGOID			39
! #define TSDICTNAMENSP		40
! #define TSDICTOID			41
! #define TSPARSERNAMENSP		42
! #define TSPARSEROID			43
! #define TSTEMPLATENAMENSP	44
! #define TSTEMPLATEOID		45
! #define TYPENAMENSP			46
! #define TYPEOID				47
  
  extern void InitCatalogCache(void);
  extern void InitCatalogCachePhase2(void);
--- 28,84 ----
   *		Keep them in alphabetical order.
   */
  
! enum SysCacheIdent {
! 	AGGFNOID = 0,
! 	AMNAME,
! 	AMOID,
! 	AMOPOPID,
! 	AMOPSTRATEGY,
! 	AMPROCNUM,
! 	ATTNAME,
! 	ATTNUM,
! 	AUTHMEMMEMROLE,
! 	AUTHMEMROLEMEM,
! 	AUTHNAME,
! 	AUTHOID,
! 	CASTSOURCETARGET,
! 	CLAAMNAMENSP,
! 	CLAOID,
! 	CONDEFAULT,
! 	CONNAMENSP,
! 	CONSTRNAME,
! 	CONSTROID,
! 	CONVOID,
! 	DATABASEOID,
! 	ENUMOID,
! 	ENUMTYPOIDNAME,
! 	INDEXRELID,
! 	LANGNAME,
! 	LANGOID,
! 	NAMESPACENAME,
! 	NAMESPACEOID,
! 	OPERNAMENSP,
! 	OPEROID,
! 	OPFAMILYAMNAMENSP,
! 	OPFAMILYOID,
! 	PROCNAMEARGSNSP,
! 	PROCOID,
! 	RELNAMENSP,
! 	RELOID,
! 	RULERELNAME,
! 	STATRELATT,
! 	TSCONFIGMAP,
! 	TSCONFIGNAMENSP,
! 	TSCONFIGOID,
! 	TSDICTNAMENSP,
! 	TSDICTOID,
! 	TSPARSERNAMENSP,
! 	TSPARSEROID,
! 	TSTEMPLATENAMENSP,
! 	TSTEMPLATEOID,
! 	TYPENAMENSP,
! 	TYPEOID,
! };
  
  extern void InitCatalogCache(void);
  extern void InitCatalogCachePhase2(void);
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index cba489d..58ee537 100644
*** a/src/test/regress/expected/alter_table.out
--- /bsrc/test/regress/expected/alter_table.out
*************** create table atacc1 (test int);
*** 383,391 ****
  create table atacc2 (test2 int);
  create table atacc3 (test3 int) inherits (atacc1, atacc2);
  alter table only atacc2 add constraint foo check (test2>0);
  -- fail and then succeed on atacc2
  insert into atacc2 (test2) values (-3);
- ERROR:  new row for relation "atacc2" violates check constraint "foo"
  insert into atacc2 (test2) values (3);
  -- both succeed on atacc3
  insert into atacc3 (test2) values (-3);
--- 383,391 ----
  create table atacc2 (test2 int);
  create table atacc3 (test3 int) inherits (atacc1, atacc2);
  alter table only atacc2 add constraint foo check (test2>0);
+ ERROR:  constraint must be added to child tables too
  -- fail and then succeed on atacc2
  insert into atacc2 (test2) values (-3);
  insert into atacc2 (test2) values (3);
  -- both succeed on atacc3
  insert into atacc3 (test2) values (-3);
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index f81776f..b2c684b 100644
*** a/src/test/regress/expected/inherit.out
--- /bsrc/test/regress/expected/inherit.out
*************** drop function p2text(p2);
*** 692,694 ****
--- 692,770 ----
  drop table c1;
  drop table p2;
  drop table p1;
+ CREATE TABLE ac (aa TEXT);
+ alter table ac add constraint ac_check check (aa is not null);
+ CREATE TABLE bc (bb TEXT) INHERITS (ac);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+  ac_check | c       | f          |           1 | (aa IS NOT NULL)
+ (2 rows)
+ 
+ alter table bc drop constraint ac_check;
+ ERROR:  cannot drop inherited constraint "ac_check" of relation "bc"
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname | contype | conislocal | coninhcount | consrc 
+ ---------+---------+------------+-------------+--------
+ (0 rows)
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+  ac_check | c       | f          |           1 | (aa IS NOT NULL)
+ (2 rows)
+ 
+ insert into ac (aa) values (NULL);
+ ERROR:  new row for relation "ac" violates check constraint "ac_check"
+ insert into bc (aa) values (NULL);
+ ERROR:  new row for relation "bc" violates check constraint "ac_check"
+ alter table bc drop constraint ac_check;
+ ERROR:  cannot drop inherited constraint "ac_check" of relation "bc"
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname | contype | conislocal | coninhcount | consrc 
+ ---------+---------+------------+-------------+--------
+ (0 rows)
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ alter table bc no inherit ac;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+ (2 rows)
+ 
+ alter table bc drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+ (1 row)
+ 
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname | contype | conislocal | coninhcount | consrc 
+ ---------+---------+------------+-------------+--------
+ (0 rows)
+ 
+ drop table bc;
+ drop table ac;
+ create table ac (a int constraint check_a check (a <> 0));
+ create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac);
+ NOTICE:  merging column "a" with inherited definition
+ NOTICE:  merging constraint "check_a" with inherited definition
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname | contype | conislocal | coninhcount |  consrc  
+ ---------+---------+------------+-------------+----------
+  check_a | c       | t          |           0 | (a <> 0)
+  check_b | c       | t          |           0 | (b <> 0)
+  check_a | c       | f          |           1 | (a <> 0)
+ (3 rows)
+ 
+ drop table bc;
+ drop table ac;
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
index b0499a6..9c17849 100644
*** a/src/test/regress/sql/inherit.sql
--- /bsrc/test/regress/sql/inherit.sql
*************** drop function p2text(p2);
*** 196,198 ****
--- 196,236 ----
  drop table c1;
  drop table p2;
  drop table p1;
+ 
+ CREATE TABLE ac (aa TEXT);
+ alter table ac add constraint ac_check check (aa is not null);
+ CREATE TABLE bc (bb TEXT) INHERITS (ac);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ 
+ alter table bc drop constraint ac_check;
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ 
+ insert into ac (aa) values (NULL);
+ insert into bc (aa) values (NULL);
+ 
+ alter table bc drop constraint ac_check;
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ alter table bc no inherit ac;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ alter table bc drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ 
+ drop table bc;
+ drop table ac;
+ 
+ create table ac (a int constraint check_a check (a <> 0));
+ create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ 
+ 
+ drop table bc;
+ drop table ac;
#3NikhilS
nikkhils@gmail.com
In reply to: Alex Hunsaker (#2)
Re: [PATCHES] [badalex@gmail.com: Re: [BUGS] Problem identifying constraints which should not be inherited]

Hi Alex,

On Sun, Mar 30, 2008 at 7:10 AM, Alex Hunsaker <badalex@gmail.com> wrote:

(trimmed cc's)

Find attached inherited_constraint_v2.patch

Changes since v1:
-rebased against latest HEAD
-changed enum { Anum_pg_constraint_... } back into #define
Anum_pg_constraint_...
-remove whitespace damage I added
-fixed regression tests I added to be more robust
-fixed
create table ac (a int constraint check_a check (a <> 0));
create table bc (a int constraint check_a check (a <> 0)) inherits (ac);
so it properly works (removed crud I put into
AddRelationRawConstraints and created a proper fix in DefineRelation)

I was taking a look at this patch to add the pg_dump related changes. Just
wanted to give you a heads up as this patch crashes if we run "make
installcheck". Seems there is an issue introduced in the CREATE TABLE
REFERENCES code path due to your patch (this is without my pg_dump changes
just to be sure). Looks like some memory overwrite issue. The trace is as
follows:

Core was generated by `postgres: nikhils regression [local] CREATE
TABLE '.
Program terminated with signal 11, Segmentation fault.
#0 0x08378024 in AllocSetCheck (context=0xa060368) at aset.c:1112
1112 if (dsize > 0 && dsize < chsize &&
*chdata_end != 0x7E)
(gdb) bt
#0 0x08378024 in AllocSetCheck (context=0xa060368) at aset.c:1112
#1 0x0837704f in AllocSetDelete (context=0xa060368) at aset.c:487
#2 0x083783c2 in MemoryContextDelete (context=0xa060368) at mcxt.c:196
#3 0x083797fb in PortalDrop (portal=0xa0845bc, isTopCommit=0 '\0') at
portalmem.c:448
#4 0x08281939 in exec_simple_query (
query_string=0xa07e564 "CREATE TABLE enumtest_child (parent rainbow
REFERENCES enumtest_parent);") at postgres.c:992
#5 0x082857d4 in PostgresMain (argc=4, argv=0x9ffbe28, username=0x9ffbcc4
"nikhils") at postgres.c:3550
#6 0x0824917b in BackendRun (port=0xa003180) at postmaster.c:3204
#7 0x082486a2 in BackendStartup (port=0xa003180) at postmaster.c:2827
#8 0x08245e9c in ServerLoop () at postmaster.c:1271
#9 0x082457fd in PostmasterMain (argc=3, argv=0x9ff9c60) at postmaster.c
:1019
#10 0x081e1c03 in main (argc=3, argv=0x9ff9c60) at main.c:188

Regards,
Nikhils
--
EnterpriseDB http://www.enterprisedb.com

#4Alex Hunsaker
badalex@gmail.com
In reply to: NikhilS (#3)
1 attachment(s)
Re: [PATCHES] [badalex@gmail.com: Re: [BUGS] Problem identifying constraints which should not be inherited]

On Mon, Mar 31, 2008 at 2:36 AM, NikhilS <nikkhils@gmail.com> wrote:

Hi Alex,

I was taking a look at this patch to add the pg_dump related changes. Just
wanted to give you a heads up as this patch crashes if we run "make
installcheck". Seems there is an issue introduced in the CREATE TABLE
REFERENCES code path due to your patch (this is without my pg_dump changes
just to be sure). Looks like some memory overwrite issue. The trace is as
follows:

Ouch, sorry i did not reply sooner... been out with the flu. Oddly
enough make check and make installcheck worked great on my 64 bit box.
But on my laptop(32 bits) make check lights up like a christmas tree.
Which is why I did not notice the problem. :(

Attached is a patch that fixes the problem... (it was debugging from
an earlier version)

Attachments:

fix.patchtext/x-diff; name=fix.patchDownload
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index f105d39..7d12156 100644
*** a/src/backend/parser/parse_utilcmd.c
--- /bsrc/backend/parser/parse_utilcmd.c
*************** transformColumnDefinition(ParseState *ps
*** 409,417 ****
  	{
  		constraint = lfirst(clist);
  
- 		constraint->is_local = true;
- 		constraint->inhcount = 0;
- 
  		/*
  		 * If this column constraint is a FOREIGN KEY constraint, then we fill
  		 * in the current attribute's name and throw it into the list of FK
--- 409,414 ----
#5NikhilS
nikkhils@gmail.com
In reply to: Alex Hunsaker (#4)
1 attachment(s)
Re: [badalex@gmail.com: Re: [BUGS] Problem identifying constraints which should not be inherited]

Hi,

On Tue, Apr 1, 2008 at 7:33 AM, Alex Hunsaker <badalex@gmail.com> wrote:

I was taking a look at this patch to add the pg_dump related changes.

Just

wanted to give you a heads up as this patch crashes if we run "make
installcheck". Seems there is an issue introduced in the CREATE TABLE
REFERENCES code path due to your patch (this is without my pg_dump

changes

just to be sure). Looks like some memory overwrite issue. The trace is

as

follows:

Attached is a patch that fixes the problem... (it was debugging from
an earlier version)

Yup, that fixes the problem.

PFA, a revised version of Alex' patch. I have added the relevant pg_dump
related changes too. As I mentioned earlier, I still don't know whether
Alex' syscache related changes are necessary, but I will leave it to the
patch reviewers to decide :)

This combined patch meets the following TODOs:

* Add logic to disallow ADD CONSTRAINT ONLY to parent of an inheritance
hierarchy

* Add logic to mark inherited constraints in the children:
This is achieved by introducing bool "conislocal" and int4 "coninhcount"
attributes in pg_constraint. The behaviour of these 2 attributes is pretty
similar to attislocal and attinhcount attributes in pg_attribute.

* Add logic to disallow dropping inherited constraints directly on children
Obviously they will get dropped if a DROP CONSTRAINT is fired on the parent.
with recurse set to true (this is the default behaviour)

* Modify the pg_dump logic to use the new pg_constraint based attributes
logic for versions above 80300 (or should it be PG_VERSION_NUM 80400?).

Please direct comments/feedback towards both me and Alex.

Regards,
Nikhils
--
EnterpriseDB http://www.enterprisedb.com

Attachments:

inherited_constraints_v3.0.patchtext/x-patch; name=inherited_constraints_v3.0.patchDownload
Index: src/backend/catalog/heap.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/catalog/heap.c,v
retrieving revision 1.332
diff -c -r1.332 heap.c
*** src/backend/catalog/heap.c	27 Mar 2008 03:57:33 -0000	1.332
--- src/backend/catalog/heap.c	1 Apr 2008 07:32:15 -0000
***************
*** 77,83 ****
  				   char new_rel_kind,
  				   Oid new_array_type);
  static void RelationRemoveInheritance(Oid relid);
! static void StoreRelCheck(Relation rel, char *ccname, char *ccbin);
  static void StoreConstraints(Relation rel, TupleDesc tupdesc);
  static void SetRelationNumChecks(Relation rel, int numchecks);
  static List *insert_ordered_unique_oid(List *list, Oid datum);
--- 77,83 ----
  				   char new_rel_kind,
  				   Oid new_array_type);
  static void RelationRemoveInheritance(Oid relid);
! static void StoreRelCheck(Relation rel, char *ccname, char *ccbin, const bool is_local, const int inhcount);
  static void StoreConstraints(Relation rel, TupleDesc tupdesc);
  static void SetRelationNumChecks(Relation rel, int numchecks);
  static List *insert_ordered_unique_oid(List *list, Oid datum);
***************
*** 1531,1537 ****
   * in the pg_class entry for the relation.
   */
  static void
! StoreRelCheck(Relation rel, char *ccname, char *ccbin)
  {
  	Node	   *expr;
  	char	   *ccsrc;
--- 1531,1537 ----
   * in the pg_class entry for the relation.
   */
  static void
! StoreRelCheck(Relation rel, char *ccname, char *ccbin, const bool is_local, const int inhcount)
  {
  	Node	   *expr;
  	char	   *ccsrc;
***************
*** 1608,1614 ****
  						  InvalidOid,	/* no associated index */
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc);		/* Source form check constraint */
  
  	pfree(ccsrc);
  }
--- 1608,1616 ----
  						  InvalidOid,	/* no associated index */
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc, /* Source form check constraint */
! 						  is_local, /* islocal */
! 						  inhcount); /* coninhcount */
  
  	pfree(ccsrc);
  }
***************
*** 1643,1649 ****
  
  	for (i = 0; i < constr->num_check; i++)
  		StoreRelCheck(rel, constr->check[i].ccname,
! 					  constr->check[i].ccbin);
  
  	if (constr->num_check > 0)
  		SetRelationNumChecks(rel, constr->num_check);
--- 1645,1653 ----
  
  	for (i = 0; i < constr->num_check; i++)
  		StoreRelCheck(rel, constr->check[i].ccname,
! 					  constr->check[i].ccbin,
! 					  constr->check[i].is_local,
! 					  constr->check[i].inhcount);
  
  	if (constr->num_check > 0)
  		SetRelationNumChecks(rel, constr->num_check);
***************
*** 1863,1869 ****
  		/*
  		 * OK, store it.
  		 */
! 		StoreRelCheck(rel, ccname, nodeToString(expr));
  
  		numchecks++;
  
--- 1867,1873 ----
  		/*
  		 * OK, store it.
  		 */
! 		StoreRelCheck(rel, ccname, nodeToString(expr), cdef->is_local, cdef->inhcount);
  
  		numchecks++;
  
Index: src/backend/catalog/index.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/catalog/index.c,v
retrieving revision 1.296
diff -c -r1.296 index.c
*** src/backend/catalog/index.c	26 Mar 2008 21:10:37 -0000	1.296
--- src/backend/catalog/index.c	1 Apr 2008 07:32:15 -0000
***************
*** 716,722 ****
  										   InvalidOid,	/* no associated index */
  										   NULL,		/* no check constraint */
  										   NULL,
! 										   NULL);
  
  			referenced.classId = ConstraintRelationId;
  			referenced.objectId = conOid;
--- 716,724 ----
  										   InvalidOid,	/* no associated index */
  										   NULL,		/* no check constraint */
  										   NULL,
! 										   NULL,
! 										   true, /* islocal */
! 										   0); /* inhcount */
  
  			referenced.classId = ConstraintRelationId;
  			referenced.objectId = conOid;
Index: src/backend/catalog/pg_constraint.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/catalog/pg_constraint.c,v
retrieving revision 1.40
diff -c -r1.40 pg_constraint.c
*** src/backend/catalog/pg_constraint.c	26 Mar 2008 21:10:37 -0000	1.40
--- src/backend/catalog/pg_constraint.c	1 Apr 2008 07:32:15 -0000
***************
*** 60,66 ****
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc)
  {
  	Relation	conDesc;
  	Oid			conOid;
--- 60,68 ----
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc,
! 					  const bool conIsLocal,
! 					  const int conInhCount)
  {
  	Relation	conDesc;
  	Oid			conOid;
***************
*** 171,176 ****
--- 173,181 ----
  	else
  		nulls[Anum_pg_constraint_conffeqop - 1] = 'n';
  
+ 	values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
+ 	values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
+ 
  	/*
  	 * initialize the binary form of the check constraint.
  	 */
Index: src/backend/commands/tablecmds.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/commands/tablecmds.c,v
retrieving revision 1.250
diff -c -r1.250 tablecmds.c
*** src/backend/commands/tablecmds.c	31 Mar 2008 03:34:27 -0000	1.250
--- src/backend/commands/tablecmds.c	1 Apr 2008 07:32:15 -0000
***************
*** 170,175 ****
--- 170,177 ----
  static List *MergeAttributes(List *schema, List *supers, bool istemp,
  				List **supOids, List **supconstr, int *supOidCount);
  static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
+ static void MergeInheritedConstraints(Relation rel, List *old_constraints,
+ 									List *new_constriants);
  static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
  static void add_nonduplicate_constraint(Constraint *cdef,
  							ConstrCheck *check, int *ncheck);
***************
*** 232,245 ****
  				 bool recurse, bool recursing);
  static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
  			   IndexStmt *stmt, bool is_rebuild);
  static void ATExecAddConstraint(AlteredTableInfo *tab, Relation rel,
  					Node *newConstraint);
  static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
  						  FkConstraint *fkconstraint);
- static void ATPrepDropConstraint(List **wqueue, Relation rel,
- 					 bool recurse, AlterTableCmd *cmd);
  static void ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior, bool quiet);
  static void ATPrepAlterColumnType(List **wqueue,
  					  AlteredTableInfo *tab, Relation rel,
  					  bool recurse, bool recursing,
--- 234,248 ----
  				 bool recurse, bool recursing);
  static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
  			   IndexStmt *stmt, bool is_rebuild);
+ static void ATPrepAddConstraint(List **wqueue, Relation rel, bool recurse,
+ 				AlterTableCmd *cmd);
  static void ATExecAddConstraint(AlteredTableInfo *tab, Relation rel,
  					Node *newConstraint);
  static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
  						  FkConstraint *fkconstraint);
  static void ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior, 
! 					 bool recurse, bool recursing);
  static void ATPrepAlterColumnType(List **wqueue,
  					  AlteredTableInfo *tab, Relation rel,
  					  bool recurse, bool recursing,
***************
*** 263,269 ****
  static void ATExecAddInherit(Relation rel, RangeVar *parent);
  static void ATExecDropInherit(Relation rel, RangeVar *parent);
  static void copy_relation_data(Relation rel, SMgrRelation dst);
! 
  
  /* ----------------------------------------------------------------
   *		DefineRelation
--- 266,276 ----
  static void ATExecAddInherit(Relation rel, RangeVar *parent);
  static void ATExecDropInherit(Relation rel, RangeVar *parent);
  static void copy_relation_data(Relation rel, SMgrRelation dst);
! static bool ConstraintIsEquiv(const HeapTuple a, const HeapTuple b,
! 							  const TupleDesc tupleDesc);
! static char *decompile_conbin(HeapTuple contup, TupleDesc tupdesc);
! static void change_opfuncid(Node *node, Oid opfuncid);
! static bool change_opfuncid_walker(Node *node, void *opfuncid);
  
  /* ----------------------------------------------------------------
   *		DefineRelation
***************
*** 488,493 ****
--- 495,505 ----
  		}
  	}
  
+ 	if (old_constraints && stmt->constraints)
+ 	{
+ 		MergeInheritedConstraints(rel, old_constraints, stmt->constraints);
+ 	}
+ 
  	/*
  	 * Parse and add the defaults/constraints, if any.
  	 */
***************
*** 1059,1064 ****
--- 1071,1078 ----
  				Constraint *cdef = makeNode(Constraint);
  				Node	   *expr;
  
+ 				cdef->is_local = false;
+ 				cdef->inhcount = check[i].inhcount + 1;
  				cdef->contype = CONSTR_CHECK;
  				cdef->name = pstrdup(check[i].ccname);
  				cdef->raw_expr = NULL;
***************
*** 1181,1186 ****
--- 1195,1286 ----
  	return schema;
  }
  
+ /*
+  * Go through constraints in new constraints
+  * if we have a matching constraint in old_constraints and they have a
+  * matching definition, then remove the constraint from new_constraints
+  */
+ static void
+ MergeInheritedConstraints(Relation rel,
+ 						  List *old_constraints,
+ 						  List *new_constraints)
+ {
+ 	ListCell *cur_item;
+ 	ListCell *prev_item;
+ 	ListCell *listptr;
+ 
+ 	prev_item = NULL;
+ 	cur_item = list_head(new_constraints);
+ 
+ 	while (cur_item != NULL)
+ 	{
+ 		bool get_next = true;
+ 		Constraint *cdef = (Constraint *) lfirst(cur_item);
+ 
+ 		if (cdef->contype == CONSTR_CHECK &&
+ 			cdef->cooked_expr == NULL &&
+ 			cdef->name &&
+ 			cdef->raw_expr)
+ 		{
+ 			foreach(listptr, old_constraints)
+ 			{
+ 				Constraint *cdef1 = (Constraint *) lfirst(listptr);
+ 
+ 				if (cdef1->contype == CONSTR_CHECK &&
+ 					cdef1->name &&
+ 					strcmp(cdef->name, cdef1->name) == 0)
+ 				{
+ 					ParseState		*pstate;
+ 					RangeTblEntry	*rte;
+ 					Node			*expr;
+ 					char 			*cooked;
+ 
+ 					pstate = make_parsestate(NULL);
+ 					rte = addRangeTableEntryForRelation(pstate,
+ 														rel,
+ 														NULL,
+ 														false,
+ 														true);
+ 					addRTEtoQuery(pstate, rte, true, true, true);
+ 
+ 					expr = transformExpr(pstate, cdef->raw_expr);
+ 					expr = coerce_to_boolean(pstate, expr, "CHECK");
+ 					change_opfuncid(expr, 0);
+ 
+ 					cooked = nodeToString(expr);
+ 
+ 					if (strcmp(cooked, cdef1->cooked_expr) == 0)
+ 					{
+ 						ereport(NOTICE,
+ 							(errmsg("merging constraint \"%s\" with inherited definition",
+ 							cdef->name)));
+ 
+ 						list_delete_cell(new_constraints, cur_item, prev_item);
+ 						pfree(cdef);
+ 						get_next = false;
+ 						if(prev_item)
+ 							cur_item = lnext(prev_item);
+ 						else
+ 							cur_item = list_head(new_constraints);
+ 					}else
+ 					{
+ 						ereport(ERROR,
+ 								(errmsg("constraint \"%s\" does not match inherited definition",
+ 								cdef->name)));
+ 					}
+ 
+ 					break;
+ 				}
+ 			}
+ 		}
+ 
+ 		if(get_next)
+ 		{
+ 			prev_item = cur_item;
+ 			cur_item = lnext(prev_item);
+ 		}
+ 	}
+ }
  
  /*
   * In multiple-inheritance situations, it's possible to inherit
***************
*** 1214,1219 ****
--- 1314,1321 ----
  	/* No match on name, so add it to array */
  	check[*ncheck].ccname = cdef->name;
  	check[*ncheck].ccbin = pstrdup(cdef->cooked_expr);
+ 	check[*ncheck].inhcount = cdef->inhcount;
+ 	check[*ncheck].is_local = cdef->is_local;
  	(*ncheck)++;
  }
  
***************
*** 1312,1317 ****
--- 1414,1451 ----
  	return attmap;
  }
  
+ /*
+  * change an opfuncid of a Node
+  *
+  * Note that the passed node tree is modified in-place!
+  */
+ static void
+ change_opfuncid(Node *node, Oid opfuncid)
+ {
+ 	Oid *o = palloc(sizeof(Oid));
+ 	*o = opfuncid;
+ 	(void) change_opfuncid_walker(node, (void*)o);
+ 	pfree(o);
+ }
+ 
+ static bool
+ change_opfuncid_walker(Node *node, void *opfuncid)
+ {
+ 	if (node == NULL)
+ 		return false;
+ 
+ 	if (IsA(node, OpExpr))
+ 	{
+ 		Oid		*o = (Oid*)opfuncid;
+ 		OpExpr	*op = (OpExpr *) node;
+ 
+ 		op->opfuncid = *o;
+ 		return false;
+ 	}
+ 	return expression_tree_walker(node, change_opfuncid_walker,
+ 								  opfuncid);
+ 
+ }
  
  /*
   * StoreCatalogInheritance
***************
*** 2044,2070 ****
  			break;
  		case AT_AddConstraint:	/* ADD CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 
! 			/*
! 			 * Currently we recurse only for CHECK constraints, never for
! 			 * foreign-key constraints.  UNIQUE/PKEY constraints won't be seen
! 			 * here.
! 			 */
! 			if (IsA(cmd->def, Constraint))
! 				ATSimpleRecursion(wqueue, rel, cmd, recurse);
! 			/* No command-specific prep needed */
  			pass = AT_PASS_ADD_CONSTR;
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 			/* Performs own recursion */
! 			ATPrepDropConstraint(wqueue, rel, recurse, cmd);
! 			pass = AT_PASS_DROP;
! 			break;
! 		case AT_DropConstraintQuietly:	/* DROP CONSTRAINT for child */
! 			ATSimplePermissions(rel, false);
! 			ATSimpleRecursion(wqueue, rel, cmd, recurse);
! 			/* No command-specific prep needed */
  			pass = AT_PASS_DROP;
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
--- 2178,2193 ----
  			break;
  		case AT_AddConstraint:	/* ADD CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 			ATPrepAddConstraint(wqueue, rel, recurse, cmd);
  			pass = AT_PASS_ADD_CONSTR;
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 			/* we follow AT_DropColumns example for better or for worse so
! 			 * Recursion occurs during execution phase
! 			 * No command-specific prep needed except saving recurse flag */
! 			if (recurse)
! 				cmd->subtype = AT_DropConstraintRecurse;
  			pass = AT_PASS_DROP;
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
***************
*** 2253,2262 ****
  			ATExecAddConstraint(tab, rel, cmd->def);
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, false);
  			break;
! 		case AT_DropConstraintQuietly:	/* DROP CONSTRAINT for child */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, true);
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
  			ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def);
--- 2376,2385 ----
  			ATExecAddConstraint(tab, rel, cmd->def);
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, false, false);
  			break;
! 		case AT_DropConstraintRecurse:	/* DROP CONSTRAINT for child */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, true, false);
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
  			ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def);
***************
*** 3931,3936 ****
--- 4054,4108 ----
   * ALTER TABLE ADD CONSTRAINT
   */
  static void
+ ATPrepAddConstraint(List **wqueue, Relation rel, bool recurse,
+ 				AlterTableCmd *cmd)
+ {
+ 	/*
+ 	 * Currently we recurse only for CHECK constraints, never for
+ 	 * foreign-key constraints.  UNIQUE/PKEY constraints won't be seen
+ 	 * here.
+ 	 */
+ 	if (!IsA(cmd->def, Constraint))
+ 	{
+ 		return;
+ 	}
+ 
+ 	/*
+ 	 * Recurse to add the column to child classes, if requested.
+ 	 *
+ 	 * We must recurse one level at a time, so that multiply-inheriting
+ 	 * children are visited the right number of times and end up with the
+ 	 * right coninhcount.
+ 	 */
+ 	if (recurse)
+ 	{
+ 		AlterTableCmd	*childCmd = copyObject(cmd);
+ 		Constraint		*constr = (Constraint *) childCmd->def;
+ 
+ 		/* child should see column as singly inherited */
+ 		constr->inhcount = 1;
+ 		constr->is_local = false;
+ 
+ 		ATOneLevelRecursion(wqueue, rel, childCmd);
+ 	}
+ 	else
+ 	{
+ 		/*
+ 		 * 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 (find_inheritance_children(RelationGetRelid(rel)) != NIL)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ 					 errmsg("constraint must be added to children tables too")));
+ 	}
+ }
+ 
+ 
+ /*
+  * ALTER TABLE ADD CONSTRAINT
+  */
+ static void
  ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint)
  {
  	switch (nodeTag(newConstraint))
***************
*** 4290,4296 ****
  									  indexOid,
  									  NULL,		/* no check constraint */
  									  NULL,
! 									  NULL);
  
  	/*
  	 * Create the triggers that will enforce the constraint.
--- 4462,4470 ----
  									  indexOid,
  									  NULL,		/* no check constraint */
  									  NULL,
! 									  NULL,
! 									  true, /* islocal */
! 									  0); /* inhcount */
  
  	/*
  	 * Create the triggers that will enforce the constraint.
***************
*** 4810,4853 ****
   * ALTER TABLE DROP CONSTRAINT
   */
  static void
! ATPrepDropConstraint(List **wqueue, Relation rel,
! 					 bool recurse, AlterTableCmd *cmd)
  {
  	/*
! 	 * We don't want errors or noise from child tables, so we have to pass
! 	 * down a modified command.
  	 */
! 	if (recurse)
  	{
! 		AlterTableCmd *childCmd = copyObject(cmd);
  
! 		childCmd->subtype = AT_DropConstraintQuietly;
! 		ATSimpleRecursion(wqueue, rel, childCmd, recurse);
! 	}
! }
  
! static void
! ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior, bool quiet)
! {
! 	int			deleted;
  
! 	deleted = RemoveRelConstraints(rel, constrName, behavior);
  
! 	if (!quiet)
! 	{
! 		/* If zero constraints deleted, complain */
! 		if (deleted == 0)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_UNDEFINED_OBJECT),
! 					 errmsg("constraint \"%s\" does not exist",
! 							constrName)));
! 		/* Otherwise if more than one constraint deleted, notify */
! 		else if (deleted > 1)
! 			ereport(NOTICE,
! 					(errmsg("multiple constraints named \"%s\" were dropped",
! 							constrName)));
  	}
  }
  
  /*
--- 4984,5101 ----
   * ALTER TABLE DROP CONSTRAINT
   */
  static void
! ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior,
! 					 bool recurse, bool recursing)
  {
+ 	HeapTuple	tuple;
+ 	List		*children;
+ 	Relation	conrel;
+ 	TupleDesc	tupdesc;
+ 	Oid		relid;
+ 	Form_pg_constraint con;
+ 
+ 	relid = RelationGetRelid(rel);
+ 	conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
+ 	tupdesc = RelationGetDescr(conrel);
+ 
+ 	if (recursing)
+ 		ATSimplePermissions(rel, false);
+ 
+ 	tuple = SearchSysCache(CONSTRNAME,
+ 						   ObjectIdGetDatum(relid),
+ 						   CStringGetDatum(constrName),
+ 						   0, 0);
+ 
+ 	if (!HeapTupleIsValid(tuple))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_COLUMN),
+ 				 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
+ 						constrName, RelationGetRelationName(rel))));
+ 
+ 	con = (Form_pg_constraint) GETSTRUCT(tuple);
+ 
+ 	/* Don't drop inherited constraints */
+ 	if (con->coninhcount > 0 && !recursing)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ 				 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
+ 						constrName,
+ 						RelationGetRelationName(rel))));
+ 
+ 	ReleaseSysCache(tuple);
+ 
  	/*
! 	 * Propagate to children as appropriate.  Unlike most other ALTER
! 	 * 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.
  	 */
! 	children = find_inheritance_children(RelationGetRelid(rel));
! 
! 	if (children)
  	{
! 		ListCell   *child;
  
! 		foreach(child, children)
! 		{
! 			Oid			childrelid = lfirst_oid(child);
! 			Relation	childrel;
! 			Form_pg_constraint childcon;
  
! 			childrel = heap_open(childrelid, AccessExclusiveLock);
! 			CheckTableNotInUse(childrel, "ALTER TABLE");
  
! 			tuple = SearchSysCacheCopy(CONSTRNAME,
! 								   ObjectIdGetDatum(childrelid),
! 								   CStringGetDatum(constrName),
! 								   0, 0);
! 			if (!HeapTupleIsValid(tuple))
! 				elog(ERROR, "cache lookup failed for constraint \"%s\" of relation %u",
! 					 constrName, childrelid);
! 
! 			childcon = (Form_pg_constraint) GETSTRUCT(tuple);
! 
! 			if (childcon->coninhcount <= 0)
! 				elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
! 					 childrelid, constrName);
  
! 			if (recurse)
! 			{
! 				if (childcon->coninhcount == 1 && !childcon->conislocal)
! 				{
! 					ATExecDropConstraint(childrel, constrName, behavior, true, true);
! 				}
! 				else
! 				{
! 					childcon->coninhcount--;
! 
! 					simple_heap_update(conrel, &tuple->t_self, tuple);
! 					CatalogUpdateIndexes(conrel, tuple);
! 				}
! 			}
! 			else
! 			{
! 				/*
! 				 * If we were told to drop ONLY in this table (no recursion),
! 				 * we need to mark the inheritors' attribute as locally
! 				 * defined rather than inherited.
! 				 */
! 
! 				childcon->coninhcount--;
! 				childcon->conislocal = true;
! 
! 				simple_heap_update(conrel, &tuple->t_self, tuple);
! 				CatalogUpdateIndexes(conrel, tuple);
! 			}
! 
! 			heap_freetuple(tuple);
! 			heap_close(childrel, AccessExclusiveLock);
! 		}
  	}
+ 
+ 	RemoveRelConstraints(rel, constrName, behavior);
+ 
+ 	heap_close(conrel, RowExclusiveLock);
  }
  
  /*
***************
*** 6255,6260 ****
--- 6503,6525 ----
  }
  
  /*
+  * See if two constraints are "functionally" equivalent, but not necessarily equal
+  */
+ static bool
+ ConstraintIsEquiv(const HeapTuple a, const HeapTuple b, const TupleDesc tupleDesc)
+ {
+ 	const Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
+ 	const Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
+ 
+ 	if (acon->condeferrable != bcon->condeferrable ||
+ 		acon->condeferred != bcon->condeferred ||
+ 			strcmp(decompile_conbin(a, tupleDesc),
+ 				   decompile_conbin(b, tupleDesc)) != 0)
+ 		return 0;
+ 	else
+ 		return 1;
+ }
+ /*
   * Check columns in child table match up with columns in parent, and increment
   * their attinhcount.
   *
***************
*** 6348,6356 ****
   * make it possible to ensure no records are mistakenly inserted into the
   * master in partitioned tables rather than the appropriate child.
   *
-  * XXX This is O(N^2) which may be an issue with tables with hundreds of
-  * constraints. As long as tables have more like 10 constraints it shouldn't be
-  * a problem though. Even 100 constraints ought not be the end of the world.
   */
  static void
  MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
--- 6613,6618 ----
***************
*** 6360,6445 ****
  	SysScanDesc scan;
  	ScanKeyData key;
  	HeapTuple	constraintTuple;
- 	ListCell   *elem;
- 	List	   *constraints;
  
! 	/* First gather up the child's constraint definitions */
! 	catalogRelation = heap_open(ConstraintRelationId, AccessShareLock);
  	tupleDesc = RelationGetDescr(catalogRelation);
  
  	ScanKeyInit(&key,
  				Anum_pg_constraint_conrelid,
  				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(child_rel)));
  	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
  							  true, SnapshotNow, 1, &key);
  
- 	constraints = NIL;
- 	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
- 	{
- 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
- 
- 		if (con->contype != CONSTRAINT_CHECK)
- 			continue;
- 
- 		constraints = lappend(constraints, heap_copytuple(constraintTuple));
- 	}
- 
- 	systable_endscan(scan);
- 
- 	/* Then scan through the parent's constraints looking for matches */
- 	ScanKeyInit(&key,
- 				Anum_pg_constraint_conrelid,
- 				BTEqualStrategyNumber, F_OIDEQ,
- 				ObjectIdGetDatum(RelationGetRelid(parent_rel)));
- 	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId, true,
- 							  SnapshotNow, 1, &key);
- 
  	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
  	{
  		Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
- 		bool		found = false;
- 		Form_pg_constraint child_con = NULL;
- 		HeapTuple	child_contuple = NULL;
  
  		if (parent_con->contype != CONSTRAINT_CHECK)
  			continue;
  
! 		foreach(elem, constraints)
! 		{
! 			child_contuple = (HeapTuple) lfirst(elem);
! 			child_con = (Form_pg_constraint) GETSTRUCT(child_contuple);
! 			if (strcmp(NameStr(parent_con->conname),
! 					   NameStr(child_con->conname)) == 0)
! 			{
! 				found = true;
! 				break;
! 			}
! 		}
  
! 		if (!found)
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("child table is missing constraint \"%s\"",
  							NameStr(parent_con->conname))));
  
! 		if (parent_con->condeferrable != child_con->condeferrable ||
! 			parent_con->condeferred != child_con->condeferred ||
! 			strcmp(decompile_conbin(constraintTuple, tupleDesc),
! 				   decompile_conbin(child_contuple, tupleDesc)) != 0)
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("constraint definition for check constraint \"%s\" does not match",
  							NameStr(parent_con->conname))));
  
! 		/*
! 		 * TODO: add conislocal,coninhcount to constraints. This is where we
! 		 * would have to bump them just like attributes
! 		 */
  	}
  
  	systable_endscan(scan);
! 	heap_close(catalogRelation, AccessShareLock);
  }
  
  /*
--- 6622,6675 ----
  	SysScanDesc scan;
  	ScanKeyData key;
  	HeapTuple	constraintTuple;
  
! 	catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
  	tupleDesc = RelationGetDescr(catalogRelation);
  
  	ScanKeyInit(&key,
  				Anum_pg_constraint_conrelid,
  				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(parent_rel)));
  	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
  							  true, SnapshotNow, 1, &key);
  
  	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
  	{
+ 		HeapTuple childTuple;
+ 		Form_pg_constraint child_con;
  		Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
  
  		if (parent_con->contype != CONSTRAINT_CHECK)
  			continue;
  
! 		childTuple = SearchSysCacheCopy(CONSTRNAME,
! 									ObjectIdGetDatum(RelationGetRelid(child_rel)),
! 									CStringGetDatum(NameStr(parent_con->conname)),
! 									0, 0);
  
! 		if (!HeapTupleIsValid(childTuple))
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("child table is missing constraint \"%s\"",
  							NameStr(parent_con->conname))));
  
! 		child_con = (Form_pg_constraint) GETSTRUCT(childTuple);
! 
! 		if (!ConstraintIsEquiv(constraintTuple, childTuple, tupleDesc))
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("constraint definition for check constraint \"%s\" does not match",
  							NameStr(parent_con->conname))));
  
! 		/* increment coninhcount */
! 		child_con->coninhcount++;
! 		simple_heap_update(catalogRelation, &childTuple->t_self, childTuple);
! 		CatalogUpdateIndexes(catalogRelation, childTuple);
! 		heap_freetuple(childTuple);
  	}
  
  	systable_endscan(scan);
! 	heap_close(catalogRelation, RowExclusiveLock);
  }
  
  /*
***************
*** 6464,6471 ****
--- 6694,6703 ----
  	ScanKeyData key[3];
  	HeapTuple	inheritsTuple,
  				attributeTuple,
+ 				constraintTuple,
  				depTuple;
  	bool		found = false;
+ 	TupleDesc	tupdesc;
  
  	/*
  	 * AccessShareLock on the parent is probably enough, seeing that DROP
***************
*** 6553,6558 ****
--- 6785,6825 ----
  	systable_endscan(scan);
  	heap_close(catalogRelation, RowExclusiveLock);
  
+ 	catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
+ 	ScanKeyInit(&key[0],
+ 				Anum_pg_constraint_conrelid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(RelationGetRelid(rel)));
+ 	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
+ 							  true, SnapshotNow, 1, key);
+ 
+ 	tupdesc = RelationGetDescr(catalogRelation);
+ 
+ 	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
+ 	{
+ 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
+ 
+ 		if (con->contype != CONSTRAINT_CHECK)
+ 			continue;
+ 
+ 		if (con->coninhcount > 0)
+ 		{
+ 			HeapTuple	copyTuple = heap_copytuple(constraintTuple);
+ 			Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
+ 
+ 			copy_con->coninhcount--;
+ 			if (copy_con->coninhcount == 0)
+ 				copy_con->conislocal = true;
+ 
+ 			simple_heap_update(catalogRelation, &copyTuple->t_self, copyTuple);
+ 			CatalogUpdateIndexes(catalogRelation, copyTuple);
+ 			heap_freetuple(copyTuple);
+ 		}
+ 	}
+ 
+ 	systable_endscan(scan);
+ 	heap_close(catalogRelation, RowExclusiveLock);
+ 
  	/*
  	 * Drop the dependency
  	 *
Index: src/backend/commands/typecmds.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/commands/typecmds.c,v
retrieving revision 1.117
diff -c -r1.117 typecmds.c
*** src/backend/commands/typecmds.c	27 Mar 2008 03:57:33 -0000	1.117
--- src/backend/commands/typecmds.c	1 Apr 2008 07:32:15 -0000
***************
*** 2206,2212 ****
  						  InvalidOid,
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc);		/* Source form check constraint */
  
  	/*
  	 * Return the compiled constraint expression so the calling routine can
--- 2206,2214 ----
  						  InvalidOid,
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc,	/* Source form check constraint */
! 						  true, /* is local */
! 						  0); /* inhcount */
  
  	/*
  	 * Return the compiled constraint expression so the calling routine can
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.390
diff -c -r1.390 copyfuncs.c
*** src/backend/nodes/copyfuncs.c	21 Mar 2008 22:41:48 -0000	1.390
--- src/backend/nodes/copyfuncs.c	1 Apr 2008 07:32:15 -0000
***************
*** 1814,1819 ****
--- 1814,1821 ----
  	COPY_NODE_FIELD(keys);
  	COPY_NODE_FIELD(options);
  	COPY_STRING_FIELD(indexspace);
+ 	COPY_SCALAR_FIELD(inhcount);
+ 	COPY_SCALAR_FIELD(is_local);
  
  	return newnode;
  }
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.320
diff -c -r1.320 equalfuncs.c
*** src/backend/nodes/equalfuncs.c	21 Mar 2008 22:41:48 -0000	1.320
--- src/backend/nodes/equalfuncs.c	1 Apr 2008 07:32:15 -0000
***************
*** 1839,1844 ****
--- 1839,1846 ----
  	COMPARE_NODE_FIELD(keys);
  	COMPARE_NODE_FIELD(options);
  	COMPARE_STRING_FIELD(indexspace);
+ 	COMPARE_SCALAR_FIELD(inhcount);
+ 	COMPARE_SCALAR_FIELD(is_local);
  
  	return true;
  }
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.324
diff -c -r1.324 outfuncs.c
*** src/backend/nodes/outfuncs.c	21 Mar 2008 22:41:48 -0000	1.324
--- src/backend/nodes/outfuncs.c	1 Apr 2008 07:32:15 -0000
***************
*** 2033,2038 ****
--- 2033,2041 ----
  			appendStringInfo(str, "<unrecognized_constraint>");
  			break;
  	}
+ 
+ 	WRITE_INT_FIELD(inhcount);
+ 	WRITE_BOOL_FIELD(is_local);
  }
  
  static void
Index: src/backend/parser/gram.y
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.611
diff -c -r2.611 gram.y
*** src/backend/parser/gram.y	28 Mar 2008 00:21:55 -0000	2.611
--- src/backend/parser/gram.y	1 Apr 2008 07:32:15 -0000
***************
*** 2068,2073 ****
--- 2068,2075 ----
  					n->cooked_expr = NULL;
  					n->keys = NULL;
  					n->indexspace = NULL;
+ 					n->is_local = true;
+ 					n->inhcount = 0;
  					$$ = (Node *)n;
  				}
  			| DEFAULT b_expr
***************
*** 2208,2213 ****
--- 2210,2217 ----
  					n->raw_expr = $3;
  					n->cooked_expr = NULL;
  					n->indexspace = NULL;
+ 					n->is_local = true;
+ 					n->inhcount = 0;
  					$$ = (Node *)n;
  				}
  			| UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
Index: src/backend/parser/parse_utilcmd.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/parser/parse_utilcmd.c,v
retrieving revision 2.11
diff -c -r2.11 parse_utilcmd.c
*** src/backend/parser/parse_utilcmd.c	25 Mar 2008 22:42:43 -0000	2.11
--- src/backend/parser/parse_utilcmd.c	1 Apr 2008 07:32:15 -0000
***************
*** 685,690 ****
--- 685,692 ----
  			n->cooked_expr = nodeToString(ccbin_node);
  			n->indexspace = NULL;
  			cxt->ckconstraints = lappend(cxt->ckconstraints, (Node *) n);
+ 			n->is_local = true;
+ 			n->inhcount = 0;
  		}
  	}
  
Index: src/backend/utils/cache/catcache.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/utils/cache/catcache.c,v
retrieving revision 1.142
diff -c -r1.142 catcache.c
*** src/backend/utils/cache/catcache.c	26 Mar 2008 21:10:39 -0000	1.142
--- src/backend/utils/cache/catcache.c	1 Apr 2008 07:32:16 -0000
***************
*** 962,968 ****
  		else
  		{
  			if (cache->cc_key[i] != ObjectIdAttributeNumber)
! 				elog(FATAL, "only sys attr supported in caches is OID");
  			keytype = OIDOID;
  		}
  
--- 962,968 ----
  		else
  		{
  			if (cache->cc_key[i] != ObjectIdAttributeNumber)
! 				elog(FATAL, "only sys attr supported in caches is OID for relation: %s", cache->cc_relname);
  			keytype = OIDOID;
  		}
  
Index: src/backend/utils/cache/syscache.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/utils/cache/syscache.c,v
retrieving revision 1.114
diff -c -r1.114 syscache.c
*** src/backend/utils/cache/syscache.c	1 Jan 2008 19:45:53 -0000	1.114
--- src/backend/utils/cache/syscache.c	1 Apr 2008 07:32:16 -0000
***************
*** 305,310 ****
--- 305,322 ----
  		},
  		128
  	},
+ 	{ConstraintRelationId,		// CONSTRNAME
+ 		ConstraintRelidNameIndexId,
+ 		Anum_pg_constraint_conrelid,
+ 		2,
+ 		{
+ 			Anum_pg_constraint_conrelid,
+ 			Anum_pg_constraint_conname,
+ 			0,
+ 			0
+ 		},
+ 		256,
+ 	},
  	{ConstraintRelationId,		/* CONSTROID */
  		ConstraintOidIndexId,
  		0,
Index: src/bin/pg_dump/common.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/bin/pg_dump/common.c,v
retrieving revision 1.103
diff -c -r1.103 common.c
*** src/bin/pg_dump/common.c	27 Mar 2008 03:57:33 -0000	1.103
--- src/bin/pg_dump/common.c	1 Apr 2008 07:32:16 -0000
***************
*** 62,69 ****
  
  static void flagInhTables(TableInfo *tbinfo, int numTables,
  			  InhInfo *inhinfo, int numInherits);
! static void flagInhAttrs(TableInfo *tbinfo, int numTables,
! 			 InhInfo *inhinfo, int numInherits);
  static DumpableObject **buildIndexArray(void *objArray, int numObjs,
  				Size objSize);
  static int	DOCatalogIdCompare(const void *p1, const void *p2);
--- 62,70 ----
  
  static void flagInhTables(TableInfo *tbinfo, int numTables,
  			  InhInfo *inhinfo, int numInherits);
! static void flagInhAttrs(TableInfo *tblinfo, int numTables,
! 			 InhInfo *inhinfo, int numInherits,
! 			 int remoteVersion);
  static DumpableObject **buildIndexArray(void *objArray, int numObjs,
  				Size objSize);
  static int	DOCatalogIdCompare(const void *p1, const void *p2);
***************
*** 77,83 ****
   *	  Collect information about all potentially dumpable objects
   */
  TableInfo *
! getSchemaData(int *numTablesPtr)
  {
  	NamespaceInfo *nsinfo;
  	AggInfo    *agginfo;
--- 78,84 ----
   *	  Collect information about all potentially dumpable objects
   */
  TableInfo *
! getSchemaData(int *numTablesPtr, int remoteVersion)
  {
  	NamespaceInfo *nsinfo;
  	AggInfo    *agginfo;
***************
*** 191,197 ****
  
  	if (g_verbose)
  		write_msg(NULL, "flagging inherited columns in subtables\n");
! 	flagInhAttrs(tblinfo, numTables, inhinfo, numInherits);
  
  	if (g_verbose)
  		write_msg(NULL, "reading indexes\n");
--- 192,198 ----
  
  	if (g_verbose)
  		write_msg(NULL, "flagging inherited columns in subtables\n");
! 	flagInhAttrs(tblinfo, numTables, inhinfo, numInherits, remoteVersion);
  
  	if (g_verbose)
  		write_msg(NULL, "reading indexes\n");
***************
*** 258,264 ****
   */
  static void
  flagInhAttrs(TableInfo *tblinfo, int numTables,
! 			 InhInfo *inhinfo, int numInherits)
  {
  	int			i,
  				j,
--- 259,266 ----
   */
  static void
  flagInhAttrs(TableInfo *tblinfo, int numTables,
! 			 InhInfo *inhinfo, int numInherits,
! 			 int remoteVersion)
  {
  	int			i,
  				j,
***************
*** 423,454 ****
  		 * different schemas, because reverse-listing of function calls may
  		 * produce different text (schema-qualified or not) depending on
  		 * search path.  We really need a more bulletproof way of detecting
! 		 * inherited constraints --- pg_constraint should record this
  		 * explicitly!
  		 */
! 		for (j = 0; j < tbinfo->ncheck; j++)
  		{
! 			ConstraintInfo *constr;
! 
! 			constr = &(tbinfo->checkexprs[j]);
! 
! 			for (k = 0; k < numParents; k++)
  			{
! 				int			l;
  
! 				parent = parents[k];
! 				for (l = 0; l < parent->ncheck; l++)
  				{
! 					ConstraintInfo *pconstr = &(parent->checkexprs[l]);
  
! 					if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0)
  					{
! 						constr->coninherited = true;
! 						break;
  					}
  				}
- 				if (constr->coninherited)
- 					break;
  			}
  		}
  	}
--- 425,460 ----
  		 * different schemas, because reverse-listing of function calls may
  		 * produce different text (schema-qualified or not) depending on
  		 * search path.  We really need a more bulletproof way of detecting
! 		 * inherited constraints 
! 		 * The above is pre-80300 behaviour, now pg_constraint records this
  		 * explicitly!
  		 */
! 		if (remoteVersion <= 80300)
  		{
! 			for (j = 0; j < tbinfo->ncheck; j++)
  			{
! 				ConstraintInfo *constr;
  
! 				constr = &(tbinfo->checkexprs[j]);
! 
! 				for (k = 0; k < numParents; k++)
  				{
! 					int			l;
  
! 					parent = parents[k];
! 					for (l = 0; l < parent->ncheck; l++)
  					{
! 						ConstraintInfo *pconstr = &(parent->checkexprs[l]);
! 
! 						if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0)
! 						{
! 							constr->coninherited = true;
! 							break;
! 						}
  					}
+ 					if (constr->coninherited)
+ 						break;
  				}
  			}
  		}
  	}
Index: src/bin/pg_dump/pg_dump.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/bin/pg_dump/pg_dump.c,v
retrieving revision 1.486
diff -c -r1.486 pg_dump.c
*** src/bin/pg_dump/pg_dump.c	28 Mar 2008 00:21:56 -0000	1.486
--- src/bin/pg_dump/pg_dump.c	1 Apr 2008 07:32:16 -0000
***************
*** 631,637 ****
  	 * Now scan the database and create DumpableObject structs for all the
  	 * objects we intend to dump.
  	 */
! 	tblinfo = getSchemaData(&numTables);
  
  	if (!schemaOnly)
  		getTableData(tblinfo, numTables, oids);
--- 631,637 ----
  	 * Now scan the database and create DumpableObject structs for all the
  	 * objects we intend to dump.
  	 */
! 	tblinfo = getSchemaData(&numTables, g_fout->remoteVersion);
  
  	if (!schemaOnly)
  		getTableData(tblinfo, numTables, oids);
***************
*** 4575,4584 ****
  						  tbinfo->dobj.name);
  
  			resetPQExpBuffer(q);
! 			if (g_fout->remoteVersion >= 70400)
  			{
  				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! 							"pg_catalog.pg_get_constraintdef(oid) AS consrc "
  								  "FROM pg_catalog.pg_constraint "
  								  "WHERE conrelid = '%u'::pg_catalog.oid "
  								  "   AND contype = 'c' "
--- 4575,4597 ----
  						  tbinfo->dobj.name);
  
  			resetPQExpBuffer(q);
! 			if (g_fout->remoteVersion > 80300)
  			{
+ 				/* 8.3 did not have conislocal attribute in pg_constraint */
  				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! 							"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
! 							"conislocal "
! 								  "FROM pg_catalog.pg_constraint "
! 								  "WHERE conrelid = '%u'::pg_catalog.oid "
! 								  "   AND contype = 'c' "
! 								  "ORDER BY conname",
! 								  tbinfo->dobj.catId.oid);
! 			}
! 			else if (g_fout->remoteVersion >= 70400)
! 			{
! 				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! 							"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
! 						  	"false as conislocal "
  								  "FROM pg_catalog.pg_constraint "
  								  "WHERE conrelid = '%u'::pg_catalog.oid "
  								  "   AND contype = 'c' "
***************
*** 4589,4595 ****
  			{
  				/* no pg_get_constraintdef, must use consrc */
  				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! 								  "'CHECK (' || consrc || ')' AS consrc "
  								  "FROM pg_catalog.pg_constraint "
  								  "WHERE conrelid = '%u'::pg_catalog.oid "
  								  "   AND contype = 'c' "
--- 4602,4609 ----
  			{
  				/* no pg_get_constraintdef, must use consrc */
  				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! 								  "'CHECK (' || consrc || ')' AS consrc, "
! 								  "false as conislocal "
  								  "FROM pg_catalog.pg_constraint "
  								  "WHERE conrelid = '%u'::pg_catalog.oid "
  								  "   AND contype = 'c' "
***************
*** 4601,4607 ****
  				/* 7.2 did not have OIDs in pg_relcheck */
  				appendPQExpBuffer(q, "SELECT tableoid, 0 as oid, "
  								  "rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc "
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
--- 4615,4622 ----
  				/* 7.2 did not have OIDs in pg_relcheck */
  				appendPQExpBuffer(q, "SELECT tableoid, 0 as oid, "
  								  "rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc, "
! 								  "false as conislocal "
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
***************
*** 4611,4617 ****
  			{
  				appendPQExpBuffer(q, "SELECT tableoid, oid, "
  								  "rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc "
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
--- 4626,4633 ----
  			{
  				appendPQExpBuffer(q, "SELECT tableoid, oid, "
  								  "rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc, "
! 								  "false as conislocal "
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
***************
*** 4623,4629 ****
  				appendPQExpBuffer(q, "SELECT "
  								  "(SELECT oid FROM pg_class WHERE relname = 'pg_relcheck') AS tableoid, "
  								  "oid, rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc "
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
--- 4639,4646 ----
  				appendPQExpBuffer(q, "SELECT "
  								  "(SELECT oid FROM pg_class WHERE relname = 'pg_relcheck') AS tableoid, "
  								  "oid, rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc, "
! 								  " false as conislocal"
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
***************
*** 4657,4664 ****
  				constrs[j].contype = 'c';
  				constrs[j].condef = strdup(PQgetvalue(res, j, 3));
  				constrs[j].conindex = 0;
- 				constrs[j].coninherited = false;
  				constrs[j].separate = false;
  
  				constrs[j].dobj.dump = tbinfo->dobj.dump;
  
--- 4674,4682 ----
  				constrs[j].contype = 'c';
  				constrs[j].condef = strdup(PQgetvalue(res, j, 3));
  				constrs[j].conindex = 0;
  				constrs[j].separate = false;
+ 				/* depending on conislocal value, set coninherited */
+ 				constrs[j].coninherited = (PQgetvalue(res, j, 4)[0] == 't');
  
  				constrs[j].dobj.dump = tbinfo->dobj.dump;
  
***************
*** 4673,4680 ****
  
  				/*
  				 * If the constraint is inherited, this will be detected
! 				 * later.  We also detect later if the constraint must be
! 				 * split out from the table definition.
  				 */
  			}
  			PQclear(res);
--- 4691,4698 ----
  
  				/*
  				 * If the constraint is inherited, this will be detected
! 				 * later for pre-80300 case. We also detect later if the constraint
! 				 * must be split out from the table definition.
  				 */
  			}
  			PQclear(res);
Index: src/bin/pg_dump/pg_dump.h
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/bin/pg_dump/pg_dump.h,v
retrieving revision 1.139
diff -c -r1.139 pg_dump.h
*** src/bin/pg_dump/pg_dump.h	1 Jan 2008 19:45:55 -0000	1.139
--- src/bin/pg_dump/pg_dump.h	1 Apr 2008 07:32:16 -0000
***************
*** 429,435 ****
   *	common utility functions
   */
  
! extern TableInfo *getSchemaData(int *numTablesPtr);
  
  typedef enum _OidOptions
  {
--- 429,435 ----
   *	common utility functions
   */
  
! extern TableInfo *getSchemaData(int *numTablesPtr, int remoteVersion);
  
  typedef enum _OidOptions
  {
Index: src/include/access/tupdesc.h
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/include/access/tupdesc.h,v
retrieving revision 1.53
diff -c -r1.53 tupdesc.h
*** src/include/access/tupdesc.h	1 Jan 2008 19:45:56 -0000	1.53
--- src/include/access/tupdesc.h	1 Apr 2008 07:32:16 -0000
***************
*** 27,34 ****
  
  typedef struct constrCheck
  {
! 	char	   *ccname;
! 	char	   *ccbin;			/* nodeToString representation of expr */
  } ConstrCheck;
  
  /* This structure contains constraints of a tuple */
--- 27,36 ----
  
  typedef struct constrCheck
  {
! 	char	*ccname;
! 	char	*ccbin;			/* nodeToString representation of expr */
! 	bool	is_local;
! 	int		inhcount;
  } ConstrCheck;
  
  /* This structure contains constraints of a tuple */
Index: src/include/catalog/catversion.h
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/include/catalog/catversion.h,v
retrieving revision 1.444
diff -c -r1.444 catversion.h
*** src/include/catalog/catversion.h	23 Mar 2008 00:24:19 -0000	1.444
--- src/include/catalog/catversion.h	1 Apr 2008 07:32:16 -0000
***************
*** 53,58 ****
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	200803222
  
  #endif
--- 53,58 ----
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	200803290
  
  #endif
Index: src/include/catalog/indexing.h
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/include/catalog/indexing.h,v
retrieving revision 1.102
diff -c -r1.102 indexing.h
*** src/include/catalog/indexing.h	1 Jan 2008 19:45:56 -0000	1.102
--- src/include/catalog/indexing.h	1 Apr 2008 07:32:16 -0000
***************
*** 121,126 ****
--- 121,128 ----
  #define ConstraintTypidIndexId	2666
  DECLARE_UNIQUE_INDEX(pg_constraint_oid_index, 2667, on pg_constraint using btree(oid oid_ops));
  #define ConstraintOidIndexId  2667
+ DECLARE_UNIQUE_INDEX(pg_constraint_relid_coname_index, 2700, on pg_constraint using btree(conrelid oid_ops, conname name_ops));
+ #define ConstraintRelidNameIndexId  2700
  
  DECLARE_UNIQUE_INDEX(pg_conversion_default_index, 2668, on pg_conversion using btree(connamespace oid_ops, conforencoding int4_ops, contoencoding int4_ops, oid oid_ops));
  #define ConversionDefaultIndexId  2668
Index: src/include/catalog/pg_constraint.h
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/include/catalog/pg_constraint.h,v
retrieving revision 1.28
diff -c -r1.28 pg_constraint.h
*** src/include/catalog/pg_constraint.h	27 Mar 2008 03:57:34 -0000	1.28
--- src/include/catalog/pg_constraint.h	1 Apr 2008 07:32:16 -0000
***************
*** 71,76 ****
--- 71,82 ----
  	char		confdeltype;	/* foreign key's ON DELETE action */
  	char		confmatchtype;	/* foreign key's match type */
  
+ 	/* Has a local definition (hence, do not drop when coninhcount is 0) */
+ 	bool 		conislocal;
+ 
+ 	/* Number of times inherited from direct parent relation(s) */
+ 	int4 		coninhcount;
+ 
  	/*
  	 * VARIABLE LENGTH FIELDS start here.  These fields may be NULL, too.
  	 */
***************
*** 125,150 ****
   *		compiler constants for pg_constraint
   * ----------------
   */
! #define Natts_pg_constraint					18
! #define Anum_pg_constraint_conname			1
! #define Anum_pg_constraint_connamespace		2
! #define Anum_pg_constraint_contype			3
! #define Anum_pg_constraint_condeferrable	4
! #define Anum_pg_constraint_condeferred		5
! #define Anum_pg_constraint_conrelid			6
! #define Anum_pg_constraint_contypid			7
! #define Anum_pg_constraint_confrelid		8
! #define Anum_pg_constraint_confupdtype		9
! #define Anum_pg_constraint_confdeltype		10
! #define Anum_pg_constraint_confmatchtype	11
! #define Anum_pg_constraint_conkey			12
! #define Anum_pg_constraint_confkey			13
! #define Anum_pg_constraint_conpfeqop		14
! #define Anum_pg_constraint_conppeqop		15
! #define Anum_pg_constraint_conffeqop		16
! #define Anum_pg_constraint_conbin			17
! #define Anum_pg_constraint_consrc			18
! 
  
  /* Valid values for contype */
  #define CONSTRAINT_CHECK			'c'
--- 131,157 ----
   *		compiler constants for pg_constraint
   * ----------------
   */
! #define Natts_pg_constraint					20
! #define	Anum_pg_constraint_conname			1
! #define	Anum_pg_constraint_connamespace		2
! #define	Anum_pg_constraint_contype			3
! #define	Anum_pg_constraint_condeferrable	4
! #define	Anum_pg_constraint_condeferred		5
! #define	Anum_pg_constraint_conrelid			6
! #define	Anum_pg_constraint_contypid			7
! #define	Anum_pg_constraint_confrelid		8
! #define	Anum_pg_constraint_confupdtype		9
! #define	Anum_pg_constraint_confdeltype		10
! #define	Anum_pg_constraint_confmatchtype	11
! #define	Anum_pg_constraint_conislocal		12
! #define	Anum_pg_constraint_coninhcount		13
! #define	Anum_pg_constraint_conkey			14
! #define	Anum_pg_constraint_confkey			15
! #define	Anum_pg_constraint_conpfeqop		16
! #define	Anum_pg_constraint_conppeqop		17
! #define	Anum_pg_constraint_conffeqop		18
! #define	Anum_pg_constraint_conbin			19
! #define	Anum_pg_constraint_consrc			20
  
  /* Valid values for contype */
  #define CONSTRAINT_CHECK			'c'
***************
*** 192,198 ****
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc);
  
  extern void RemoveConstraintById(Oid conId);
  extern void RenameConstraintById(Oid conId, const char *newname);
--- 199,207 ----
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc,
! 					  const bool conIsLocal,
! 					  const int conInhCout);
  
  extern void RemoveConstraintById(Oid conId);
  extern void RenameConstraintById(Oid conId, const char *newname);
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.361
diff -c -r1.361 parsenodes.h
*** src/include/nodes/parsenodes.h	21 Mar 2008 22:41:48 -0000	1.361
--- src/include/nodes/parsenodes.h	1 Apr 2008 07:32:16 -0000
***************
*** 902,908 ****
  	AT_ProcessedConstraint,		/* pre-processed add constraint (local in
  								 * parser/parse_utilcmd.c) */
  	AT_DropConstraint,			/* drop constraint */
! 	AT_DropConstraintQuietly,	/* drop constraint, no error/warning (local in
  								 * commands/tablecmds.c) */
  	AT_AlterColumnType,			/* alter column type */
  	AT_ChangeOwner,				/* change owner */
--- 902,908 ----
  	AT_ProcessedConstraint,		/* pre-processed add constraint (local in
  								 * parser/parse_utilcmd.c) */
  	AT_DropConstraint,			/* drop constraint */
! 	AT_DropConstraintRecurse,	/* drop constraint recursion (local in
  								 * commands/tablecmds.c) */
  	AT_AlterColumnType,			/* alter column type */
  	AT_ChangeOwner,				/* change owner */
***************
*** 1165,1170 ****
--- 1165,1172 ----
  	List	   *options;		/* options from WITH clause */
  	char	   *indexspace;		/* index tablespace for PKEY/UNIQUE
  								 * constraints; NULL for default */
+ 	bool is_local;
+ 	int inhcount;
  } Constraint;
  
  /* ----------
Index: src/include/utils/syscache.h
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/include/utils/syscache.h,v
retrieving revision 1.71
diff -c -r1.71 syscache.h
*** src/include/utils/syscache.h	1 Jan 2008 19:45:59 -0000	1.71
--- src/include/utils/syscache.h	1 Apr 2008 07:32:16 -0000
***************
*** 28,81 ****
   *		Keep them in alphabetical order.
   */
  
! #define AGGFNOID			0
! #define AMNAME				1
! #define AMOID				2
! #define AMOPOPID			3
! #define AMOPSTRATEGY		4
! #define AMPROCNUM			5
! #define ATTNAME				6
! #define ATTNUM				7
! #define AUTHMEMMEMROLE		8
! #define AUTHMEMROLEMEM		9
! #define AUTHNAME			10
! #define AUTHOID				11
! #define CASTSOURCETARGET	12
! #define CLAAMNAMENSP		13
! #define CLAOID				14
! #define CONDEFAULT			15
! #define CONNAMENSP			16
! #define CONSTROID			17
! #define CONVOID				18
! #define DATABASEOID			19
! #define ENUMOID				20
! #define ENUMTYPOIDNAME		21
! #define INDEXRELID			22
! #define LANGNAME			23
! #define LANGOID				24
! #define NAMESPACENAME		25
! #define NAMESPACEOID		26
! #define OPERNAMENSP			27
! #define OPEROID				28
! #define OPFAMILYAMNAMENSP	29
! #define OPFAMILYOID			30
! #define PROCNAMEARGSNSP		31
! #define PROCOID				32
! #define RELNAMENSP			33
! #define RELOID				34
! #define RULERELNAME			35
! #define STATRELATT			36
! #define TSCONFIGMAP			37
! #define TSCONFIGNAMENSP		38
! #define TSCONFIGOID			39
! #define TSDICTNAMENSP		40
! #define TSDICTOID			41
! #define TSPARSERNAMENSP		42
! #define TSPARSEROID			43
! #define TSTEMPLATENAMENSP	44
! #define TSTEMPLATEOID		45
! #define TYPENAMENSP			46
! #define TYPEOID				47
  
  extern void InitCatalogCache(void);
  extern void InitCatalogCachePhase2(void);
--- 28,84 ----
   *		Keep them in alphabetical order.
   */
  
! enum SysCacheIdent {
! 	AGGFNOID = 0,
! 	AMNAME,
! 	AMOID,
! 	AMOPOPID,
! 	AMOPSTRATEGY,
! 	AMPROCNUM,
! 	ATTNAME,
! 	ATTNUM,
! 	AUTHMEMMEMROLE,
! 	AUTHMEMROLEMEM,
! 	AUTHNAME,
! 	AUTHOID,
! 	CASTSOURCETARGET,
! 	CLAAMNAMENSP,
! 	CLAOID,
! 	CONDEFAULT,
! 	CONNAMENSP,
! 	CONSTRNAME,
! 	CONSTROID,
! 	CONVOID,
! 	DATABASEOID,
! 	ENUMOID,
! 	ENUMTYPOIDNAME,
! 	INDEXRELID,
! 	LANGNAME,
! 	LANGOID,
! 	NAMESPACENAME,
! 	NAMESPACEOID,
! 	OPERNAMENSP,
! 	OPEROID,
! 	OPFAMILYAMNAMENSP,
! 	OPFAMILYOID,
! 	PROCNAMEARGSNSP,
! 	PROCOID,
! 	RELNAMENSP,
! 	RELOID,
! 	RULERELNAME,
! 	STATRELATT,
! 	TSCONFIGMAP,
! 	TSCONFIGNAMENSP,
! 	TSCONFIGOID,
! 	TSDICTNAMENSP,
! 	TSDICTOID,
! 	TSPARSERNAMENSP,
! 	TSPARSEROID,
! 	TSTEMPLATENAMENSP,
! 	TSTEMPLATEOID,
! 	TYPENAMENSP,
! 	TYPEOID,
! };
  
  extern void InitCatalogCache(void);
  extern void InitCatalogCachePhase2(void);
Index: src/test/regress/expected/alter_table.out
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/test/regress/expected/alter_table.out,v
retrieving revision 1.104
diff -c -r1.104 alter_table.out
*** src/test/regress/expected/alter_table.out	29 Oct 2007 21:31:28 -0000	1.104
--- src/test/regress/expected/alter_table.out	1 Apr 2008 07:32:16 -0000
***************
*** 383,391 ****
  create table atacc2 (test2 int);
  create table atacc3 (test3 int) inherits (atacc1, atacc2);
  alter table only atacc2 add constraint foo check (test2>0);
  -- fail and then succeed on atacc2
  insert into atacc2 (test2) values (-3);
- ERROR:  new row for relation "atacc2" violates check constraint "foo"
  insert into atacc2 (test2) values (3);
  -- both succeed on atacc3
  insert into atacc3 (test2) values (-3);
--- 383,391 ----
  create table atacc2 (test2 int);
  create table atacc3 (test3 int) inherits (atacc1, atacc2);
  alter table only atacc2 add constraint foo check (test2>0);
+ ERROR:  constraint must be added to children tables too
  -- fail and then succeed on atacc2
  insert into atacc2 (test2) values (-3);
  insert into atacc2 (test2) values (3);
  -- both succeed on atacc3
  insert into atacc3 (test2) values (-3);
Index: src/test/regress/expected/inherit.out
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/test/regress/expected/inherit.out,v
retrieving revision 1.23
diff -c -r1.23 inherit.out
*** src/test/regress/expected/inherit.out	1 Dec 2007 23:44:44 -0000	1.23
--- src/test/regress/expected/inherit.out	1 Apr 2008 07:32:16 -0000
***************
*** 692,694 ****
--- 692,770 ----
  drop table c1;
  drop table p2;
  drop table p1;
+ CREATE TABLE ac (aa TEXT);
+ alter table ac add constraint ac_check check (aa is not null);
+ CREATE TABLE bc (bb TEXT) INHERITS (ac);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+  ac_check | c       | f          |           1 | (aa IS NOT NULL)
+ (2 rows)
+ 
+ alter table bc drop constraint ac_check;
+ ERROR:  cannot drop inherited constraint "ac_check" of relation "bc"
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname | contype | conislocal | coninhcount | consrc 
+ ---------+---------+------------+-------------+--------
+ (0 rows)
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+  ac_check | c       | f          |           1 | (aa IS NOT NULL)
+ (2 rows)
+ 
+ insert into ac (aa) values (NULL);
+ ERROR:  new row for relation "ac" violates check constraint "ac_check"
+ insert into bc (aa) values (NULL);
+ ERROR:  new row for relation "bc" violates check constraint "ac_check"
+ alter table bc drop constraint ac_check;
+ ERROR:  cannot drop inherited constraint "ac_check" of relation "bc"
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname | contype | conislocal | coninhcount | consrc 
+ ---------+---------+------------+-------------+--------
+ (0 rows)
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ alter table bc no inherit ac;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+ (2 rows)
+ 
+ alter table bc drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+ (1 row)
+ 
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname | contype | conislocal | coninhcount | consrc 
+ ---------+---------+------------+-------------+--------
+ (0 rows)
+ 
+ drop table bc;
+ drop table ac;
+ create table ac (a int constraint check_a check (a <> 0));
+ create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac);
+ NOTICE:  merging column "a" with inherited definition
+ NOTICE:  merging constraint "check_a" with inherited definition
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+  conname | contype | conislocal | coninhcount |  consrc  
+ ---------+---------+------------+-------------+----------
+  check_a | c       | t          |           0 | (a <> 0)
+  check_a | c       | f          |           1 | (a <> 0)
+  check_b | c       | t          |           0 | (b <> 0)
+ (3 rows)
+ 
+ drop table bc;
+ drop table ac;
Index: src/test/regress/sql/inherit.sql
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/test/regress/sql/inherit.sql,v
retrieving revision 1.11
diff -c -r1.11 inherit.sql
*** src/test/regress/sql/inherit.sql	17 Jul 2007 05:02:03 -0000	1.11
--- src/test/regress/sql/inherit.sql	1 Apr 2008 07:32:16 -0000
***************
*** 196,198 ****
--- 196,236 ----
  drop table c1;
  drop table p2;
  drop table p1;
+ 
+ CREATE TABLE ac (aa TEXT);
+ alter table ac add constraint ac_check check (aa is not null);
+ CREATE TABLE bc (bb TEXT) INHERITS (ac);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ 
+ alter table bc drop constraint ac_check;
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ 
+ insert into ac (aa) values (NULL);
+ insert into bc (aa) values (NULL);
+ 
+ alter table bc drop constraint ac_check;
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ alter table bc no inherit ac;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ alter table bc drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ 
+ drop table bc;
+ drop table ac;
+ 
+ create table ac (a int constraint check_a check (a <> 0));
+ create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname;
+ 
+ 
+ drop table bc;
+ drop table ac;
#6Alex Hunsaker
badalex@gmail.com
In reply to: NikhilS (#5)
1 attachment(s)
Re: [badalex@gmail.com: Re: [BUGS] Problem identifying constraints which should not be inherited]

On Tue, Apr 1, 2008 at 2:40 AM, NikhilS <nikkhils@gmail.com> wrote:

PFA, a revised version of Alex' patch. I have added the relevant pg_dump
related changes too. As I mentioned earlier, I still don't know whether
Alex' syscache related changes are necessary, but I will leave it to the
patch reviewers to decide :)

This combined patch meets the following TODOs:

* Add logic to disallow ADD CONSTRAINT ONLY to parent of an inheritance
hierarchy

* Add logic to mark inherited constraints in the children:
This is achieved by introducing bool "conislocal" and int4 "coninhcount"
attributes in pg_constraint. The behaviour of these 2 attributes is pretty
similar to attislocal and attinhcount attributes in pg_attribute.

* Add logic to disallow dropping inherited constraints directly on children
Obviously they will get dropped if a DROP CONSTRAINT is fired on the
parent. with recurse set to true (this is the default behaviour)

* Modify the pg_dump logic to use the new pg_constraint based attributes
logic for versions above 80300 (or should it be PG_VERSION_NUM 80400?).

Please direct comments/feedback towards both me and Alex.

Regards,
Nikhils
--
EnterpriseDB http://www.enterprisedb.com

Find attached v4 of the patch, changes since v3:

-updated/added catalog documentation for new coninhcount and conislocal columns.
-fixed some regression tests order by (causing them to fail randomly)
-some coding style fixes
-removed some unused vars

Please direct comments/feedback towards both me and NikhilS.

Attachments:

inherited_constraints_v4.patchapplication/octet-stream; name=inherited_constraints_v4.patchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index a15ac6c..8c56652 100644
*** a/doc/src/sgml/catalogs.sgml
--- /bdoc/src/sgml/catalogs.sgml
***************
*** 1908,1913 ****
--- 1908,1927 ----
       </row>
  
       <row>
+       <entry><structfield>conislocal</structfield></entry>
+       <entry><type>bool</type></entry>
+       <entry></entry>
+       <entry>This constraint is defined locally in the relation. Note that a constraint can be locally defined and inherited simultaneously</entry>
+      </row>
+ 
+      <row>
+       <entry><structfield>coninhcount</structfield></entry>
+       <entry><type>int4</type></entry>
+       <entry></entry>
+       <entry>The number of direct ancestors this constraint has. A constraint with a nonzero number of ancestors cannot be dropped nor renamed</entry>
+      </row>
+ 
+      <row>
        <entry><structfield>conkey</structfield></entry>
        <entry><type>int2[]</type></entry>
        <entry><literal><link linkend="catalog-pg-attribute"><structname>pg_attribute</structname></link>.attnum</></entry>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 3553c3b..783186c 100644
*** a/src/backend/catalog/heap.c
--- /bsrc/backend/catalog/heap.c
*************** static Oid AddNewRelationType(const char
*** 77,83 ****
  				   char new_rel_kind,
  				   Oid new_array_type);
  static void RelationRemoveInheritance(Oid relid);
! static void StoreRelCheck(Relation rel, char *ccname, char *ccbin);
  static void StoreConstraints(Relation rel, TupleDesc tupdesc);
  static void SetRelationNumChecks(Relation rel, int numchecks);
  static List *insert_ordered_unique_oid(List *list, Oid datum);
--- 77,83 ----
  				   char new_rel_kind,
  				   Oid new_array_type);
  static void RelationRemoveInheritance(Oid relid);
! static void StoreRelCheck(Relation rel, char *ccname, char *ccbin, const bool is_local, const int inhcount);
  static void StoreConstraints(Relation rel, TupleDesc tupdesc);
  static void SetRelationNumChecks(Relation rel, int numchecks);
  static List *insert_ordered_unique_oid(List *list, Oid datum);
*************** StoreAttrDefault(Relation rel, AttrNumbe
*** 1531,1537 ****
   * in the pg_class entry for the relation.
   */
  static void
! StoreRelCheck(Relation rel, char *ccname, char *ccbin)
  {
  	Node	   *expr;
  	char	   *ccsrc;
--- 1531,1537 ----
   * in the pg_class entry for the relation.
   */
  static void
! StoreRelCheck(Relation rel, char *ccname, char *ccbin, const bool is_local, const int inhcount)
  {
  	Node	   *expr;
  	char	   *ccsrc;
*************** StoreRelCheck(Relation rel, char *ccname
*** 1608,1614 ****
  						  InvalidOid,	/* no associated index */
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc);		/* Source form check constraint */
  
  	pfree(ccsrc);
  }
--- 1608,1616 ----
  						  InvalidOid,	/* no associated index */
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc, /* Source form check constraint */
! 						  is_local, /* conislocal */
! 						  inhcount); /* coninhcount */
  
  	pfree(ccsrc);
  }
*************** StoreConstraints(Relation rel, TupleDesc
*** 1643,1649 ****
  
  	for (i = 0; i < constr->num_check; i++)
  		StoreRelCheck(rel, constr->check[i].ccname,
! 					  constr->check[i].ccbin);
  
  	if (constr->num_check > 0)
  		SetRelationNumChecks(rel, constr->num_check);
--- 1645,1653 ----
  
  	for (i = 0; i < constr->num_check; i++)
  		StoreRelCheck(rel, constr->check[i].ccname,
! 					  constr->check[i].ccbin,
! 					  constr->check[i].is_local,
! 					  constr->check[i].inhcount);
  
  	if (constr->num_check > 0)
  		SetRelationNumChecks(rel, constr->num_check);
*************** AddRelationRawConstraints(Relation rel,
*** 1863,1869 ****
  		/*
  		 * OK, store it.
  		 */
! 		StoreRelCheck(rel, ccname, nodeToString(expr));
  
  		numchecks++;
  
--- 1867,1873 ----
  		/*
  		 * OK, store it.
  		 */
! 		StoreRelCheck(rel, ccname, nodeToString(expr), cdef->is_local, cdef->inhcount);
  
  		numchecks++;
  
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index f07d9ae..6db3084 100644
*** a/src/backend/catalog/index.c
--- /bsrc/backend/catalog/index.c
*************** index_create(Oid heapRelationId,
*** 716,722 ****
  										   InvalidOid,	/* no associated index */
  										   NULL,		/* no check constraint */
  										   NULL,
! 										   NULL);
  
  			referenced.classId = ConstraintRelationId;
  			referenced.objectId = conOid;
--- 716,724 ----
  										   InvalidOid,	/* no associated index */
  										   NULL,		/* no check constraint */
  										   NULL,
! 										   NULL,
! 										   true, /* islocal */
! 										   0); /* inhcount */
  
  			referenced.classId = ConstraintRelationId;
  			referenced.objectId = conOid;
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index d9b20fd..eafc143 100644
*** a/src/backend/catalog/pg_constraint.c
--- /bsrc/backend/catalog/pg_constraint.c
*************** CreateConstraintEntry(const char *constr
*** 60,66 ****
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc)
  {
  	Relation	conDesc;
  	Oid			conOid;
--- 60,68 ----
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc,
! 					  const bool conIsLocal,
! 					  const int conInhCount)
  {
  	Relation	conDesc;
  	Oid			conOid;
*************** CreateConstraintEntry(const char *constr
*** 171,176 ****
--- 173,181 ----
  	else
  		nulls[Anum_pg_constraint_conffeqop - 1] = 'n';
  
+ 	values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
+ 	values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
+ 
  	/*
  	 * initialize the binary form of the check constraint.
  	 */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 91b64ac..50a72c3 100644
*** a/src/backend/commands/tablecmds.c
--- /bsrc/backend/commands/tablecmds.c
*************** static void truncate_check_rel(Relation 
*** 170,175 ****
--- 170,177 ----
  static List *MergeAttributes(List *schema, List *supers, bool istemp,
  				List **supOids, List **supconstr, int *supOidCount);
  static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
+ static void MergeInheritedConstraints(Relation rel, List *old_constraints,
+ 									List *new_constriants);
  static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
  static void add_nonduplicate_constraint(Constraint *cdef,
  							ConstrCheck *check, int *ncheck);
*************** static void ATExecDropColumn(Relation re
*** 232,245 ****
  				 bool recurse, bool recursing);
  static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
  			   IndexStmt *stmt, bool is_rebuild);
  static void ATExecAddConstraint(AlteredTableInfo *tab, Relation rel,
  					Node *newConstraint);
  static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
  						  FkConstraint *fkconstraint);
- static void ATPrepDropConstraint(List **wqueue, Relation rel,
- 					 bool recurse, AlterTableCmd *cmd);
  static void ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior, bool quiet);
  static void ATPrepAlterColumnType(List **wqueue,
  					  AlteredTableInfo *tab, Relation rel,
  					  bool recurse, bool recursing,
--- 234,248 ----
  				 bool recurse, bool recursing);
  static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
  			   IndexStmt *stmt, bool is_rebuild);
+ static void ATPrepAddConstraint(List **wqueue, Relation rel, bool recurse,
+ 				AlterTableCmd *cmd);
  static void ATExecAddConstraint(AlteredTableInfo *tab, Relation rel,
  					Node *newConstraint);
  static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
  						  FkConstraint *fkconstraint);
  static void ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior, 
! 					 bool recurse, bool recursing);
  static void ATPrepAlterColumnType(List **wqueue,
  					  AlteredTableInfo *tab, Relation rel,
  					  bool recurse, bool recursing,
*************** static void ATExecEnableDisableRule(Rela
*** 263,269 ****
  static void ATExecAddInherit(Relation rel, RangeVar *parent);
  static void ATExecDropInherit(Relation rel, RangeVar *parent);
  static void copy_relation_data(Relation rel, SMgrRelation dst);
! 
  
  /* ----------------------------------------------------------------
   *		DefineRelation
--- 266,276 ----
  static void ATExecAddInherit(Relation rel, RangeVar *parent);
  static void ATExecDropInherit(Relation rel, RangeVar *parent);
  static void copy_relation_data(Relation rel, SMgrRelation dst);
! static bool ConstraintIsEquiv(const HeapTuple a, const HeapTuple b,
! 							  const TupleDesc tupleDesc);
! static char *decompile_conbin(HeapTuple contup, TupleDesc tupdesc);
! static void change_opfuncid(Node *node, Oid opfuncid);
! static bool change_opfuncid_walker(Node *node, void *opfuncid);
  
  /* ----------------------------------------------------------------
   *		DefineRelation
*************** DefineRelation(CreateStmt *stmt, char re
*** 488,493 ****
--- 495,503 ----
  		}
  	}
  
+ 	if (old_constraints && stmt->constraints)
+ 		MergeInheritedConstraints(rel, old_constraints, stmt->constraints);
+ 
  	/*
  	 * Parse and add the defaults/constraints, if any.
  	 */
*************** MergeAttributes(List *schema, List *supe
*** 1059,1064 ****
--- 1069,1076 ----
  				Constraint *cdef = makeNode(Constraint);
  				Node	   *expr;
  
+ 				cdef->is_local = false;
+ 				cdef->inhcount = check[i].inhcount + 1;
  				cdef->contype = CONSTR_CHECK;
  				cdef->name = pstrdup(check[i].ccname);
  				cdef->raw_expr = NULL;
*************** MergeAttributes(List *schema, List *supe
*** 1181,1186 ****
--- 1193,1294 ----
  	return schema;
  }
  
+ /*
+  * Go through constraints in new_constraints
+  * if we have a matching constraint in old_constraints and they have a
+  * matching definition, then remove the constraint from new_constraints
+  */
+ static void
+ MergeInheritedConstraints(Relation rel,
+ 						  List *old_constraints,
+ 						  List *new_constraints)
+ {
+ 	ListCell *cur_item;
+ 	ListCell *prev_item;
+ 	ListCell *listptr;
+ 
+ 	prev_item = NULL;
+ 	cur_item = list_head(new_constraints);
+ 
+ 	while (cur_item != NULL)
+ 	{
+ 		bool get_next = true;
+ 		Constraint *cdef = (Constraint *) lfirst(cur_item);
+ 
+ 		/*
+ 		 * We only merge CHECK constraints right now
+ 		 */
+ 		if (cdef->contype == CONSTR_CHECK &&
+ 			cdef->cooked_expr == NULL &&
+ 			cdef->name &&
+ 			cdef->raw_expr)
+ 		{
+ 			foreach(listptr, old_constraints)
+ 			{
+ 				Constraint *cdef1 = (Constraint *) lfirst(listptr);
+ 
+ 				if (cdef1->contype == CONSTR_CHECK &&
+ 					cdef1->name &&
+ 					strcmp(cdef->name, cdef1->name) == 0)
+ 				{
+ 					ParseState		*pstate;
+ 					RangeTblEntry	*rte;
+ 					Node			*expr;
+ 					char			*cooked;
+ 
+ 					pstate = make_parsestate(NULL);
+ 					rte = addRangeTableEntryForRelation(pstate,
+ 														rel,
+ 														NULL,
+ 														false,
+ 														true);
+ 					addRTEtoQuery(pstate, rte, true, true, true);
+ 
+ 					expr = transformExpr(pstate, cdef->raw_expr);
+ 					expr = coerce_to_boolean(pstate, expr, "CHECK");
+ 
+ 					/*
+ 					 * We need to fixup the opfuncid because it
+ 					 * got cleared in cdef1->cooked_expr
+ 					 */
+ 					change_opfuncid(expr, 0);
+ 
+ 					cooked = nodeToString(expr);
+ 
+ 					if (strcmp(cooked, cdef1->cooked_expr) == 0)
+ 					{
+ 						ereport(NOTICE,
+ 							(errmsg("merging constraint \"%s\" with inherited definition",
+ 							cdef->name)));
+ 
+ 						list_delete_cell(new_constraints, cur_item, prev_item);
+ 
+ 						pfree(cdef);
+ 						get_next = false;
+ 
+ 						if (prev_item)
+ 							cur_item = lnext(prev_item);
+ 						else
+ 							cur_item = list_head(new_constraints);
+ 					}else
+ 					{
+ 						ereport(ERROR,
+ 								(errmsg("constraint \"%s\" does not match inherited definition",
+ 								cdef->name)));
+ 					}
+ 
+ 					break;
+ 				}
+ 			}
+ 		}
+ 
+ 		if(get_next)
+ 		{
+ 			prev_item = cur_item;
+ 			cur_item = lnext(prev_item);
+ 		}
+ 	}
+ }
  
  /*
   * In multiple-inheritance situations, it's possible to inherit
*************** add_nonduplicate_constraint(Constraint *
*** 1214,1219 ****
--- 1322,1329 ----
  	/* No match on name, so add it to array */
  	check[*ncheck].ccname = cdef->name;
  	check[*ncheck].ccbin = pstrdup(cdef->cooked_expr);
+ 	check[*ncheck].inhcount = cdef->inhcount;
+ 	check[*ncheck].is_local = cdef->is_local;
  	(*ncheck)++;
  }
  
*************** varattnos_map_schema(TupleDesc old, List
*** 1312,1317 ****
--- 1422,1459 ----
  	return attmap;
  }
  
+ /*
+  * Change an opfuncid of a Node
+  *
+  * Note that the passed node tree is modified in-place!
+  */
+ static void
+ change_opfuncid(Node *node, Oid opfuncid)
+ {
+ 	Oid *o = palloc(sizeof(Oid));
+ 	*o = opfuncid;
+ 	(void) change_opfuncid_walker(node, (void*)o);
+ 	pfree(o);
+ }
+ 
+ static bool
+ change_opfuncid_walker(Node *node, void *opfuncid)
+ {
+ 	if (node == NULL)
+ 		return false;
+ 
+ 	if (IsA(node, OpExpr))
+ 	{
+ 		Oid		*o = (Oid*)opfuncid;
+ 		OpExpr	*op = (OpExpr *) node;
+ 
+ 		op->opfuncid = *o;
+ 		return false;
+ 	}
+ 	return expression_tree_walker(node, change_opfuncid_walker,
+ 								  opfuncid);
+ 
+ }
  
  /*
   * StoreCatalogInheritance
*************** ATPrepCmd(List **wqueue, Relation rel, A
*** 2044,2070 ****
  			break;
  		case AT_AddConstraint:	/* ADD CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 
! 			/*
! 			 * Currently we recurse only for CHECK constraints, never for
! 			 * foreign-key constraints.  UNIQUE/PKEY constraints won't be seen
! 			 * here.
! 			 */
! 			if (IsA(cmd->def, Constraint))
! 				ATSimpleRecursion(wqueue, rel, cmd, recurse);
! 			/* No command-specific prep needed */
  			pass = AT_PASS_ADD_CONSTR;
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 			/* Performs own recursion */
! 			ATPrepDropConstraint(wqueue, rel, recurse, cmd);
! 			pass = AT_PASS_DROP;
! 			break;
! 		case AT_DropConstraintQuietly:	/* DROP CONSTRAINT for child */
! 			ATSimplePermissions(rel, false);
! 			ATSimpleRecursion(wqueue, rel, cmd, recurse);
! 			/* No command-specific prep needed */
  			pass = AT_PASS_DROP;
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
--- 2186,2200 ----
  			break;
  		case AT_AddConstraint:	/* ADD CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 			ATPrepAddConstraint(wqueue, rel, recurse, cmd);
  			pass = AT_PASS_ADD_CONSTR;
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 			 /* Recursion occurs during execution phase */
! 			 /* No command-specific prep needed except saving recurse flag */
! 			if (recurse)
! 				cmd->subtype = AT_DropConstraintRecurse;
  			pass = AT_PASS_DROP;
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
*************** ATExecCmd(AlteredTableInfo *tab, Relatio
*** 2253,2262 ****
  			ATExecAddConstraint(tab, rel, cmd->def);
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, false);
  			break;
! 		case AT_DropConstraintQuietly:	/* DROP CONSTRAINT for child */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, true);
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
  			ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def);
--- 2383,2392 ----
  			ATExecAddConstraint(tab, rel, cmd->def);
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, false, false);
  			break;
! 		case AT_DropConstraintRecurse:	/* DROP CONSTRAINT for child */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, true, false);
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
  			ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def);
*************** ATExecAddColumn(AlteredTableInfo *tab, R
*** 3273,3279 ****
  		 * _list_ of defaults, but we just do one.
  		 */
  		AddRelationRawConstraints(rel, list_make1(rawEnt), NIL);
- 
  		/* Make the additional catalog changes visible */
  		CommandCounterIncrement();
  	}
--- 3403,3408 ----
*************** ATExecAddIndex(AlteredTableInfo *tab, Re
*** 3936,3941 ****
--- 4065,4119 ----
   * ALTER TABLE ADD CONSTRAINT
   */
  static void
+ ATPrepAddConstraint(List **wqueue, Relation rel, bool recurse,
+ 				AlterTableCmd *cmd)
+ {
+ 	/*
+ 	 * Currently we recurse only for CHECK constraints, never for
+ 	 * foreign-key constraints.  UNIQUE/PKEY constraints won't be seen
+ 	 * here.
+ 	 */
+ 	if (!IsA(cmd->def, Constraint))
+ 	{
+ 		return;
+ 	}
+ 
+ 	/*
+ 	 * Recurse to add the column to child classes, if requested.
+ 	 *
+ 	 * We must recurse one level at a time, so that multiply-inheriting
+ 	 * children are visited the right number of times and end up with the
+ 	 * right coninhcount.
+ 	 */
+ 	if (recurse)
+ 	{
+ 		AlterTableCmd	*childCmd = copyObject(cmd);
+ 		Constraint		*constr	  = (Constraint *) childCmd->def;
+ 
+ 		/* child should see column as singly inherited */
+ 		constr->inhcount = 1;
+ 		constr->is_local = false;
+ 
+ 		ATOneLevelRecursion(wqueue, rel, childCmd);
+ 	}
+ 	else
+ 	{
+ 		/*
+ 		 * 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 (find_inheritance_children(RelationGetRelid(rel)) != NIL)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ 					 errmsg("constraint must be added to child tables too")));
+ 	}
+ }
+ 
+ 
+ /*
+  * ALTER TABLE ADD CONSTRAINT
+  */
+ static void
  ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint)
  {
  	switch (nodeTag(newConstraint))
*************** ATAddForeignKeyConstraint(AlteredTableIn
*** 4295,4301 ****
  									  indexOid,
  									  NULL,		/* no check constraint */
  									  NULL,
! 									  NULL);
  
  	/*
  	 * Create the triggers that will enforce the constraint.
--- 4473,4481 ----
  									  indexOid,
  									  NULL,		/* no check constraint */
  									  NULL,
! 									  NULL,
! 									  true, /* islocal */
! 									  0); /* inhcount */
  
  	/*
  	 * Create the triggers that will enforce the constraint.
*************** createForeignKeyTriggers(Relation rel, F
*** 4815,4858 ****
   * ALTER TABLE DROP CONSTRAINT
   */
  static void
! ATPrepDropConstraint(List **wqueue, Relation rel,
! 					 bool recurse, AlterTableCmd *cmd)
  {
  	/*
! 	 * We don't want errors or noise from child tables, so we have to pass
! 	 * down a modified command.
  	 */
! 	if (recurse)
  	{
! 		AlterTableCmd *childCmd = copyObject(cmd);
  
! 		childCmd->subtype = AT_DropConstraintQuietly;
! 		ATSimpleRecursion(wqueue, rel, childCmd, recurse);
! 	}
! }
  
! static void
! ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior, bool quiet)
! {
! 	int			deleted;
  
! 	deleted = RemoveRelConstraints(rel, constrName, behavior);
  
! 	if (!quiet)
! 	{
! 		/* If zero constraints deleted, complain */
! 		if (deleted == 0)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_UNDEFINED_OBJECT),
! 					 errmsg("constraint \"%s\" does not exist",
! 							constrName)));
! 		/* Otherwise if more than one constraint deleted, notify */
! 		else if (deleted > 1)
! 			ereport(NOTICE,
! 					(errmsg("multiple constraints named \"%s\" were dropped",
! 							constrName)));
  	}
  }
  
  /*
--- 4995,5109 ----
   * ALTER TABLE DROP CONSTRAINT
   */
  static void
! ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior,
! 					 bool recurse, bool recursing)
  {
+ 	HeapTuple	tuple;
+ 	List		*children;
+ 	Relation	conrel;
+ 	Form_pg_constraint con;
+ 
+ 	conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
+ 
+ 	/* At top level, permission check was done in ATPrepCmd, else do it */
+ 	if (recursing)
+ 		ATSimplePermissions(rel, false);
+ 
+ 	tuple = SearchSysCache(CONSTRNAME,
+ 						   ObjectIdGetDatum(RelationGetRelid(rel)),
+ 						   CStringGetDatum(constrName),
+ 						   0, 0);
+ 
+ 	if (!HeapTupleIsValid(tuple))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_COLUMN),
+ 				 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
+ 						constrName, RelationGetRelationName(rel))));
+ 
+ 	con = (Form_pg_constraint) GETSTRUCT(tuple);
+ 
+ 	/* Don't drop inherited constraints */
+ 	if (con->coninhcount > 0 && !recursing)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ 				 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
+ 						constrName,
+ 						RelationGetRelationName(rel))));
+ 
+ 	ReleaseSysCache(tuple);
+ 
  	/*
! 	 * Propagate to children as appropriate.  Unlike most other ALTER
! 	 * 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.
  	 */
! 	children = find_inheritance_children(RelationGetRelid(rel));
! 
! 	if (children)
  	{
! 		ListCell   *child;
  
! 		foreach(child, children)
! 		{
! 			Oid			childrelid = lfirst_oid(child);
! 			Relation	childrel;
! 			Form_pg_constraint childcon;
  
! 			childrel = heap_open(childrelid, AccessExclusiveLock);
! 			CheckTableNotInUse(childrel, "ALTER TABLE");
  
! 			tuple = SearchSysCacheCopy(CONSTRNAME,
! 								   ObjectIdGetDatum(childrelid),
! 								   CStringGetDatum(constrName),
! 								   0, 0);
! 			if (!HeapTupleIsValid(tuple))
! 				elog(ERROR, "cache lookup failed for constraint \"%s\" of relation %u",
! 					 constrName, childrelid);
! 
! 			childcon = (Form_pg_constraint) GETSTRUCT(tuple);
! 
! 			if (childcon->coninhcount <= 0)
! 				elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
! 					 childrelid, constrName);
  
! 			if (recurse)
! 			{
! 				if (childcon->coninhcount == 1 && !childcon->conislocal)
! 				{
! 					ATExecDropConstraint(childrel, constrName, behavior, true, true);
! 				}
! 				else
! 				{
! 					childcon->coninhcount--;
! 
! 					simple_heap_update(conrel, &tuple->t_self, tuple);
! 					CatalogUpdateIndexes(conrel, tuple);
! 				}
! 			}
! 			else
! 			{
! 				/*
! 				 * If we were told to drop ONLY in this table (no recursion),
! 				 * we need to mark the inheritors' attribute as locally
! 				 * defined rather than inherited.
! 				 */
! 
! 				childcon->coninhcount--;
! 				childcon->conislocal = true;
! 
! 				simple_heap_update(conrel, &tuple->t_self, tuple);
! 				CatalogUpdateIndexes(conrel, tuple);
! 			}
! 
! 			heap_freetuple(tuple);
! 			heap_close(childrel, AccessExclusiveLock);
! 		}
  	}
+ 
+ 	RemoveRelConstraints(rel, constrName, behavior);
+ 
+ 	heap_close(conrel, RowExclusiveLock);
  }
  
  /*
*************** decompile_conbin(HeapTuple contup, Tuple
*** 6260,6265 ****
--- 6511,6533 ----
  }
  
  /*
+  * See if two constraints are "functionally" equivalent, but not necessarily equal
+  */
+ static bool
+ ConstraintIsEquiv(const HeapTuple a, const HeapTuple b, const TupleDesc tupleDesc)
+ {
+ 	const Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
+ 	const Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
+ 
+ 	if (acon->condeferrable != bcon->condeferrable ||
+ 		acon->condeferred != bcon->condeferred ||
+ 			strcmp(decompile_conbin(a, tupleDesc),
+ 				   decompile_conbin(b, tupleDesc)) != 0)
+ 		return 0;
+ 	else
+ 		return 1;
+ }
+ /*
   * Check columns in child table match up with columns in parent, and increment
   * their attinhcount.
   *
*************** MergeAttributesIntoExisting(Relation chi
*** 6353,6361 ****
   * make it possible to ensure no records are mistakenly inserted into the
   * master in partitioned tables rather than the appropriate child.
   *
-  * XXX This is O(N^2) which may be an issue with tables with hundreds of
-  * constraints. As long as tables have more like 10 constraints it shouldn't be
-  * a problem though. Even 100 constraints ought not be the end of the world.
   */
  static void
  MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
--- 6621,6626 ----
*************** MergeConstraintsIntoExisting(Relation ch
*** 6365,6450 ****
  	SysScanDesc scan;
  	ScanKeyData key;
  	HeapTuple	constraintTuple;
- 	ListCell   *elem;
- 	List	   *constraints;
  
! 	/* First gather up the child's constraint definitions */
! 	catalogRelation = heap_open(ConstraintRelationId, AccessShareLock);
  	tupleDesc = RelationGetDescr(catalogRelation);
  
  	ScanKeyInit(&key,
  				Anum_pg_constraint_conrelid,
  				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(child_rel)));
  	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
  							  true, SnapshotNow, 1, &key);
  
- 	constraints = NIL;
- 	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
- 	{
- 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
- 
- 		if (con->contype != CONSTRAINT_CHECK)
- 			continue;
- 
- 		constraints = lappend(constraints, heap_copytuple(constraintTuple));
- 	}
- 
- 	systable_endscan(scan);
- 
- 	/* Then scan through the parent's constraints looking for matches */
- 	ScanKeyInit(&key,
- 				Anum_pg_constraint_conrelid,
- 				BTEqualStrategyNumber, F_OIDEQ,
- 				ObjectIdGetDatum(RelationGetRelid(parent_rel)));
- 	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId, true,
- 							  SnapshotNow, 1, &key);
- 
  	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
  	{
  		Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
- 		bool		found = false;
- 		Form_pg_constraint child_con = NULL;
- 		HeapTuple	child_contuple = NULL;
  
  		if (parent_con->contype != CONSTRAINT_CHECK)
  			continue;
  
! 		foreach(elem, constraints)
! 		{
! 			child_contuple = (HeapTuple) lfirst(elem);
! 			child_con = (Form_pg_constraint) GETSTRUCT(child_contuple);
! 			if (strcmp(NameStr(parent_con->conname),
! 					   NameStr(child_con->conname)) == 0)
! 			{
! 				found = true;
! 				break;
! 			}
! 		}
  
! 		if (!found)
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("child table is missing constraint \"%s\"",
  							NameStr(parent_con->conname))));
  
! 		if (parent_con->condeferrable != child_con->condeferrable ||
! 			parent_con->condeferred != child_con->condeferred ||
! 			strcmp(decompile_conbin(constraintTuple, tupleDesc),
! 				   decompile_conbin(child_contuple, tupleDesc)) != 0)
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("constraint definition for check constraint \"%s\" does not match",
  							NameStr(parent_con->conname))));
  
! 		/*
! 		 * TODO: add conislocal,coninhcount to constraints. This is where we
! 		 * would have to bump them just like attributes
! 		 */
  	}
  
  	systable_endscan(scan);
! 	heap_close(catalogRelation, AccessShareLock);
  }
  
  /*
--- 6630,6683 ----
  	SysScanDesc scan;
  	ScanKeyData key;
  	HeapTuple	constraintTuple;
  
! 	catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
  	tupleDesc = RelationGetDescr(catalogRelation);
  
  	ScanKeyInit(&key,
  				Anum_pg_constraint_conrelid,
  				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(parent_rel)));
  	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
  							  true, SnapshotNow, 1, &key);
  
  	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
  	{
+ 		HeapTuple childTuple;
+ 		Form_pg_constraint child_con;
  		Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
  
  		if (parent_con->contype != CONSTRAINT_CHECK)
  			continue;
  
! 		childTuple = SearchSysCacheCopy(CONSTRNAME,
! 									ObjectIdGetDatum(RelationGetRelid(child_rel)),
! 									CStringGetDatum(NameStr(parent_con->conname)),
! 									0, 0);
  
! 		if (!HeapTupleIsValid(childTuple))
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("child table is missing constraint \"%s\"",
  							NameStr(parent_con->conname))));
  
! 		child_con = (Form_pg_constraint) GETSTRUCT(childTuple);
! 
! 		if (!ConstraintIsEquiv(constraintTuple, childTuple, tupleDesc))
  			ereport(ERROR,
  					(errcode(ERRCODE_DATATYPE_MISMATCH),
  					 errmsg("constraint definition for check constraint \"%s\" does not match",
  							NameStr(parent_con->conname))));
  
! 		/* increment coninhcount */
! 		child_con->coninhcount++;
! 		simple_heap_update(catalogRelation, &childTuple->t_self, childTuple);
! 		CatalogUpdateIndexes(catalogRelation, childTuple);
! 		heap_freetuple(childTuple);
  	}
  
  	systable_endscan(scan);
! 	heap_close(catalogRelation, RowExclusiveLock);
  }
  
  /*
*************** ATExecDropInherit(Relation rel, RangeVar
*** 6469,6476 ****
--- 6702,6711 ----
  	ScanKeyData key[3];
  	HeapTuple	inheritsTuple,
  				attributeTuple,
+ 				constraintTuple,
  				depTuple;
  	bool		found = false;
+ 	TupleDesc	tupdesc;
  
  	/*
  	 * AccessShareLock on the parent is probably enough, seeing that DROP
*************** ATExecDropInherit(Relation rel, RangeVar
*** 6508,6514 ****
  			break;
  		}
  	}
- 
  	systable_endscan(scan);
  	heap_close(catalogRelation, RowExclusiveLock);
  
--- 6743,6748 ----
*************** ATExecDropInherit(Relation rel, RangeVar
*** 6558,6563 ****
--- 6792,6832 ----
  	systable_endscan(scan);
  	heap_close(catalogRelation, RowExclusiveLock);
  
+ 	catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
+ 	ScanKeyInit(&key[0],
+ 				Anum_pg_constraint_conrelid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(RelationGetRelid(rel)));
+ 	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
+ 							  true, SnapshotNow, 1, key);
+ 
+ 	tupdesc = RelationGetDescr(catalogRelation);
+ 
+ 	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
+ 	{
+ 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
+ 
+ 		if (con->contype != CONSTRAINT_CHECK)
+ 			continue;
+ 
+ 		if (con->coninhcount > 0)
+ 		{
+ 			HeapTuple	copyTuple = heap_copytuple(constraintTuple);
+ 			Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
+ 
+ 			copy_con->coninhcount--;
+ 			if (copy_con->coninhcount == 0)
+ 				copy_con->conislocal = true;
+ 
+ 			simple_heap_update(catalogRelation, &copyTuple->t_self, copyTuple);
+ 			CatalogUpdateIndexes(catalogRelation, copyTuple);
+ 			heap_freetuple(copyTuple);
+ 		}
+ 	}
+ 
+ 	systable_endscan(scan);
+ 	heap_close(catalogRelation, RowExclusiveLock);
+ 
  	/*
  	 * Drop the dependency
  	 *
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 188fe43..123209d 100644
*** a/src/backend/commands/typecmds.c
--- /bsrc/backend/commands/typecmds.c
*************** domainAddConstraint(Oid domainOid, Oid d
*** 2206,2212 ****
  						  InvalidOid,
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc);		/* Source form check constraint */
  
  	/*
  	 * Return the compiled constraint expression so the calling routine can
--- 2206,2214 ----
  						  InvalidOid,
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc,	/* Source form check constraint */
! 						  true, /* is local */
! 						  0); /* inhcount */
  
  	/*
  	 * Return the compiled constraint expression so the calling routine can
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index aa38052..005886e 100644
*** a/src/backend/nodes/copyfuncs.c
--- /bsrc/backend/nodes/copyfuncs.c
*************** _copyConstraint(Constraint *from)
*** 1808,1813 ****
--- 1808,1815 ----
  	COPY_NODE_FIELD(keys);
  	COPY_NODE_FIELD(options);
  	COPY_STRING_FIELD(indexspace);
+ 	COPY_SCALAR_FIELD(inhcount);
+ 	COPY_SCALAR_FIELD(is_local);
  
  	return newnode;
  }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index dc85594..e232a37 100644
*** a/src/backend/nodes/equalfuncs.c
--- /bsrc/backend/nodes/equalfuncs.c
*************** _equalConstraint(Constraint *a, Constrai
*** 1837,1842 ****
--- 1837,1844 ----
  	COMPARE_NODE_FIELD(keys);
  	COMPARE_NODE_FIELD(options);
  	COMPARE_STRING_FIELD(indexspace);
+ 	COMPARE_SCALAR_FIELD(inhcount);
+ 	COMPARE_SCALAR_FIELD(is_local);
  
  	return true;
  }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index d06f035..382f6b6 100644
*** a/src/backend/nodes/outfuncs.c
--- /bsrc/backend/nodes/outfuncs.c
*************** _outConstraint(StringInfo str, Constrain
*** 2028,2033 ****
--- 2028,2036 ----
  			appendStringInfo(str, "<unrecognized_constraint>");
  			break;
  	}
+ 
+ 	WRITE_INT_FIELD(inhcount);
+ 	WRITE_BOOL_FIELD(is_local);
  }
  
  static void
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 6ca09b8..1575dbe 100644
*** a/src/backend/parser/gram.y
--- /bsrc/backend/parser/gram.y
*************** ColConstraintElem:
*** 2069,2074 ****
--- 2069,2076 ----
  					n->cooked_expr = NULL;
  					n->keys = NULL;
  					n->indexspace = NULL;
+ 					n->is_local = true;
+ 					n->inhcount = 0;
  					$$ = (Node *)n;
  				}
  			| DEFAULT b_expr
*************** ConstraintElem:
*** 2209,2214 ****
--- 2211,2218 ----
  					n->raw_expr = $3;
  					n->cooked_expr = NULL;
  					n->indexspace = NULL;
+ 					n->is_local = true;
+ 					n->inhcount = 0;
  					$$ = (Node *)n;
  				}
  			| UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index db1bcd8..d0e4d41 100644
*** a/src/backend/parser/parse_utilcmd.c
--- /bsrc/backend/parser/parse_utilcmd.c
*************** transformInhRelation(ParseState *pstate,
*** 688,693 ****
--- 688,695 ----
  			n->cooked_expr = nodeToString(ccbin_node);
  			n->indexspace = NULL;
  			cxt->ckconstraints = lappend(cxt->ckconstraints, (Node *) n);
+ 			n->is_local = true;
+ 			n->inhcount = 0;
  		}
  	}
  
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 3f3c67d..8f1af5f 100644
*** a/src/backend/utils/cache/catcache.c
--- /bsrc/backend/utils/cache/catcache.c
*************** CatalogCacheInitializeCache(CatCache *ca
*** 962,968 ****
  		else
  		{
  			if (cache->cc_key[i] != ObjectIdAttributeNumber)
! 				elog(FATAL, "only sys attr supported in caches is OID");
  			keytype = OIDOID;
  		}
  
--- 962,968 ----
  		else
  		{
  			if (cache->cc_key[i] != ObjectIdAttributeNumber)
! 				elog(FATAL, "only sys attr supported in caches is OID for relation: %s", cache->cc_relname);
  			keytype = OIDOID;
  		}
  
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 7ff0e8a..789c6a4 100644
*** a/src/backend/utils/cache/syscache.c
--- /bsrc/backend/utils/cache/syscache.c
*************** static const struct cachedesc cacheinfo[
*** 305,310 ****
--- 305,322 ----
  		},
  		128
  	},
+ 	{ConstraintRelationId,		// CONSTRNAME
+ 		ConstraintRelidNameIndexId,
+ 		Anum_pg_constraint_conrelid,
+ 		2,
+ 		{
+ 			Anum_pg_constraint_conrelid,
+ 			Anum_pg_constraint_conname,
+ 			0,
+ 			0
+ 		},
+ 		256,
+ 	},
  	{ConstraintRelationId,		/* CONSTROID */
  		ConstraintOidIndexId,
  		0,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index b1dfd02..3129bfb 100644
*** a/src/bin/pg_dump/common.c
--- /bsrc/bin/pg_dump/common.c
*************** static DumpableObject **oprinfoindex;
*** 62,69 ****
  
  static void flagInhTables(TableInfo *tbinfo, int numTables,
  			  InhInfo *inhinfo, int numInherits);
! static void flagInhAttrs(TableInfo *tbinfo, int numTables,
! 			 InhInfo *inhinfo, int numInherits);
  static DumpableObject **buildIndexArray(void *objArray, int numObjs,
  				Size objSize);
  static int	DOCatalogIdCompare(const void *p1, const void *p2);
--- 62,70 ----
  
  static void flagInhTables(TableInfo *tbinfo, int numTables,
  			  InhInfo *inhinfo, int numInherits);
! static void flagInhAttrs(TableInfo *tblinfo, int numTables,
! 			 InhInfo *inhinfo, int numInherits,
! 			 int remoteVersion);
  static DumpableObject **buildIndexArray(void *objArray, int numObjs,
  				Size objSize);
  static int	DOCatalogIdCompare(const void *p1, const void *p2);
*************** static int	strInArray(const char *patter
*** 77,83 ****
   *	  Collect information about all potentially dumpable objects
   */
  TableInfo *
! getSchemaData(int *numTablesPtr)
  {
  	NamespaceInfo *nsinfo;
  	AggInfo    *agginfo;
--- 78,84 ----
   *	  Collect information about all potentially dumpable objects
   */
  TableInfo *
! getSchemaData(int *numTablesPtr, int remoteVersion)
  {
  	NamespaceInfo *nsinfo;
  	AggInfo    *agginfo;
*************** getSchemaData(int *numTablesPtr)
*** 191,197 ****
  
  	if (g_verbose)
  		write_msg(NULL, "flagging inherited columns in subtables\n");
! 	flagInhAttrs(tblinfo, numTables, inhinfo, numInherits);
  
  	if (g_verbose)
  		write_msg(NULL, "reading indexes\n");
--- 192,198 ----
  
  	if (g_verbose)
  		write_msg(NULL, "flagging inherited columns in subtables\n");
! 	flagInhAttrs(tblinfo, numTables, inhinfo, numInherits, remoteVersion);
  
  	if (g_verbose)
  		write_msg(NULL, "reading indexes\n");
*************** flagInhTables(TableInfo *tblinfo, int nu
*** 258,264 ****
   */
  static void
  flagInhAttrs(TableInfo *tblinfo, int numTables,
! 			 InhInfo *inhinfo, int numInherits)
  {
  	int			i,
  				j,
--- 259,266 ----
   */
  static void
  flagInhAttrs(TableInfo *tblinfo, int numTables,
! 			 InhInfo *inhinfo, int numInherits,
! 			 int remoteVersion)
  {
  	int			i,
  				j,
*************** flagInhAttrs(TableInfo *tblinfo, int num
*** 423,454 ****
  		 * different schemas, because reverse-listing of function calls may
  		 * produce different text (schema-qualified or not) depending on
  		 * search path.  We really need a more bulletproof way of detecting
! 		 * inherited constraints --- pg_constraint should record this
  		 * explicitly!
  		 */
! 		for (j = 0; j < tbinfo->ncheck; j++)
  		{
! 			ConstraintInfo *constr;
! 
! 			constr = &(tbinfo->checkexprs[j]);
! 
! 			for (k = 0; k < numParents; k++)
  			{
! 				int			l;
  
! 				parent = parents[k];
! 				for (l = 0; l < parent->ncheck; l++)
  				{
! 					ConstraintInfo *pconstr = &(parent->checkexprs[l]);
  
! 					if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0)
  					{
! 						constr->coninherited = true;
! 						break;
  					}
  				}
- 				if (constr->coninherited)
- 					break;
  			}
  		}
  	}
--- 425,460 ----
  		 * different schemas, because reverse-listing of function calls may
  		 * produce different text (schema-qualified or not) depending on
  		 * search path.  We really need a more bulletproof way of detecting
! 		 * inherited constraints 
! 		 * The above is pre-80300 behaviour, now pg_constraint records this
  		 * explicitly!
  		 */
! 		if (remoteVersion <= 80300)
  		{
! 			for (j = 0; j < tbinfo->ncheck; j++)
  			{
! 				ConstraintInfo *constr;
  
! 				constr = &(tbinfo->checkexprs[j]);
! 
! 				for (k = 0; k < numParents; k++)
  				{
! 					int			l;
  
! 					parent = parents[k];
! 					for (l = 0; l < parent->ncheck; l++)
  					{
! 						ConstraintInfo *pconstr = &(parent->checkexprs[l]);
! 
! 						if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0)
! 						{
! 							constr->coninherited = true;
! 							break;
! 						}
  					}
+ 					if (constr->coninherited)
+ 						break;
  				}
  			}
  		}
  	}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 36ba457..4c6096e 100644
*** a/src/bin/pg_dump/pg_dump.c
--- /bsrc/bin/pg_dump/pg_dump.c
*************** main(int argc, char **argv)
*** 637,643 ****
  	 * Now scan the database and create DumpableObject structs for all the
  	 * objects we intend to dump.
  	 */
! 	tblinfo = getSchemaData(&numTables);
  
  	if (!schemaOnly)
  		getTableData(tblinfo, numTables, oids);
--- 637,643 ----
  	 * Now scan the database and create DumpableObject structs for all the
  	 * objects we intend to dump.
  	 */
! 	tblinfo = getSchemaData(&numTables, g_fout->remoteVersion);
  
  	if (!schemaOnly)
  		getTableData(tblinfo, numTables, oids);
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4580,4589 ****
  						  tbinfo->dobj.name);
  
  			resetPQExpBuffer(q);
! 			if (g_fout->remoteVersion >= 70400)
  			{
  				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! 							"pg_catalog.pg_get_constraintdef(oid) AS consrc "
  								  "FROM pg_catalog.pg_constraint "
  								  "WHERE conrelid = '%u'::pg_catalog.oid "
  								  "   AND contype = 'c' "
--- 4580,4602 ----
  						  tbinfo->dobj.name);
  
  			resetPQExpBuffer(q);
! 			if (g_fout->remoteVersion > 80300)
  			{
+ 				/* 8.3 did not have conislocal attribute in pg_constraint */
  				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! 							"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
! 							"conislocal "
! 								  "FROM pg_catalog.pg_constraint "
! 								  "WHERE conrelid = '%u'::pg_catalog.oid "
! 								  "   AND contype = 'c' "
! 								  "ORDER BY conname",
! 								  tbinfo->dobj.catId.oid);
! 			}
! 			else if (g_fout->remoteVersion >= 70400)
! 			{
! 				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! 							"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
! 						  	"false as conislocal "
  								  "FROM pg_catalog.pg_constraint "
  								  "WHERE conrelid = '%u'::pg_catalog.oid "
  								  "   AND contype = 'c' "
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4594,4600 ****
  			{
  				/* no pg_get_constraintdef, must use consrc */
  				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! 								  "'CHECK (' || consrc || ')' AS consrc "
  								  "FROM pg_catalog.pg_constraint "
  								  "WHERE conrelid = '%u'::pg_catalog.oid "
  								  "   AND contype = 'c' "
--- 4607,4614 ----
  			{
  				/* no pg_get_constraintdef, must use consrc */
  				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! 								  "'CHECK (' || consrc || ')' AS consrc, "
! 								  "false as conislocal "
  								  "FROM pg_catalog.pg_constraint "
  								  "WHERE conrelid = '%u'::pg_catalog.oid "
  								  "   AND contype = 'c' "
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4606,4612 ****
  				/* 7.2 did not have OIDs in pg_relcheck */
  				appendPQExpBuffer(q, "SELECT tableoid, 0 as oid, "
  								  "rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc "
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
--- 4620,4627 ----
  				/* 7.2 did not have OIDs in pg_relcheck */
  				appendPQExpBuffer(q, "SELECT tableoid, 0 as oid, "
  								  "rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc, "
! 								  "false as conislocal "
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4616,4622 ****
  			{
  				appendPQExpBuffer(q, "SELECT tableoid, oid, "
  								  "rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc "
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
--- 4631,4638 ----
  			{
  				appendPQExpBuffer(q, "SELECT tableoid, oid, "
  								  "rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc, "
! 								  "false as conislocal "
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4628,4634 ****
  				appendPQExpBuffer(q, "SELECT "
  								  "(SELECT oid FROM pg_class WHERE relname = 'pg_relcheck') AS tableoid, "
  								  "oid, rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc "
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
--- 4644,4651 ----
  				appendPQExpBuffer(q, "SELECT "
  								  "(SELECT oid FROM pg_class WHERE relname = 'pg_relcheck') AS tableoid, "
  								  "oid, rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc, "
! 								  " false as conislocal"
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4662,4669 ****
  				constrs[j].contype = 'c';
  				constrs[j].condef = strdup(PQgetvalue(res, j, 3));
  				constrs[j].conindex = 0;
- 				constrs[j].coninherited = false;
  				constrs[j].separate = false;
  
  				constrs[j].dobj.dump = tbinfo->dobj.dump;
  
--- 4679,4687 ----
  				constrs[j].contype = 'c';
  				constrs[j].condef = strdup(PQgetvalue(res, j, 3));
  				constrs[j].conindex = 0;
  				constrs[j].separate = false;
+ 				/* depending on conislocal value, set coninherited */
+ 				constrs[j].coninherited = (PQgetvalue(res, j, 4)[0] == 't');
  
  				constrs[j].dobj.dump = tbinfo->dobj.dump;
  
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4678,4685 ****
  
  				/*
  				 * If the constraint is inherited, this will be detected
! 				 * later.  We also detect later if the constraint must be
! 				 * split out from the table definition.
  				 */
  			}
  			PQclear(res);
--- 4696,4703 ----
  
  				/*
  				 * If the constraint is inherited, this will be detected
! 				 * later for pre-80300 case. We also detect later if the constraint
! 				 * must be split out from the table definition.
  				 */
  			}
  			PQclear(res);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index f4cd179..23f76ec 100644
*** a/src/bin/pg_dump/pg_dump.h
--- /bsrc/bin/pg_dump/pg_dump.h
*************** extern char g_opaque_type[10];	/* name f
*** 429,435 ****
   *	common utility functions
   */
  
! extern TableInfo *getSchemaData(int *numTablesPtr);
  
  typedef enum _OidOptions
  {
--- 429,435 ----
   *	common utility functions
   */
  
! extern TableInfo *getSchemaData(int *numTablesPtr, int remoteVersion);
  
  typedef enum _OidOptions
  {
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index c5fb69a..aa683be 100644
*** a/src/include/access/tupdesc.h
--- /bsrc/include/access/tupdesc.h
*************** typedef struct attrDefault
*** 27,34 ****
  
  typedef struct constrCheck
  {
! 	char	   *ccname;
! 	char	   *ccbin;			/* nodeToString representation of expr */
  } ConstrCheck;
  
  /* This structure contains constraints of a tuple */
--- 27,36 ----
  
  typedef struct constrCheck
  {
! 	char	*ccname;
! 	char	*ccbin;			/* nodeToString representation of expr */
! 	bool	is_local;
! 	int		inhcount;
  } ConstrCheck;
  
  /* This structure contains constraints of a tuple */
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index e787c52..9b17a73 100644
*** a/src/include/catalog/catversion.h
--- /bsrc/include/catalog/catversion.h
***************
*** 53,58 ****
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	200804292
  
  #endif
--- 53,58 ----
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	200803290
  
  #endif
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 7dc5215..d962930 100644
*** a/src/include/catalog/indexing.h
--- /bsrc/include/catalog/indexing.h
*************** DECLARE_INDEX(pg_constraint_contypid_ind
*** 121,126 ****
--- 121,128 ----
  #define ConstraintTypidIndexId	2666
  DECLARE_UNIQUE_INDEX(pg_constraint_oid_index, 2667, on pg_constraint using btree(oid oid_ops));
  #define ConstraintOidIndexId  2667
+ DECLARE_UNIQUE_INDEX(pg_constraint_relid_coname_index, 2700, on pg_constraint using btree(conrelid oid_ops, conname name_ops));
+ #define ConstraintRelidNameIndexId  2700
  
  DECLARE_UNIQUE_INDEX(pg_conversion_default_index, 2668, on pg_conversion using btree(connamespace oid_ops, conforencoding int4_ops, contoencoding int4_ops, oid oid_ops));
  #define ConversionDefaultIndexId  2668
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index 501899d..64caa94 100644
*** a/src/include/catalog/pg_constraint.h
--- /bsrc/include/catalog/pg_constraint.h
*************** CATALOG(pg_constraint,2606)
*** 71,76 ****
--- 71,82 ----
  	char		confdeltype;	/* foreign key's ON DELETE action */
  	char		confmatchtype;	/* foreign key's match type */
  
+ 	/* Has a local definition (hence, do not drop when coninhcount is 0) */
+ 	bool		conislocal;
+ 
+ 	/* Number of times inherited from direct parent relation(s) */
+ 	int4		coninhcount;
+ 
  	/*
  	 * VARIABLE LENGTH FIELDS start here.  These fields may be NULL, too.
  	 */
*************** typedef FormData_pg_constraint *Form_pg_
*** 125,150 ****
   *		compiler constants for pg_constraint
   * ----------------
   */
! #define Natts_pg_constraint					18
! #define Anum_pg_constraint_conname			1
! #define Anum_pg_constraint_connamespace		2
! #define Anum_pg_constraint_contype			3
! #define Anum_pg_constraint_condeferrable	4
! #define Anum_pg_constraint_condeferred		5
! #define Anum_pg_constraint_conrelid			6
! #define Anum_pg_constraint_contypid			7
! #define Anum_pg_constraint_confrelid		8
! #define Anum_pg_constraint_confupdtype		9
! #define Anum_pg_constraint_confdeltype		10
! #define Anum_pg_constraint_confmatchtype	11
! #define Anum_pg_constraint_conkey			12
! #define Anum_pg_constraint_confkey			13
! #define Anum_pg_constraint_conpfeqop		14
! #define Anum_pg_constraint_conppeqop		15
! #define Anum_pg_constraint_conffeqop		16
! #define Anum_pg_constraint_conbin			17
! #define Anum_pg_constraint_consrc			18
! 
  
  /* Valid values for contype */
  #define CONSTRAINT_CHECK			'c'
--- 131,157 ----
   *		compiler constants for pg_constraint
   * ----------------
   */
! #define Natts_pg_constraint					20
! #define	Anum_pg_constraint_conname			1
! #define	Anum_pg_constraint_connamespace		2
! #define	Anum_pg_constraint_contype			3
! #define	Anum_pg_constraint_condeferrable	4
! #define	Anum_pg_constraint_condeferred		5
! #define	Anum_pg_constraint_conrelid			6
! #define	Anum_pg_constraint_contypid			7
! #define	Anum_pg_constraint_confrelid		8
! #define	Anum_pg_constraint_confupdtype		9
! #define	Anum_pg_constraint_confdeltype		10
! #define	Anum_pg_constraint_confmatchtype	11
! #define	Anum_pg_constraint_conislocal		12
! #define	Anum_pg_constraint_coninhcount		13
! #define	Anum_pg_constraint_conkey			14
! #define	Anum_pg_constraint_confkey			15
! #define	Anum_pg_constraint_conpfeqop		16
! #define	Anum_pg_constraint_conppeqop		17
! #define	Anum_pg_constraint_conffeqop		18
! #define	Anum_pg_constraint_conbin			19
! #define	Anum_pg_constraint_consrc			20
  
  /* Valid values for contype */
  #define CONSTRAINT_CHECK			'c'
*************** extern Oid CreateConstraintEntry(const c
*** 192,198 ****
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc);
  
  extern void RemoveConstraintById(Oid conId);
  extern void RenameConstraintById(Oid conId, const char *newname);
--- 199,207 ----
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc,
! 					  const bool conIsLocal,
! 					  const int conInhCout);
  
  extern void RemoveConstraintById(Oid conId);
  extern void RenameConstraintById(Oid conId, const char *newname);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 54dfe7c..cd965ff 100644
*** a/src/include/nodes/parsenodes.h
--- /bsrc/include/nodes/parsenodes.h
*************** typedef enum AlterTableType
*** 895,901 ****
  	AT_ProcessedConstraint,		/* pre-processed add constraint (local in
  								 * parser/parse_utilcmd.c) */
  	AT_DropConstraint,			/* drop constraint */
! 	AT_DropConstraintQuietly,	/* drop constraint, no error/warning (local in
  								 * commands/tablecmds.c) */
  	AT_AlterColumnType,			/* alter column type */
  	AT_ChangeOwner,				/* change owner */
--- 895,901 ----
  	AT_ProcessedConstraint,		/* pre-processed add constraint (local in
  								 * parser/parse_utilcmd.c) */
  	AT_DropConstraint,			/* drop constraint */
! 	AT_DropConstraintRecurse,	/* drop constraint recursion (local in
  								 * commands/tablecmds.c) */
  	AT_AlterColumnType,			/* alter column type */
  	AT_ChangeOwner,				/* change owner */
*************** typedef struct Constraint
*** 1158,1163 ****
--- 1158,1165 ----
  	List	   *options;		/* options from WITH clause */
  	char	   *indexspace;		/* index tablespace for PKEY/UNIQUE
  								 * constraints; NULL for default */
+ 	bool is_local;
+ 	int inhcount;
  } Constraint;
  
  /* ----------
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 76248de..b99da72 100644
*** a/src/include/utils/syscache.h
--- /bsrc/include/utils/syscache.h
***************
*** 28,81 ****
   *		Keep them in alphabetical order.
   */
  
! #define AGGFNOID			0
! #define AMNAME				1
! #define AMOID				2
! #define AMOPOPID			3
! #define AMOPSTRATEGY		4
! #define AMPROCNUM			5
! #define ATTNAME				6
! #define ATTNUM				7
! #define AUTHMEMMEMROLE		8
! #define AUTHMEMROLEMEM		9
! #define AUTHNAME			10
! #define AUTHOID				11
! #define CASTSOURCETARGET	12
! #define CLAAMNAMENSP		13
! #define CLAOID				14
! #define CONDEFAULT			15
! #define CONNAMENSP			16
! #define CONSTROID			17
! #define CONVOID				18
! #define DATABASEOID			19
! #define ENUMOID				20
! #define ENUMTYPOIDNAME		21
! #define INDEXRELID			22
! #define LANGNAME			23
! #define LANGOID				24
! #define NAMESPACENAME		25
! #define NAMESPACEOID		26
! #define OPERNAMENSP			27
! #define OPEROID				28
! #define OPFAMILYAMNAMENSP	29
! #define OPFAMILYOID			30
! #define PROCNAMEARGSNSP		31
! #define PROCOID				32
! #define RELNAMENSP			33
! #define RELOID				34
! #define RULERELNAME			35
! #define STATRELATT			36
! #define TSCONFIGMAP			37
! #define TSCONFIGNAMENSP		38
! #define TSCONFIGOID			39
! #define TSDICTNAMENSP		40
! #define TSDICTOID			41
! #define TSPARSERNAMENSP		42
! #define TSPARSEROID			43
! #define TSTEMPLATENAMENSP	44
! #define TSTEMPLATEOID		45
! #define TYPENAMENSP			46
! #define TYPEOID				47
  
  extern void InitCatalogCache(void);
  extern void InitCatalogCachePhase2(void);
--- 28,84 ----
   *		Keep them in alphabetical order.
   */
  
! enum SysCacheIdent {
! 	AGGFNOID = 0,
! 	AMNAME,
! 	AMOID,
! 	AMOPOPID,
! 	AMOPSTRATEGY,
! 	AMPROCNUM,
! 	ATTNAME,
! 	ATTNUM,
! 	AUTHMEMMEMROLE,
! 	AUTHMEMROLEMEM,
! 	AUTHNAME,
! 	AUTHOID,
! 	CASTSOURCETARGET,
! 	CLAAMNAMENSP,
! 	CLAOID,
! 	CONDEFAULT,
! 	CONNAMENSP,
! 	CONSTRNAME,
! 	CONSTROID,
! 	CONVOID,
! 	DATABASEOID,
! 	ENUMOID,
! 	ENUMTYPOIDNAME,
! 	INDEXRELID,
! 	LANGNAME,
! 	LANGOID,
! 	NAMESPACENAME,
! 	NAMESPACEOID,
! 	OPERNAMENSP,
! 	OPEROID,
! 	OPFAMILYAMNAMENSP,
! 	OPFAMILYOID,
! 	PROCNAMEARGSNSP,
! 	PROCOID,
! 	RELNAMENSP,
! 	RELOID,
! 	RULERELNAME,
! 	STATRELATT,
! 	TSCONFIGMAP,
! 	TSCONFIGNAMENSP,
! 	TSCONFIGOID,
! 	TSDICTNAMENSP,
! 	TSDICTOID,
! 	TSPARSERNAMENSP,
! 	TSPARSEROID,
! 	TSTEMPLATENAMENSP,
! 	TSTEMPLATEOID,
! 	TYPENAMENSP,
! 	TYPEOID,
! };
  
  extern void InitCatalogCache(void);
  extern void InitCatalogCachePhase2(void);
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 51d5afa..25e0e8e 100644
*** a/src/test/regress/expected/alter_table.out
--- /bsrc/test/regress/expected/alter_table.out
*************** create table atacc1 (test int);
*** 383,391 ****
  create table atacc2 (test2 int);
  create table atacc3 (test3 int) inherits (atacc1, atacc2);
  alter table only atacc2 add constraint foo check (test2>0);
  -- fail and then succeed on atacc2
  insert into atacc2 (test2) values (-3);
- ERROR:  new row for relation "atacc2" violates check constraint "foo"
  insert into atacc2 (test2) values (3);
  -- both succeed on atacc3
  insert into atacc3 (test2) values (-3);
--- 383,391 ----
  create table atacc2 (test2 int);
  create table atacc3 (test3 int) inherits (atacc1, atacc2);
  alter table only atacc2 add constraint foo check (test2>0);
+ ERROR:  constraint must be added to child tables too
  -- fail and then succeed on atacc2
  insert into atacc2 (test2) values (-3);
  insert into atacc2 (test2) values (3);
  -- both succeed on atacc3
  insert into atacc3 (test2) values (-3);
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index f81776f..f96e2a5 100644
*** a/src/test/regress/expected/inherit.out
--- /bsrc/test/regress/expected/inherit.out
*************** drop function p2text(p2);
*** 692,694 ****
--- 692,770 ----
  drop table c1;
  drop table p2;
  drop table p1;
+ CREATE TABLE ac (aa TEXT);
+ alter table ac add constraint ac_check check (aa is not null);
+ CREATE TABLE bc (bb TEXT) INHERITS (ac);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+  ac_check | c       | f          |           1 | (aa IS NOT NULL)
+ (2 rows)
+ 
+ alter table bc drop constraint ac_check;
+ ERROR:  cannot drop inherited constraint "ac_check" of relation "bc"
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname | contype | conislocal | coninhcount | consrc 
+ ---------+---------+------------+-------------+--------
+ (0 rows)
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+  ac_check | c       | f          |           1 | (aa IS NOT NULL)
+ (2 rows)
+ 
+ insert into ac (aa) values (NULL);
+ ERROR:  new row for relation "ac" violates check constraint "ac_check"
+ insert into bc (aa) values (NULL);
+ ERROR:  new row for relation "bc" violates check constraint "ac_check"
+ alter table bc drop constraint ac_check;
+ ERROR:  cannot drop inherited constraint "ac_check" of relation "bc"
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname | contype | conislocal | coninhcount | consrc 
+ ---------+---------+------------+-------------+--------
+ (0 rows)
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ alter table bc no inherit ac;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+ (2 rows)
+ 
+ alter table bc drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+ (1 row)
+ 
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname | contype | conislocal | coninhcount | consrc 
+ ---------+---------+------------+-------------+--------
+ (0 rows)
+ 
+ drop table bc;
+ drop table ac;
+ create table ac (a int constraint check_a check (a <> 0));
+ create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac);
+ NOTICE:  merging column "a" with inherited definition
+ NOTICE:  merging constraint "check_a" with inherited definition
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname | contype | conislocal | coninhcount |  consrc  
+ ---------+---------+------------+-------------+----------
+  check_a | c       | t          |           0 | (a <> 0)
+  check_a | c       | f          |           1 | (a <> 0)
+  check_b | c       | t          |           0 | (b <> 0)
+ (3 rows)
+ 
+ drop table bc;
+ drop table ac;
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
index b0499a6..e72143d 100644
*** a/src/test/regress/sql/inherit.sql
--- /bsrc/test/regress/sql/inherit.sql
*************** drop function p2text(p2);
*** 196,198 ****
--- 196,236 ----
  drop table c1;
  drop table p2;
  drop table p1;
+ 
+ CREATE TABLE ac (aa TEXT);
+ alter table ac add constraint ac_check check (aa is not null);
+ CREATE TABLE bc (bb TEXT) INHERITS (ac);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ 
+ alter table bc drop constraint ac_check;
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ 
+ insert into ac (aa) values (NULL);
+ insert into bc (aa) values (NULL);
+ 
+ alter table bc drop constraint ac_check;
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ alter table bc no inherit ac;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ alter table bc drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ 
+ drop table bc;
+ drop table ac;
+ 
+ create table ac (a int constraint check_a check (a <> 0));
+ create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ 
+ 
+ drop table bc;
+ drop table ac;
#7Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alex Hunsaker (#6)
Re: [PATCHES] [badalex@gmail.com: Re: [BUGS] Problem identifying constraints which should not be inherited]

"Alex Hunsaker" <badalex@gmail.com> writes:

[ patch to fix behavior of inherited constraints ]

Looking over this patch, I see that it introduces a syscache on
pg_constraint (conrelid, conname), which requires a unique index
underlying it. This is not workable because domain constraint
entries in pg_constraint will have conrelid = 0. The index would
therefore have the effect of forbidding the same constraint name
to be used for two different domains' constraints.

The fact that pg_constraint stores both relation and domain constraints
is a fairly ugly crock, not least because it means there is no natural
primary key for the table. I've thought for some time that we should
split it into two catalogs. (We could provide a union view to avoid
breaking clients that look at it.) However it seems a bit ill-advised
to tackle that change as an essential part of this patch.

Was there any particularly strong reason why you introduced the syscache
instead of working with the available indexes?

regards, tom lane

#8Alex Hunsaker
badalex@gmail.com
In reply to: Tom Lane (#7)
Re: [PATCHES] [badalex@gmail.com: Re: [BUGS] Problem identifying constraints which should not be inherited]

On Tue, May 6, 2008 at 7:57 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

"Alex Hunsaker" <badalex@gmail.com> writes:

[ patch to fix behavior of inherited constraints ]

Looking over this patch, I see that it introduces a syscache on
pg_constraint (conrelid, conname), which requires a unique index
underlying it. This is not workable because domain constraint
entries in pg_constraint will have conrelid = 0. The index would
therefore have the effect of forbidding the same constraint name
to be used for two different domains' constraints.

The fact that pg_constraint stores both relation and domain constraints
is a fairly ugly crock, not least because it means there is no natural
primary key for the table. I've thought for some time that we should
split it into two catalogs. (We could provide a union view to avoid
breaking clients that look at it.) However it seems a bit ill-advised
to tackle that change as an essential part of this patch.

Was there any particularly strong reason why you introduced the syscache
instead of working with the available indexes?

regards, tom lane

None other than the syscache stuff was way easier to work with than
the 25-50 lines of boilerplate code that Ill need everywhere I use
CONSTRNAME. (see the hunk to MergeAttributesIntoExistsing for an
example of what i mean). Not a big deal though, NikhilS was not sure
about those changes in the first place.

Ill just rip it out for now. Patch forthcoming.

#9Alex Hunsaker
badalex@gmail.com
In reply to: Alex Hunsaker (#8)
2 attachment(s)
Re: [badalex@gmail.com: Re: [BUGS] Problem identifying constraints which should not be inherited]

On Tue, May 6, 2008 at 9:20 PM, Alex Hunsaker <badalex@gmail.com> wrote:

Tom Lane <tgl@sss.pgh.pa.us> wrote:
Was there any particularly strong reason why you introduced the syscache
instead of working with the available indexes?

regards, tom lane

None other than the syscache stuff was way easier to work with than
the 25-50 lines of boilerplate code that Ill need everywhere I use
CONSTRNAME. (see the hunk to MergeAttributesIntoExistsing for an
example of what i mean). Not a big deal though, NikhilS was not sure
about those changes in the first place.

Ill just rip it out for now. Patch forthcoming.

Find attached a diff from v4-v5, and a full patch.

src/backend/commands/tablecmds.c | 242 +++++++++++++++++++++++-------------
src/backend/utils/cache/syscache.c | 12 --
src/include/catalog/indexing.h | 2 -
src/include/utils/syscache.h | 1 -
4 files changed, 153 insertions(+), 104 deletions(-)

Currently this loops through all the constraints for a relation (old
behavior of MergeAttributesIntoExisting)... Do you think its worth
adding a non-unique index to speed this up? If so I can whip up a
patch real quick if you think its worth it... else

Attachments:

inherited_constraints_v4-v5.patchapplication/octet-stream; name=inherited_constraints_v4-v5.patchDownload
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 50a72c3..91299f9 100644
*** a/src/backend/commands/tablecmds.c
--- /bsrc/backend/commands/tablecmds.c
*************** ATExecDropConstraint(Relation rel, const
*** 4999,5037 ****
  					 DropBehavior behavior,
  					 bool recurse, bool recursing)
  {
! 	HeapTuple	tuple;
! 	List		*children;
! 	Relation	conrel;
! 	Form_pg_constraint con;
  
! 	conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
  
  	/* At top level, permission check was done in ATPrepCmd, else do it */
  	if (recursing)
  		ATSimplePermissions(rel, false);
  
! 	tuple = SearchSysCache(CONSTRNAME,
! 						   ObjectIdGetDatum(RelationGetRelid(rel)),
! 						   CStringGetDatum(constrName),
! 						   0, 0);
  
! 	if (!HeapTupleIsValid(tuple))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_UNDEFINED_COLUMN),
! 				 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
! 						constrName, RelationGetRelationName(rel))));
  
! 	con = (Form_pg_constraint) GETSTRUCT(tuple);
  
! 	/* Don't drop inherited constraints */
! 	if (con->coninhcount > 0 && !recursing)
! 		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
  				 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
  						constrName,
  						RelationGetRelationName(rel))));
  
! 	ReleaseSysCache(tuple);
  
  	/*
  	 * Propagate to children as appropriate.  Unlike most other ALTER
--- 4999,5050 ----
  					 DropBehavior behavior,
  					 bool recurse, bool recursing)
  {
! 	List				*children;
! 	Relation			conrel;
! 	Form_pg_constraint	con;
! 	SysScanDesc			scan;
! 	ScanKeyData			key;
! 	HeapTuple			tuple;
! 	bool				found = false;
  
! 	conrel = heap_open(ConstraintRelationId, AccessExclusiveLock);
  
  	/* At top level, permission check was done in ATPrepCmd, else do it */
  	if (recursing)
  		ATSimplePermissions(rel, false);
  
! 	/* Find the constraint, make sure we don't drop it if its inherited */
! 	ScanKeyInit(&key,
! 				Anum_pg_constraint_conrelid,
! 				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(rel)));
! 	scan = systable_beginscan(conrel, ConstraintRelidIndexId,
! 							  true, SnapshotNow, 1, &key);
  
! 	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
! 	{
! 		con = (Form_pg_constraint) GETSTRUCT(tuple);
  
! 		if (strcmp(NameStr(con->conname),
! 				   constrName) != 0)
! 			continue;
  
! 		/* Don't drop inherited constraints */
! 		if (con->contype == CONSTRAINT_CHECK && con->coninhcount > 0 && !recursing)
! 			ereport(ERROR,
  				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
  				 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
  						constrName,
  						RelationGetRelationName(rel))));
  
! 		found = true;
! 	}
! 
! 	if(!found)
! 		elog(ERROR, "failed to find constraint \"%s\" of relation %u",
! 			 constrName, RelationGetRelid(rel));
! 
! 	systable_endscan(scan);
  
  	/*
  	 * Propagate to children as appropriate.  Unlike most other ALTER
*************** ATExecDropConstraint(Relation rel, const
*** 5048,5109 ****
  		{
  			Oid			childrelid = lfirst_oid(child);
  			Relation	childrel;
- 			Form_pg_constraint childcon;
  
  			childrel = heap_open(childrelid, AccessExclusiveLock);
  			CheckTableNotInUse(childrel, "ALTER TABLE");
  
! 			tuple = SearchSysCacheCopy(CONSTRNAME,
! 								   ObjectIdGetDatum(childrelid),
! 								   CStringGetDatum(constrName),
! 								   0, 0);
! 			if (!HeapTupleIsValid(tuple))
! 				elog(ERROR, "cache lookup failed for constraint \"%s\" of relation %u",
! 					 constrName, childrelid);
! 
! 			childcon = (Form_pg_constraint) GETSTRUCT(tuple);
! 
! 			if (childcon->coninhcount <= 0)
! 				elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
! 					 childrelid, constrName);
  
! 			if (recurse)
  			{
! 				if (childcon->coninhcount == 1 && !childcon->conislocal)
  				{
! 					ATExecDropConstraint(childrel, constrName, behavior, true, true);
  				}
  				else
  				{
! 					childcon->coninhcount--;
  
! 					simple_heap_update(conrel, &tuple->t_self, tuple);
! 					CatalogUpdateIndexes(conrel, tuple);
! 				}
! 			}
! 			else
! 			{
! 				/*
! 				 * If we were told to drop ONLY in this table (no recursion),
! 				 * we need to mark the inheritors' attribute as locally
! 				 * defined rather than inherited.
! 				 */
  
! 				childcon->coninhcount--;
! 				childcon->conislocal = true;
  
! 				simple_heap_update(conrel, &tuple->t_self, tuple);
! 				CatalogUpdateIndexes(conrel, tuple);
  			}
  
! 			heap_freetuple(tuple);
  			heap_close(childrel, AccessExclusiveLock);
  		}
  	}
  
  	RemoveRelConstraints(rel, constrName, behavior);
  
! 	heap_close(conrel, RowExclusiveLock);
  }
  
  /*
--- 5061,5144 ----
  		{
  			Oid			childrelid = lfirst_oid(child);
  			Relation	childrel;
  
  			childrel = heap_open(childrelid, AccessExclusiveLock);
  			CheckTableNotInUse(childrel, "ALTER TABLE");
  
! 			ScanKeyInit(&key,
! 				Anum_pg_constraint_conrelid,
! 				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(childrelid));
! 			scan = systable_beginscan(conrel, ConstraintRelidIndexId,
! 							  true, SnapshotNow, 1, &key);
! 
! 			found = false;
  
! 			while (HeapTupleIsValid(tuple = systable_getnext(scan)))
  			{
! 				HeapTuple copy_tuple;
! 
! 				con = (Form_pg_constraint) GETSTRUCT(tuple);
! 
! 				if (con->contype != CONSTRAINT_CHECK)
! 					continue;
! 
! 				if (strcmp(NameStr(con->conname),
! 						   constrName) != 0)
! 					continue;
! 				else
! 					found = true;
! 
! 				if (con->coninhcount <= 0)
! 					elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
! 						childrelid, constrName);
! 
! 				copy_tuple = heap_copytuple(tuple);
! 				con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
! 
! 				if (recurse)
  				{
! 					if (con->coninhcount == 1 && !con->conislocal)
! 					{
! 						ATExecDropConstraint(childrel, constrName, behavior, true, true);
! 					}
! 					else
! 					{
! 						con->coninhcount--;
! 						simple_heap_update(conrel, &copy_tuple->t_self, copy_tuple);
! 						CatalogUpdateIndexes(conrel, copy_tuple);
! 					}
  				}
  				else
  				{
! 					/*
! 					 * If we were told to drop ONLY in this table (no recursion),
! 					 * we need to mark the inheritors' attribute as locally
! 					 * defined rather than inherited.
! 					 */
  
! 					con->coninhcount--;
! 					con->conislocal = true;
  
! 					simple_heap_update(conrel, &copy_tuple->t_self, copy_tuple);
! 					CatalogUpdateIndexes(conrel, copy_tuple);
! 				}
  
! 				heap_freetuple(copy_tuple);
  			}
  
! 			if (!found)
! 					elog(ERROR, "failed to find constraint \"%s\" of relation %u",
! 						 constrName, childrelid);
! 
! 			systable_endscan(scan);
  			heap_close(childrel, AccessExclusiveLock);
  		}
  	}
  
  	RemoveRelConstraints(rel, constrName, behavior);
  
! 	heap_close(conrel, AccessExclusiveLock);
  }
  
  /*
*************** MergeAttributesIntoExisting(Relation chi
*** 6621,6683 ****
   * make it possible to ensure no records are mistakenly inserted into the
   * master in partitioned tables rather than the appropriate child.
   *
   */
  static void
  MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
  {
! 	Relation	catalogRelation;
! 	TupleDesc	tupleDesc;
! 	SysScanDesc scan;
! 	ScanKeyData key;
! 	HeapTuple	constraintTuple;
  
! 	catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
! 	tupleDesc = RelationGetDescr(catalogRelation);
  
! 	ScanKeyInit(&key,
  				Anum_pg_constraint_conrelid,
  				BTEqualStrategyNumber, F_OIDEQ,
  				ObjectIdGetDatum(RelationGetRelid(parent_rel)));
! 	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
! 							  true, SnapshotNow, 1, &key);
  
! 	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
  	{
! 		HeapTuple childTuple;
! 		Form_pg_constraint child_con;
! 		Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
  
  		if (parent_con->contype != CONSTRAINT_CHECK)
  			continue;
  
! 		childTuple = SearchSysCacheCopy(CONSTRNAME,
! 									ObjectIdGetDatum(RelationGetRelid(child_rel)),
! 									CStringGetDatum(NameStr(parent_con->conname)),
! 									0, 0);
  
! 		if (!HeapTupleIsValid(childTuple))
! 			ereport(ERROR,
! 					(errcode(ERRCODE_DATATYPE_MISMATCH),
! 					 errmsg("child table is missing constraint \"%s\"",
! 							NameStr(parent_con->conname))));
  
! 		child_con = (Form_pg_constraint) GETSTRUCT(childTuple);
  
! 		if (!ConstraintIsEquiv(constraintTuple, childTuple, tupleDesc))
! 			ereport(ERROR,
! 					(errcode(ERRCODE_DATATYPE_MISMATCH),
! 					 errmsg("constraint definition for check constraint \"%s\" does not match",
! 							NameStr(parent_con->conname))));
  
! 		/* increment coninhcount */
! 		child_con->coninhcount++;
! 		simple_heap_update(catalogRelation, &childTuple->t_self, childTuple);
! 		CatalogUpdateIndexes(catalogRelation, childTuple);
! 		heap_freetuple(childTuple);
  	}
  
! 	systable_endscan(scan);
! 	heap_close(catalogRelation, RowExclusiveLock);
  }
  
  /*
--- 6656,6747 ----
   * make it possible to ensure no records are mistakenly inserted into the
   * master in partitioned tables rather than the appropriate child.
   *
+  * XXX This is O(N^2) which may be an issue with tables with hundreds of
+  * constraints. As long as tables have more like 10 constraints it shouldn't be
+  * 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;
! 	SysScanDesc parent_scan;
! 	ScanKeyData parent_key;
! 	HeapTuple	constraint_tuple;
  
! 	catalog_relation = heap_open(ConstraintRelationId, RowExclusiveLock);
! 	tuple_desc = RelationGetDescr(catalog_relation);
  
! 	/* First gather up the parents constraint definitions */
! 	ScanKeyInit(&parent_key,
  				Anum_pg_constraint_conrelid,
  				BTEqualStrategyNumber, F_OIDEQ,
  				ObjectIdGetDatum(RelationGetRelid(parent_rel)));
! 	parent_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId,
! 							  true, SnapshotNow, 1, &parent_key);
  
! 	while (HeapTupleIsValid(constraint_tuple = systable_getnext(parent_scan)))
  	{
! 		Form_pg_constraint	parent_con = (Form_pg_constraint) GETSTRUCT(constraint_tuple);
! 		HeapTuple			child_tuple;
! 		Form_pg_constraint	child_con;
! 		SysScanDesc			child_scan;
! 		ScanKeyData			child_key;
! 		bool				found = false;
  
  		if (parent_con->contype != CONSTRAINT_CHECK)
  			continue;
  
! 		/* Now look for matching child matches */
! 		ScanKeyInit(&child_key,
! 				Anum_pg_constraint_conrelid,
! 				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(child_rel)));
! 		child_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId,
! 							  true, SnapshotNow, 1, &child_key);
! 
! 		while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
! 		{
! 			HeapTuple child_copy;
  
! 			child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
  
! 			if (child_con->contype != CONSTRAINT_CHECK)
! 				continue;
  
! 			child_copy = heap_copytuple(child_tuple);
! 			child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
! 
! 			if (strcmp(NameStr(parent_con->conname),
! 					   NameStr(child_con->conname)) != 0)
! 				continue;
! 
! 			if (!ConstraintIsEquiv(constraint_tuple, child_tuple, tuple_desc))
! 				ereport(ERROR,
! 				(errcode(ERRCODE_DATATYPE_MISMATCH),
! 				 errmsg("constraint definition for check constraint \"%s\" does not match",
! 					NameStr(parent_con->conname))));
  
! 			found = true;
! 
! 			/* increment coninhcount */
! 			child_con->coninhcount++;
! 			simple_heap_update(catalog_relation, &child_copy->t_self, child_copy);
! 			CatalogUpdateIndexes(catalog_relation, child_copy);
! 			heap_freetuple(child_copy);
! 		}
! 
! 		systable_endscan(child_scan);
! 
! 		if (!found)
! 			ereport(ERROR,
! 				(errcode(ERRCODE_DATATYPE_MISMATCH),
! 				 errmsg("child table is missing constraint \"%s\"",
! 						NameStr(parent_con->conname))));
  	}
  
! 	systable_endscan(parent_scan);
! 	heap_close(catalog_relation, RowExclusiveLock);
  }
  
  /*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 789c6a4..7ff0e8a 100644
*** a/src/backend/utils/cache/syscache.c
--- /bsrc/backend/utils/cache/syscache.c
*************** static const struct cachedesc cacheinfo[
*** 305,322 ****
  		},
  		128
  	},
- 	{ConstraintRelationId,		// CONSTRNAME
- 		ConstraintRelidNameIndexId,
- 		Anum_pg_constraint_conrelid,
- 		2,
- 		{
- 			Anum_pg_constraint_conrelid,
- 			Anum_pg_constraint_conname,
- 			0,
- 			0
- 		},
- 		256,
- 	},
  	{ConstraintRelationId,		/* CONSTROID */
  		ConstraintOidIndexId,
  		0,
--- 305,310 ----
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index d962930..7dc5215 100644
*** a/src/include/catalog/indexing.h
--- /bsrc/include/catalog/indexing.h
*************** DECLARE_INDEX(pg_constraint_contypid_ind
*** 121,128 ****
  #define ConstraintTypidIndexId	2666
  DECLARE_UNIQUE_INDEX(pg_constraint_oid_index, 2667, on pg_constraint using btree(oid oid_ops));
  #define ConstraintOidIndexId  2667
- DECLARE_UNIQUE_INDEX(pg_constraint_relid_coname_index, 2700, on pg_constraint using btree(conrelid oid_ops, conname name_ops));
- #define ConstraintRelidNameIndexId  2700
  
  DECLARE_UNIQUE_INDEX(pg_conversion_default_index, 2668, on pg_conversion using btree(connamespace oid_ops, conforencoding int4_ops, contoencoding int4_ops, oid oid_ops));
  #define ConversionDefaultIndexId  2668
--- 121,126 ----
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index b99da72..c97a256 100644
*** a/src/include/utils/syscache.h
--- /bsrc/include/utils/syscache.h
*************** enum SysCacheIdent {
*** 46,52 ****
  	CLAOID,
  	CONDEFAULT,
  	CONNAMENSP,
- 	CONSTRNAME,
  	CONSTROID,
  	CONVOID,
  	DATABASEOID,
--- 46,51 ----
inherited_constraints_v5.patchapplication/octet-stream; name=inherited_constraints_v5.patchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index a15ac6c..8c56652 100644
*** a/doc/src/sgml/catalogs.sgml
--- /bdoc/src/sgml/catalogs.sgml
***************
*** 1908,1913 ****
--- 1908,1927 ----
       </row>
  
       <row>
+       <entry><structfield>conislocal</structfield></entry>
+       <entry><type>bool</type></entry>
+       <entry></entry>
+       <entry>This constraint is defined locally in the relation. Note that a constraint can be locally defined and inherited simultaneously</entry>
+      </row>
+ 
+      <row>
+       <entry><structfield>coninhcount</structfield></entry>
+       <entry><type>int4</type></entry>
+       <entry></entry>
+       <entry>The number of direct ancestors this constraint has. A constraint with a nonzero number of ancestors cannot be dropped nor renamed</entry>
+      </row>
+ 
+      <row>
        <entry><structfield>conkey</structfield></entry>
        <entry><type>int2[]</type></entry>
        <entry><literal><link linkend="catalog-pg-attribute"><structname>pg_attribute</structname></link>.attnum</></entry>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 3553c3b..783186c 100644
*** a/src/backend/catalog/heap.c
--- /bsrc/backend/catalog/heap.c
*************** static Oid AddNewRelationType(const char
*** 77,83 ****
  				   char new_rel_kind,
  				   Oid new_array_type);
  static void RelationRemoveInheritance(Oid relid);
! static void StoreRelCheck(Relation rel, char *ccname, char *ccbin);
  static void StoreConstraints(Relation rel, TupleDesc tupdesc);
  static void SetRelationNumChecks(Relation rel, int numchecks);
  static List *insert_ordered_unique_oid(List *list, Oid datum);
--- 77,83 ----
  				   char new_rel_kind,
  				   Oid new_array_type);
  static void RelationRemoveInheritance(Oid relid);
! static void StoreRelCheck(Relation rel, char *ccname, char *ccbin, const bool is_local, const int inhcount);
  static void StoreConstraints(Relation rel, TupleDesc tupdesc);
  static void SetRelationNumChecks(Relation rel, int numchecks);
  static List *insert_ordered_unique_oid(List *list, Oid datum);
*************** StoreAttrDefault(Relation rel, AttrNumbe
*** 1531,1537 ****
   * in the pg_class entry for the relation.
   */
  static void
! StoreRelCheck(Relation rel, char *ccname, char *ccbin)
  {
  	Node	   *expr;
  	char	   *ccsrc;
--- 1531,1537 ----
   * in the pg_class entry for the relation.
   */
  static void
! StoreRelCheck(Relation rel, char *ccname, char *ccbin, const bool is_local, const int inhcount)
  {
  	Node	   *expr;
  	char	   *ccsrc;
*************** StoreRelCheck(Relation rel, char *ccname
*** 1608,1614 ****
  						  InvalidOid,	/* no associated index */
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc);		/* Source form check constraint */
  
  	pfree(ccsrc);
  }
--- 1608,1616 ----
  						  InvalidOid,	/* no associated index */
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc, /* Source form check constraint */
! 						  is_local, /* conislocal */
! 						  inhcount); /* coninhcount */
  
  	pfree(ccsrc);
  }
*************** StoreConstraints(Relation rel, TupleDesc
*** 1643,1649 ****
  
  	for (i = 0; i < constr->num_check; i++)
  		StoreRelCheck(rel, constr->check[i].ccname,
! 					  constr->check[i].ccbin);
  
  	if (constr->num_check > 0)
  		SetRelationNumChecks(rel, constr->num_check);
--- 1645,1653 ----
  
  	for (i = 0; i < constr->num_check; i++)
  		StoreRelCheck(rel, constr->check[i].ccname,
! 					  constr->check[i].ccbin,
! 					  constr->check[i].is_local,
! 					  constr->check[i].inhcount);
  
  	if (constr->num_check > 0)
  		SetRelationNumChecks(rel, constr->num_check);
*************** AddRelationRawConstraints(Relation rel,
*** 1863,1869 ****
  		/*
  		 * OK, store it.
  		 */
! 		StoreRelCheck(rel, ccname, nodeToString(expr));
  
  		numchecks++;
  
--- 1867,1873 ----
  		/*
  		 * OK, store it.
  		 */
! 		StoreRelCheck(rel, ccname, nodeToString(expr), cdef->is_local, cdef->inhcount);
  
  		numchecks++;
  
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index f07d9ae..6db3084 100644
*** a/src/backend/catalog/index.c
--- /bsrc/backend/catalog/index.c
*************** index_create(Oid heapRelationId,
*** 716,722 ****
  										   InvalidOid,	/* no associated index */
  										   NULL,		/* no check constraint */
  										   NULL,
! 										   NULL);
  
  			referenced.classId = ConstraintRelationId;
  			referenced.objectId = conOid;
--- 716,724 ----
  										   InvalidOid,	/* no associated index */
  										   NULL,		/* no check constraint */
  										   NULL,
! 										   NULL,
! 										   true, /* islocal */
! 										   0); /* inhcount */
  
  			referenced.classId = ConstraintRelationId;
  			referenced.objectId = conOid;
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index d9b20fd..eafc143 100644
*** a/src/backend/catalog/pg_constraint.c
--- /bsrc/backend/catalog/pg_constraint.c
*************** CreateConstraintEntry(const char *constr
*** 60,66 ****
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc)
  {
  	Relation	conDesc;
  	Oid			conOid;
--- 60,68 ----
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc,
! 					  const bool conIsLocal,
! 					  const int conInhCount)
  {
  	Relation	conDesc;
  	Oid			conOid;
*************** CreateConstraintEntry(const char *constr
*** 171,176 ****
--- 173,181 ----
  	else
  		nulls[Anum_pg_constraint_conffeqop - 1] = 'n';
  
+ 	values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
+ 	values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
+ 
  	/*
  	 * initialize the binary form of the check constraint.
  	 */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 91b64ac..91299f9 100644
*** a/src/backend/commands/tablecmds.c
--- /bsrc/backend/commands/tablecmds.c
*************** static void truncate_check_rel(Relation 
*** 170,175 ****
--- 170,177 ----
  static List *MergeAttributes(List *schema, List *supers, bool istemp,
  				List **supOids, List **supconstr, int *supOidCount);
  static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
+ static void MergeInheritedConstraints(Relation rel, List *old_constraints,
+ 									List *new_constriants);
  static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
  static void add_nonduplicate_constraint(Constraint *cdef,
  							ConstrCheck *check, int *ncheck);
*************** static void ATExecDropColumn(Relation re
*** 232,245 ****
  				 bool recurse, bool recursing);
  static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
  			   IndexStmt *stmt, bool is_rebuild);
  static void ATExecAddConstraint(AlteredTableInfo *tab, Relation rel,
  					Node *newConstraint);
  static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
  						  FkConstraint *fkconstraint);
- static void ATPrepDropConstraint(List **wqueue, Relation rel,
- 					 bool recurse, AlterTableCmd *cmd);
  static void ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior, bool quiet);
  static void ATPrepAlterColumnType(List **wqueue,
  					  AlteredTableInfo *tab, Relation rel,
  					  bool recurse, bool recursing,
--- 234,248 ----
  				 bool recurse, bool recursing);
  static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
  			   IndexStmt *stmt, bool is_rebuild);
+ static void ATPrepAddConstraint(List **wqueue, Relation rel, bool recurse,
+ 				AlterTableCmd *cmd);
  static void ATExecAddConstraint(AlteredTableInfo *tab, Relation rel,
  					Node *newConstraint);
  static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
  						  FkConstraint *fkconstraint);
  static void ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior, 
! 					 bool recurse, bool recursing);
  static void ATPrepAlterColumnType(List **wqueue,
  					  AlteredTableInfo *tab, Relation rel,
  					  bool recurse, bool recursing,
*************** static void ATExecEnableDisableRule(Rela
*** 263,269 ****
  static void ATExecAddInherit(Relation rel, RangeVar *parent);
  static void ATExecDropInherit(Relation rel, RangeVar *parent);
  static void copy_relation_data(Relation rel, SMgrRelation dst);
! 
  
  /* ----------------------------------------------------------------
   *		DefineRelation
--- 266,276 ----
  static void ATExecAddInherit(Relation rel, RangeVar *parent);
  static void ATExecDropInherit(Relation rel, RangeVar *parent);
  static void copy_relation_data(Relation rel, SMgrRelation dst);
! static bool ConstraintIsEquiv(const HeapTuple a, const HeapTuple b,
! 							  const TupleDesc tupleDesc);
! static char *decompile_conbin(HeapTuple contup, TupleDesc tupdesc);
! static void change_opfuncid(Node *node, Oid opfuncid);
! static bool change_opfuncid_walker(Node *node, void *opfuncid);
  
  /* ----------------------------------------------------------------
   *		DefineRelation
*************** DefineRelation(CreateStmt *stmt, char re
*** 488,493 ****
--- 495,503 ----
  		}
  	}
  
+ 	if (old_constraints && stmt->constraints)
+ 		MergeInheritedConstraints(rel, old_constraints, stmt->constraints);
+ 
  	/*
  	 * Parse and add the defaults/constraints, if any.
  	 */
*************** MergeAttributes(List *schema, List *supe
*** 1059,1064 ****
--- 1069,1076 ----
  				Constraint *cdef = makeNode(Constraint);
  				Node	   *expr;
  
+ 				cdef->is_local = false;
+ 				cdef->inhcount = check[i].inhcount + 1;
  				cdef->contype = CONSTR_CHECK;
  				cdef->name = pstrdup(check[i].ccname);
  				cdef->raw_expr = NULL;
*************** MergeAttributes(List *schema, List *supe
*** 1181,1186 ****
--- 1193,1294 ----
  	return schema;
  }
  
+ /*
+  * Go through constraints in new_constraints
+  * if we have a matching constraint in old_constraints and they have a
+  * matching definition, then remove the constraint from new_constraints
+  */
+ static void
+ MergeInheritedConstraints(Relation rel,
+ 						  List *old_constraints,
+ 						  List *new_constraints)
+ {
+ 	ListCell *cur_item;
+ 	ListCell *prev_item;
+ 	ListCell *listptr;
+ 
+ 	prev_item = NULL;
+ 	cur_item = list_head(new_constraints);
+ 
+ 	while (cur_item != NULL)
+ 	{
+ 		bool get_next = true;
+ 		Constraint *cdef = (Constraint *) lfirst(cur_item);
+ 
+ 		/*
+ 		 * We only merge CHECK constraints right now
+ 		 */
+ 		if (cdef->contype == CONSTR_CHECK &&
+ 			cdef->cooked_expr == NULL &&
+ 			cdef->name &&
+ 			cdef->raw_expr)
+ 		{
+ 			foreach(listptr, old_constraints)
+ 			{
+ 				Constraint *cdef1 = (Constraint *) lfirst(listptr);
+ 
+ 				if (cdef1->contype == CONSTR_CHECK &&
+ 					cdef1->name &&
+ 					strcmp(cdef->name, cdef1->name) == 0)
+ 				{
+ 					ParseState		*pstate;
+ 					RangeTblEntry	*rte;
+ 					Node			*expr;
+ 					char			*cooked;
+ 
+ 					pstate = make_parsestate(NULL);
+ 					rte = addRangeTableEntryForRelation(pstate,
+ 														rel,
+ 														NULL,
+ 														false,
+ 														true);
+ 					addRTEtoQuery(pstate, rte, true, true, true);
+ 
+ 					expr = transformExpr(pstate, cdef->raw_expr);
+ 					expr = coerce_to_boolean(pstate, expr, "CHECK");
+ 
+ 					/*
+ 					 * We need to fixup the opfuncid because it
+ 					 * got cleared in cdef1->cooked_expr
+ 					 */
+ 					change_opfuncid(expr, 0);
+ 
+ 					cooked = nodeToString(expr);
+ 
+ 					if (strcmp(cooked, cdef1->cooked_expr) == 0)
+ 					{
+ 						ereport(NOTICE,
+ 							(errmsg("merging constraint \"%s\" with inherited definition",
+ 							cdef->name)));
+ 
+ 						list_delete_cell(new_constraints, cur_item, prev_item);
+ 
+ 						pfree(cdef);
+ 						get_next = false;
+ 
+ 						if (prev_item)
+ 							cur_item = lnext(prev_item);
+ 						else
+ 							cur_item = list_head(new_constraints);
+ 					}else
+ 					{
+ 						ereport(ERROR,
+ 								(errmsg("constraint \"%s\" does not match inherited definition",
+ 								cdef->name)));
+ 					}
+ 
+ 					break;
+ 				}
+ 			}
+ 		}
+ 
+ 		if(get_next)
+ 		{
+ 			prev_item = cur_item;
+ 			cur_item = lnext(prev_item);
+ 		}
+ 	}
+ }
  
  /*
   * In multiple-inheritance situations, it's possible to inherit
*************** add_nonduplicate_constraint(Constraint *
*** 1214,1219 ****
--- 1322,1329 ----
  	/* No match on name, so add it to array */
  	check[*ncheck].ccname = cdef->name;
  	check[*ncheck].ccbin = pstrdup(cdef->cooked_expr);
+ 	check[*ncheck].inhcount = cdef->inhcount;
+ 	check[*ncheck].is_local = cdef->is_local;
  	(*ncheck)++;
  }
  
*************** varattnos_map_schema(TupleDesc old, List
*** 1312,1317 ****
--- 1422,1459 ----
  	return attmap;
  }
  
+ /*
+  * Change an opfuncid of a Node
+  *
+  * Note that the passed node tree is modified in-place!
+  */
+ static void
+ change_opfuncid(Node *node, Oid opfuncid)
+ {
+ 	Oid *o = palloc(sizeof(Oid));
+ 	*o = opfuncid;
+ 	(void) change_opfuncid_walker(node, (void*)o);
+ 	pfree(o);
+ }
+ 
+ static bool
+ change_opfuncid_walker(Node *node, void *opfuncid)
+ {
+ 	if (node == NULL)
+ 		return false;
+ 
+ 	if (IsA(node, OpExpr))
+ 	{
+ 		Oid		*o = (Oid*)opfuncid;
+ 		OpExpr	*op = (OpExpr *) node;
+ 
+ 		op->opfuncid = *o;
+ 		return false;
+ 	}
+ 	return expression_tree_walker(node, change_opfuncid_walker,
+ 								  opfuncid);
+ 
+ }
  
  /*
   * StoreCatalogInheritance
*************** ATPrepCmd(List **wqueue, Relation rel, A
*** 2044,2070 ****
  			break;
  		case AT_AddConstraint:	/* ADD CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 
! 			/*
! 			 * Currently we recurse only for CHECK constraints, never for
! 			 * foreign-key constraints.  UNIQUE/PKEY constraints won't be seen
! 			 * here.
! 			 */
! 			if (IsA(cmd->def, Constraint))
! 				ATSimpleRecursion(wqueue, rel, cmd, recurse);
! 			/* No command-specific prep needed */
  			pass = AT_PASS_ADD_CONSTR;
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 			/* Performs own recursion */
! 			ATPrepDropConstraint(wqueue, rel, recurse, cmd);
! 			pass = AT_PASS_DROP;
! 			break;
! 		case AT_DropConstraintQuietly:	/* DROP CONSTRAINT for child */
! 			ATSimplePermissions(rel, false);
! 			ATSimpleRecursion(wqueue, rel, cmd, recurse);
! 			/* No command-specific prep needed */
  			pass = AT_PASS_DROP;
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
--- 2186,2200 ----
  			break;
  		case AT_AddConstraint:	/* ADD CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 			ATPrepAddConstraint(wqueue, rel, recurse, cmd);
  			pass = AT_PASS_ADD_CONSTR;
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
  			ATSimplePermissions(rel, false);
! 			 /* Recursion occurs during execution phase */
! 			 /* No command-specific prep needed except saving recurse flag */
! 			if (recurse)
! 				cmd->subtype = AT_DropConstraintRecurse;
  			pass = AT_PASS_DROP;
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
*************** ATExecCmd(AlteredTableInfo *tab, Relatio
*** 2253,2262 ****
  			ATExecAddConstraint(tab, rel, cmd->def);
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, false);
  			break;
! 		case AT_DropConstraintQuietly:	/* DROP CONSTRAINT for child */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, true);
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
  			ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def);
--- 2383,2392 ----
  			ATExecAddConstraint(tab, rel, cmd->def);
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, false, false);
  			break;
! 		case AT_DropConstraintRecurse:	/* DROP CONSTRAINT for child */
! 			ATExecDropConstraint(rel, cmd->name, cmd->behavior, true, false);
  			break;
  		case AT_AlterColumnType:		/* ALTER COLUMN TYPE */
  			ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def);
*************** ATExecAddColumn(AlteredTableInfo *tab, R
*** 3273,3279 ****
  		 * _list_ of defaults, but we just do one.
  		 */
  		AddRelationRawConstraints(rel, list_make1(rawEnt), NIL);
- 
  		/* Make the additional catalog changes visible */
  		CommandCounterIncrement();
  	}
--- 3403,3408 ----
*************** ATExecAddIndex(AlteredTableInfo *tab, Re
*** 3936,3941 ****
--- 4065,4119 ----
   * ALTER TABLE ADD CONSTRAINT
   */
  static void
+ ATPrepAddConstraint(List **wqueue, Relation rel, bool recurse,
+ 				AlterTableCmd *cmd)
+ {
+ 	/*
+ 	 * Currently we recurse only for CHECK constraints, never for
+ 	 * foreign-key constraints.  UNIQUE/PKEY constraints won't be seen
+ 	 * here.
+ 	 */
+ 	if (!IsA(cmd->def, Constraint))
+ 	{
+ 		return;
+ 	}
+ 
+ 	/*
+ 	 * Recurse to add the column to child classes, if requested.
+ 	 *
+ 	 * We must recurse one level at a time, so that multiply-inheriting
+ 	 * children are visited the right number of times and end up with the
+ 	 * right coninhcount.
+ 	 */
+ 	if (recurse)
+ 	{
+ 		AlterTableCmd	*childCmd = copyObject(cmd);
+ 		Constraint		*constr	  = (Constraint *) childCmd->def;
+ 
+ 		/* child should see column as singly inherited */
+ 		constr->inhcount = 1;
+ 		constr->is_local = false;
+ 
+ 		ATOneLevelRecursion(wqueue, rel, childCmd);
+ 	}
+ 	else
+ 	{
+ 		/*
+ 		 * 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 (find_inheritance_children(RelationGetRelid(rel)) != NIL)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ 					 errmsg("constraint must be added to child tables too")));
+ 	}
+ }
+ 
+ 
+ /*
+  * ALTER TABLE ADD CONSTRAINT
+  */
+ static void
  ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint)
  {
  	switch (nodeTag(newConstraint))
*************** ATAddForeignKeyConstraint(AlteredTableIn
*** 4295,4301 ****
  									  indexOid,
  									  NULL,		/* no check constraint */
  									  NULL,
! 									  NULL);
  
  	/*
  	 * Create the triggers that will enforce the constraint.
--- 4473,4481 ----
  									  indexOid,
  									  NULL,		/* no check constraint */
  									  NULL,
! 									  NULL,
! 									  true, /* islocal */
! 									  0); /* inhcount */
  
  	/*
  	 * Create the triggers that will enforce the constraint.
*************** createForeignKeyTriggers(Relation rel, F
*** 4815,4858 ****
   * ALTER TABLE DROP CONSTRAINT
   */
  static void
! ATPrepDropConstraint(List **wqueue, Relation rel,
! 					 bool recurse, AlterTableCmd *cmd)
  {
! 	/*
! 	 * We don't want errors or noise from child tables, so we have to pass
! 	 * down a modified command.
! 	 */
! 	if (recurse)
  	{
! 		AlterTableCmd *childCmd = copyObject(cmd);
  
! 		childCmd->subtype = AT_DropConstraintQuietly;
! 		ATSimpleRecursion(wqueue, rel, childCmd, recurse);
  	}
- }
  
! static void
! ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior, bool quiet)
! {
! 	int			deleted;
  
! 	deleted = RemoveRelConstraints(rel, constrName, behavior);
  
! 	if (!quiet)
  	{
! 		/* If zero constraints deleted, complain */
! 		if (deleted == 0)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_UNDEFINED_OBJECT),
! 					 errmsg("constraint \"%s\" does not exist",
! 							constrName)));
! 		/* Otherwise if more than one constraint deleted, notify */
! 		else if (deleted > 1)
! 			ereport(NOTICE,
! 					(errmsg("multiple constraints named \"%s\" were dropped",
! 							constrName)));
  	}
  }
  
  /*
--- 4995,5144 ----
   * ALTER TABLE DROP CONSTRAINT
   */
  static void
! ATExecDropConstraint(Relation rel, const char *constrName,
! 					 DropBehavior behavior,
! 					 bool recurse, bool recursing)
  {
! 	List				*children;
! 	Relation			conrel;
! 	Form_pg_constraint	con;
! 	SysScanDesc			scan;
! 	ScanKeyData			key;
! 	HeapTuple			tuple;
! 	bool				found = false;
! 
! 	conrel = heap_open(ConstraintRelationId, AccessExclusiveLock);
! 
! 	/* At top level, permission check was done in ATPrepCmd, else do it */
! 	if (recursing)
! 		ATSimplePermissions(rel, false);
! 
! 	/* Find the constraint, make sure we don't drop it if its inherited */
! 	ScanKeyInit(&key,
! 				Anum_pg_constraint_conrelid,
! 				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(rel)));
! 	scan = systable_beginscan(conrel, ConstraintRelidIndexId,
! 							  true, SnapshotNow, 1, &key);
! 
! 	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
  	{
! 		con = (Form_pg_constraint) GETSTRUCT(tuple);
  
! 		if (strcmp(NameStr(con->conname),
! 				   constrName) != 0)
! 			continue;
! 
! 		/* Don't drop inherited constraints */
! 		if (con->contype == CONSTRAINT_CHECK && con->coninhcount > 0 && !recursing)
! 			ereport(ERROR,
! 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
! 				 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
! 						constrName,
! 						RelationGetRelationName(rel))));
! 
! 		found = true;
  	}
  
! 	if(!found)
! 		elog(ERROR, "failed to find constraint \"%s\" of relation %u",
! 			 constrName, RelationGetRelid(rel));
  
! 	systable_endscan(scan);
  
! 	/*
! 	 * Propagate to children as appropriate.  Unlike most other ALTER
! 	 * 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.
! 	 */
! 	children = find_inheritance_children(RelationGetRelid(rel));
! 
! 	if (children)
  	{
! 		ListCell   *child;
! 
! 		foreach(child, children)
! 		{
! 			Oid			childrelid = lfirst_oid(child);
! 			Relation	childrel;
! 
! 			childrel = heap_open(childrelid, AccessExclusiveLock);
! 			CheckTableNotInUse(childrel, "ALTER TABLE");
! 
! 			ScanKeyInit(&key,
! 				Anum_pg_constraint_conrelid,
! 				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(childrelid));
! 			scan = systable_beginscan(conrel, ConstraintRelidIndexId,
! 							  true, SnapshotNow, 1, &key);
! 
! 			found = false;
! 
! 			while (HeapTupleIsValid(tuple = systable_getnext(scan)))
! 			{
! 				HeapTuple copy_tuple;
! 
! 				con = (Form_pg_constraint) GETSTRUCT(tuple);
! 
! 				if (con->contype != CONSTRAINT_CHECK)
! 					continue;
! 
! 				if (strcmp(NameStr(con->conname),
! 						   constrName) != 0)
! 					continue;
! 				else
! 					found = true;
! 
! 				if (con->coninhcount <= 0)
! 					elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
! 						childrelid, constrName);
! 
! 				copy_tuple = heap_copytuple(tuple);
! 				con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
! 
! 				if (recurse)
! 				{
! 					if (con->coninhcount == 1 && !con->conislocal)
! 					{
! 						ATExecDropConstraint(childrel, constrName, behavior, true, true);
! 					}
! 					else
! 					{
! 						con->coninhcount--;
! 						simple_heap_update(conrel, &copy_tuple->t_self, copy_tuple);
! 						CatalogUpdateIndexes(conrel, copy_tuple);
! 					}
! 				}
! 				else
! 				{
! 					/*
! 					 * If we were told to drop ONLY in this table (no recursion),
! 					 * we need to mark the inheritors' attribute as locally
! 					 * defined rather than inherited.
! 					 */
! 
! 					con->coninhcount--;
! 					con->conislocal = true;
! 
! 					simple_heap_update(conrel, &copy_tuple->t_self, copy_tuple);
! 					CatalogUpdateIndexes(conrel, copy_tuple);
! 				}
! 
! 				heap_freetuple(copy_tuple);
! 			}
! 
! 			if (!found)
! 					elog(ERROR, "failed to find constraint \"%s\" of relation %u",
! 						 constrName, childrelid);
! 
! 			systable_endscan(scan);
! 			heap_close(childrel, AccessExclusiveLock);
! 		}
  	}
+ 
+ 	RemoveRelConstraints(rel, constrName, behavior);
+ 
+ 	heap_close(conrel, AccessExclusiveLock);
  }
  
  /*
*************** decompile_conbin(HeapTuple contup, Tuple
*** 6260,6265 ****
--- 6546,6568 ----
  }
  
  /*
+  * See if two constraints are "functionally" equivalent, but not necessarily equal
+  */
+ static bool
+ ConstraintIsEquiv(const HeapTuple a, const HeapTuple b, const TupleDesc tupleDesc)
+ {
+ 	const Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
+ 	const Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
+ 
+ 	if (acon->condeferrable != bcon->condeferrable ||
+ 		acon->condeferred != bcon->condeferred ||
+ 			strcmp(decompile_conbin(a, tupleDesc),
+ 				   decompile_conbin(b, tupleDesc)) != 0)
+ 		return 0;
+ 	else
+ 		return 1;
+ }
+ /*
   * Check columns in child table match up with columns in parent, and increment
   * their attinhcount.
   *
*************** MergeAttributesIntoExisting(Relation chi
*** 6360,6450 ****
  static void
  MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
  {
! 	Relation	catalogRelation;
! 	TupleDesc	tupleDesc;
! 	SysScanDesc scan;
! 	ScanKeyData key;
! 	HeapTuple	constraintTuple;
! 	ListCell   *elem;
! 	List	   *constraints;
! 
! 	/* First gather up the child's constraint definitions */
! 	catalogRelation = heap_open(ConstraintRelationId, AccessShareLock);
! 	tupleDesc = RelationGetDescr(catalogRelation);
  
! 	ScanKeyInit(&key,
  				Anum_pg_constraint_conrelid,
  				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(child_rel)));
! 	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
! 							  true, SnapshotNow, 1, &key);
  
! 	constraints = NIL;
! 	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
  	{
! 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
  
! 		if (con->contype != CONSTRAINT_CHECK)
  			continue;
  
! 		constraints = lappend(constraints, heap_copytuple(constraintTuple));
! 	}
! 
! 	systable_endscan(scan);
! 
! 	/* Then scan through the parent's constraints looking for matches */
! 	ScanKeyInit(&key,
  				Anum_pg_constraint_conrelid,
  				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(parent_rel)));
! 	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId, true,
! 							  SnapshotNow, 1, &key);
  
! 	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
! 	{
! 		Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
! 		bool		found = false;
! 		Form_pg_constraint child_con = NULL;
! 		HeapTuple	child_contuple = NULL;
  
! 		if (parent_con->contype != CONSTRAINT_CHECK)
! 			continue;
  
- 		foreach(elem, constraints)
- 		{
- 			child_contuple = (HeapTuple) lfirst(elem);
- 			child_con = (Form_pg_constraint) GETSTRUCT(child_contuple);
  			if (strcmp(NameStr(parent_con->conname),
! 					   NameStr(child_con->conname)) == 0)
! 			{
! 				found = true;
! 				break;
! 			}
  		}
  
! 		if (!found)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_DATATYPE_MISMATCH),
! 					 errmsg("child table is missing constraint \"%s\"",
! 							NameStr(parent_con->conname))));
  
! 		if (parent_con->condeferrable != child_con->condeferrable ||
! 			parent_con->condeferred != child_con->condeferred ||
! 			strcmp(decompile_conbin(constraintTuple, tupleDesc),
! 				   decompile_conbin(child_contuple, tupleDesc)) != 0)
  			ereport(ERROR,
! 					(errcode(ERRCODE_DATATYPE_MISMATCH),
! 					 errmsg("constraint definition for check constraint \"%s\" does not match",
! 							NameStr(parent_con->conname))));
! 
! 		/*
! 		 * TODO: add conislocal,coninhcount to constraints. This is where we
! 		 * would have to bump them just like attributes
! 		 */
  	}
  
! 	systable_endscan(scan);
! 	heap_close(catalogRelation, AccessShareLock);
  }
  
  /*
--- 6663,6747 ----
  static void
  MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
  {
! 	Relation	catalog_relation;
! 	TupleDesc	tuple_desc;
! 	SysScanDesc parent_scan;
! 	ScanKeyData parent_key;
! 	HeapTuple	constraint_tuple;
  
! 	catalog_relation = heap_open(ConstraintRelationId, RowExclusiveLock);
! 	tuple_desc = RelationGetDescr(catalog_relation);
! 
! 	/* First gather up the parents constraint definitions */
! 	ScanKeyInit(&parent_key,
  				Anum_pg_constraint_conrelid,
  				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(parent_rel)));
! 	parent_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId,
! 							  true, SnapshotNow, 1, &parent_key);
  
! 	while (HeapTupleIsValid(constraint_tuple = systable_getnext(parent_scan)))
  	{
! 		Form_pg_constraint	parent_con = (Form_pg_constraint) GETSTRUCT(constraint_tuple);
! 		HeapTuple			child_tuple;
! 		Form_pg_constraint	child_con;
! 		SysScanDesc			child_scan;
! 		ScanKeyData			child_key;
! 		bool				found = false;
  
! 		if (parent_con->contype != CONSTRAINT_CHECK)
  			continue;
  
! 		/* Now look for matching child matches */
! 		ScanKeyInit(&child_key,
  				Anum_pg_constraint_conrelid,
  				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(child_rel)));
! 		child_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId,
! 							  true, SnapshotNow, 1, &child_key);
  
! 		while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
! 		{
! 			HeapTuple child_copy;
  
! 			child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
! 
! 			if (child_con->contype != CONSTRAINT_CHECK)
! 				continue;
! 
! 			child_copy = heap_copytuple(child_tuple);
! 			child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
  
  			if (strcmp(NameStr(parent_con->conname),
! 					   NameStr(child_con->conname)) != 0)
! 				continue;
! 
! 			if (!ConstraintIsEquiv(constraint_tuple, child_tuple, tuple_desc))
! 				ereport(ERROR,
! 				(errcode(ERRCODE_DATATYPE_MISMATCH),
! 				 errmsg("constraint definition for check constraint \"%s\" does not match",
! 					NameStr(parent_con->conname))));
! 
! 			found = true;
! 
! 			/* increment coninhcount */
! 			child_con->coninhcount++;
! 			simple_heap_update(catalog_relation, &child_copy->t_self, child_copy);
! 			CatalogUpdateIndexes(catalog_relation, child_copy);
! 			heap_freetuple(child_copy);
  		}
  
! 		systable_endscan(child_scan);
  
! 		if (!found)
  			ereport(ERROR,
! 				(errcode(ERRCODE_DATATYPE_MISMATCH),
! 				 errmsg("child table is missing constraint \"%s\"",
! 						NameStr(parent_con->conname))));
  	}
  
! 	systable_endscan(parent_scan);
! 	heap_close(catalog_relation, RowExclusiveLock);
  }
  
  /*
*************** ATExecDropInherit(Relation rel, RangeVar
*** 6469,6476 ****
--- 6766,6775 ----
  	ScanKeyData key[3];
  	HeapTuple	inheritsTuple,
  				attributeTuple,
+ 				constraintTuple,
  				depTuple;
  	bool		found = false;
+ 	TupleDesc	tupdesc;
  
  	/*
  	 * AccessShareLock on the parent is probably enough, seeing that DROP
*************** ATExecDropInherit(Relation rel, RangeVar
*** 6508,6514 ****
  			break;
  		}
  	}
- 
  	systable_endscan(scan);
  	heap_close(catalogRelation, RowExclusiveLock);
  
--- 6807,6812 ----
*************** ATExecDropInherit(Relation rel, RangeVar
*** 6558,6563 ****
--- 6856,6896 ----
  	systable_endscan(scan);
  	heap_close(catalogRelation, RowExclusiveLock);
  
+ 	catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
+ 	ScanKeyInit(&key[0],
+ 				Anum_pg_constraint_conrelid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(RelationGetRelid(rel)));
+ 	scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
+ 							  true, SnapshotNow, 1, key);
+ 
+ 	tupdesc = RelationGetDescr(catalogRelation);
+ 
+ 	while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
+ 	{
+ 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
+ 
+ 		if (con->contype != CONSTRAINT_CHECK)
+ 			continue;
+ 
+ 		if (con->coninhcount > 0)
+ 		{
+ 			HeapTuple	copyTuple = heap_copytuple(constraintTuple);
+ 			Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
+ 
+ 			copy_con->coninhcount--;
+ 			if (copy_con->coninhcount == 0)
+ 				copy_con->conislocal = true;
+ 
+ 			simple_heap_update(catalogRelation, &copyTuple->t_self, copyTuple);
+ 			CatalogUpdateIndexes(catalogRelation, copyTuple);
+ 			heap_freetuple(copyTuple);
+ 		}
+ 	}
+ 
+ 	systable_endscan(scan);
+ 	heap_close(catalogRelation, RowExclusiveLock);
+ 
  	/*
  	 * Drop the dependency
  	 *
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 188fe43..123209d 100644
*** a/src/backend/commands/typecmds.c
--- /bsrc/backend/commands/typecmds.c
*************** domainAddConstraint(Oid domainOid, Oid d
*** 2206,2212 ****
  						  InvalidOid,
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc);		/* Source form check constraint */
  
  	/*
  	 * Return the compiled constraint expression so the calling routine can
--- 2206,2214 ----
  						  InvalidOid,
  						  expr, /* Tree form check constraint */
  						  ccbin,	/* Binary form check constraint */
! 						  ccsrc,	/* Source form check constraint */
! 						  true, /* is local */
! 						  0); /* inhcount */
  
  	/*
  	 * Return the compiled constraint expression so the calling routine can
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index aa38052..005886e 100644
*** a/src/backend/nodes/copyfuncs.c
--- /bsrc/backend/nodes/copyfuncs.c
*************** _copyConstraint(Constraint *from)
*** 1808,1813 ****
--- 1808,1815 ----
  	COPY_NODE_FIELD(keys);
  	COPY_NODE_FIELD(options);
  	COPY_STRING_FIELD(indexspace);
+ 	COPY_SCALAR_FIELD(inhcount);
+ 	COPY_SCALAR_FIELD(is_local);
  
  	return newnode;
  }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index dc85594..e232a37 100644
*** a/src/backend/nodes/equalfuncs.c
--- /bsrc/backend/nodes/equalfuncs.c
*************** _equalConstraint(Constraint *a, Constrai
*** 1837,1842 ****
--- 1837,1844 ----
  	COMPARE_NODE_FIELD(keys);
  	COMPARE_NODE_FIELD(options);
  	COMPARE_STRING_FIELD(indexspace);
+ 	COMPARE_SCALAR_FIELD(inhcount);
+ 	COMPARE_SCALAR_FIELD(is_local);
  
  	return true;
  }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index d06f035..382f6b6 100644
*** a/src/backend/nodes/outfuncs.c
--- /bsrc/backend/nodes/outfuncs.c
*************** _outConstraint(StringInfo str, Constrain
*** 2028,2033 ****
--- 2028,2036 ----
  			appendStringInfo(str, "<unrecognized_constraint>");
  			break;
  	}
+ 
+ 	WRITE_INT_FIELD(inhcount);
+ 	WRITE_BOOL_FIELD(is_local);
  }
  
  static void
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 6ca09b8..1575dbe 100644
*** a/src/backend/parser/gram.y
--- /bsrc/backend/parser/gram.y
*************** ColConstraintElem:
*** 2069,2074 ****
--- 2069,2076 ----
  					n->cooked_expr = NULL;
  					n->keys = NULL;
  					n->indexspace = NULL;
+ 					n->is_local = true;
+ 					n->inhcount = 0;
  					$$ = (Node *)n;
  				}
  			| DEFAULT b_expr
*************** ConstraintElem:
*** 2209,2214 ****
--- 2211,2218 ----
  					n->raw_expr = $3;
  					n->cooked_expr = NULL;
  					n->indexspace = NULL;
+ 					n->is_local = true;
+ 					n->inhcount = 0;
  					$$ = (Node *)n;
  				}
  			| UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index db1bcd8..d0e4d41 100644
*** a/src/backend/parser/parse_utilcmd.c
--- /bsrc/backend/parser/parse_utilcmd.c
*************** transformInhRelation(ParseState *pstate,
*** 688,693 ****
--- 688,695 ----
  			n->cooked_expr = nodeToString(ccbin_node);
  			n->indexspace = NULL;
  			cxt->ckconstraints = lappend(cxt->ckconstraints, (Node *) n);
+ 			n->is_local = true;
+ 			n->inhcount = 0;
  		}
  	}
  
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 3f3c67d..8f1af5f 100644
*** a/src/backend/utils/cache/catcache.c
--- /bsrc/backend/utils/cache/catcache.c
*************** CatalogCacheInitializeCache(CatCache *ca
*** 962,968 ****
  		else
  		{
  			if (cache->cc_key[i] != ObjectIdAttributeNumber)
! 				elog(FATAL, "only sys attr supported in caches is OID");
  			keytype = OIDOID;
  		}
  
--- 962,968 ----
  		else
  		{
  			if (cache->cc_key[i] != ObjectIdAttributeNumber)
! 				elog(FATAL, "only sys attr supported in caches is OID for relation: %s", cache->cc_relname);
  			keytype = OIDOID;
  		}
  
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index b1dfd02..3129bfb 100644
*** a/src/bin/pg_dump/common.c
--- /bsrc/bin/pg_dump/common.c
*************** static DumpableObject **oprinfoindex;
*** 62,69 ****
  
  static void flagInhTables(TableInfo *tbinfo, int numTables,
  			  InhInfo *inhinfo, int numInherits);
! static void flagInhAttrs(TableInfo *tbinfo, int numTables,
! 			 InhInfo *inhinfo, int numInherits);
  static DumpableObject **buildIndexArray(void *objArray, int numObjs,
  				Size objSize);
  static int	DOCatalogIdCompare(const void *p1, const void *p2);
--- 62,70 ----
  
  static void flagInhTables(TableInfo *tbinfo, int numTables,
  			  InhInfo *inhinfo, int numInherits);
! static void flagInhAttrs(TableInfo *tblinfo, int numTables,
! 			 InhInfo *inhinfo, int numInherits,
! 			 int remoteVersion);
  static DumpableObject **buildIndexArray(void *objArray, int numObjs,
  				Size objSize);
  static int	DOCatalogIdCompare(const void *p1, const void *p2);
*************** static int	strInArray(const char *patter
*** 77,83 ****
   *	  Collect information about all potentially dumpable objects
   */
  TableInfo *
! getSchemaData(int *numTablesPtr)
  {
  	NamespaceInfo *nsinfo;
  	AggInfo    *agginfo;
--- 78,84 ----
   *	  Collect information about all potentially dumpable objects
   */
  TableInfo *
! getSchemaData(int *numTablesPtr, int remoteVersion)
  {
  	NamespaceInfo *nsinfo;
  	AggInfo    *agginfo;
*************** getSchemaData(int *numTablesPtr)
*** 191,197 ****
  
  	if (g_verbose)
  		write_msg(NULL, "flagging inherited columns in subtables\n");
! 	flagInhAttrs(tblinfo, numTables, inhinfo, numInherits);
  
  	if (g_verbose)
  		write_msg(NULL, "reading indexes\n");
--- 192,198 ----
  
  	if (g_verbose)
  		write_msg(NULL, "flagging inherited columns in subtables\n");
! 	flagInhAttrs(tblinfo, numTables, inhinfo, numInherits, remoteVersion);
  
  	if (g_verbose)
  		write_msg(NULL, "reading indexes\n");
*************** flagInhTables(TableInfo *tblinfo, int nu
*** 258,264 ****
   */
  static void
  flagInhAttrs(TableInfo *tblinfo, int numTables,
! 			 InhInfo *inhinfo, int numInherits)
  {
  	int			i,
  				j,
--- 259,266 ----
   */
  static void
  flagInhAttrs(TableInfo *tblinfo, int numTables,
! 			 InhInfo *inhinfo, int numInherits,
! 			 int remoteVersion)
  {
  	int			i,
  				j,
*************** flagInhAttrs(TableInfo *tblinfo, int num
*** 423,454 ****
  		 * different schemas, because reverse-listing of function calls may
  		 * produce different text (schema-qualified or not) depending on
  		 * search path.  We really need a more bulletproof way of detecting
! 		 * inherited constraints --- pg_constraint should record this
  		 * explicitly!
  		 */
! 		for (j = 0; j < tbinfo->ncheck; j++)
  		{
! 			ConstraintInfo *constr;
! 
! 			constr = &(tbinfo->checkexprs[j]);
! 
! 			for (k = 0; k < numParents; k++)
  			{
! 				int			l;
  
! 				parent = parents[k];
! 				for (l = 0; l < parent->ncheck; l++)
  				{
! 					ConstraintInfo *pconstr = &(parent->checkexprs[l]);
  
! 					if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0)
  					{
! 						constr->coninherited = true;
! 						break;
  					}
  				}
- 				if (constr->coninherited)
- 					break;
  			}
  		}
  	}
--- 425,460 ----
  		 * different schemas, because reverse-listing of function calls may
  		 * produce different text (schema-qualified or not) depending on
  		 * search path.  We really need a more bulletproof way of detecting
! 		 * inherited constraints 
! 		 * The above is pre-80300 behaviour, now pg_constraint records this
  		 * explicitly!
  		 */
! 		if (remoteVersion <= 80300)
  		{
! 			for (j = 0; j < tbinfo->ncheck; j++)
  			{
! 				ConstraintInfo *constr;
  
! 				constr = &(tbinfo->checkexprs[j]);
! 
! 				for (k = 0; k < numParents; k++)
  				{
! 					int			l;
  
! 					parent = parents[k];
! 					for (l = 0; l < parent->ncheck; l++)
  					{
! 						ConstraintInfo *pconstr = &(parent->checkexprs[l]);
! 
! 						if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0)
! 						{
! 							constr->coninherited = true;
! 							break;
! 						}
  					}
+ 					if (constr->coninherited)
+ 						break;
  				}
  			}
  		}
  	}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 36ba457..4c6096e 100644
*** a/src/bin/pg_dump/pg_dump.c
--- /bsrc/bin/pg_dump/pg_dump.c
*************** main(int argc, char **argv)
*** 637,643 ****
  	 * Now scan the database and create DumpableObject structs for all the
  	 * objects we intend to dump.
  	 */
! 	tblinfo = getSchemaData(&numTables);
  
  	if (!schemaOnly)
  		getTableData(tblinfo, numTables, oids);
--- 637,643 ----
  	 * Now scan the database and create DumpableObject structs for all the
  	 * objects we intend to dump.
  	 */
! 	tblinfo = getSchemaData(&numTables, g_fout->remoteVersion);
  
  	if (!schemaOnly)
  		getTableData(tblinfo, numTables, oids);
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4580,4589 ****
  						  tbinfo->dobj.name);
  
  			resetPQExpBuffer(q);
! 			if (g_fout->remoteVersion >= 70400)
  			{
  				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! 							"pg_catalog.pg_get_constraintdef(oid) AS consrc "
  								  "FROM pg_catalog.pg_constraint "
  								  "WHERE conrelid = '%u'::pg_catalog.oid "
  								  "   AND contype = 'c' "
--- 4580,4602 ----
  						  tbinfo->dobj.name);
  
  			resetPQExpBuffer(q);
! 			if (g_fout->remoteVersion > 80300)
  			{
+ 				/* 8.3 did not have conislocal attribute in pg_constraint */
  				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! 							"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
! 							"conislocal "
! 								  "FROM pg_catalog.pg_constraint "
! 								  "WHERE conrelid = '%u'::pg_catalog.oid "
! 								  "   AND contype = 'c' "
! 								  "ORDER BY conname",
! 								  tbinfo->dobj.catId.oid);
! 			}
! 			else if (g_fout->remoteVersion >= 70400)
! 			{
! 				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! 							"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
! 						  	"false as conislocal "
  								  "FROM pg_catalog.pg_constraint "
  								  "WHERE conrelid = '%u'::pg_catalog.oid "
  								  "   AND contype = 'c' "
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4594,4600 ****
  			{
  				/* no pg_get_constraintdef, must use consrc */
  				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! 								  "'CHECK (' || consrc || ')' AS consrc "
  								  "FROM pg_catalog.pg_constraint "
  								  "WHERE conrelid = '%u'::pg_catalog.oid "
  								  "   AND contype = 'c' "
--- 4607,4614 ----
  			{
  				/* no pg_get_constraintdef, must use consrc */
  				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
! 								  "'CHECK (' || consrc || ')' AS consrc, "
! 								  "false as conislocal "
  								  "FROM pg_catalog.pg_constraint "
  								  "WHERE conrelid = '%u'::pg_catalog.oid "
  								  "   AND contype = 'c' "
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4606,4612 ****
  				/* 7.2 did not have OIDs in pg_relcheck */
  				appendPQExpBuffer(q, "SELECT tableoid, 0 as oid, "
  								  "rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc "
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
--- 4620,4627 ----
  				/* 7.2 did not have OIDs in pg_relcheck */
  				appendPQExpBuffer(q, "SELECT tableoid, 0 as oid, "
  								  "rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc, "
! 								  "false as conislocal "
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4616,4622 ****
  			{
  				appendPQExpBuffer(q, "SELECT tableoid, oid, "
  								  "rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc "
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
--- 4631,4638 ----
  			{
  				appendPQExpBuffer(q, "SELECT tableoid, oid, "
  								  "rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc, "
! 								  "false as conislocal "
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4628,4634 ****
  				appendPQExpBuffer(q, "SELECT "
  								  "(SELECT oid FROM pg_class WHERE relname = 'pg_relcheck') AS tableoid, "
  								  "oid, rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc "
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
--- 4644,4651 ----
  				appendPQExpBuffer(q, "SELECT "
  								  "(SELECT oid FROM pg_class WHERE relname = 'pg_relcheck') AS tableoid, "
  								  "oid, rcname AS conname, "
! 								  "'CHECK (' || rcsrc || ')' AS consrc, "
! 								  " false as conislocal"
  								  "FROM pg_relcheck "
  								  "WHERE rcrelid = '%u'::oid "
  								  "ORDER BY rcname",
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4662,4669 ****
  				constrs[j].contype = 'c';
  				constrs[j].condef = strdup(PQgetvalue(res, j, 3));
  				constrs[j].conindex = 0;
- 				constrs[j].coninherited = false;
  				constrs[j].separate = false;
  
  				constrs[j].dobj.dump = tbinfo->dobj.dump;
  
--- 4679,4687 ----
  				constrs[j].contype = 'c';
  				constrs[j].condef = strdup(PQgetvalue(res, j, 3));
  				constrs[j].conindex = 0;
  				constrs[j].separate = false;
+ 				/* depending on conislocal value, set coninherited */
+ 				constrs[j].coninherited = (PQgetvalue(res, j, 4)[0] == 't');
  
  				constrs[j].dobj.dump = tbinfo->dobj.dump;
  
*************** getTableAttrs(TableInfo *tblinfo, int nu
*** 4678,4685 ****
  
  				/*
  				 * If the constraint is inherited, this will be detected
! 				 * later.  We also detect later if the constraint must be
! 				 * split out from the table definition.
  				 */
  			}
  			PQclear(res);
--- 4696,4703 ----
  
  				/*
  				 * If the constraint is inherited, this will be detected
! 				 * later for pre-80300 case. We also detect later if the constraint
! 				 * must be split out from the table definition.
  				 */
  			}
  			PQclear(res);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index f4cd179..23f76ec 100644
*** a/src/bin/pg_dump/pg_dump.h
--- /bsrc/bin/pg_dump/pg_dump.h
*************** extern char g_opaque_type[10];	/* name f
*** 429,435 ****
   *	common utility functions
   */
  
! extern TableInfo *getSchemaData(int *numTablesPtr);
  
  typedef enum _OidOptions
  {
--- 429,435 ----
   *	common utility functions
   */
  
! extern TableInfo *getSchemaData(int *numTablesPtr, int remoteVersion);
  
  typedef enum _OidOptions
  {
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index c5fb69a..aa683be 100644
*** a/src/include/access/tupdesc.h
--- /bsrc/include/access/tupdesc.h
*************** typedef struct attrDefault
*** 27,34 ****
  
  typedef struct constrCheck
  {
! 	char	   *ccname;
! 	char	   *ccbin;			/* nodeToString representation of expr */
  } ConstrCheck;
  
  /* This structure contains constraints of a tuple */
--- 27,36 ----
  
  typedef struct constrCheck
  {
! 	char	*ccname;
! 	char	*ccbin;			/* nodeToString representation of expr */
! 	bool	is_local;
! 	int		inhcount;
  } ConstrCheck;
  
  /* This structure contains constraints of a tuple */
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index e787c52..9b17a73 100644
*** a/src/include/catalog/catversion.h
--- /bsrc/include/catalog/catversion.h
***************
*** 53,58 ****
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	200804292
  
  #endif
--- 53,58 ----
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	200803290
  
  #endif
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index 501899d..64caa94 100644
*** a/src/include/catalog/pg_constraint.h
--- /bsrc/include/catalog/pg_constraint.h
*************** CATALOG(pg_constraint,2606)
*** 71,76 ****
--- 71,82 ----
  	char		confdeltype;	/* foreign key's ON DELETE action */
  	char		confmatchtype;	/* foreign key's match type */
  
+ 	/* Has a local definition (hence, do not drop when coninhcount is 0) */
+ 	bool		conislocal;
+ 
+ 	/* Number of times inherited from direct parent relation(s) */
+ 	int4		coninhcount;
+ 
  	/*
  	 * VARIABLE LENGTH FIELDS start here.  These fields may be NULL, too.
  	 */
*************** typedef FormData_pg_constraint *Form_pg_
*** 125,150 ****
   *		compiler constants for pg_constraint
   * ----------------
   */
! #define Natts_pg_constraint					18
! #define Anum_pg_constraint_conname			1
! #define Anum_pg_constraint_connamespace		2
! #define Anum_pg_constraint_contype			3
! #define Anum_pg_constraint_condeferrable	4
! #define Anum_pg_constraint_condeferred		5
! #define Anum_pg_constraint_conrelid			6
! #define Anum_pg_constraint_contypid			7
! #define Anum_pg_constraint_confrelid		8
! #define Anum_pg_constraint_confupdtype		9
! #define Anum_pg_constraint_confdeltype		10
! #define Anum_pg_constraint_confmatchtype	11
! #define Anum_pg_constraint_conkey			12
! #define Anum_pg_constraint_confkey			13
! #define Anum_pg_constraint_conpfeqop		14
! #define Anum_pg_constraint_conppeqop		15
! #define Anum_pg_constraint_conffeqop		16
! #define Anum_pg_constraint_conbin			17
! #define Anum_pg_constraint_consrc			18
! 
  
  /* Valid values for contype */
  #define CONSTRAINT_CHECK			'c'
--- 131,157 ----
   *		compiler constants for pg_constraint
   * ----------------
   */
! #define Natts_pg_constraint					20
! #define	Anum_pg_constraint_conname			1
! #define	Anum_pg_constraint_connamespace		2
! #define	Anum_pg_constraint_contype			3
! #define	Anum_pg_constraint_condeferrable	4
! #define	Anum_pg_constraint_condeferred		5
! #define	Anum_pg_constraint_conrelid			6
! #define	Anum_pg_constraint_contypid			7
! #define	Anum_pg_constraint_confrelid		8
! #define	Anum_pg_constraint_confupdtype		9
! #define	Anum_pg_constraint_confdeltype		10
! #define	Anum_pg_constraint_confmatchtype	11
! #define	Anum_pg_constraint_conislocal		12
! #define	Anum_pg_constraint_coninhcount		13
! #define	Anum_pg_constraint_conkey			14
! #define	Anum_pg_constraint_confkey			15
! #define	Anum_pg_constraint_conpfeqop		16
! #define	Anum_pg_constraint_conppeqop		17
! #define	Anum_pg_constraint_conffeqop		18
! #define	Anum_pg_constraint_conbin			19
! #define	Anum_pg_constraint_consrc			20
  
  /* Valid values for contype */
  #define CONSTRAINT_CHECK			'c'
*************** extern Oid CreateConstraintEntry(const c
*** 192,198 ****
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc);
  
  extern void RemoveConstraintById(Oid conId);
  extern void RenameConstraintById(Oid conId, const char *newname);
--- 199,207 ----
  					  Oid indexRelId,
  					  Node *conExpr,
  					  const char *conBin,
! 					  const char *conSrc,
! 					  const bool conIsLocal,
! 					  const int conInhCout);
  
  extern void RemoveConstraintById(Oid conId);
  extern void RenameConstraintById(Oid conId, const char *newname);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 54dfe7c..cd965ff 100644
*** a/src/include/nodes/parsenodes.h
--- /bsrc/include/nodes/parsenodes.h
*************** typedef enum AlterTableType
*** 895,901 ****
  	AT_ProcessedConstraint,		/* pre-processed add constraint (local in
  								 * parser/parse_utilcmd.c) */
  	AT_DropConstraint,			/* drop constraint */
! 	AT_DropConstraintQuietly,	/* drop constraint, no error/warning (local in
  								 * commands/tablecmds.c) */
  	AT_AlterColumnType,			/* alter column type */
  	AT_ChangeOwner,				/* change owner */
--- 895,901 ----
  	AT_ProcessedConstraint,		/* pre-processed add constraint (local in
  								 * parser/parse_utilcmd.c) */
  	AT_DropConstraint,			/* drop constraint */
! 	AT_DropConstraintRecurse,	/* drop constraint recursion (local in
  								 * commands/tablecmds.c) */
  	AT_AlterColumnType,			/* alter column type */
  	AT_ChangeOwner,				/* change owner */
*************** typedef struct Constraint
*** 1158,1163 ****
--- 1158,1165 ----
  	List	   *options;		/* options from WITH clause */
  	char	   *indexspace;		/* index tablespace for PKEY/UNIQUE
  								 * constraints; NULL for default */
+ 	bool is_local;
+ 	int inhcount;
  } Constraint;
  
  /* ----------
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 76248de..c97a256 100644
*** a/src/include/utils/syscache.h
--- /bsrc/include/utils/syscache.h
***************
*** 28,81 ****
   *		Keep them in alphabetical order.
   */
  
! #define AGGFNOID			0
! #define AMNAME				1
! #define AMOID				2
! #define AMOPOPID			3
! #define AMOPSTRATEGY		4
! #define AMPROCNUM			5
! #define ATTNAME				6
! #define ATTNUM				7
! #define AUTHMEMMEMROLE		8
! #define AUTHMEMROLEMEM		9
! #define AUTHNAME			10
! #define AUTHOID				11
! #define CASTSOURCETARGET	12
! #define CLAAMNAMENSP		13
! #define CLAOID				14
! #define CONDEFAULT			15
! #define CONNAMENSP			16
! #define CONSTROID			17
! #define CONVOID				18
! #define DATABASEOID			19
! #define ENUMOID				20
! #define ENUMTYPOIDNAME		21
! #define INDEXRELID			22
! #define LANGNAME			23
! #define LANGOID				24
! #define NAMESPACENAME		25
! #define NAMESPACEOID		26
! #define OPERNAMENSP			27
! #define OPEROID				28
! #define OPFAMILYAMNAMENSP	29
! #define OPFAMILYOID			30
! #define PROCNAMEARGSNSP		31
! #define PROCOID				32
! #define RELNAMENSP			33
! #define RELOID				34
! #define RULERELNAME			35
! #define STATRELATT			36
! #define TSCONFIGMAP			37
! #define TSCONFIGNAMENSP		38
! #define TSCONFIGOID			39
! #define TSDICTNAMENSP		40
! #define TSDICTOID			41
! #define TSPARSERNAMENSP		42
! #define TSPARSEROID			43
! #define TSTEMPLATENAMENSP	44
! #define TSTEMPLATEOID		45
! #define TYPENAMENSP			46
! #define TYPEOID				47
  
  extern void InitCatalogCache(void);
  extern void InitCatalogCachePhase2(void);
--- 28,83 ----
   *		Keep them in alphabetical order.
   */
  
! enum SysCacheIdent {
! 	AGGFNOID = 0,
! 	AMNAME,
! 	AMOID,
! 	AMOPOPID,
! 	AMOPSTRATEGY,
! 	AMPROCNUM,
! 	ATTNAME,
! 	ATTNUM,
! 	AUTHMEMMEMROLE,
! 	AUTHMEMROLEMEM,
! 	AUTHNAME,
! 	AUTHOID,
! 	CASTSOURCETARGET,
! 	CLAAMNAMENSP,
! 	CLAOID,
! 	CONDEFAULT,
! 	CONNAMENSP,
! 	CONSTROID,
! 	CONVOID,
! 	DATABASEOID,
! 	ENUMOID,
! 	ENUMTYPOIDNAME,
! 	INDEXRELID,
! 	LANGNAME,
! 	LANGOID,
! 	NAMESPACENAME,
! 	NAMESPACEOID,
! 	OPERNAMENSP,
! 	OPEROID,
! 	OPFAMILYAMNAMENSP,
! 	OPFAMILYOID,
! 	PROCNAMEARGSNSP,
! 	PROCOID,
! 	RELNAMENSP,
! 	RELOID,
! 	RULERELNAME,
! 	STATRELATT,
! 	TSCONFIGMAP,
! 	TSCONFIGNAMENSP,
! 	TSCONFIGOID,
! 	TSDICTNAMENSP,
! 	TSDICTOID,
! 	TSPARSERNAMENSP,
! 	TSPARSEROID,
! 	TSTEMPLATENAMENSP,
! 	TSTEMPLATEOID,
! 	TYPENAMENSP,
! 	TYPEOID,
! };
  
  extern void InitCatalogCache(void);
  extern void InitCatalogCachePhase2(void);
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 51d5afa..25e0e8e 100644
*** a/src/test/regress/expected/alter_table.out
--- /bsrc/test/regress/expected/alter_table.out
*************** create table atacc1 (test int);
*** 383,391 ****
  create table atacc2 (test2 int);
  create table atacc3 (test3 int) inherits (atacc1, atacc2);
  alter table only atacc2 add constraint foo check (test2>0);
  -- fail and then succeed on atacc2
  insert into atacc2 (test2) values (-3);
- ERROR:  new row for relation "atacc2" violates check constraint "foo"
  insert into atacc2 (test2) values (3);
  -- both succeed on atacc3
  insert into atacc3 (test2) values (-3);
--- 383,391 ----
  create table atacc2 (test2 int);
  create table atacc3 (test3 int) inherits (atacc1, atacc2);
  alter table only atacc2 add constraint foo check (test2>0);
+ ERROR:  constraint must be added to child tables too
  -- fail and then succeed on atacc2
  insert into atacc2 (test2) values (-3);
  insert into atacc2 (test2) values (3);
  -- both succeed on atacc3
  insert into atacc3 (test2) values (-3);
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index f81776f..f96e2a5 100644
*** a/src/test/regress/expected/inherit.out
--- /bsrc/test/regress/expected/inherit.out
*************** drop function p2text(p2);
*** 692,694 ****
--- 692,770 ----
  drop table c1;
  drop table p2;
  drop table p1;
+ CREATE TABLE ac (aa TEXT);
+ alter table ac add constraint ac_check check (aa is not null);
+ CREATE TABLE bc (bb TEXT) INHERITS (ac);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+  ac_check | c       | f          |           1 | (aa IS NOT NULL)
+ (2 rows)
+ 
+ alter table bc drop constraint ac_check;
+ ERROR:  cannot drop inherited constraint "ac_check" of relation "bc"
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname | contype | conislocal | coninhcount | consrc 
+ ---------+---------+------------+-------------+--------
+ (0 rows)
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+  ac_check | c       | f          |           1 | (aa IS NOT NULL)
+ (2 rows)
+ 
+ insert into ac (aa) values (NULL);
+ ERROR:  new row for relation "ac" violates check constraint "ac_check"
+ insert into bc (aa) values (NULL);
+ ERROR:  new row for relation "bc" violates check constraint "ac_check"
+ alter table bc drop constraint ac_check;
+ ERROR:  cannot drop inherited constraint "ac_check" of relation "bc"
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname | contype | conislocal | coninhcount | consrc 
+ ---------+---------+------------+-------------+--------
+ (0 rows)
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ alter table bc no inherit ac;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+ (2 rows)
+ 
+ alter table bc drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname  | contype | conislocal | coninhcount |      consrc      
+ ----------+---------+------------+-------------+------------------
+  ac_check | c       | t          |           0 | (aa IS NOT NULL)
+ (1 row)
+ 
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname | contype | conislocal | coninhcount | consrc 
+ ---------+---------+------------+-------------+--------
+ (0 rows)
+ 
+ drop table bc;
+ drop table ac;
+ create table ac (a int constraint check_a check (a <> 0));
+ create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac);
+ NOTICE:  merging column "a" with inherited definition
+ NOTICE:  merging constraint "check_a" with inherited definition
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+  conname | contype | conislocal | coninhcount |  consrc  
+ ---------+---------+------------+-------------+----------
+  check_a | c       | t          |           0 | (a <> 0)
+  check_a | c       | f          |           1 | (a <> 0)
+  check_b | c       | t          |           0 | (b <> 0)
+ (3 rows)
+ 
+ drop table bc;
+ drop table ac;
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
index b0499a6..e72143d 100644
*** a/src/test/regress/sql/inherit.sql
--- /bsrc/test/regress/sql/inherit.sql
*************** drop function p2text(p2);
*** 196,198 ****
--- 196,236 ----
  drop table c1;
  drop table p2;
  drop table p1;
+ 
+ CREATE TABLE ac (aa TEXT);
+ alter table ac add constraint ac_check check (aa is not null);
+ CREATE TABLE bc (bb TEXT) INHERITS (ac);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ 
+ alter table bc drop constraint ac_check;
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ 
+ insert into ac (aa) values (NULL);
+ insert into bc (aa) values (NULL);
+ 
+ alter table bc drop constraint ac_check;
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ 
+ alter table ac add constraint ac_check check (aa is not null);
+ alter table bc no inherit ac;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ alter table bc drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ alter table ac drop constraint ac_check;
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ 
+ drop table bc;
+ drop table ac;
+ 
+ create table ac (a int constraint check_a check (a <> 0));
+ create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac);
+ select pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by pc.relname, 1;
+ 
+ 
+ drop table bc;
+ drop table ac;
#10Alex Hunsaker
badalex@gmail.com
In reply to: Alex Hunsaker (#9)
Re: [badalex@gmail.com: Re: [BUGS] Problem identifying constraints which should not be inherited]

On Wed, May 7, 2008 at 12:20 AM, Alex Hunsaker <badalex@gmail.com> wrote:

Find attached a diff from v4-v5, and a full patch.

src/backend/commands/tablecmds.c | 242 +++++++++++++++++++++++-------------

src/backend/utils/cache/syscache.c | 12 --

src/include/catalog/indexing.h | 2 -
src/include/utils/syscache.h | 1 -
4 files changed, 153 insertions(+), 104 deletions(-)

Currently this loops through all the constraints for a relation (old
behavior of MergeAttributesIntoExisting)... Do you think its worth
adding a non-unique index to speed this up? If so I can whip up a
patch real quick if you think its worth it... else

*sigh* Here is a fiix for a possible bogus "failed to find constraint"
error when we are trying to drop a constraint that is not a check
constraint
(interesting no regression tests failed... caught it while reviewing
the patch I just posted)

*** a/src/backend/commands/tablecmds.c
--- /bsrc/backend/commands/tablecmds.c
*************** ATExecDropConstraint(Relation rel, const
*** 5080,5094 ****

con = (Form_pg_constraint) GETSTRUCT(tuple);

- if (con->contype != CONSTRAINT_CHECK)
- continue;
-
if (strcmp(NameStr(con->conname),
constrName) != 0)
continue;
else
found = true;

                                if (con->coninhcount <= 0)
                                        elog(ERROR, "relation %u has
non-inherited constraint \"%s\"",
                                                childrelid, constrName);
--- 5080,5095 ----

con = (Form_pg_constraint) GETSTRUCT(tuple);

if (strcmp(NameStr(con->conname),
constrName) != 0)
continue;
else
found = true;

+                               /* Right now only CHECK constraints
can be inherited */
+                               if (con->contype != CONSTRAINT_CHECK)
+                                       continue;
+
                                if (con->coninhcount <= 0)
                                        elog(ERROR, "relation %u has
non-inherited constraint \"%s\"",
                                                childrelid, constrName);
#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alex Hunsaker (#9)
Re: [badalex@gmail.com: Re: [BUGS] Problem identifying constraints which should not be inherited]

"Alex Hunsaker" <badalex@gmail.com> writes:

Currently this loops through all the constraints for a relation (old
behavior of MergeAttributesIntoExisting)... Do you think its worth
adding a non-unique index to speed this up?

No. If we were to refactor pg_constraint as I mentioned earlier,
then it could have a natural primary key (reloid, constrname)
(replacing the existing nonunique index on reloid) and then a number
of things could be sped up. But just piling more indexes on a
fundamentally bad design doesn't appeal to me ...

Will review the revised patch today.

regards, tom lane

#12Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alex Hunsaker (#9)
Re: [badalex@gmail.com: Re: [BUGS] Problem identifying constraints which should not be inherited]

"Alex Hunsaker" <badalex@gmail.com> writes:

[ patch to change inherited-check-constraint behavior ]

Applied after rather heavy editorializations. You didn't do very well on
getting it to work in multiple-inheritance scenarios, such as

create table p (f1 int check (f1>0));
create table c1 (f2 int) inherits (p);
create table c2 (f3 int) inherits (p);
create table cc () inherits (c1,c2);

Here the same constraint is multiply inherited. The base case as above
worked okay, but adding the constraint to an existing inheritance tree
via ALTER TABLE, not so much.

I also didn't like the choice to add is_local/inhcount fields to
ConstrCheck: that struct is fairly heavily used, but you were leaving the
fields undefined/invalid in most code paths, which would surely lead to
bugs down the road when somebody expected them to contain valid data.
I considered extending the patch to always set them up, but rejected that
idea because ConstrCheck is essentially a creature of the executor, which
couldn't care less about constraint inheritance. After some reflection
I chose to put the fields in CookedConstraint instead, which is used only
in the table creation / constraint addition code paths. That required
a bit of refactoring of the API of heap_create_with_catalog, but I think
it ended up noticeably cleaner: constraints and defaults are fed to
heap.c in only one format now.

I found one case that has not really worked as intended for a long time:
ALTER TABLE ADD CHECK ... (that is, ADD CONSTRAINT without specifying
a constraint name) failed to ensure that the same constraint name was used
at child tables as at the parent, and thus the constraints ended up not
being seen as related at all. Fixing this was a bit ugly since it meant
that ADD CONSTRAINT has to recurse to child tables during Phase 2 after
all, and has to be able to add work queue entries for Phase 3 at that
time, which is not something needed by any other ALTER TABLE operation.

I'm not sure if we ought to try to back-patch that --- it'd be a
behavioral change with non-obvious implications. In the back branches,
ADD CHECK followed by DROP CONSTRAINT will end up not deleting the
child-table constraints, which is probably a bug but I wouldn't be
surprised if applications were depending on the behavior.

regards, tom lane

#13Alex Hunsaker
badalex@gmail.com
In reply to: Tom Lane (#12)
Re: [badalex@gmail.com: Re: [BUGS] Problem identifying constraints which should not be inherited]

On Fri, May 9, 2008 at 5:37 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

"Alex Hunsaker" <badalex@gmail.com> writes:

[ patch to change inherited-check-constraint behavior ]

Applied after rather heavy editorializations. You didn't do very well on
getting it to work in multiple-inheritance scenarios, such as

create table p (f1 int check (f1>0));
create table c1 (f2 int) inherits (p);
create table c2 (f3 int) inherits (p);
create table cc () inherits (c1,c2);

Here the same constraint is multiply inherited. The base case as above
worked okay, but adding the constraint to an existing inheritance tree
via ALTER TABLE, not so much.

Ouch. Ok Ill (obviously) review what you committed so I can do a lot
better next time.
Thanks for muddling through it!

I also didn't like the choice to add is_local/inhcount fields to
ConstrCheck: that struct is fairly heavily used, but you were leaving the
fields undefined/invalid in most code paths, which would surely lead to
bugs down the road when somebody expected them to contain valid data.
I considered extending the patch to always set them up, but rejected that
idea because ConstrCheck is essentially a creature of the executor, which
couldn't care less about constraint inheritance. After some reflection
I chose to put the fields in CookedConstraint instead, which is used only
in the table creation / constraint addition code paths. That required
a bit of refactoring of the API of heap_create_with_catalog, but I think
it ended up noticeably cleaner: constraints and defaults are fed to
heap.c in only one format now.

That sounds *way* cleaner and hopefully got rid of some of those gross
hacks I had to do.
Interestingly enough thats similar to how I initially started doing
it. But it felt way to intrusive, so i dropped it.
Course I then failed the non-intrusive with the ConstrCheck changes...

I found one case that has not really worked as intended for a long time:
ALTER TABLE ADD CHECK ... (that is, ADD CONSTRAINT without specifying
a constraint name) failed to ensure that the same constraint name was used
at child tables as at the parent, and thus the constraints ended up not
being seen as related at all. Fixing this was a bit ugly since it meant
that ADD CONSTRAINT has to recurse to child tables during Phase 2 after
all, and has to be able to add work queue entries for Phase 3 at that
time, which is not something needed by any other ALTER TABLE operation.

Ouch...

I'm not sure if we ought to try to back-patch that --- it'd be a
behavioral change with non-obvious implications. In the back branches,
ADD CHECK followed by DROP CONSTRAINT will end up not deleting the
child-table constraints, which is probably a bug but I wouldn't be
surprised if applications were depending on the behavior.

Given the lack complaints it does not seem worth a back patch IMHO.

Show quoted text

regards, tom lane

#14Nikhils
nikkhils@gmail.com
In reply to: Alex Hunsaker (#13)
Re: [badalex@gmail.com: Re: [BUGS] Problem identifying constraints which should not be inherited]

Hi,
On Sat, May 10, 2008 at 6:11 AM, Alex Hunsaker <badalex@gmail.com> wrote:

On Fri, May 9, 2008 at 5:37 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

"Alex Hunsaker" <badalex@gmail.com> writes:

[ patch to change inherited-check-constraint behavior ]

Applied after rather heavy editorializations. You didn't do very well on
getting it to work in multiple-inheritance scenarios, such as

create table p (f1 int check (f1>0));
create table c1 (f2 int) inherits (p);
create table c2 (f3 int) inherits (p);
create table cc () inherits (c1,c2);

Here the same constraint is multiply inherited. The base case as above
worked okay, but adding the constraint to an existing inheritance tree
via ALTER TABLE, not so much.

Ouch. Ok Ill (obviously) review what you committed so I can do a lot
better next time.
Thanks for muddling through it!

Ouchie indeed!

I'm not sure if we ought to try to back-patch that --- it'd be a
behavioral change with non-obvious implications. In the back branches,
ADD CHECK followed by DROP CONSTRAINT will end up not deleting the
child-table constraints, which is probably a bug but I wouldn't be
surprised if applications were depending on the behavior.

Given the lack complaints it does not seem worth a back patch IMHO.

Yeah, same IMHO. I do hope we have covered things properly for inherited
check constraints by now. One minor thing that myself and Alex discussed was
the usage of "child tables" in tablecmds.c, especially in error messages.
Again English is not my native language, but shouldn't that be worded as
"children tables"? Admittedly even this does not sound any better than
"child tables" though :). It is nit-picking really, but I can submit a
cleanup patch to reword this if the list thinks so..

Regards,
Nikhils
--
EnterpriseDB http://www.enterprisedb.com

#15Tom Lane
tgl@sss.pgh.pa.us
In reply to: Nikhils (#14)
Re: [badalex@gmail.com: Re: [BUGS] Problem identifying constraints which should not be inherited]

Nikhils <nikkhils@gmail.com> writes:

... One minor thing that myself and Alex discussed was
the usage of "child tables" in tablecmds.c, especially in error messages.
Again English is not my native language, but shouldn't that be worded as
"children tables"? Admittedly even this does not sound any better than
"child tables" though :).

No, "child tables" sounds better to me. English doesn't usually
pluralize adjectives.

regards, tom lane