From 56f87688c437ac18ba245599633c391bfc0241a8 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 22 Jul 2003 22:14:57 +0000
Subject: [PATCH] Error message editing for foreign-key triggers.

---
 src/backend/utils/adt/ri_triggers.c       | 496 +++++++++-------------
 src/include/utils/elog.h                  |   4 +-
 src/test/regress/expected/alter_table.out |   3 +-
 src/test/regress/expected/cluster.out     |   3 +-
 src/test/regress/expected/foreign_key.out |  93 ++--
 5 files changed, 280 insertions(+), 319 deletions(-)

diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index b940315bbf1..ed95d8ed6c4 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -17,7 +17,7 @@
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.51 2003/06/11 15:02:25 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.52 2003/07/22 22:14:57 tgl Exp $
  *
  * ----------
  */
@@ -204,16 +204,8 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 	 */
 	ri_CheckTrigger(fcinfo, "RI_FKey_check", RI_TRIGTYPE_INUP);
 
-	/*
-	 * Check for the correct # of call arguments
-	 */
 	tgnargs = trigdata->tg_trigger->tgnargs;
 	tgargs = trigdata->tg_trigger->tgargs;
-	if (tgnargs < 4 || (tgnargs % 2) != 0)
-		elog(ERROR, "wrong # of arguments in call to RI_FKey_check()");
-	if (tgnargs > RI_MAX_ARGUMENTS)
-		elog(ERROR, "too many keys (%d max) in call to RI_FKey_check()",
-			 RI_MAX_NUMKEYS);
 
 	/*
 	 * Get the relation descriptors of the FK and PK tables and the new
@@ -221,16 +213,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 	 *
 	 * pk_rel is opened in RowShareLock mode since that's what our eventual
 	 * SELECT FOR UPDATE will get on it.
-	 *
-	 * Error check here is needed because of ancient pg_dump bug; see notes
-	 * in CreateTrigger().
 	 */
-	if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
-		elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
-			 "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
-			 trigdata->tg_trigger->tgname,
-			 RelationGetRelationName(trigdata->tg_relation));
-
 	pk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
 	fk_rel = trigdata->tg_relation;
 	if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
@@ -277,7 +260,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 							 tgnargs, tgargs);
 
 		if (SPI_connect() != SPI_OK_CONNECT)
-			elog(ERROR, "SPI_connect() failed in RI_FKey_check()");
+			elog(ERROR, "SPI_connect failed");
 
 		if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 		{
@@ -308,7 +291,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 						tgargs[RI_CONSTRAINT_NAME_ARGNO]);
 
 		if (SPI_finish() != SPI_OK_FINISH)
-			elog(ERROR, "SPI_finish() failed in RI_FKey_check()");
+			elog(ERROR, "SPI_finish failed");
 
 		heap_close(pk_rel, RowShareLock);
 
@@ -319,10 +302,9 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 	match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);
 
 	if (match_type == RI_MATCH_TYPE_PARTIAL)
-	{
-		elog(ERROR, "MATCH PARTIAL not yet supported");
-		return PointerGetDatum(NULL);
-	}
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("MATCH PARTIAL not yet implemented")));
 
 	ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
 						 RI_PLAN_CHECK_LOOKUPPK, fk_rel, pk_rel,
@@ -356,10 +338,12 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 					 * Not allowed - MATCH FULL says either all or none of
 					 * the attributes can be NULLs
 					 */
-					elog(ERROR, "%s referential integrity violation - "
-						 "MATCH FULL doesn't allow mixing of NULL "
-						 "and NON-NULL key values",
-						 tgargs[RI_CONSTRAINT_NAME_ARGNO]);
+					ereport(ERROR,
+							(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
+							 errmsg("insert or update on \"%s\" violates foreign key constraint \"%s\"",
+									RelationGetRelationName(trigdata->tg_relation),
+									tgargs[RI_CONSTRAINT_NAME_ARGNO]),
+							 errdetail("MATCH FULL does not allow mixing of NULL and non-NULL key values.")));
 					heap_close(pk_rel, RowShareLock);
 					return PointerGetDatum(NULL);
 
@@ -380,7 +364,9 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 					 * query below to only include non-null columns, or by
 					 * writing a special version here)
 					 */
-					elog(ERROR, "MATCH PARTIAL not yet implemented");
+					ereport(ERROR,
+							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+							 errmsg("MATCH PARTIAL not yet implemented")));
 					heap_close(pk_rel, RowShareLock);
 					return PointerGetDatum(NULL);
 			}
@@ -409,7 +395,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 	}
 
 	if (SPI_connect() != SPI_OK_CONNECT)
-		elog(ERROR, "SPI_connect() failed in RI_FKey_check()");
+		elog(ERROR, "SPI_connect failed");
 
 	/*
 	 * Fetch or prepare a saved plan for the real check
@@ -462,7 +448,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 					tgargs[RI_CONSTRAINT_NAME_ARGNO]);
 
 	if (SPI_finish() != SPI_OK_FINISH)
-		elog(ERROR, "SPI_finish() failed in RI_FKey_check()");
+		elog(ERROR, "SPI_finish failed");
 
 	heap_close(pk_rel, RowShareLock);
 
@@ -554,7 +540,9 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 					 * query below to only include non-null columns, or by
 					 * writing a special version here)
 					 */
-					elog(ERROR, "MATCH PARTIAL not yet implemented");
+					ereport(ERROR,
+							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+							 errmsg("MATCH PARTIAL not yet implemented")));
 					break;
 			}
 
@@ -568,7 +556,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 	}
 
 	if (SPI_connect() != SPI_OK_CONNECT)
-		elog(ERROR, "SPI_connect() failed in RI_FKey_check()");
+		elog(ERROR, "SPI_connect failed");
 
 	/*
 	 * Fetch or prepare a saved plan for the real check
@@ -620,7 +608,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 							 SPI_OK_SELECT, NULL);
 
 	if (SPI_finish() != SPI_OK_FINISH)
-		elog(ERROR, "SPI_finish() failed in ri_Check_Pk_Match()");
+		elog(ERROR, "SPI_finish failed");
 
 	return result;
 }
@@ -656,16 +644,8 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 	 */
 	ri_CheckTrigger(fcinfo, "RI_FKey_noaction_del", RI_TRIGTYPE_DELETE);
 
-	/*
-	 * Check for the correct # of call arguments
-	 */
 	tgnargs = trigdata->tg_trigger->tgnargs;
 	tgargs = trigdata->tg_trigger->tgargs;
-	if (tgnargs < 4 || (tgnargs % 2) != 0)
-		elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_del()");
-	if (tgnargs > RI_MAX_ARGUMENTS)
-		elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_del()",
-			 RI_MAX_NUMKEYS);
 
 	/*
 	 * Nothing to do if no column names to compare given
@@ -680,12 +660,6 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 	 * fk_rel is opened in RowShareLock mode since that's what our eventual
 	 * SELECT FOR UPDATE will get on it.
 	 */
-	if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
-		elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
-			 "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
-			 trigdata->tg_trigger->tgname,
-			 RelationGetRelationName(trigdata->tg_relation));
-
 	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
 	pk_rel = trigdata->tg_relation;
 	old_row = trigdata->tg_trigtuple;
@@ -740,7 +714,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 			}
 
 			if (SPI_connect() != SPI_OK_CONNECT)
-				elog(ERROR, "SPI_connect() failed in RI_FKey_noaction_del()");
+				elog(ERROR, "SPI_connect failed");
 
 			/*
 			 * Fetch or prepare a saved plan for the restrict delete
@@ -794,7 +768,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 							tgargs[RI_CONSTRAINT_NAME_ARGNO]);
 
 			if (SPI_finish() != SPI_OK_FINISH)
-				elog(ERROR, "SPI_finish() failed in RI_FKey_noaction_del()");
+				elog(ERROR, "SPI_finish failed");
 
 			heap_close(fk_rel, RowShareLock);
 
@@ -804,14 +778,16 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 			 * Handle MATCH PARTIAL restrict delete.
 			 */
 		case RI_MATCH_TYPE_PARTIAL:
-			elog(ERROR, "MATCH PARTIAL not yet supported");
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("MATCH PARTIAL not yet implemented")));
 			return PointerGetDatum(NULL);
 	}
 
 	/*
 	 * Never reached
 	 */
-	elog(ERROR, "internal error #2 in ri_triggers.c");
+	elog(ERROR, "invalid match_type");
 	return PointerGetDatum(NULL);
 }
 
@@ -847,16 +823,8 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 	 */
 	ri_CheckTrigger(fcinfo, "RI_FKey_noaction_upd", RI_TRIGTYPE_UPDATE);
 
-	/*
-	 * Check for the correct # of call arguments
-	 */
 	tgnargs = trigdata->tg_trigger->tgnargs;
 	tgargs = trigdata->tg_trigger->tgargs;
-	if (tgnargs < 4 || (tgnargs % 2) != 0)
-		elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_upd()");
-	if (tgnargs > RI_MAX_ARGUMENTS)
-		elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_upd()",
-			 RI_MAX_NUMKEYS);
 
 	/*
 	 * Nothing to do if no column names to compare given
@@ -871,12 +839,6 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 	 * fk_rel is opened in RowShareLock mode since that's what our eventual
 	 * SELECT FOR UPDATE will get on it.
 	 */
-	if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
-		elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
-			 "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
-			 trigdata->tg_trigger->tgname,
-			 RelationGetRelationName(trigdata->tg_relation));
-
 	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
 	pk_rel = trigdata->tg_relation;
 	new_row = trigdata->tg_newtuple;
@@ -943,7 +905,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 			}
 
 			if (SPI_connect() != SPI_OK_CONNECT)
-				elog(ERROR, "SPI_connect() failed in RI_FKey_noaction_upd()");
+				elog(ERROR, "SPI_connect failed");
 
 			/*
 			 * Fetch or prepare a saved plan for the noaction update
@@ -997,7 +959,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 							tgargs[RI_CONSTRAINT_NAME_ARGNO]);
 
 			if (SPI_finish() != SPI_OK_FINISH)
-				elog(ERROR, "SPI_finish() failed in RI_FKey_noaction_upd()");
+				elog(ERROR, "SPI_finish failed");
 
 			heap_close(fk_rel, RowShareLock);
 
@@ -1007,14 +969,16 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 			 * Handle MATCH PARTIAL noaction update.
 			 */
 		case RI_MATCH_TYPE_PARTIAL:
-			elog(ERROR, "MATCH PARTIAL not yet supported");
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("MATCH PARTIAL not yet implemented")));
 			return PointerGetDatum(NULL);
 	}
 
 	/*
 	 * Never reached
 	 */
-	elog(ERROR, "internal error #3 in ri_triggers.c");
+	elog(ERROR, "invalid match_type");
 	return PointerGetDatum(NULL);
 }
 
@@ -1046,16 +1010,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 	 */
 	ri_CheckTrigger(fcinfo, "RI_FKey_cascade_del", RI_TRIGTYPE_DELETE);
 
-	/*
-	 * Check for the correct # of call arguments
-	 */
 	tgnargs = trigdata->tg_trigger->tgnargs;
 	tgargs = trigdata->tg_trigger->tgargs;
-	if (tgnargs < 4 || (tgnargs % 2) != 0)
-		elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_del()");
-	if (tgnargs > RI_MAX_ARGUMENTS)
-		elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_del()",
-			 RI_MAX_NUMKEYS);
 
 	/*
 	 * Nothing to do if no column names to compare given
@@ -1070,12 +1026,6 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 	 * fk_rel is opened in RowExclusiveLock mode since that's what our
 	 * eventual DELETE will get on it.
 	 */
-	if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
-		elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
-			 "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
-			 trigdata->tg_trigger->tgname,
-			 RelationGetRelationName(trigdata->tg_relation));
-
 	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
 	old_row = trigdata->tg_trigtuple;
@@ -1117,7 +1067,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 			}
 
 			if (SPI_connect() != SPI_OK_CONNECT)
-				elog(ERROR, "SPI_connect() failed in RI_FKey_cascade_del()");
+				elog(ERROR, "SPI_connect failed");
 
 			/*
 			 * Fetch or prepare a saved plan for the cascaded delete
@@ -1171,7 +1121,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 							tgargs[RI_CONSTRAINT_NAME_ARGNO]);
 
 			if (SPI_finish() != SPI_OK_FINISH)
-				elog(ERROR, "SPI_finish() failed in RI_FKey_cascade_del()");
+				elog(ERROR, "SPI_finish failed");
 
 			heap_close(fk_rel, RowExclusiveLock);
 
@@ -1181,14 +1131,16 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 			 * Handle MATCH PARTIAL cascaded delete.
 			 */
 		case RI_MATCH_TYPE_PARTIAL:
-			elog(ERROR, "MATCH PARTIAL not yet supported");
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("MATCH PARTIAL not yet implemented")));
 			return PointerGetDatum(NULL);
 	}
 
 	/*
 	 * Never reached
 	 */
-	elog(ERROR, "internal error #4 in ri_triggers.c");
+	elog(ERROR, "invalid match_type");
 	return PointerGetDatum(NULL);
 }
 
@@ -1222,16 +1174,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 	 */
 	ri_CheckTrigger(fcinfo, "RI_FKey_cascade_upd", RI_TRIGTYPE_UPDATE);
 
-	/*
-	 * Check for the correct # of call arguments
-	 */
 	tgnargs = trigdata->tg_trigger->tgnargs;
 	tgargs = trigdata->tg_trigger->tgargs;
-	if (tgnargs < 4 || (tgnargs % 2) != 0)
-		elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_upd()");
-	if (tgnargs > RI_MAX_ARGUMENTS)
-		elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_upd()",
-			 RI_MAX_NUMKEYS);
 
 	/*
 	 * Nothing to do if no column names to compare given
@@ -1246,12 +1190,6 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 	 * fk_rel is opened in RowExclusiveLock mode since that's what our
 	 * eventual UPDATE will get on it.
 	 */
-	if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
-		elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
-			 "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
-			 trigdata->tg_trigger->tgname,
-			 RelationGetRelationName(trigdata->tg_relation));
-
 	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
 	new_row = trigdata->tg_newtuple;
@@ -1304,7 +1242,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 			}
 
 			if (SPI_connect() != SPI_OK_CONNECT)
-				elog(ERROR, "SPI_connect() failed in RI_FKey_cascade_upd()");
+				elog(ERROR, "SPI_connect failed");
 
 			/*
 			 * Fetch or prepare a saved plan for the cascaded update of
@@ -1367,7 +1305,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 							tgargs[RI_CONSTRAINT_NAME_ARGNO]);
 
 			if (SPI_finish() != SPI_OK_FINISH)
-				elog(ERROR, "SPI_finish() failed in RI_FKey_cascade_upd()");
+				elog(ERROR, "SPI_finish failed");
 
 			heap_close(fk_rel, RowExclusiveLock);
 
@@ -1377,14 +1315,16 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 			 * Handle MATCH PARTIAL cascade update.
 			 */
 		case RI_MATCH_TYPE_PARTIAL:
-			elog(ERROR, "MATCH PARTIAL not yet supported");
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("MATCH PARTIAL not yet implemented")));
 			return PointerGetDatum(NULL);
 	}
 
 	/*
 	 * Never reached
 	 */
-	elog(ERROR, "internal error #5 in ri_triggers.c");
+	elog(ERROR, "invalid match_type");
 	return PointerGetDatum(NULL);
 }
 
@@ -1423,16 +1363,8 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 	 */
 	ri_CheckTrigger(fcinfo, "RI_FKey_restrict_del", RI_TRIGTYPE_DELETE);
 
-	/*
-	 * Check for the correct # of call arguments
-	 */
 	tgnargs = trigdata->tg_trigger->tgnargs;
 	tgargs = trigdata->tg_trigger->tgargs;
-	if (tgnargs < 4 || (tgnargs % 2) != 0)
-		elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_del()");
-	if (tgnargs > RI_MAX_ARGUMENTS)
-		elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_del()",
-			 RI_MAX_NUMKEYS);
 
 	/*
 	 * Nothing to do if no column names to compare given
@@ -1447,12 +1379,6 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 	 * fk_rel is opened in RowShareLock mode since that's what our eventual
 	 * SELECT FOR UPDATE will get on it.
 	 */
-	if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
-		elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
-			 "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
-			 trigdata->tg_trigger->tgname,
-			 RelationGetRelationName(trigdata->tg_relation));
-
 	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
 	pk_rel = trigdata->tg_relation;
 	old_row = trigdata->tg_trigtuple;
@@ -1494,7 +1420,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 			}
 
 			if (SPI_connect() != SPI_OK_CONNECT)
-				elog(ERROR, "SPI_connect() failed in RI_FKey_restrict_del()");
+				elog(ERROR, "SPI_connect failed");
 
 			/*
 			 * Fetch or prepare a saved plan for the restrict delete
@@ -1548,7 +1474,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 							tgargs[RI_CONSTRAINT_NAME_ARGNO]);
 
 			if (SPI_finish() != SPI_OK_FINISH)
-				elog(ERROR, "SPI_finish() failed in RI_FKey_restrict_del()");
+				elog(ERROR, "SPI_finish failed");
 
 			heap_close(fk_rel, RowShareLock);
 
@@ -1558,14 +1484,16 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 			 * Handle MATCH PARTIAL restrict delete.
 			 */
 		case RI_MATCH_TYPE_PARTIAL:
-			elog(ERROR, "MATCH PARTIAL not yet supported");
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("MATCH PARTIAL not yet implemented")));
 			return PointerGetDatum(NULL);
 	}
 
 	/*
 	 * Never reached
 	 */
-	elog(ERROR, "internal error #6 in ri_triggers.c");
+	elog(ERROR, "invalid match_type");
 	return PointerGetDatum(NULL);
 }
 
@@ -1605,16 +1533,8 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 	 */
 	ri_CheckTrigger(fcinfo, "RI_FKey_restrict_upd", RI_TRIGTYPE_UPDATE);
 
-	/*
-	 * Check for the correct # of call arguments
-	 */
 	tgnargs = trigdata->tg_trigger->tgnargs;
 	tgargs = trigdata->tg_trigger->tgargs;
-	if (tgnargs < 4 || (tgnargs % 2) != 0)
-		elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_upd()");
-	if (tgnargs > RI_MAX_ARGUMENTS)
-		elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_upd()",
-			 RI_MAX_NUMKEYS);
 
 	/*
 	 * Nothing to do if no column names to compare given
@@ -1629,12 +1549,6 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 	 * fk_rel is opened in RowShareLock mode since that's what our eventual
 	 * SELECT FOR UPDATE will get on it.
 	 */
-	if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
-		elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
-			 "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
-			 trigdata->tg_trigger->tgname,
-			 RelationGetRelationName(trigdata->tg_relation));
-
 	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
 	pk_rel = trigdata->tg_relation;
 	new_row = trigdata->tg_newtuple;
@@ -1687,7 +1601,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 			}
 
 			if (SPI_connect() != SPI_OK_CONNECT)
-				elog(ERROR, "SPI_connect() failed in RI_FKey_restrict_upd()");
+				elog(ERROR, "SPI_connect failed");
 
 			/*
 			 * Fetch or prepare a saved plan for the restrict update
@@ -1741,7 +1655,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 							tgargs[RI_CONSTRAINT_NAME_ARGNO]);
 
 			if (SPI_finish() != SPI_OK_FINISH)
-				elog(ERROR, "SPI_finish() failed in RI_FKey_restrict_upd()");
+				elog(ERROR, "SPI_finish failed");
 
 			heap_close(fk_rel, RowShareLock);
 
@@ -1751,14 +1665,16 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 			 * Handle MATCH PARTIAL restrict update.
 			 */
 		case RI_MATCH_TYPE_PARTIAL:
-			elog(ERROR, "MATCH PARTIAL not yet supported");
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("MATCH PARTIAL not yet implemented")));
 			return PointerGetDatum(NULL);
 	}
 
 	/*
 	 * Never reached
 	 */
-	elog(ERROR, "internal error #7 in ri_triggers.c");
+	elog(ERROR, "invalid match_type");
 	return PointerGetDatum(NULL);
 }
 
@@ -1790,16 +1706,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 	 */
 	ri_CheckTrigger(fcinfo, "RI_FKey_setnull_del", RI_TRIGTYPE_DELETE);
 
-	/*
-	 * Check for the correct # of call arguments
-	 */
 	tgnargs = trigdata->tg_trigger->tgnargs;
 	tgargs = trigdata->tg_trigger->tgargs;
-	if (tgnargs < 4 || (tgnargs % 2) != 0)
-		elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_del()");
-	if (tgnargs > RI_MAX_ARGUMENTS)
-		elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_del()",
-			 RI_MAX_NUMKEYS);
 
 	/*
 	 * Nothing to do if no column names to compare given
@@ -1814,12 +1722,6 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 	 * fk_rel is opened in RowExclusiveLock mode since that's what our
 	 * eventual UPDATE will get on it.
 	 */
-	if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
-		elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
-			 "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
-			 trigdata->tg_trigger->tgname,
-			 RelationGetRelationName(trigdata->tg_relation));
-
 	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
 	old_row = trigdata->tg_trigtuple;
@@ -1861,7 +1763,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 			}
 
 			if (SPI_connect() != SPI_OK_CONNECT)
-				elog(ERROR, "SPI_connect() failed in RI_FKey_setnull_del()");
+				elog(ERROR, "SPI_connect failed");
 
 			/*
 			 * Fetch or prepare a saved plan for the set null delete
@@ -1923,7 +1825,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 							tgargs[RI_CONSTRAINT_NAME_ARGNO]);
 
 			if (SPI_finish() != SPI_OK_FINISH)
-				elog(ERROR, "SPI_finish() failed in RI_FKey_setnull_del()");
+				elog(ERROR, "SPI_finish failed");
 
 			heap_close(fk_rel, RowExclusiveLock);
 
@@ -1933,14 +1835,16 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 			 * Handle MATCH PARTIAL set null delete.
 			 */
 		case RI_MATCH_TYPE_PARTIAL:
-			elog(ERROR, "MATCH PARTIAL not yet supported");
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("MATCH PARTIAL not yet implemented")));
 			return PointerGetDatum(NULL);
 	}
 
 	/*
 	 * Never reached
 	 */
-	elog(ERROR, "internal error #8 in ri_triggers.c");
+	elog(ERROR, "invalid match_type");
 	return PointerGetDatum(NULL);
 }
 
@@ -1975,16 +1879,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 	 */
 	ri_CheckTrigger(fcinfo, "RI_FKey_setnull_upd", RI_TRIGTYPE_UPDATE);
 
-	/*
-	 * Check for the correct # of call arguments
-	 */
 	tgnargs = trigdata->tg_trigger->tgnargs;
 	tgargs = trigdata->tg_trigger->tgargs;
-	if (tgnargs < 4 || (tgnargs % 2) != 0)
-		elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_upd()");
-	if (tgnargs > RI_MAX_ARGUMENTS)
-		elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_upd()",
-			 RI_MAX_NUMKEYS);
 
 	/*
 	 * Nothing to do if no column names to compare given
@@ -1999,12 +1895,6 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 	 * fk_rel is opened in RowExclusiveLock mode since that's what our
 	 * eventual UPDATE will get on it.
 	 */
-	if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
-		elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
-			 "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
-			 trigdata->tg_trigger->tgname,
-			 RelationGetRelationName(trigdata->tg_relation));
-
 	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
 	new_row = trigdata->tg_newtuple;
@@ -2058,7 +1948,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 			}
 
 			if (SPI_connect() != SPI_OK_CONNECT)
-				elog(ERROR, "SPI_connect() failed in RI_FKey_setnull_upd()");
+				elog(ERROR, "SPI_connect failed");
 
 			/*
 			 * "MATCH <unspecified>" only changes columns corresponding to
@@ -2153,7 +2043,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 							tgargs[RI_CONSTRAINT_NAME_ARGNO]);
 
 			if (SPI_finish() != SPI_OK_FINISH)
-				elog(ERROR, "SPI_finish() failed in RI_FKey_setnull_upd()");
+				elog(ERROR, "SPI_finish failed");
 
 			heap_close(fk_rel, RowExclusiveLock);
 
@@ -2163,14 +2053,16 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 			 * Handle MATCH PARTIAL set null update.
 			 */
 		case RI_MATCH_TYPE_PARTIAL:
-			elog(ERROR, "MATCH PARTIAL not yet supported");
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("MATCH PARTIAL not yet implemented")));
 			return PointerGetDatum(NULL);
 	}
 
 	/*
 	 * Never reached
 	 */
-	elog(ERROR, "internal error #9 in ri_triggers.c");
+	elog(ERROR, "invalid match_type");
 	return PointerGetDatum(NULL);
 }
 
@@ -2201,16 +2093,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 	 */
 	ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_del", RI_TRIGTYPE_DELETE);
 
-	/*
-	 * Check for the correct # of call arguments
-	 */
 	tgnargs = trigdata->tg_trigger->tgnargs;
 	tgargs = trigdata->tg_trigger->tgargs;
-	if (tgnargs < 4 || (tgnargs % 2) != 0)
-		elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_del()");
-	if (tgnargs > RI_MAX_ARGUMENTS)
-		elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_del()",
-			 RI_MAX_NUMKEYS);
 
 	/*
 	 * Nothing to do if no column names to compare given
@@ -2225,12 +2109,6 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 	 * fk_rel is opened in RowExclusiveLock mode since that's what our
 	 * eventual UPDATE will get on it.
 	 */
-	if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
-		elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
-			 "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
-			 trigdata->tg_trigger->tgname,
-			 RelationGetRelationName(trigdata->tg_relation));
-
 	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
 	old_row = trigdata->tg_trigtuple;
@@ -2272,7 +2150,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 			}
 
 			if (SPI_connect() != SPI_OK_CONNECT)
-				elog(ERROR, "SPI_connect() failed in RI_FKey_setdefault_del()");
+				elog(ERROR, "SPI_connect failed");
 
 			/*
 			 * Prepare a plan for the set default delete operation.
@@ -2365,7 +2243,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 							tgargs[RI_CONSTRAINT_NAME_ARGNO]);
 
 			if (SPI_finish() != SPI_OK_FINISH)
-				elog(ERROR, "SPI_finish() failed in RI_FKey_setdefault_del()");
+				elog(ERROR, "SPI_finish failed");
 
 			heap_close(fk_rel, RowExclusiveLock);
 
@@ -2385,14 +2263,16 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 			 * Handle MATCH PARTIAL set null delete.
 			 */
 		case RI_MATCH_TYPE_PARTIAL:
-			elog(ERROR, "MATCH PARTIAL not yet supported");
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("MATCH PARTIAL not yet implemented")));
 			return PointerGetDatum(NULL);
 	}
 
 	/*
 	 * Never reached
 	 */
-	elog(ERROR, "internal error #10 in ri_triggers.c");
+	elog(ERROR, "invalid match_type");
 	return PointerGetDatum(NULL);
 }
 
@@ -2425,16 +2305,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 	 */
 	ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_upd", RI_TRIGTYPE_UPDATE);
 
-	/*
-	 * Check for the correct # of call arguments
-	 */
 	tgnargs = trigdata->tg_trigger->tgnargs;
 	tgargs = trigdata->tg_trigger->tgargs;
-	if (tgnargs < 4 || (tgnargs % 2) != 0)
-		elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_upd()");
-	if (tgnargs > RI_MAX_ARGUMENTS)
-		elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_upd()",
-			 RI_MAX_NUMKEYS);
 
 	/*
 	 * Nothing to do if no column names to compare given
@@ -2449,12 +2321,6 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 	 * fk_rel is opened in RowExclusiveLock mode since that's what our
 	 * eventual UPDATE will get on it.
 	 */
-	if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
-		elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
-			 "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
-			 trigdata->tg_trigger->tgname,
-			 RelationGetRelationName(trigdata->tg_relation));
-
 	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
 	new_row = trigdata->tg_newtuple;
@@ -2509,7 +2375,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 			}
 
 			if (SPI_connect() != SPI_OK_CONNECT)
-				elog(ERROR, "SPI_connect() failed in RI_FKey_setdefault_upd()");
+				elog(ERROR, "SPI_connect failed");
 
 			/*
 			 * Prepare a plan for the set default delete operation.
@@ -2612,7 +2478,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 							tgargs[RI_CONSTRAINT_NAME_ARGNO]);
 
 			if (SPI_finish() != SPI_OK_FINISH)
-				elog(ERROR, "SPI_finish() failed in RI_FKey_setdefault_upd()");
+				elog(ERROR, "SPI_finish failed");
 
 			heap_close(fk_rel, RowExclusiveLock);
 
@@ -2632,14 +2498,16 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 			 * Handle MATCH PARTIAL set null delete.
 			 */
 		case RI_MATCH_TYPE_PARTIAL:
-			elog(ERROR, "MATCH PARTIAL not yet supported");
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("MATCH PARTIAL not yet implemented")));
 			return PointerGetDatum(NULL);
 	}
 
 	/*
 	 * Never reached
 	 */
-	elog(ERROR, "internal error #11 in ri_triggers.c");
+	elog(ERROR, "invalid match_type");
 	return PointerGetDatum(NULL);
 }
 
@@ -2669,11 +2537,13 @@ RI_FKey_keyequal_upd(TriggerData *trigdata)
 	 */
 	tgnargs = trigdata->tg_trigger->tgnargs;
 	tgargs = trigdata->tg_trigger->tgargs;
-	if (tgnargs < 4 || (tgnargs % 2) != 0)
-		elog(ERROR, "wrong # of arguments in call to RI_FKey_keyequal_upd()");
-	if (tgnargs > RI_MAX_ARGUMENTS)
-		elog(ERROR, "too many keys (%d max) in call to RI_FKey_keyequal_upd()",
-			 RI_MAX_NUMKEYS);
+	if (tgnargs < 4 ||
+		tgnargs > RI_MAX_ARGUMENTS ||
+		(tgnargs % 2) != 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
+				 errmsg("%s() called with wrong number of trigger arguments",
+						"RI_FKey_keyequal_upd")));
 
 	/*
 	 * Nothing to do if no column names to compare given
@@ -2688,10 +2558,12 @@ RI_FKey_keyequal_upd(TriggerData *trigdata)
 	 * Use minimal locking for fk_rel here.
 	 */
 	if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
-		elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
-			 "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
-			 trigdata->tg_trigger->tgname,
-			 RelationGetRelationName(trigdata->tg_relation));
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("no target table given for trigger \"%s\" on \"%s\"",
+						trigdata->tg_trigger->tgname,
+						RelationGetRelationName(trigdata->tg_relation)),
+				 errhint("Remove this RI trigger and its mates, then do ALTER TABLE ADD CONSTRAINT.")));
 
 	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, AccessShareLock);
 	pk_rel = trigdata->tg_relation;
@@ -2722,14 +2594,16 @@ RI_FKey_keyequal_upd(TriggerData *trigdata)
 			 * Handle MATCH PARTIAL set null delete.
 			 */
 		case RI_MATCH_TYPE_PARTIAL:
-			elog(ERROR, "MATCH PARTIAL not yet supported");
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("MATCH PARTIAL not yet implemented")));
 			break;
 	}
 
 	/*
 	 * Never reached
 	 */
-	elog(ERROR, "internal error #12 in ri_triggers.c");
+	elog(ERROR, "invalid match_type");
 	return false;
 }
 
@@ -2794,7 +2668,7 @@ ri_DetermineMatchType(char *str)
 	if (strcmp(str, "PARTIAL") == 0)
 		return RI_MATCH_TYPE_PARTIAL;
 
-	elog(ERROR, "unrecognized referential integrity MATCH type '%s'", str);
+	elog(ERROR, "unrecognized referential integrity match type \"%s\"", str);
 	return 0;
 }
 
@@ -2844,18 +2718,22 @@ ri_BuildQueryKeyFull(RI_QueryKey *key, Oid constr_id, int32 constr_queryno,
 	{
 		fno = SPI_fnumber(fk_rel->rd_att, argv[j]);
 		if (fno == SPI_ERROR_NOATTRIBUTE)
-			elog(ERROR, "constraint %s: table %s does not have an attribute %s",
-				 argv[RI_CONSTRAINT_NAME_ARGNO],
-				 RelationGetRelationName(fk_rel),
-				 argv[j]);
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_COLUMN),
+					 errmsg("table \"%s\" does not have attribute \"%s\" referenced by constraint \"%s\"",
+							RelationGetRelationName(fk_rel),
+							argv[j],
+							argv[RI_CONSTRAINT_NAME_ARGNO])));
 		key->keypair[i][RI_KEYPAIR_FK_IDX] = fno;
 
 		fno = SPI_fnumber(pk_rel->rd_att, argv[j + 1]);
 		if (fno == SPI_ERROR_NOATTRIBUTE)
-			elog(ERROR, "constraint %s: table %s does not have an attribute %s",
-				 argv[RI_CONSTRAINT_NAME_ARGNO],
-				 RelationGetRelationName(pk_rel),
-				 argv[j + 1]);
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_COLUMN),
+					 errmsg("table \"%s\" does not have attribute \"%s\" referenced by constraint \"%s\"",
+							RelationGetRelationName(pk_rel),
+							argv[j + 1],
+							argv[RI_CONSTRAINT_NAME_ARGNO])));
 		key->keypair[i][RI_KEYPAIR_PK_IDX] = fno;
 	}
 }
@@ -2867,34 +2745,75 @@ static void
 ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname, int tgkind)
 {
 	TriggerData *trigdata = (TriggerData *) fcinfo->context;
+	int			tgnargs;
 
 	if (!CALLED_AS_TRIGGER(fcinfo))
-		elog(ERROR, "%s() not fired by trigger manager", funcname);
+		ereport(ERROR,
+				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
+				 errmsg("%s() was not fired by trigger manager", funcname)));
+
+	/*
+	 * Check proper event
+	 */
 	if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
 		!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
-		elog(ERROR, "%s() must be fired AFTER ROW", funcname);
+		ereport(ERROR,
+				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
+				 errmsg("%s() must be fired AFTER ROW", funcname)));
 
 	switch (tgkind)
 	{
 		case RI_TRIGTYPE_INSERT:
 			if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
-				elog(ERROR, "%s() must be fired for INSERT", funcname);
+				ereport(ERROR,
+						(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
+						 errmsg("%s() must be fired for INSERT", funcname)));
 			break;
 		case RI_TRIGTYPE_UPDATE:
 			if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
-				elog(ERROR, "%s() must be fired for UPDATE", funcname);
+				ereport(ERROR,
+						(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
+						 errmsg("%s() must be fired for UPDATE", funcname)));
 			break;
 		case RI_TRIGTYPE_INUP:
 			if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event) &&
 				!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
-				elog(ERROR, "%s() must be fired for INSERT or UPDATE",
-					 funcname);
+				ereport(ERROR,
+						(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
+						 errmsg("%s() must be fired for INSERT or UPDATE",
+					 funcname)));
 			break;
 		case RI_TRIGTYPE_DELETE:
 			if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
-				elog(ERROR, "%s() must be fired for DELETE", funcname);
+				ereport(ERROR,
+						(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
+						 errmsg("%s() must be fired for DELETE", funcname)));
 			break;
 	}
+
+	/*
+	 * Check for the correct # of call arguments
+	 */
+	tgnargs = trigdata->tg_trigger->tgnargs;
+	if (tgnargs < 4 ||
+		tgnargs > RI_MAX_ARGUMENTS ||
+		(tgnargs % 2) != 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
+				 errmsg("%s() called with wrong number of trigger arguments",
+						funcname)));
+
+	/*
+	 * Check that tgconstrrelid is known.  We need to check here because of
+	 * ancient pg_dump bug; see notes in CreateTrigger().
+	 */
+	if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("no target table given for trigger \"%s\" on \"%s\"",
+						trigdata->tg_trigger->tgname,
+						RelationGetRelationName(trigdata->tg_relation)),
+				 errhint("Remove this RI trigger and its mates, then do ALTER TABLE ADD CONSTRAINT.")));
 }
 
 
@@ -3025,7 +2944,7 @@ ri_PerformCheck(RI_QueryKey *qkey, void *qplan,
 
 	/* Check result */
 	if (spi_result < 0)
-		elog(ERROR, "SPI_execp() failed in ri_PerformCheck()");
+		elog(ERROR, "SPI_execp failed");
 
 	if (expect_OK >= 0 && spi_result != expect_OK)
 		ri_ReportViolation(qkey, constrname ? constrname : "",
@@ -3069,9 +2988,9 @@ ri_ExtractValues(RI_QueryKey *qkey, int key_idx,
  *
  * If the failed constraint was on insert/update to the FK table,
  * we want the key names and values extracted from there, and the error
- * message to look like 'key blah referenced from FK not found in PK'.
+ * message to look like 'key blah is not present in PK'.
  * Otherwise, the attr names and values come from the PK table and the
- * message looks like 'key blah in PK still referenced from FK'.
+ * message looks like 'key blah is still referenced from FK'.
  */
 static void
 ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
@@ -3084,27 +3003,31 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
 	char	   *name_ptr = key_names;
 	char	   *val_ptr = key_values;
 	bool		onfk;
-	Relation	rel,
-				other_rel;
+	Relation	rel;
 	int			idx,
 				key_idx;
 
+	if (spi_err)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("referential integrity query on \"%s\" from constraint \"%s\" on \"%s\" gave unexpected result",
+						RelationGetRelationName(pk_rel),
+						constrname,
+						RelationGetRelationName(fk_rel)),
+				 errhint("This is most likely due to a rule having rewritten the query.")));
+
 	/*
-	 * rel is set to where the tuple description is coming from, and it also
-	 * is the first relation mentioned in the message, other_rel is
-	 * respectively the other relation.
+	 * rel is set to where the tuple description is coming from.
 	 */
 	onfk = (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK);
 	if (onfk)
 	{
 		rel = fk_rel;
-		other_rel = pk_rel;
 		key_idx = RI_KEYPAIR_FK_IDX;
 	}
 	else
 	{
 		rel = pk_rel;
-		other_rel = fk_rel;
 		key_idx = RI_KEYPAIR_PK_IDX;
 	}
 
@@ -3115,14 +3038,12 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
 	 */
 	if (qkey->nkeypairs == 0)
 	{
-		if (spi_err)
-			elog(ERROR, "%s referential action on %s from %s rewritten by rule",
-				 constrname,
-				 RelationGetRelationName(fk_rel),
-				 RelationGetRelationName(pk_rel));
-		else
-			elog(ERROR, "%s referential integrity violation - no rows found in %s",
-				 constrname, RelationGetRelationName(pk_rel));
+		ereport(ERROR,
+				(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
+				 errmsg("insert or update on \"%s\" violates foreign key constraint \"%s\"",
+						RelationGetRelationName(fk_rel), constrname),
+				 errdetail("No rows were found in \"%s\".",
+						   RelationGetRelationName(pk_rel))));
 	}
 
 	/* Get printable versions of the keys involved */
@@ -3151,24 +3072,25 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
 
 		name_ptr += sprintf(name_ptr, "%s%s", idx > 0 ? "," : "", name);
 		val_ptr += sprintf(val_ptr, "%s%s", idx > 0 ? "," : "", val);
-  }
-
-  if (spi_err)
-	  elog(ERROR, "%s referential action on %s from %s for (%s)=(%s) rewritten by rule",
-		   constrname,
-		   RelationGetRelationName(fk_rel),
-		   RelationGetRelationName(pk_rel),
-		   key_names, key_values);
-  else if (onfk)
-	  elog(ERROR, "%s referential integrity violation - key (%s)=(%s) referenced from %s not found in %s",
-		   constrname, key_names, key_values,
-		   RelationGetRelationName(rel),
-		   RelationGetRelationName(other_rel));
-  else
-	  elog(ERROR, "%s referential integrity violation - key (%s)=(%s) in %s still referenced from %s",
-		   constrname, key_names, key_values,
-		   RelationGetRelationName(rel),
-		   RelationGetRelationName(other_rel));
+	}
+
+	if (onfk)
+	  ereport(ERROR,
+			  (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
+			   errmsg("insert or update on \"%s\" violates foreign key constraint \"%s\"",
+					  RelationGetRelationName(fk_rel), constrname),
+			   errdetail("Key (%s)=(%s) is not present in \"%s\".",
+						 key_names, key_values,
+						 RelationGetRelationName(pk_rel))));
+	else
+	  ereport(ERROR,
+			  (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
+			   errmsg("update or delete on \"%s\" violates foreign key constraint \"%s\" on \"%s\"",
+					  RelationGetRelationName(pk_rel),
+					  constrname, RelationGetRelationName(fk_rel)),
+			   errdetail("Key (%s)=(%s) is still referenced from \"%s\".",
+						 key_names, key_values,
+						 RelationGetRelationName(fk_rel))));
 }
 
 /* ----------
@@ -3215,10 +3137,12 @@ ri_BuildQueryKeyPkCheck(RI_QueryKey *key, Oid constr_id, int32 constr_queryno,
 	{
 		fno = SPI_fnumber(pk_rel->rd_att, argv[j]);
 		if (fno == SPI_ERROR_NOATTRIBUTE)
-			elog(ERROR, "constraint %s: table %s does not have an attribute %s",
-				 argv[RI_CONSTRAINT_NAME_ARGNO],
-				 RelationGetRelationName(pk_rel),
-				 argv[j + 1]);
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_COLUMN),
+					 errmsg("table \"%s\" does not have attribute \"%s\" referenced by constraint \"%s\"",
+							RelationGetRelationName(pk_rel),
+							argv[j],
+							argv[RI_CONSTRAINT_NAME_ARGNO])));
 		key->keypair[i][RI_KEYPAIR_PK_IDX] = fno;
 		key->keypair[i][RI_KEYPAIR_FK_IDX] = 0;
 	}
@@ -3343,7 +3267,9 @@ ri_HashPreparedPlan(RI_QueryKey *key, void *plan)
 											  (void *) key,
 											  HASH_ENTER, &found);
 	if (entry == NULL)
-		elog(ERROR, "out of memory for RI plan cache");
+		ereport(ERROR,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory")));
 	entry->plan = plan;
 }
 
@@ -3532,7 +3458,7 @@ ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue)
 
 		/*
 		 * Since fmgr_info could fail, call it *before* creating the
-		 * hashtable entry --- otherwise we could elog leaving an
+		 * hashtable entry --- otherwise we could ereport leaving an
 		 * incomplete entry in the hashtable.  Also, because this will be
 		 * a permanent table entry, we must make sure any subsidiary
 		 * structures of the fmgr record are kept in TopMemoryContext.
@@ -3543,7 +3469,9 @@ ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue)
 												  (void *) &typeid,
 												  HASH_ENTER, &found);
 		if (entry == NULL)
-			elog(ERROR, "out of memory for RI operator cache");
+			ereport(ERROR,
+					(errcode(ERRCODE_OUT_OF_MEMORY),
+					 errmsg("out of memory")));
 
 		entry->typeid = typeid;
 		memcpy(&(entry->oprfmgrinfo), &finfo, sizeof(FmgrInfo));
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 732b0e7cfbd..661187269db 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: elog.h,v 1.54 2003/07/22 19:00:12 tgl Exp $
+ * $Id: elog.h,v 1.55 2003/07/22 22:14:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -162,7 +162,7 @@
 #define ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION		MAKE_SQLSTATE('2','3', '0','0','0')
 #define ERRCODE_RESTRICT_VIOLATION			MAKE_SQLSTATE('2','3', '0','0','1')
 #define ERRCODE_NOT_NULL_VIOLATION			MAKE_SQLSTATE('2','3', '5','0','2')
-#define ERRCODE_FOREIGN_KEY_VALUE_NOT_FOUND	MAKE_SQLSTATE('2','3', '5','0','3')
+#define ERRCODE_FOREIGN_KEY_VIOLATION		MAKE_SQLSTATE('2','3', '5','0','3')
 #define ERRCODE_UNIQUE_VIOLATION			MAKE_SQLSTATE('2','3', '5','0','5')
 #define ERRCODE_CHECK_VIOLATION				MAKE_SQLSTATE('2','3', '5','1','4')
 
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 677dce17ea5..19e60662c7f 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -316,7 +316,8 @@ ERROR:  column "b" referenced in foreign key constraint does not exist
 -- Try (and fail) to add constraint due to invalid data
 ALTER TABLE tmp3 add constraint tmpconstr foreign key (a) references tmp2 match full;
 NOTICE:  ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
-ERROR:  tmpconstr referential integrity violation - key (a)=(5) referenced from tmp3 not found in tmp2
+ERROR:  insert or update on "tmp3" violates foreign key constraint "tmpconstr"
+DETAIL:  Key (a)=(5) is not present in "tmp2".
 -- Delete failing row
 DELETE FROM tmp3 where a=5;
 -- Try (and succeed)
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 50507fc961a..9a28df62d10 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -248,7 +248,8 @@ SELECT a,b,c,substring(d for 30), length(d) from clstr_tst;
 
 -- Verify that foreign key link still works
 INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail');
-ERROR:  clstr_tst_con referential integrity violation - key (b)=(1111) referenced from clstr_tst not found in clstr_tst_s
+ERROR:  insert or update on "clstr_tst" violates foreign key constraint "clstr_tst_con"
+DETAIL:  Key (b)=(1111) is not present in "clstr_tst_s".
 SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass;
     conname     
 ----------------
diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out
index 7ac2eb387d3..cb6fc35eb6d 100644
--- a/src/test/regress/expected/foreign_key.out
+++ b/src/test/regress/expected/foreign_key.out
@@ -22,7 +22,8 @@ INSERT INTO FKTABLE VALUES (3, 4);
 INSERT INTO FKTABLE VALUES (NULL, 1);
 -- Insert a failed row into FK TABLE
 INSERT INTO FKTABLE VALUES (100, 2);
-ERROR:  $1 referential integrity violation - key (ftest1)=(100) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "$1"
+DETAIL:  Key (ftest1)=(100) is not present in "pktable".
 -- Check FKTABLE
 SELECT * FROM FKTABLE;
  ftest1 | ftest2 
@@ -80,13 +81,17 @@ INSERT INTO FKTABLE VALUES (3, 6, 12);
 INSERT INTO FKTABLE VALUES (NULL, NULL, 0);
 -- Insert failed rows into FK TABLE
 INSERT INTO FKTABLE VALUES (100, 2, 4);
-ERROR:  constrname referential integrity violation - key (ftest1,ftest2)=(100,2) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "constrname"
+DETAIL:  Key (ftest1,ftest2)=(100,2) is not present in "pktable".
 INSERT INTO FKTABLE VALUES (2, 2, 4);
-ERROR:  constrname referential integrity violation - key (ftest1,ftest2)=(2,2) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "constrname"
+DETAIL:  Key (ftest1,ftest2)=(2,2) is not present in "pktable".
 INSERT INTO FKTABLE VALUES (NULL, 2, 4);
-ERROR:  constrname referential integrity violation - MATCH FULL doesn't allow mixing of NULL and NON-NULL key values
+ERROR:  insert or update on "fktable" violates foreign key constraint "constrname"
+DETAIL:  MATCH FULL does not allow mixing of NULL and non-NULL key values.
 INSERT INTO FKTABLE VALUES (1, NULL, 4);
-ERROR:  constrname referential integrity violation - MATCH FULL doesn't allow mixing of NULL and NON-NULL key values
+ERROR:  insert or update on "fktable" violates foreign key constraint "constrname"
+DETAIL:  MATCH FULL does not allow mixing of NULL and non-NULL key values.
 -- Check FKTABLE
 SELECT * FROM FKTABLE;
  ftest1 | ftest2 | ftest3 
@@ -165,13 +170,17 @@ INSERT INTO FKTABLE VALUES (3, 6, 12);
 INSERT INTO FKTABLE VALUES (NULL, NULL, 0);
 -- Insert failed rows into FK TABLE
 INSERT INTO FKTABLE VALUES (100, 2, 4);
-ERROR:  constrname2 referential integrity violation - key (ftest1,ftest2)=(100,2) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "constrname2"
+DETAIL:  Key (ftest1,ftest2)=(100,2) is not present in "pktable".
 INSERT INTO FKTABLE VALUES (2, 2, 4);
-ERROR:  constrname2 referential integrity violation - key (ftest1,ftest2)=(2,2) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "constrname2"
+DETAIL:  Key (ftest1,ftest2)=(2,2) is not present in "pktable".
 INSERT INTO FKTABLE VALUES (NULL, 2, 4);
-ERROR:  constrname2 referential integrity violation - MATCH FULL doesn't allow mixing of NULL and NON-NULL key values
+ERROR:  insert or update on "fktable" violates foreign key constraint "constrname2"
+DETAIL:  MATCH FULL does not allow mixing of NULL and non-NULL key values.
 INSERT INTO FKTABLE VALUES (1, NULL, 4);
-ERROR:  constrname2 referential integrity violation - MATCH FULL doesn't allow mixing of NULL and NON-NULL key values
+ERROR:  insert or update on "fktable" violates foreign key constraint "constrname2"
+DETAIL:  MATCH FULL does not allow mixing of NULL and non-NULL key values.
 -- Check FKTABLE
 SELECT * FROM FKTABLE;
  ftest1 | ftest2 | ftest3 
@@ -250,7 +259,8 @@ INSERT INTO FKTABLE VALUES (3, 4);
 INSERT INTO FKTABLE VALUES (NULL, 1);
 -- Insert a failed row into FK TABLE
 INSERT INTO FKTABLE VALUES (100, 2);
-ERROR:  $1 referential integrity violation - key (ftest1)=(100) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "$1"
+DETAIL:  Key (ftest1)=(100) is not present in "pktable".
 -- Check FKTABLE
 SELECT * FROM FKTABLE;
  ftest1 | ftest2 
@@ -274,7 +284,8 @@ SELECT * FROM PKTABLE;
 
 -- Delete a row from PK TABLE (should fail)
 DELETE FROM PKTABLE WHERE ptest1=1;
-ERROR:  $1 referential integrity violation - key (ptest1)=(1) in pktable still referenced from fktable
+ERROR:  update or delete on "pktable" violates foreign key constraint "$1" on "fktable"
+DETAIL:  Key (ptest1)=(1) is still referenced from "fktable".
 -- Delete a row from PK TABLE (should succeed)
 DELETE FROM PKTABLE WHERE ptest1=5;
 -- Check PKTABLE for deletes
@@ -289,7 +300,8 @@ SELECT * FROM PKTABLE;
 
 -- Update a row from PK TABLE (should fail)
 UPDATE PKTABLE SET ptest1=0 WHERE ptest1=2;
-ERROR:  $1 referential integrity violation - key (ptest1)=(2) in pktable still referenced from fktable
+ERROR:  update or delete on "pktable" violates foreign key constraint "$1" on "fktable"
+DETAIL:  Key (ptest1)=(2) is still referenced from "fktable".
 -- Update a row from PK TABLE (should succeed)
 UPDATE PKTABLE SET ptest1=0 WHERE ptest1=4;
 -- Check PKTABLE for updates
@@ -324,7 +336,8 @@ INSERT INTO FKTABLE VALUES (NULL, 2, 7, 4);
 INSERT INTO FKTABLE VALUES (NULL, 3, 4, 5);
 -- Insert a failed values
 INSERT INTO FKTABLE VALUES (1, 2, 7, 6);
-ERROR:  constrname3 referential integrity violation - key (ftest1,ftest2,ftest3)=(1,2,7) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "constrname3"
+DETAIL:  Key (ftest1,ftest2,ftest3)=(1,2,7) is not present in "pktable".
 -- Show FKTABLE
 SELECT * from FKTABLE;
  ftest1 | ftest2 | ftest3 | ftest4 
@@ -338,12 +351,14 @@ SELECT * from FKTABLE;
 
 -- Try to update something that should fail
 UPDATE PKTABLE set ptest2=5 where ptest2=2;
-ERROR:  constrname3 referential integrity violation - key (ptest1,ptest2,ptest3)=(1,2,3) in pktable still referenced from fktable
+ERROR:  update or delete on "pktable" violates foreign key constraint "constrname3" on "fktable"
+DETAIL:  Key (ptest1,ptest2,ptest3)=(1,2,3) is still referenced from "fktable".
 -- Try to update something that should succeed
 UPDATE PKTABLE set ptest1=1 WHERE ptest2=3;
 -- Try to delete something that should fail
 DELETE FROM PKTABLE where ptest1=1 and ptest2=2 and ptest3=3;
-ERROR:  constrname3 referential integrity violation - key (ptest1,ptest2,ptest3)=(1,2,3) in pktable still referenced from fktable
+ERROR:  update or delete on "pktable" violates foreign key constraint "constrname3" on "fktable"
+DETAIL:  Key (ptest1,ptest2,ptest3)=(1,2,3) is still referenced from "fktable".
 -- Try to delete something that should work
 DELETE FROM PKTABLE where ptest1=2;
 -- Show PKTABLE and FKTABLE
@@ -387,7 +402,8 @@ INSERT INTO FKTABLE VALUES (NULL, 2, 7, 4);
 INSERT INTO FKTABLE VALUES (NULL, 3, 4, 5);
 -- Insert a failed values
 INSERT INTO FKTABLE VALUES (1, 2, 7, 6);
-ERROR:  constrname3 referential integrity violation - key (ftest1,ftest2,ftest3)=(1,2,7) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "constrname3"
+DETAIL:  Key (ftest1,ftest2,ftest3)=(1,2,7) is not present in "pktable".
 -- Show FKTABLE
 SELECT * from FKTABLE;
  ftest1 | ftest2 | ftest3 | ftest4 
@@ -485,7 +501,8 @@ INSERT INTO FKTABLE VALUES (NULL, 2, 7, 4);
 INSERT INTO FKTABLE VALUES (NULL, 3, 4, 5);
 -- Insert a failed values
 INSERT INTO FKTABLE VALUES (1, 2, 7, 6);
-ERROR:  constrname3 referential integrity violation - key (ftest1,ftest2,ftest3)=(1,2,7) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "constrname3"
+DETAIL:  Key (ftest1,ftest2,ftest3)=(1,2,7) is not present in "pktable".
 -- Show FKTABLE
 SELECT * from FKTABLE;
  ftest1 | ftest2 | ftest3 | ftest4 
@@ -591,7 +608,8 @@ INSERT INTO FKTABLE VALUES (NULL, 2, 7, 4);
 INSERT INTO FKTABLE VALUES (NULL, 3, 4, 5);
 -- Insert a failed values
 INSERT INTO FKTABLE VALUES (1, 2, 7, 6);
-ERROR:  constrname3 referential integrity violation - key (ftest1,ftest2,ftest3)=(1,2,7) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "constrname3"
+DETAIL:  Key (ftest1,ftest2,ftest3)=(1,2,7) is not present in "pktable".
 -- Show FKTABLE
 SELECT * from FKTABLE;
  ftest1 | ftest2 | ftest3 | ftest4 
@@ -607,7 +625,8 @@ SELECT * from FKTABLE;
 
 -- Try to update something that will fail
 UPDATE PKTABLE set ptest2=5 where ptest2=2;
-ERROR:  constrname3 referential integrity violation - key (ftest1,ftest2,ftest3)=(1,-1,3) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "constrname3"
+DETAIL:  Key (ftest1,ftest2,ftest3)=(1,-1,3) is not present in "pktable".
 -- Try to update something that will set default
 UPDATE PKTABLE set ptest1=0, ptest2=5, ptest3=10 where ptest2=2;
 UPDATE PKTABLE set ptest2=10 where ptest2=4;
@@ -819,17 +838,20 @@ insert into pktable(base1) values (1);
 insert into pktable(base1) values (2);
 --  let's insert a non-existant fktable value
 insert into fktable(ftest1) values (3);
-ERROR:  $1 referential integrity violation - key (ftest1)=(3) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "$1"
+DETAIL:  Key (ftest1)=(3) is not present in "pktable".
 --  let's make a valid row for that
 insert into pktable(base1) values (3);
 insert into fktable(ftest1) values (3);
 -- let's try removing a row that should fail from pktable
 delete from pktable where base1>2;
-ERROR:  $1 referential integrity violation - key (base1)=(3) in pktable still referenced from fktable
+ERROR:  update or delete on "pktable" violates foreign key constraint "$1" on "fktable"
+DETAIL:  Key (base1)=(3) is still referenced from "fktable".
 -- okay, let's try updating all of the base1 values to *4
 -- which should fail.
 update pktable set base1=base1*4;
-ERROR:  $1 referential integrity violation - key (base1)=(3) in pktable still referenced from fktable
+ERROR:  update or delete on "pktable" violates foreign key constraint "$1" on "fktable"
+DETAIL:  Key (base1)=(3) is still referenced from "fktable".
 -- okay, let's try an update that should work.
 update pktable set base1=base1*4 where base1<3;
 -- and a delete that should work
@@ -845,17 +867,20 @@ insert into pktable(base1, ptest1) values (1, 1);
 insert into pktable(base1, ptest1) values (2, 2);
 --  let's insert a non-existant fktable value
 insert into fktable(ftest1, ftest2) values (3, 1);
-ERROR:  $1 referential integrity violation - key (ftest1,ftest2)=(3,1) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "$1"
+DETAIL:  Key (ftest1,ftest2)=(3,1) is not present in "pktable".
 --  let's make a valid row for that
 insert into pktable(base1,ptest1) values (3, 1);
 insert into fktable(ftest1, ftest2) values (3, 1);
 -- let's try removing a row that should fail from pktable
 delete from pktable where base1>2;
-ERROR:  $1 referential integrity violation - key (base1,ptest1)=(3,1) in pktable still referenced from fktable
+ERROR:  update or delete on "pktable" violates foreign key constraint "$1" on "fktable"
+DETAIL:  Key (base1,ptest1)=(3,1) is still referenced from "fktable".
 -- okay, let's try updating all of the base1 values to *4
 -- which should fail.
 update pktable set base1=base1*4;
-ERROR:  $1 referential integrity violation - key (base1,ptest1)=(3,1) in pktable still referenced from fktable
+ERROR:  update or delete on "pktable" violates foreign key constraint "$1" on "fktable"
+DETAIL:  Key (base1,ptest1)=(3,1) is still referenced from "fktable".
 -- okay, let's try an update that should work.
 update pktable set base1=base1*4 where base1<3;
 -- and a delete that should work
@@ -876,13 +901,16 @@ insert into pktable (base1, ptest1, base2, ptest2) values (2, 2, 2, 1);
 insert into pktable (base1, ptest1, base2, ptest2) values (1, 3, 2, 2);
 -- fails (3,2) isn't in base1, ptest1
 insert into pktable (base1, ptest1, base2, ptest2) values (2, 3, 3, 2);
-ERROR:  $1 referential integrity violation - key (base2,ptest2)=(3,2) referenced from pktable not found in pktable
+ERROR:  insert or update on "pktable" violates foreign key constraint "$1"
+DETAIL:  Key (base2,ptest2)=(3,2) is not present in "pktable".
 -- fails (2,2) is being referenced
 delete from pktable where base1=2;
-ERROR:  $1 referential integrity violation - key (base1,ptest1)=(2,2) in pktable still referenced from pktable
+ERROR:  update or delete on "pktable" violates foreign key constraint "$1" on "pktable"
+DETAIL:  Key (base1,ptest1)=(2,2) is still referenced from "pktable".
 -- fails (1,1) is being referenced (twice)
 update pktable set base1=3 where base1=1;
-ERROR:  $1 referential integrity violation - key (base1,ptest1)=(1,1) in pktable still referenced from pktable
+ERROR:  update or delete on "pktable" violates foreign key constraint "$1" on "pktable"
+DETAIL:  Key (base1,ptest1)=(1,1) is still referenced from "pktable".
 -- this sequence of two deletes will work, since after the first there will be no (2,*) references
 delete from pktable where base2=2;
 delete from pktable where base1=2;
@@ -963,7 +991,8 @@ NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "fktable_pkey" fo
 NOTICE:  CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
 -- default to immediate: should fail
 INSERT INTO fktable VALUES (5, 10);
-ERROR:  $1 referential integrity violation - key (fk)=(10) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "$1"
+DETAIL:  Key (fk)=(10) is not present in "pktable".
 -- explicitely defer the constraint
 BEGIN;
 SET CONSTRAINTS ALL DEFERRED;
@@ -993,7 +1022,8 @@ BEGIN;
 SET CONSTRAINTS ALL IMMEDIATE;
 -- should fail
 INSERT INTO fktable VALUES (500, 1000);
-ERROR:  $1 referential integrity violation - key (fk)=(1000) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "$1"
+DETAIL:  Key (fk)=(1000) is not present in "pktable".
 COMMIT;
 DROP TABLE fktable, pktable;
 -- tricky behavior: according to SQL99, if a deferred constraint is set
@@ -1017,7 +1047,8 @@ SET CONSTRAINTS ALL DEFERRED;
 INSERT INTO fktable VALUES (1000, 2000);
 -- should cause transaction abort, due to preceding error
 SET CONSTRAINTS ALL IMMEDIATE;
-ERROR:  $1 referential integrity violation - key (fk)=(2000) referenced from fktable not found in pktable
+ERROR:  insert or update on "fktable" violates foreign key constraint "$1"
+DETAIL:  Key (fk)=(2000) is not present in "pktable".
 INSERT INTO pktable VALUES (2000, 3); -- too late
 ERROR:  current transaction is aborted, queries ignored until end of transaction block
 COMMIT;
-- 
GitLab