diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
new file mode 100644
index a910f81..19cf77f
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
*************** static bool MergeWithExistingConstraint(
*** 103,111 ****
  							bool allow_merge, bool is_local,
  							bool is_no_inherit);
  static void SetRelationNumChecks(Relation rel, int numchecks);
- static Node *cookConstraint(ParseState *pstate,
- 			   Node *raw_constraint,
- 			   char *relname);
  static List *insert_ordered_unique_oid(List *list, Oid datum);
  
  
--- 103,108 ----
*************** AddRelationNewConstraints(Relation rel,
*** 2218,2224 ****
  			 * it's valid as a CHECK constraint.
  			 */
  			expr = cookConstraint(pstate, cdef->raw_expr,
! 								  RelationGetRelationName(rel));
  		}
  		else
  		{
--- 2215,2221 ----
  			 * it's valid as a CHECK constraint.
  			 */
  			expr = cookConstraint(pstate, cdef->raw_expr,
! 								  RelationGetRelationName(rel), true);
  		}
  		else
  		{
*************** cookDefault(ParseState *pstate,
*** 2569,2578 ****
   * Parse state must be set up to recognize any vars that might appear
   * in the expression.
   */
! static Node *
  cookConstraint(ParseState *pstate,
  			   Node *raw_constraint,
! 			   char *relname)
  {
  	Node	   *expr;
  
--- 2566,2576 ----
   * Parse state must be set up to recognize any vars that might appear
   * in the expression.
   */
! Node *
  cookConstraint(ParseState *pstate,
  			   Node *raw_constraint,
! 			   char *relname,
! 			   bool is_check)
  {
  	Node	   *expr;
  
*************** cookConstraint(ParseState *pstate,
*** 2584,2590 ****
  	/*
  	 * Make sure it yields a boolean result.
  	 */
! 	expr = coerce_to_boolean(pstate, expr, "CHECK");
  
  	/*
  	 * Take care of collations.
--- 2582,2588 ----
  	/*
  	 * Make sure it yields a boolean result.
  	 */
! 	expr = coerce_to_boolean(pstate, expr, is_check ? "CHECK" : "WHERE");
  
  	/*
  	 * Take care of collations.
*************** cookConstraint(ParseState *pstate,
*** 2598,2605 ****
  	if (list_length(pstate->p_rtable) != 1)
  		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
! 			errmsg("only table \"%s\" can be referenced in check constraint",
! 				   relname)));
  
  	return expr;
  }
--- 2596,2603 ----
  	if (list_length(pstate->p_rtable) != 1)
  		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
! 			errmsg("only table \"%s\" can be referenced in %s constraint",
! 				   relname, is_check ? "check" : "foreign key")));
  
  	return expr;
  }
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
new file mode 100644
index 3483107..261a715
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
*************** ATAddForeignKeyConstraint(AlteredTableIn
*** 5831,5836 ****
--- 5831,5840 ----
  	Oid			constrOid;
  	bool		old_check_ok;
  	ListCell   *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
+ 	Node	   *expr = NULL;
+ 	char	   *ccbin = NULL;
+ 	char	   *ccsrc = NULL;
+ 
  
  	/*
  	 * Grab an exclusive lock on the pk table, so that someone doesn't delete
*************** ATAddForeignKeyConstraint(AlteredTableIn
*** 6142,6147 ****
--- 6146,6182 ----
  		ffeqoperators[i] = ffeqop;
  	}
  
+ 	if (fkconstraint->raw_expr)
+ 	{
+ 		/* FK with WHERE clause, cook it */
+ 
+ 		/*
+ 		 * Create a dummy ParseState and insert the target relation as its sole
+ 		 * rangetable entry.  We need a ParseState for transformExpr.
+ 		 */
+ 		ParseState *pstate = make_parsestate(NULL);
+ 		RangeTblEntry *rte  = addRangeTableEntryForRelation(pstate,
+ 															rel,
+ 															NULL,
+ 															false,
+ 															true);
+ 		addRTEtoQuery(pstate, rte, true, true, true);
+ 
+ 		/* now cook it */
+ 		expr = cookConstraint(pstate, fkconstraint->raw_expr,
+ 							  RelationGetRelationName(rel), false);
+ 
+ 		/* Flatten expression to string form for storage. */
+ 		ccbin = nodeToString(expr);
+ 
+ 		/* Also deparse it to form the consrc field. */
+ 		ccsrc = deparse_expression(expr,
+ 								   deparse_context_for(RelationGetRelationName(rel),
+ 													   RelationGetRelid(rel)),
+ 								   false, false);
+ 	}
+ 
+ 
  	/*
  	 * Record the FK constraint in pg_constraint.
  	 */
*************** ATAddForeignKeyConstraint(AlteredTableIn
*** 6167,6175 ****
  									  fkconstraint->fk_del_action,
  									  fkconstraint->fk_matchtype,
  									  NULL,		/* no exclusion constraint */
! 									  NULL,		/* no check constraint */
! 									  NULL,
! 									  NULL,
  									  true,		/* islocal */
  									  0,		/* inhcount */
  									  true,		/* isnoinherit */
--- 6202,6210 ----
  									  fkconstraint->fk_del_action,
  									  fkconstraint->fk_matchtype,
  									  NULL,		/* no exclusion constraint */
! 									  expr,
! 									  ccbin,
! 									  ccsrc,
  									  true,		/* islocal */
  									  0,		/* inhcount */
  									  true,		/* isnoinherit */
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
new file mode 100644
index 1922097..b8aaccd
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** ConstraintElem:
*** 3111,3117 ****
  					$$ = (Node *)n;
  				}
  			| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
! 				opt_column_list key_match key_actions ConstraintAttributeSpec
  				{
  					Constraint *n = makeNode(Constraint);
  					n->contype = CONSTR_FOREIGN;
--- 3111,3118 ----
  					$$ = (Node *)n;
  				}
  			| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
! 				opt_column_list ExclusionWhereClause key_match key_actions
! 				ConstraintAttributeSpec
  				{
  					Constraint *n = makeNode(Constraint);
  					n->contype = CONSTR_FOREIGN;
*************** ConstraintElem:
*** 3119,3128 ****
  					n->pktable			= $7;
  					n->fk_attrs			= $4;
  					n->pk_attrs			= $8;
! 					n->fk_matchtype		= $9;
! 					n->fk_upd_action	= (char) ($10 >> 8);
! 					n->fk_del_action	= (char) ($10 & 0xFF);
! 					processCASbits($11, @11, "FOREIGN KEY",
  								   &n->deferrable, &n->initdeferred,
  								   &n->skip_validation, NULL,
  								   yyscanner);
--- 3120,3130 ----
  					n->pktable			= $7;
  					n->fk_attrs			= $4;
  					n->pk_attrs			= $8;
! 					n->raw_expr 		= $9;
! 					n->fk_matchtype		= $10;
! 					n->fk_upd_action	= (char) ($11 >> 8);
! 					n->fk_del_action	= (char) ($11 & 0xFF);
! 					processCASbits($12, @12, "FOREIGN KEY",
  								   &n->deferrable, &n->initdeferred,
  								   &n->skip_validation, NULL,
  								   yyscanner);
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
new file mode 100644
index 917130f..311d33f
*** a/src/backend/utils/adt/ri_triggers.c
--- b/src/backend/utils/adt/ri_triggers.c
***************
*** 40,45 ****
--- 40,46 ----
  #include "commands/trigger.h"
  #include "executor/executor.h"
  #include "executor/spi.h"
+ #include "optimizer/clauses.h"
  #include "parser/parse_coerce.h"
  #include "parser/parse_relation.h"
  #include "miscadmin.h"
*************** typedef struct RI_ConstraintInfo
*** 123,128 ****
--- 124,131 ----
  												 * PK) */
  	Oid			ff_eq_oprs[RI_MAX_NUMKEYS];		/* equality operators (FK =
  												 * FK) */
+ 	char		*consrc;		/* source of optional where expression */
+ 	char		*conbin;		/* node tree of optional where expression */
  } RI_ConstraintInfo;
  
  
*************** RI_FKey_check(TriggerData *trigdata)
*** 383,388 ****
--- 386,435 ----
  			break;
  	}
  
+ 	if(riinfo->conbin)
+ 	{
+ 		/* constraint has a where clause, let's evaluate it */
+ 		ExprContext *econtext;
+ 		TupleDesc	tupDesc;
+ 		TupleTableSlot *slot;
+ 		bool result;
+ 		EState *estate;
+ 		List *qual;
+ 		List *exprState;
+ 
+ 		/* need to create a new executor to evaluate the expression with */
+ 		estate = CreateExecutorState();
+ 
+ 		/* ExecQual needs the expression in this form */
+ 		/* TODO: cache this somewhere? put on the riinfo? */
+ 		qual = make_ands_implicit(stringToNode(riinfo->conbin));
+ 
+ 		exprState = (List *) ExecPrepareExpr((Expr *) qual, estate);
+ 
+ 		/* set up tupleslot */
+ 		tupDesc = RelationGetDescr(fk_rel);
+ 		econtext = GetPerTupleExprContext(estate);
+ 		slot = ExecInitExtraTupleSlot(estate);
+ 		ExecSetSlotDescriptor(slot, tupDesc);
+ 		ExecStoreTuple(trigdata->tg_trigtuple, slot, InvalidBuffer, false);
+ 		econtext->ecxt_scantuple = slot;
+ 
+ 		/* evaluate the expression */
+ 		result = ExecQual(exprState, econtext, false);
+ 		FreeExecutorState(estate);
+ 		ReleaseTupleDesc(tupDesc);
+ 
+ 		if (!result)
+ 		{
+ 			/*
+ 			 * where clause expression eval returned false, so
+ 			 * the constraint does not apply to this row
+ 			 */
+ 			heap_close(pk_rel, RowShareLock);
+ 			return PointerGetDatum(NULL);
+ 		}
+ 	}
+ 
  	if (SPI_connect() != SPI_OK_CONNECT)
  		elog(ERROR, "SPI_connect failed");
  
*************** ri_restrict_del(TriggerData *trigdata, b
*** 754,759 ****
--- 801,810 ----
  					querysep = "AND";
  					queryoids[i] = pk_type;
  				}
+ 				if(riinfo->consrc)
+ 				{
+ 					appendStringInfo(&querybuf, " AND %s", riinfo->consrc);
+ 				}
  				appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
  
  				/* Prepare and save the plan */
*************** ri_restrict_upd(TriggerData *trigdata, b
*** 977,982 ****
--- 1028,1037 ----
  					querysep = "AND";
  					queryoids[i] = pk_type;
  				}
+ 				if(riinfo->consrc)
+ 				{
+ 					appendStringInfo(&querybuf, " AND %s", riinfo->consrc);
+ 				}
  				appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
  
  				/* Prepare and save the plan */
*************** RI_FKey_cascade_del(PG_FUNCTION_ARGS)
*** 1133,1138 ****
--- 1188,1197 ----
  					querysep = "AND";
  					queryoids[i] = pk_type;
  				}
+ 				if(riinfo->consrc)
+ 				{
+ 					appendStringInfo(&querybuf, " AND %s", riinfo->consrc);
+ 				}
  
  				/* Prepare and save the plan */
  				qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
*************** RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
*** 1315,1320 ****
--- 1374,1383 ----
  					queryoids[j] = pk_type;
  				}
  				appendStringInfoString(&querybuf, qualbuf.data);
+ 				if(riinfo->consrc)
+ 				{
+ 					appendStringInfo(&querybuf, " AND %s", riinfo->consrc);
+ 				}
  
  				/* Prepare and save the plan */
  				qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys * 2, queryoids,
*************** RI_FKey_setnull_del(PG_FUNCTION_ARGS)
*** 1480,1485 ****
--- 1543,1552 ----
  					queryoids[i] = pk_type;
  				}
  				appendStringInfoString(&querybuf, qualbuf.data);
+ 				if(riinfo->consrc)
+ 				{
+ 					appendStringInfo(&querybuf, " AND %s", riinfo->consrc);
+ 				}
  
  				/* Prepare and save the plan */
  				qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
*************** RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
*** 1656,1661 ****
--- 1723,1732 ----
  					queryoids[i] = pk_type;
  				}
  				appendStringInfoString(&querybuf, qualbuf.data);
+ 				if(riinfo->consrc)
+ 				{
+ 					appendStringInfo(&querybuf, " AND %s", riinfo->consrc);
+ 				}
  
  				/* Prepare and save the plan */
  				qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
*************** RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
*** 1822,1827 ****
--- 1893,1902 ----
  					queryoids[i] = pk_type;
  				}
  				appendStringInfoString(&querybuf, qualbuf.data);
+ 				if(riinfo->consrc)
+ 				{
+ 					appendStringInfo(&querybuf, " AND %s", riinfo->consrc);
+ 				}
  
  				/* Prepare and save the plan */
  				qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
*************** RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
*** 2013,2018 ****
--- 2088,2097 ----
  					queryoids[i] = pk_type;
  				}
  				appendStringInfoString(&querybuf, qualbuf.data);
+ 				if(riinfo->consrc)
+ 				{
+ 					appendStringInfo(&querybuf, " AND %s", riinfo->consrc);
+ 				}
  
  				/* Prepare and save the plan */
  				qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
*************** ri_LoadConstraintInfo(Oid constraintOid)
*** 2788,2793 ****
--- 2867,2873 ----
  	bool		isNull;
  	ArrayType  *arr;
  	int			numkeys;
+ 	MemoryContext oldContext;
  
  	/*
  	 * On the first call initialize the hashtable
*************** ri_LoadConstraintInfo(Oid constraintOid)
*** 2908,2913 ****
--- 2988,3005 ----
  	if ((Pointer) arr != DatumGetPointer(adatum))
  		pfree(arr);				/* free de-toasted copy, if any */
  
+ 
+ 	/* copy where clause constraint info for partial keys */
+ 	oldContext = MemoryContextSwitchTo(CacheMemoryContext);
+ 	adatum = SysCacheGetAttr(CONSTROID, tup,
+ 							 Anum_pg_constraint_consrc, &isNull);
+ 	riinfo->consrc = isNull ? NULL : TextDatumGetCString(adatum);
+ 
+ 	adatum = SysCacheGetAttr(CONSTROID, tup,
+ 							 Anum_pg_constraint_conbin, &isNull);
+ 	riinfo->conbin = isNull ? NULL : TextDatumGetCString(adatum);
+ 	MemoryContextSwitchTo(oldContext);
+ 
  	ReleaseSysCache(tup);
  
  	riinfo->valid = true;
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
new file mode 100644
index b43765b..ae1ecdd
*** a/src/include/catalog/heap.h
--- b/src/include/catalog/heap.h
*************** extern Node *cookDefault(ParseState *pst
*** 107,112 ****
--- 107,116 ----
  			Oid atttypid,
  			int32 atttypmod,
  			char *attname);
+ extern Node *cookConstraint(ParseState *pstate,
+ 							Node *raw_constraint,
+ 							char *relname,
+ 							bool is_check);
  
  extern void DeleteRelationTuple(Oid relid);
  extern void DeleteAttributeTuples(Oid relid);
diff --git a/src/test/regress/expected/foreign_key_where.out b/src/test/regress/expected/foreign_key_where.out
new file mode 100644
index ...9c63590
*** a/src/test/regress/expected/foreign_key_where.out
--- b/src/test/regress/expected/foreign_key_where.out
***************
*** 0 ****
--- 1,302 ----
+ --
+ -- Test foreign keys with WHERE clauses
+ --
+ --
+ -- Basic setup
+ --
+ create type commentable_entity as enum ('blog', 'event');
+ create table blog (
+ 	id int4 primary key
+ );
+ create table event (
+ 	id int4 primary key
+ );
+ create table comment (
+ 	id int4,
+ 	parent_entity commentable_entity not null,
+ 	parent_id int4 default 66
+ );
+ --
+ -- basic fk insertion / pk deletion / pk update with restrict
+ --
+ -- add constraints
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 	references blog(id) where (parent_entity = 'blog')
+ 	on delete restrict;
+ alter table comment add constraint comment_event_fk foreign key (parent_id) 
+ 	references event(id) where (parent_entity = 'event')
+ 	on update restrict;
+ -- data setup
+ insert into blog values (10);
+ insert into event values (20);
+ insert into comment values(1, 'blog', 10);
+ insert into comment values(2, 'event', 20);
+ -- failing insert
+ insert into comment values(3, 'blog', 20); -- fails
+ ERROR:  insert or update on table "comment" violates foreign key constraint "comment_blog_fk"
+ DETAIL:  Key (parent_id)=(20) is not present in table "blog".
+ select * from comment order by id;
+  id | parent_entity | parent_id 
+ ----+---------------+-----------
+   1 | blog          |        10
+   2 | event         |        20
+ (2 rows)
+ 
+ -- delete of foreign key
+ delete from blog; -- fails
+ ERROR:  update or delete on table "blog" violates foreign key constraint "comment_blog_fk" on table "comment"
+ DETAIL:  Key (id)=(10) is still referenced from table "comment".
+ select * from comment order by id;
+  id | parent_entity | parent_id 
+ ----+---------------+-----------
+   1 | blog          |        10
+   2 | event         |        20
+ (2 rows)
+ 
+ -- update of foreign key
+ update event set id = 77; -- fails
+ ERROR:  update or delete on table "event" violates foreign key constraint "comment_event_fk" on table "comment"
+ DETAIL:  Key (id)=(20) is still referenced from table "comment".
+ select * from comment order by id;
+  id | parent_entity | parent_id 
+ ----+---------------+-----------
+   1 | blog          |        10
+   2 | event         |        20
+ (2 rows)
+ 
+ -- cleanup
+ delete from comment;
+ delete from blog;
+ delete from event;
+ alter table comment drop constraint comment_blog_fk;
+ alter table comment drop constraint comment_event_fk;
+ --
+ -- cascade deletions / updates
+ --
+ -- add constraints
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 	references blog(id) where (parent_entity = 'blog')
+ 	on delete cascade;
+ alter table comment add constraint comment_event_fk foreign key (parent_id) 
+ 	references event(id) where (parent_entity = 'event')
+ 	on update cascade;
+ -- data setup
+ insert into blog values (10);
+ insert into blog values (20);
+ insert into comment values(1, 'blog', 10);
+ insert into comment values(2, 'blog', 20);
+ insert into event values (10);
+ insert into event values (20);
+ insert into comment values(3, 'event', 10);
+ insert into comment values(4, 'event', 20);
+ -- cascading update
+ update event set id = 22 where id = 20;
+ select * from comment order by id;
+  id | parent_entity | parent_id 
+ ----+---------------+-----------
+   1 | blog          |        10
+   2 | blog          |        20
+   3 | event         |        10
+   4 | event         |        22
+ (4 rows)
+ 
+ -- cascading delete
+ delete from blog where id = 10;
+ select * from comment order by id;
+  id | parent_entity | parent_id 
+ ----+---------------+-----------
+   2 | blog          |        20
+   3 | event         |        10
+   4 | event         |        22
+ (3 rows)
+ 
+ -- cleanup
+ delete from comment;
+ delete from event;
+ delete from blog;
+ alter table comment drop constraint comment_blog_fk;
+ alter table comment drop constraint comment_event_fk;
+ --
+ -- set null
+ --
+ -- add constraints
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 	references blog(id) where (parent_entity = 'blog')
+ 	on delete set null;
+ alter table comment add constraint comment_event_fk foreign key (parent_id) 
+ 	references event(id) where (parent_entity = 'event')
+ 	on update set null;
+ -- data setup
+ insert into blog values (10);
+ insert into blog values (20);
+ insert into comment values(1, 'blog', 10);
+ insert into comment values(2, 'blog', 20);
+ insert into event values (10);
+ insert into event values (20);
+ insert into comment values(3, 'event', 10);
+ insert into comment values(4, 'event', 20);
+ -- update
+ update event set id = 22 where id = 20;
+ select * from comment order by id;
+  id | parent_entity | parent_id 
+ ----+---------------+-----------
+   1 | blog          |        10
+   2 | blog          |        20
+   3 | event         |        10
+   4 | event         |          
+ (4 rows)
+ 
+ -- delete
+ delete from blog where id = 10;
+ select * from comment order by id;
+  id | parent_entity | parent_id 
+ ----+---------------+-----------
+   1 | blog          |          
+   2 | blog          |        20
+   3 | event         |        10
+   4 | event         |          
+ (4 rows)
+ 
+ -- cleanup
+ delete from comment;
+ delete from event;
+ delete from blog;
+ alter table comment drop constraint comment_blog_fk;
+ alter table comment drop constraint comment_event_fk;
+ --
+ -- set default
+ --
+ -- add constraints
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 	references blog(id) where (parent_entity = 'blog')
+ 	on delete set default;
+ alter table comment add constraint comment_event_fk foreign key (parent_id) 
+ 	references event(id) where (parent_entity = 'event')
+ 	on update set default;
+ -- data setup
+ insert into blog values (10);
+ insert into blog values (20);
+ insert into comment values(1, 'blog', 10);
+ insert into comment values(2, 'blog', 20);
+ insert into event values (10);
+ insert into event values (20);
+ insert into comment values(3, 'event', 10);
+ insert into comment values(4, 'event', 20);
+ -- update
+ update event set id = 22 where id = 20; -- fails
+ ERROR:  insert or update on table "comment" violates foreign key constraint "comment_event_fk"
+ DETAIL:  Key (parent_id)=(66) is not present in table "event".
+ insert into event values (66);
+ update event set id = 22 where id = 20;
+ select * from comment order by id;
+  id | parent_entity | parent_id 
+ ----+---------------+-----------
+   1 | blog          |        10
+   2 | blog          |        20
+   3 | event         |        10
+   4 | event         |        66
+ (4 rows)
+ 
+ -- delete
+ delete from blog where id = 10; -- fails
+ ERROR:  insert or update on table "comment" violates foreign key constraint "comment_blog_fk"
+ DETAIL:  Key (parent_id)=(66) is not present in table "blog".
+ insert into blog values (66);
+ delete from blog where id = 10;
+ select * from comment order by id;
+  id | parent_entity | parent_id 
+ ----+---------------+-----------
+   1 | blog          |        66
+   2 | blog          |        20
+   3 | event         |        10
+   4 | event         |        66
+ (4 rows)
+ 
+ -- cleanup
+ delete from comment;
+ delete from event;
+ delete from blog;
+ alter table comment drop constraint comment_blog_fk;
+ alter table comment drop constraint comment_event_fk;
+ --
+ -- no action with deferred constraints
+ --
+ -- add constraints
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 	references blog(id) where (parent_entity = 'blog')
+ 	on delete no action
+ 	deferrable initially deferred;
+ alter table comment add constraint comment_event_fk foreign key (parent_id) 
+ 	references event(id) where (parent_entity = 'event')
+ 	on update no action
+ 	deferrable initially deferred;
+ -- data setup
+ insert into blog values (10);
+ insert into blog values (20);
+ insert into comment values(1, 'blog', 10);
+ insert into comment values(2, 'blog', 20);
+ insert into event values (10);
+ insert into event values (20);
+ insert into comment values(3, 'event', 10);
+ insert into comment values(4, 'event', 20);
+ -- update
+ begin;
+ update event set id = 22 where id = 20;
+ insert into event values (20);
+ commit;
+ select * from comment order by id;
+  id | parent_entity | parent_id 
+ ----+---------------+-----------
+   1 | blog          |        10
+   2 | blog          |        20
+   3 | event         |        10
+   4 | event         |        20
+ (4 rows)
+ 
+ -- delete
+ begin;
+ delete from blog where id = 10;
+ insert into blog values (10);
+ commit;
+ select * from comment order by id;
+  id | parent_entity | parent_id 
+ ----+---------------+-----------
+   1 | blog          |        10
+   2 | blog          |        20
+   3 | event         |        10
+   4 | event         |        20
+ (4 rows)
+ 
+ -- cleanup
+ delete from comment;
+ delete from event;
+ delete from blog;
+ alter table comment drop constraint comment_blog_fk;
+ alter table comment drop constraint comment_event_fk;
+ --
+ -- error handling
+ --
+ -- non-existent column
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 	references blog(id) where (foo = 'blog');
+ ERROR:  column "foo" does not exist
+ -- wrong type
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 		references blog(id) where (parent_entity = 1234);
+ ERROR:  operator does not exist: commentable_entity = integer
+ HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
+ -- expression not boolean
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 		references blog(id) where (parent_entity::text);
+ ERROR:  argument of WHERE must be type boolean, not type text
+ --
+ -- validation when table is pre-populated
+ --
+ insert into comment values(1, 'blog', 10);
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 		references blog(id) where (parent_entity = 'blog'); -- fails
+ ERROR:  insert or update on table "comment" violates foreign key constraint "comment_blog_fk"
+ DETAIL:  Key (parent_id)=(10) is not present in table "blog".
+ insert into blog values(10);
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 		references blog(id) where (parent_entity = 'blog');
diff --git a/src/test/regress/sql/foreign_key_where.sql b/src/test/regress/sql/foreign_key_where.sql
new file mode 100644
index ...ad23ce4
*** a/src/test/regress/sql/foreign_key_where.sql
--- b/src/test/regress/sql/foreign_key_where.sql
***************
*** 0 ****
--- 1,242 ----
+ --
+ -- Test foreign keys with WHERE clauses
+ --
+ 
+ --
+ -- Basic setup
+ --
+ create type commentable_entity as enum ('blog', 'event');
+ create table blog (
+ 	id int4 primary key
+ );
+ create table event (
+ 	id int4 primary key
+ );
+ create table comment (
+ 	id int4,
+ 	parent_entity commentable_entity not null,
+ 	parent_id int4 default 66
+ );
+ 
+ --
+ -- basic fk insertion / pk deletion / pk update with restrict
+ --
+ 
+ -- add constraints
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 	references blog(id) where (parent_entity = 'blog')
+ 	on delete restrict;
+ alter table comment add constraint comment_event_fk foreign key (parent_id)
+ 	references event(id) where (parent_entity = 'event')
+ 	on update restrict;
+ 
+ -- data setup
+ insert into blog values (10);
+ insert into event values (20);
+ insert into comment values(1, 'blog', 10);
+ insert into comment values(2, 'event', 20);
+ 
+ -- failing insert
+ insert into comment values(3, 'blog', 20); -- fails
+ select * from comment order by id;
+ 
+ -- delete of foreign key
+ delete from blog; -- fails
+ select * from comment order by id;
+ 
+ -- update of foreign key
+ update event set id = 77; -- fails
+ select * from comment order by id;
+ 
+ -- cleanup
+ delete from comment;
+ delete from blog;
+ delete from event;
+ alter table comment drop constraint comment_blog_fk;
+ alter table comment drop constraint comment_event_fk;
+ 
+ --
+ -- cascade deletions / updates
+ --
+ 
+ -- add constraints
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 	references blog(id) where (parent_entity = 'blog')
+ 	on delete cascade;
+ alter table comment add constraint comment_event_fk foreign key (parent_id)
+ 	references event(id) where (parent_entity = 'event')
+ 	on update cascade;
+ 
+ -- data setup
+ insert into blog values (10);
+ insert into blog values (20);
+ insert into comment values(1, 'blog', 10);
+ insert into comment values(2, 'blog', 20);
+ insert into event values (10);
+ insert into event values (20);
+ insert into comment values(3, 'event', 10);
+ insert into comment values(4, 'event', 20);
+ 
+ -- cascading update
+ update event set id = 22 where id = 20;
+ select * from comment order by id;
+ 
+ -- cascading delete
+ delete from blog where id = 10;
+ select * from comment order by id;
+ 
+ -- cleanup
+ delete from comment;
+ delete from event;
+ delete from blog;
+ alter table comment drop constraint comment_blog_fk;
+ alter table comment drop constraint comment_event_fk;
+ 
+ --
+ -- set null
+ --
+ 
+ -- add constraints
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 	references blog(id) where (parent_entity = 'blog')
+ 	on delete set null;
+ alter table comment add constraint comment_event_fk foreign key (parent_id)
+ 	references event(id) where (parent_entity = 'event')
+ 	on update set null;
+ 
+ -- data setup
+ insert into blog values (10);
+ insert into blog values (20);
+ insert into comment values(1, 'blog', 10);
+ insert into comment values(2, 'blog', 20);
+ insert into event values (10);
+ insert into event values (20);
+ insert into comment values(3, 'event', 10);
+ insert into comment values(4, 'event', 20);
+ 
+ -- update
+ update event set id = 22 where id = 20;
+ select * from comment order by id;
+ 
+ -- delete
+ delete from blog where id = 10;
+ select * from comment order by id;
+ 
+ -- cleanup
+ delete from comment;
+ delete from event;
+ delete from blog;
+ alter table comment drop constraint comment_blog_fk;
+ alter table comment drop constraint comment_event_fk;
+ 
+ --
+ -- set default
+ --
+ 
+ -- add constraints
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 	references blog(id) where (parent_entity = 'blog')
+ 	on delete set default;
+ alter table comment add constraint comment_event_fk foreign key (parent_id)
+ 	references event(id) where (parent_entity = 'event')
+ 	on update set default;
+ 
+ -- data setup
+ insert into blog values (10);
+ insert into blog values (20);
+ insert into comment values(1, 'blog', 10);
+ insert into comment values(2, 'blog', 20);
+ insert into event values (10);
+ insert into event values (20);
+ insert into comment values(3, 'event', 10);
+ insert into comment values(4, 'event', 20);
+ 
+ -- update
+ update event set id = 22 where id = 20; -- fails
+ insert into event values (66);
+ update event set id = 22 where id = 20;
+ select * from comment order by id;
+ 
+ -- delete
+ delete from blog where id = 10; -- fails
+ insert into blog values (66);
+ delete from blog where id = 10;
+ select * from comment order by id;
+ 
+ -- cleanup
+ delete from comment;
+ delete from event;
+ delete from blog;
+ alter table comment drop constraint comment_blog_fk;
+ alter table comment drop constraint comment_event_fk;
+ 
+ --
+ -- no action with deferred constraints
+ --
+ 
+ -- add constraints
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 	references blog(id) where (parent_entity = 'blog')
+ 	on delete no action
+ 	deferrable initially deferred;
+ alter table comment add constraint comment_event_fk foreign key (parent_id)
+ 	references event(id) where (parent_entity = 'event')
+ 	on update no action
+ 	deferrable initially deferred;
+ 
+ -- data setup
+ insert into blog values (10);
+ insert into blog values (20);
+ insert into comment values(1, 'blog', 10);
+ insert into comment values(2, 'blog', 20);
+ insert into event values (10);
+ insert into event values (20);
+ insert into comment values(3, 'event', 10);
+ insert into comment values(4, 'event', 20);
+ 
+ -- update
+ begin;
+ update event set id = 22 where id = 20;
+ insert into event values (20);
+ commit;
+ select * from comment order by id;
+ 
+ -- delete
+ begin;
+ delete from blog where id = 10;
+ insert into blog values (10);
+ commit;
+ select * from comment order by id;
+ 
+ -- cleanup
+ delete from comment;
+ delete from event;
+ delete from blog;
+ alter table comment drop constraint comment_blog_fk;
+ alter table comment drop constraint comment_event_fk;
+ 
+ --
+ -- error handling
+ --
+ 
+ -- non-existent column
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 	references blog(id) where (foo = 'blog');
+ 
+ -- wrong type
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 		references blog(id) where (parent_entity = 1234);
+ -- expression not boolean
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 		references blog(id) where (parent_entity::text);
+ 
+ 
+ --
+ -- validation when table is pre-populated
+ --
+ insert into comment values(1, 'blog', 10);
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 		references blog(id) where (parent_entity = 'blog'); -- fails
+ insert into blog values(10);
+ alter table comment add constraint comment_blog_fk foreign key (parent_id)
+ 		references blog(id) where (parent_entity = 'blog');
