diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 5de6816a49abaa8aa6575bc44dd67cef914e5342..cd1bb262a3539e9737f26ffe1d0a9fe122ec166a 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -66,9 +66,12 @@
 #define RI_KEYS_SOME_NULL				1
 #define RI_KEYS_NONE_NULL				2
 
-/* queryno values must be distinct for the convenience of ri_PerformCheck */
-#define RI_PLAN_CHECK_LOOKUPPK_NOCOLS	1
-#define RI_PLAN_CHECK_LOOKUPPK			2
+/* RI query type codes */
+/* these queries are executed against the PK (referenced) table: */
+#define RI_PLAN_CHECK_LOOKUPPK			1
+#define RI_PLAN_CHECK_LOOKUPPK_FROM_PK	2
+#define RI_PLAN_LAST_ON_PK				RI_PLAN_CHECK_LOOKUPPK_FROM_PK
+/* these queries are executed against the FK (referencing) table: */
 #define RI_PLAN_CASCADE_DEL_DODELETE	3
 #define RI_PLAN_CASCADE_UPD_DOUPDATE	4
 #define RI_PLAN_NOACTION_DEL_CHECKREF	5
@@ -77,6 +80,8 @@
 #define RI_PLAN_RESTRICT_UPD_CHECKREF	8
 #define RI_PLAN_SETNULL_DEL_DOUPDATE	9
 #define RI_PLAN_SETNULL_UPD_DOUPDATE	10
+#define RI_PLAN_SETDEFAULT_DEL_DOUPDATE	11
+#define RI_PLAN_SETDEFAULT_UPD_DOUPDATE	12
 
 #define MAX_QUOTED_NAME_LEN  (NAMEDATALEN*2+3)
 #define MAX_QUOTED_REL_NAME_LEN  (MAX_QUOTED_NAME_LEN*2)
@@ -90,9 +95,6 @@
 #define RI_TRIGTYPE_INUP   3
 #define RI_TRIGTYPE_DELETE 4
 
-#define RI_KEYPAIR_FK_IDX	0
-#define RI_KEYPAIR_PK_IDX	1
-
 
 /* ----------
  * RI_ConstraintInfo
@@ -129,13 +131,8 @@ typedef struct RI_ConstraintInfo
  */
 typedef struct RI_QueryKey
 {
-	char		constr_type;
-	Oid			constr_id;
-	int32		constr_queryno;
-	Oid			fk_relid;
-	Oid			pk_relid;
-	int32		nkeypairs;
-	int16		keypair[RI_MAX_NUMKEYS][2];
+	Oid			constr_id;		/* OID of pg_constraint entry */
+	int32		constr_queryno;	/* query type ID, see RI_PLAN_XXX above */
 } RI_QueryKey;
 
 
@@ -197,14 +194,11 @@ static void ri_GenerateQual(StringInfo buf,
 				const char *rightop, Oid rightoptype);
 static void ri_add_cast_to(StringInfo buf, Oid typid);
 static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
-static int ri_NullCheck(Relation rel, HeapTuple tup,
-			 RI_QueryKey *key, int pairidx);
-static void ri_BuildQueryKeyFull(RI_QueryKey *key,
-					 const RI_ConstraintInfo *riinfo,
-					 int32 constr_queryno);
-static void ri_BuildQueryKeyPkCheck(RI_QueryKey *key,
-						const RI_ConstraintInfo *riinfo,
-						int32 constr_queryno);
+static int ri_NullCheck(HeapTuple tup,
+			 const RI_ConstraintInfo *riinfo, bool rel_is_pk);
+static void ri_BuildQueryKey(RI_QueryKey *key,
+				 const RI_ConstraintInfo *riinfo,
+				 int32 constr_queryno);
 static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
 			 const RI_ConstraintInfo *riinfo, bool rel_is_pk);
 static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
@@ -225,18 +219,18 @@ static void ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo,
 static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
 			 RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
 			 bool cache_plan);
-static bool ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
+static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo,
+				RI_QueryKey *qkey, SPIPlanPtr qplan,
 				Relation fk_rel, Relation pk_rel,
 				HeapTuple old_tuple, HeapTuple new_tuple,
-				bool detectNewRows,
-				int expect_OK, const char *constrname);
-static void ri_ExtractValues(RI_QueryKey *qkey, int key_idx,
-				 Relation rel, HeapTuple tuple,
+				bool detectNewRows, int expect_OK);
+static void ri_ExtractValues(Relation rel, HeapTuple tup,
+				 const RI_ConstraintInfo *riinfo, bool rel_is_pk,
 				 Datum *vals, char *nulls);
-static void ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
+static void ri_ReportViolation(const RI_ConstraintInfo *riinfo,
 				   Relation pk_rel, Relation fk_rel,
 				   HeapTuple violator, TupleDesc tupdesc,
-				   bool spi_err);
+				   int queryno, bool spi_err);
 
 
 /* ----------
@@ -320,11 +314,11 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 	 */
 	if (riinfo.nkeys == 0)
 	{
-		ri_BuildQueryKeyFull(&qkey, &riinfo, RI_PLAN_CHECK_LOOKUPPK_NOCOLS);
-
 		if (SPI_connect() != SPI_OK_CONNECT)
 			elog(ERROR, "SPI_connect failed");
 
+		ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_CHECK_LOOKUPPK);
+
 		if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 		{
 			char		querystr[MAX_QUOTED_REL_NAME_LEN + 100];
@@ -348,12 +342,11 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 		/*
 		 * Execute the plan
 		 */
-		ri_PerformCheck(&qkey, qplan,
+		ri_PerformCheck(&riinfo, &qkey, qplan,
 						fk_rel, pk_rel,
 						NULL, NULL,
 						false,
-						SPI_OK_SELECT,
-						NameStr(riinfo.conname));
+						SPI_OK_SELECT);
 
 		if (SPI_finish() != SPI_OK_FINISH)
 			elog(ERROR, "SPI_finish failed");
@@ -368,9 +361,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("MATCH PARTIAL not yet implemented")));
 
-	ri_BuildQueryKeyFull(&qkey, &riinfo, RI_PLAN_CHECK_LOOKUPPK);
-
-	switch (ri_NullCheck(fk_rel, new_row, &qkey, RI_KEYPAIR_FK_IDX))
+	switch (ri_NullCheck(new_row, &riinfo, false))
 	{
 		case RI_KEYS_ALL_NULL:
 
@@ -448,6 +439,8 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 	/*
 	 * Fetch or prepare a saved plan for the real check
 	 */
+	ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_CHECK_LOOKUPPK);
+
 	if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 	{
 		StringInfoData querybuf;
@@ -493,12 +486,11 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 	/*
 	 * Now check that foreign key exists in PK table
 	 */
-	ri_PerformCheck(&qkey, qplan,
+	ri_PerformCheck(&riinfo, &qkey, qplan,
 					fk_rel, pk_rel,
 					NULL, new_row,
 					false,
-					SPI_OK_SELECT,
-					NameStr(riinfo.conname));
+					SPI_OK_SELECT);
 
 	if (SPI_finish() != SPI_OK_FINISH)
 		elog(ERROR, "SPI_finish failed");
@@ -553,9 +545,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 	int			i;
 	bool		result;
 
-	ri_BuildQueryKeyPkCheck(&qkey, riinfo, RI_PLAN_CHECK_LOOKUPPK);
-
-	switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+	switch (ri_NullCheck(old_row, riinfo, true))
 	{
 		case RI_KEYS_ALL_NULL:
 
@@ -615,6 +605,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 	/*
 	 * Fetch or prepare a saved plan for the real check
 	 */
+	ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CHECK_LOOKUPPK_FROM_PK);
+
 	if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 	{
 		StringInfoData querybuf;
@@ -659,11 +651,11 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 	/*
 	 * We have a plan now. Run it.
 	 */
-	result = ri_PerformCheck(&qkey, qplan,
+	result = ri_PerformCheck(riinfo, &qkey, qplan,
 							 fk_rel, pk_rel,
 							 old_row, NULL,
 							 true,		/* treat like update */
-							 SPI_OK_SELECT, NULL);
+							 SPI_OK_SELECT);
 
 	if (SPI_finish() != SPI_OK_FINISH)
 		elog(ERROR, "SPI_finish failed");
@@ -740,10 +732,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			ri_BuildQueryKeyFull(&qkey, &riinfo,
-								 RI_PLAN_NOACTION_DEL_CHECKREF);
-
-			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+			switch (ri_NullCheck(old_row, &riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -767,9 +756,10 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 				elog(ERROR, "SPI_connect failed");
 
 			/*
-			 * Fetch or prepare a saved plan for the restrict delete lookup if
-			 * foreign references exist
+			 * Fetch or prepare a saved plan for the restrict delete lookup
 			 */
+			ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_NOACTION_DEL_CHECKREF);
+
 			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
 				StringInfoData querybuf;
@@ -816,12 +806,11 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 			/*
 			 * We have a plan now. Run it to check for existing references.
 			 */
-			ri_PerformCheck(&qkey, qplan,
+			ri_PerformCheck(&riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
 							old_row, NULL,
 							true,		/* must detect new rows */
-							SPI_OK_SELECT,
-							NameStr(riinfo.conname));
+							SPI_OK_SELECT);
 
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(ERROR, "SPI_finish failed");
@@ -911,10 +900,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			ri_BuildQueryKeyFull(&qkey, &riinfo,
-								 RI_PLAN_NOACTION_UPD_CHECKREF);
-
-			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+			switch (ri_NullCheck(old_row, &riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -957,9 +943,10 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 				elog(ERROR, "SPI_connect failed");
 
 			/*
-			 * Fetch or prepare a saved plan for the noaction update lookup if
-			 * foreign references exist
+			 * Fetch or prepare a saved plan for the noaction update lookup
 			 */
+			ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_NOACTION_UPD_CHECKREF);
+
 			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
 				StringInfoData querybuf;
@@ -1006,12 +993,11 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 			/*
 			 * We have a plan now. Run it to check for existing references.
 			 */
-			ri_PerformCheck(&qkey, qplan,
+			ri_PerformCheck(&riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
 							old_row, NULL,
 							true,		/* must detect new rows */
-							SPI_OK_SELECT,
-							NameStr(riinfo.conname));
+							SPI_OK_SELECT);
 
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(ERROR, "SPI_finish failed");
@@ -1096,10 +1082,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			ri_BuildQueryKeyFull(&qkey, &riinfo,
-								 RI_PLAN_CASCADE_DEL_DODELETE);
-
-			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+			switch (ri_NullCheck(old_row, &riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1125,6 +1108,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 			/*
 			 * Fetch or prepare a saved plan for the cascaded delete
 			 */
+			ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_CASCADE_DEL_DODELETE);
+
 			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
 				StringInfoData querybuf;
@@ -1170,12 +1155,11 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 			 * We have a plan now. Build up the arguments from the key values
 			 * in the deleted PK tuple and delete the referencing rows
 			 */
-			ri_PerformCheck(&qkey, qplan,
+			ri_PerformCheck(&riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
 							old_row, NULL,
 							true,		/* must detect new rows */
-							SPI_OK_DELETE,
-							NameStr(riinfo.conname));
+							SPI_OK_DELETE);
 
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(ERROR, "SPI_finish failed");
@@ -1264,10 +1248,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			ri_BuildQueryKeyFull(&qkey, &riinfo,
-								 RI_PLAN_CASCADE_UPD_DOUPDATE);
-
-			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+			switch (ri_NullCheck(old_row, &riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1300,9 +1281,10 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 				elog(ERROR, "SPI_connect failed");
 
 			/*
-			 * Fetch or prepare a saved plan for the cascaded update of
-			 * foreign references
+			 * Fetch or prepare a saved plan for the cascaded update
 			 */
+			ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_CASCADE_UPD_DOUPDATE);
+
 			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
 				StringInfoData querybuf;
@@ -1360,12 +1342,11 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 			/*
 			 * We have a plan now. Run it to update the existing references.
 			 */
-			ri_PerformCheck(&qkey, qplan,
+			ri_PerformCheck(&riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
 							old_row, new_row,
 							true,		/* must detect new rows */
-							SPI_OK_UPDATE,
-							NameStr(riinfo.conname));
+							SPI_OK_UPDATE);
 
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(ERROR, "SPI_finish failed");
@@ -1455,10 +1436,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			ri_BuildQueryKeyFull(&qkey, &riinfo,
-								 RI_PLAN_RESTRICT_DEL_CHECKREF);
-
-			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+			switch (ri_NullCheck(old_row, &riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1482,9 +1460,10 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 				elog(ERROR, "SPI_connect failed");
 
 			/*
-			 * Fetch or prepare a saved plan for the restrict delete lookup if
-			 * foreign references exist
+			 * Fetch or prepare a saved plan for the restrict delete lookup
 			 */
+			ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_RESTRICT_DEL_CHECKREF);
+
 			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
 				StringInfoData querybuf;
@@ -1531,12 +1510,11 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 			/*
 			 * We have a plan now. Run it to check for existing references.
 			 */
-			ri_PerformCheck(&qkey, qplan,
+			ri_PerformCheck(&riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
 							old_row, NULL,
 							true,		/* must detect new rows */
-							SPI_OK_SELECT,
-							NameStr(riinfo.conname));
+							SPI_OK_SELECT);
 
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(ERROR, "SPI_finish failed");
@@ -1629,10 +1607,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			ri_BuildQueryKeyFull(&qkey, &riinfo,
-								 RI_PLAN_RESTRICT_UPD_CHECKREF);
-
-			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+			switch (ri_NullCheck(old_row, &riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1665,9 +1640,10 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 				elog(ERROR, "SPI_connect failed");
 
 			/*
-			 * Fetch or prepare a saved plan for the restrict update lookup if
-			 * foreign references exist
+			 * Fetch or prepare a saved plan for the restrict update lookup
 			 */
+			ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_RESTRICT_UPD_CHECKREF);
+
 			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
 				StringInfoData querybuf;
@@ -1714,12 +1690,11 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 			/*
 			 * We have a plan now. Run it to check for existing references.
 			 */
-			ri_PerformCheck(&qkey, qplan,
+			ri_PerformCheck(&riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
 							old_row, NULL,
 							true,		/* must detect new rows */
-							SPI_OK_SELECT,
-							NameStr(riinfo.conname));
+							SPI_OK_SELECT);
 
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(ERROR, "SPI_finish failed");
@@ -1804,10 +1779,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			ri_BuildQueryKeyFull(&qkey, &riinfo,
-								 RI_PLAN_SETNULL_DEL_DOUPDATE);
-
-			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+			switch (ri_NullCheck(old_row, &riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -1833,6 +1805,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 			/*
 			 * Fetch or prepare a saved plan for the set null delete operation
 			 */
+			ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_SETNULL_DEL_DOUPDATE);
+
 			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
 				StringInfoData querybuf;
@@ -1887,12 +1861,11 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 			/*
 			 * We have a plan now. Run it to check for existing references.
 			 */
-			ri_PerformCheck(&qkey, qplan,
+			ri_PerformCheck(&riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
 							old_row, NULL,
 							true,		/* must detect new rows */
-							SPI_OK_UPDATE,
-							NameStr(riinfo.conname));
+							SPI_OK_UPDATE);
 
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(ERROR, "SPI_finish failed");
@@ -1979,10 +1952,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			ri_BuildQueryKeyFull(&qkey, &riinfo,
-								 RI_PLAN_SETNULL_UPD_DOUPDATE);
-
-			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+			switch (ri_NullCheck(old_row, &riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -2017,6 +1987,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 			/*
 			 * Fetch or prepare a saved plan for the set null update operation
 			 */
+			ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_SETNULL_UPD_DOUPDATE);
+
 			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
 				StringInfoData querybuf;
@@ -2071,12 +2043,11 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 			/*
 			 * We have a plan now. Run it to update the existing references.
 			 */
-			ri_PerformCheck(&qkey, qplan,
+			ri_PerformCheck(&riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
 							old_row, NULL,
 							true,		/* must detect new rows */
-							SPI_OK_UPDATE,
-							NameStr(riinfo.conname));
+							SPI_OK_UPDATE);
 
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(ERROR, "SPI_finish failed");
@@ -2160,10 +2131,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			ri_BuildQueryKeyFull(&qkey, &riinfo,
-								 RI_PLAN_SETNULL_DEL_DOUPDATE);
-
-			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+			switch (ri_NullCheck(old_row, &riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -2191,6 +2159,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 			 * Unfortunately we need to do it on every invocation because the
 			 * default value could potentially change between calls.
 			 */
+			ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_SETDEFAULT_DEL_DOUPDATE);
+
 			{
 				StringInfoData querybuf;
 				StringInfoData qualbuf;
@@ -2245,12 +2215,11 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 			/*
 			 * We have a plan now. Run it to update the existing references.
 			 */
-			ri_PerformCheck(&qkey, qplan,
+			ri_PerformCheck(&riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
 							old_row, NULL,
 							true,		/* must detect new rows */
-							SPI_OK_UPDATE,
-							NameStr(riinfo.conname));
+							SPI_OK_UPDATE);
 
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(ERROR, "SPI_finish failed");
@@ -2346,10 +2315,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 			 */
 		case FKCONSTR_MATCH_SIMPLE:
 		case FKCONSTR_MATCH_FULL:
-			ri_BuildQueryKeyFull(&qkey, &riinfo,
-								 RI_PLAN_SETNULL_DEL_DOUPDATE);
-
-			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+			switch (ri_NullCheck(old_row, &riinfo, true))
 			{
 				case RI_KEYS_ALL_NULL:
 				case RI_KEYS_SOME_NULL:
@@ -2386,6 +2352,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 			 * Unfortunately we need to do it on every invocation because the
 			 * default value could potentially change between calls.
 			 */
+			ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_SETDEFAULT_UPD_DOUPDATE);
+
 			{
 				StringInfoData querybuf;
 				StringInfoData qualbuf;
@@ -2440,12 +2408,11 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 			/*
 			 * We have a plan now. Run it to update the existing references.
 			 */
-			ri_PerformCheck(&qkey, qplan,
+			ri_PerformCheck(&riinfo, &qkey, qplan,
 							fk_rel, pk_rel,
 							old_row, NULL,
 							true,		/* must detect new rows */
-							SPI_OK_UPDATE,
-							NameStr(riinfo.conname));
+							SPI_OK_UPDATE);
 
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(ERROR, "SPI_finish failed");
@@ -2604,7 +2571,6 @@ bool
 RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 {
 	RI_ConstraintInfo riinfo;
-	const char *constrname = trigger->tgname;
 	StringInfoData querybuf;
 	char		pkrelname[MAX_QUOTED_REL_NAME_LEN];
 	char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
@@ -2799,46 +2765,42 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 	{
 		HeapTuple	tuple = SPI_tuptable->vals[0];
 		TupleDesc	tupdesc = SPI_tuptable->tupdesc;
-		RI_QueryKey qkey;
+
+		/*
+		 * The columns to look at in the result tuple are 1..N, not whatever
+		 * they are in the fk_rel.  Hack up riinfo so that the subroutines
+		 * called here will behave properly.
+		 *
+		 * In addition to this, we have to pass the correct tupdesc to
+		 * ri_ReportViolation, overriding its normal habit of using the pk_rel
+		 * or fk_rel's tupdesc.
+		 */
+		for (i = 0; i < riinfo.nkeys; i++)
+			riinfo.fk_attnums[i] = i + 1;
 
 		/*
 		 * If it's MATCH FULL, and there are any nulls in the FK keys,
 		 * complain about that rather than the lack of a match.  MATCH FULL
 		 * disallows partially-null FK rows.
 		 */
-		if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL)
-		{
-			bool		isnull = false;
-
-			for (i = 1; i <= riinfo.nkeys; i++)
-			{
-				(void) SPI_getbinval(tuple, tupdesc, i, &isnull);
-				if (isnull)
-					break;
-			}
-			if (isnull)
-				ereport(ERROR,
-						(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
-						 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
-								RelationGetRelationName(fk_rel),
-								constrname),
-						 errdetail("MATCH FULL does not allow mixing of null and nonnull key values.")));
-		}
+		if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
+			ri_NullCheck(tuple, &riinfo, false) != RI_KEYS_NONE_NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
+					 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
+							RelationGetRelationName(fk_rel),
+							NameStr(riinfo.conname)),
+					 errdetail("MATCH FULL does not allow mixing of null and nonnull key values.")));
 
 		/*
-		 * Although we didn't cache the query, we need to set up a fake query
-		 * key to pass to ri_ReportViolation.
+		 * We tell ri_ReportViolation we were doing the RI_PLAN_CHECK_LOOKUPPK
+		 * query, which isn't true, but will cause it to use riinfo.fk_attnums
+		 * as we need.
 		 */
-		MemSet(&qkey, 0, sizeof(qkey));
-		qkey.constr_queryno = RI_PLAN_CHECK_LOOKUPPK;
-		qkey.nkeypairs = riinfo.nkeys;
-		for (i = 0; i < riinfo.nkeys; i++)
-			qkey.keypair[i][RI_KEYPAIR_FK_IDX] = i + 1;
-
-		ri_ReportViolation(&qkey, constrname,
+		ri_ReportViolation(&riinfo,
 						   pk_rel, fk_rel,
 						   tuple, tupdesc,
-						   false);
+						   RI_PLAN_CHECK_LOOKUPPK, false);
 	}
 
 	if (SPI_finish() != SPI_OK_FINISH)
@@ -3015,36 +2977,26 @@ ri_GenerateQualCollation(StringInfo buf, Oid collation)
 }
 
 /* ----------
- * ri_BuildQueryKeyFull -
+ * ri_BuildQueryKey -
  *
- *	Build up a new hashtable key for a prepared SPI plan of a
- *	constraint trigger of MATCH FULL.
+ *	Construct a hashtable key for a prepared SPI plan of an FK constraint.
  *
  *		key: output argument, *key is filled in based on the other arguments
  *		riinfo: info from pg_constraint entry
- *		constr_queryno: an internal number of the query inside the proc
- *
- *	At least for MATCH FULL this builds a unique key per plan.
+ *		constr_queryno: an internal number identifying the query type
+ *			(see RI_PLAN_XXX constants at head of file)
  * ----------
  */
 static void
-ri_BuildQueryKeyFull(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
-					 int32 constr_queryno)
+ri_BuildQueryKey(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
+				 int32 constr_queryno)
 {
-	int			i;
-
-	MemSet(key, 0, sizeof(RI_QueryKey));
-	key->constr_type = FKCONSTR_MATCH_FULL;
+	/*
+	 * We assume struct RI_QueryKey contains no padding bytes, else we'd
+	 * need to use memset to clear them.
+	 */
 	key->constr_id = riinfo->constraint_id;
 	key->constr_queryno = constr_queryno;
-	key->fk_relid = riinfo->fk_relid;
-	key->pk_relid = riinfo->pk_relid;
-	key->nkeypairs = riinfo->nkeys;
-	for (i = 0; i < riinfo->nkeys; i++)
-	{
-		key->keypair[i][RI_KEYPAIR_FK_IDX] = riinfo->fk_attnums[i];
-		key->keypair[i][RI_KEYPAIR_PK_IDX] = riinfo->pk_attnums[i];
-	}
 }
 
 /*
@@ -3269,12 +3221,10 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
 	int			save_sec_context;
 
 	/*
-	 * The query is always run against the FK table except when this is an
-	 * update/insert trigger on the FK table itself - either
-	 * RI_PLAN_CHECK_LOOKUPPK or RI_PLAN_CHECK_LOOKUPPK_NOCOLS
+	 * Use the query type code to determine whether the query is run against
+	 * the PK or FK table; we'll do the check as that table's owner
 	 */
-	if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK ||
-		qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK_NOCOLS)
+	if (qkey->constr_queryno <= RI_PLAN_LAST_ON_PK)
 		query_rel = pk_rel;
 	else
 		query_rel = fk_rel;
@@ -3307,15 +3257,15 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
  * Perform a query to enforce an RI restriction
  */
 static bool
-ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
+ri_PerformCheck(const RI_ConstraintInfo *riinfo,
+				RI_QueryKey *qkey, SPIPlanPtr qplan,
 				Relation fk_rel, Relation pk_rel,
 				HeapTuple old_tuple, HeapTuple new_tuple,
-				bool detectNewRows,
-				int expect_OK, const char *constrname)
+				bool detectNewRows, int expect_OK)
 {
 	Relation	query_rel,
 				source_rel;
-	int			key_idx;
+	bool		source_is_pk;
 	Snapshot	test_snapshot;
 	Snapshot	crosscheck_snapshot;
 	int			limit;
@@ -3326,45 +3276,44 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
 	char		nulls[RI_MAX_NUMKEYS * 2];
 
 	/*
-	 * The query is always run against the FK table except when this is an
-	 * update/insert trigger on the FK table itself - either
-	 * RI_PLAN_CHECK_LOOKUPPK or RI_PLAN_CHECK_LOOKUPPK_NOCOLS
+	 * Use the query type code to determine whether the query is run against
+	 * the PK or FK table; we'll do the check as that table's owner
 	 */
-	if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK ||
-		qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK_NOCOLS)
+	if (qkey->constr_queryno <= RI_PLAN_LAST_ON_PK)
 		query_rel = pk_rel;
 	else
 		query_rel = fk_rel;
 
 	/*
 	 * The values for the query are taken from the table on which the trigger
-	 * is called - it is normally the other one with respect to query_rel. An
-	 * exception is ri_Check_Pk_Match(), which uses the PK table for both (the
-	 * case when constrname == NULL)
+	 * is called - it is normally the other one with respect to query_rel.
+	 * An exception is ri_Check_Pk_Match(), which uses the PK table for both
+	 * (and sets queryno to RI_PLAN_CHECK_LOOKUPPK_FROM_PK).  We might
+	 * eventually need some less klugy way to determine this.
 	 */
-	if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK && constrname != NULL)
+	if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK)
 	{
 		source_rel = fk_rel;
-		key_idx = RI_KEYPAIR_FK_IDX;
+		source_is_pk = false;
 	}
 	else
 	{
 		source_rel = pk_rel;
-		key_idx = RI_KEYPAIR_PK_IDX;
+		source_is_pk = true;
 	}
 
 	/* Extract the parameters to be passed into the query */
 	if (new_tuple)
 	{
-		ri_ExtractValues(qkey, key_idx, source_rel, new_tuple,
+		ri_ExtractValues(source_rel, new_tuple, riinfo, source_is_pk,
 						 vals, nulls);
 		if (old_tuple)
-			ri_ExtractValues(qkey, key_idx, source_rel, old_tuple,
-							 vals + qkey->nkeypairs, nulls + qkey->nkeypairs);
+			ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
+							 vals + riinfo->nkeys, nulls + riinfo->nkeys);
 	}
 	else
 	{
-		ri_ExtractValues(qkey, key_idx, source_rel, old_tuple,
+		ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
 						 vals, nulls);
 	}
 
@@ -3419,20 +3368,21 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
 		elog(ERROR, "SPI_execute_snapshot returned %d", spi_result);
 
 	if (expect_OK >= 0 && spi_result != expect_OK)
-		ri_ReportViolation(qkey, constrname ? constrname : "",
+		ri_ReportViolation(riinfo,
 						   pk_rel, fk_rel,
 						   new_tuple ? new_tuple : old_tuple,
 						   NULL,
-						   true);
+						   qkey->constr_queryno, true);
 
 	/* XXX wouldn't it be clearer to do this part at the caller? */
-	if (constrname && expect_OK == SPI_OK_SELECT &&
+	if (qkey->constr_queryno != RI_PLAN_CHECK_LOOKUPPK_FROM_PK &&
+		expect_OK == SPI_OK_SELECT &&
 	(SPI_processed == 0) == (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK))
-		ri_ReportViolation(qkey, constrname,
+		ri_ReportViolation(riinfo,
 						   pk_rel, fk_rel,
 						   new_tuple ? new_tuple : old_tuple,
 						   NULL,
-						   false);
+						   qkey->constr_queryno, false);
 
 	return SPI_processed != 0;
 }
@@ -3441,18 +3391,24 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
  * Extract fields from a tuple into Datum/nulls arrays
  */
 static void
-ri_ExtractValues(RI_QueryKey *qkey, int key_idx,
-				 Relation rel, HeapTuple tuple,
+ri_ExtractValues(Relation rel, HeapTuple tup,
+				 const RI_ConstraintInfo *riinfo, bool rel_is_pk,
 				 Datum *vals, char *nulls)
 {
+	TupleDesc	tupdesc = rel->rd_att;
+	const int16 *attnums;
 	int			i;
 	bool		isnull;
 
-	for (i = 0; i < qkey->nkeypairs; i++)
+	if (rel_is_pk)
+		attnums = riinfo->pk_attnums;
+	else
+		attnums = riinfo->fk_attnums;
+
+	for (i = 0; i < riinfo->nkeys; i++)
 	{
-		vals[i] = SPI_getbinval(tuple, rel->rd_att,
-								qkey->keypair[i][key_idx],
-								&isnull);
+		vals[i] = heap_getattr(tup, attnums[i], tupdesc,
+							   &isnull);
 		nulls[i] = isnull ? 'n' : ' ';
 	}
 }
@@ -3467,23 +3423,23 @@ ri_ExtractValues(RI_QueryKey *qkey, int key_idx,
  * message looks like 'key blah is still referenced from FK'.
  */
 static void
-ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
+ri_ReportViolation(const RI_ConstraintInfo *riinfo,
 				   Relation pk_rel, Relation fk_rel,
 				   HeapTuple violator, TupleDesc tupdesc,
-				   bool spi_err)
+				   int queryno, bool spi_err)
 {
 	StringInfoData key_names;
 	StringInfoData key_values;
 	bool		onfk;
-	int			idx,
-				key_idx;
+	const int16 *attnums;
+	int			idx;
 
 	if (spi_err)
 		ereport(ERROR,
 				(errcode(ERRCODE_INTERNAL_ERROR),
 				 errmsg("referential integrity query on \"%s\" from constraint \"%s\" on \"%s\" gave unexpected result",
 						RelationGetRelationName(pk_rel),
-						constrname,
+						NameStr(riinfo->conname),
 						RelationGetRelationName(fk_rel)),
 				 errhint("This is most likely due to a rule having rewritten the query.")));
 
@@ -3491,16 +3447,16 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
 	 * Determine which relation to complain about.	If tupdesc wasn't passed
 	 * by caller, assume the violator tuple came from there.
 	 */
-	onfk = (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK);
+	onfk = (queryno == RI_PLAN_CHECK_LOOKUPPK);
 	if (onfk)
 	{
-		key_idx = RI_KEYPAIR_FK_IDX;
+		attnums = riinfo->fk_attnums;
 		if (tupdesc == NULL)
 			tupdesc = fk_rel->rd_att;
 	}
 	else
 	{
-		key_idx = RI_KEYPAIR_PK_IDX;
+		attnums = riinfo->pk_attnums;
 		if (tupdesc == NULL)
 			tupdesc = pk_rel->rd_att;
 	}
@@ -3510,12 +3466,13 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
 	 * constraint - no need to try to extract the values, and the message in
 	 * this case looks different.
 	 */
-	if (qkey->nkeypairs == 0)
+	if (riinfo->nkeys == 0)
 	{
 		ereport(ERROR,
 				(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
 				 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
-						RelationGetRelationName(fk_rel), constrname),
+						RelationGetRelationName(fk_rel),
+						NameStr(riinfo->conname)),
 				 errdetail("No rows were found in \"%s\".",
 						   RelationGetRelationName(pk_rel))));
 	}
@@ -3523,9 +3480,9 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
 	/* Get printable versions of the keys involved */
 	initStringInfo(&key_names);
 	initStringInfo(&key_values);
-	for (idx = 0; idx < qkey->nkeypairs; idx++)
+	for (idx = 0; idx < riinfo->nkeys; idx++)
 	{
-		int			fnum = qkey->keypair[idx][key_idx];
+		int			fnum = attnums[idx];
 		char	   *name,
 				   *val;
 
@@ -3547,7 +3504,8 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
 		ereport(ERROR,
 				(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
 				 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
-						RelationGetRelationName(fk_rel), constrname),
+						RelationGetRelationName(fk_rel),
+						NameStr(riinfo->conname)),
 				 errdetail("Key (%s)=(%s) is not present in table \"%s\".",
 						   key_names.data, key_values.data,
 						   RelationGetRelationName(pk_rel))));
@@ -3556,45 +3514,13 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
 				(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
 				 errmsg("update or delete on table \"%s\" violates foreign key constraint \"%s\" on table \"%s\"",
 						RelationGetRelationName(pk_rel),
-						constrname, RelationGetRelationName(fk_rel)),
+						NameStr(riinfo->conname),
+						RelationGetRelationName(fk_rel)),
 			errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
 					  key_names.data, key_values.data,
 					  RelationGetRelationName(fk_rel))));
 }
 
-/* ----------
- * ri_BuildQueryKeyPkCheck -
- *
- *	Build up a new hashtable key for a prepared SPI plan of a
- *	check for PK rows in noaction triggers.
- *
- *		key: output argument, *key is filled in based on the other arguments
- *		riinfo: info from pg_constraint entry
- *		constr_queryno: an internal number of the query inside the proc
- *
- *	At least for MATCH FULL this builds a unique key per plan.
- * ----------
- */
-static void
-ri_BuildQueryKeyPkCheck(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
-						int32 constr_queryno)
-{
-	int			i;
-
-	MemSet(key, 0, sizeof(RI_QueryKey));
-	key->constr_type = FKCONSTR_MATCH_FULL;
-	key->constr_id = riinfo->constraint_id;
-	key->constr_queryno = constr_queryno;
-	key->fk_relid = InvalidOid;
-	key->pk_relid = riinfo->pk_relid;
-	key->nkeypairs = riinfo->nkeys;
-	for (i = 0; i < riinfo->nkeys; i++)
-	{
-		key->keypair[i][RI_KEYPAIR_FK_IDX] = 0;
-		key->keypair[i][RI_KEYPAIR_PK_IDX] = riinfo->pk_attnums[i];
-	}
-}
-
 
 /* ----------
  * ri_NullCheck -
@@ -3605,18 +3531,22 @@ ri_BuildQueryKeyPkCheck(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
  * ----------
  */
 static int
-ri_NullCheck(Relation rel, HeapTuple tup, RI_QueryKey *key, int pairidx)
+ri_NullCheck(HeapTuple tup,
+			 const RI_ConstraintInfo *riinfo, bool rel_is_pk)
 {
+	const int16 *attnums;
 	int			i;
-	bool		isnull;
 	bool		allnull = true;
 	bool		nonenull = true;
 
-	for (i = 0; i < key->nkeypairs; i++)
+	if (rel_is_pk)
+		attnums = riinfo->pk_attnums;
+	else
+		attnums = riinfo->fk_attnums;
+
+	for (i = 0; i < riinfo->nkeys; i++)
 	{
-		isnull = false;
-		SPI_getbinval(tup, rel->rd_att, key->keypair[i][pairidx], &isnull);
-		if (isnull)
+		if (heap_attisnull(tup, attnums[i]))
 			nonenull = false;
 		else
 			allnull = false;
@@ -3779,14 +3709,14 @@ ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
 		/*
 		 * Get one attribute's oldvalue. If it is NULL - they're not equal.
 		 */
-		oldvalue = SPI_getbinval(oldtup, tupdesc, attnums[i], &isnull);
+		oldvalue = heap_getattr(oldtup, attnums[i], tupdesc, &isnull);
 		if (isnull)
 			return false;
 
 		/*
 		 * Get one attribute's newvalue. If it is NULL - they're not equal.
 		 */
-		newvalue = SPI_getbinval(newtup, tupdesc, attnums[i], &isnull);
+		newvalue = heap_getattr(newtup, attnums[i], tupdesc, &isnull);
 		if (isnull)
 			return false;