diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml
index 34ab2840eecc8b5d900785b3ae6914ad3584b14a..8dd9815e0c6bb6f5f5d6e638d808b145ad62e731 100644
--- a/doc/src/sgml/trigger.sgml
+++ b/doc/src/sgml/trigger.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/trigger.sgml,v 1.21 2002/03/22 19:20:32 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/trigger.sgml,v 1.22 2002/04/01 22:36:06 tgl Exp $
 -->
 
  <chapter id="triggers">
@@ -374,6 +374,7 @@ typedef struct Trigger
     int16       tgtype;
     bool        tgenabled;
     bool        tgisconstraint;
+    Oid         tgconstrrelid;
     bool        tgdeferrable;
     bool        tginitdeferred;
     int16       tgnargs;
diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
index 9bdeb3c7c7a0b812a5ae9d193f8c81b7e411c145..44def03fa4353bce8c979526be0529521a71c967 100644
--- a/src/backend/commands/command.c
+++ b/src/backend/commands/command.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.170 2002/04/01 04:35:38 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.171 2002/04/01 22:36:09 tgl Exp $
  *
  * NOTES
  *	  The PerformAddAttribute() code, like most of the relation
@@ -1639,9 +1639,6 @@ AlterTableAddConstraint(Oid myrelid,
 						!isTempNamespace(RelationGetNamespace(rel)))
 						elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint.");
 
-					/* Don't need pkrel open anymore, but hold lock */
-					heap_close(pkrel, NoLock);
-
 					/*
 					 * First we check for limited correctness of the
 					 * constraint.
@@ -1651,34 +1648,30 @@ AlterTableAddConstraint(Oid myrelid,
 					 * referenced relation, and that the column datatypes
 					 * are comparable.
 					 *
-					 * Scan through each tuple, calling the RI_FKey_Match_Ins
+					 * Scan through each tuple, calling RI_FKey_check_ins
 					 * (insert trigger) as if that tuple had just been
 					 * inserted.  If any of those fail, it should
 					 * elog(ERROR) and that's that.
 					 */
-
-					trig.tgoid = 0;
+					MemSet(&trig, 0, sizeof(trig));
+					trig.tgoid = InvalidOid;
 					if (fkconstraint->constr_name)
 						trig.tgname = fkconstraint->constr_name;
 					else
 						trig.tgname = "<unknown>";
-					trig.tgfoid = 0;
-					trig.tgtype = 0;
 					trig.tgenabled = TRUE;
 					trig.tgisconstraint = TRUE;
-					trig.tginitdeferred = FALSE;
+					trig.tgconstrrelid = RelationGetRelid(pkrel);
 					trig.tgdeferrable = FALSE;
+					trig.tginitdeferred = FALSE;
 
 					trig.tgargs = (char **) palloc(
 					 sizeof(char *) * (4 + length(fkconstraint->fk_attrs)
 									   + length(fkconstraint->pk_attrs)));
 
-					if (fkconstraint->constr_name)
-						trig.tgargs[0] = fkconstraint->constr_name;
-					else
-						trig.tgargs[0] = "<unknown>";
-					trig.tgargs[1] = pstrdup(RelationGetRelationName(rel));
-					trig.tgargs[2] = fkconstraint->pktable->relname;
+					trig.tgargs[0] = trig.tgname;
+					trig.tgargs[1] = RelationGetRelationName(rel);
+					trig.tgargs[2] = RelationGetRelationName(pkrel);
 					trig.tgargs[3] = fkconstraint->match_type;
 					count = 4;
 					foreach(list, fkconstraint->fk_attrs)
@@ -1732,6 +1725,9 @@ AlterTableAddConstraint(Oid myrelid,
 					heap_endscan(scan);
 
 					pfree(trig.tgargs);
+
+					heap_close(pkrel, NoLock);
+
 					break;
 				}
 			default:
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index c20c2637178aaad9ab78b581baa907a6c6ebfd6b..c05b2ec51a31b895cdeb882a56bb8ddd69ffd40f 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.110 2002/03/31 06:26:30 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.111 2002/04/01 22:36:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -544,6 +544,7 @@ RelationBuildTriggers(Relation relation)
 		build->tgtype = pg_trigger->tgtype;
 		build->tgenabled = pg_trigger->tgenabled;
 		build->tgisconstraint = pg_trigger->tgisconstraint;
+		build->tgconstrrelid = pg_trigger->tgconstrrelid;
 		build->tgdeferrable = pg_trigger->tgdeferrable;
 		build->tginitdeferred = pg_trigger->tginitdeferred;
 		build->tgnargs = pg_trigger->tgnargs;
@@ -763,6 +764,8 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2)
 				return false;
 			if (trig1->tgisconstraint != trig2->tgisconstraint)
 				return false;
+			if (trig1->tgconstrrelid != trig2->tgconstrrelid)
+				return false;
 			if (trig1->tgdeferrable != trig2->tgdeferrable)
 				return false;
 			if (trig1->tginitdeferred != trig2->tginitdeferred)
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index e251bc60d3d6e3e81fdc32a860dd432511f46a42..b073e51457328e442bdfc4798354982fa936521c 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 2000-2001, PostgreSQL Global Development Group
  * Copyright 1999 Jan Wieck
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.34 2002/04/01 02:02:34 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.35 2002/04/01 22:36:10 tgl Exp $
  *
  * ----------
  */
@@ -33,6 +33,7 @@
 
 #include "postgres.h"
 
+#include "catalog/pg_namespace.h"
 #include "catalog/pg_operator.h"
 #include "commands/trigger.h"
 #include "executor/spi_priv.h"
@@ -68,6 +69,9 @@
 #define RI_PLAN_SETNULL_DEL_DOUPDATE	1
 #define RI_PLAN_SETNULL_UPD_DOUPDATE	1
 
+#define MAX_QUOTED_NAME_LEN  (NAMEDATALEN*2+3)
+#define MAX_QUOTED_REL_NAME_LEN  (MAX_QUOTED_NAME_LEN*2)
+
 
 /* ----------
  * RI_QueryKey
@@ -118,6 +122,8 @@ static HTAB *ri_opreq_cache = (HTAB *) NULL;
  * Local function prototypes
  * ----------
  */
+static void quoteOneName(char *buffer, const char *name);
+static void quoteRelationName(char *buffer, Relation rel);
 static int	ri_DetermineMatchType(char *str);
 static int ri_NullCheck(Relation rel, HeapTuple tup,
 			 RI_QueryKey *key, int pairidx);
@@ -138,19 +144,6 @@ static void *ri_FetchPreparedPlan(RI_QueryKey *key);
 static void ri_HashPreparedPlan(RI_QueryKey *key, void *plan);
 
 
-/*
- * very ugly, very temporary hack to allow RI triggers to find tables
- * anywhere in the current search path.  This is just so that the regression
- * tests don't break with new search path code; we MUST find a more bullet
- * proof solution before release.
- */
-static Relation
-kluge_openr(char *relationName, LOCKMODE lockmode)
-{
-	return heap_openrv(makeRangeVar(NULL, relationName), lockmode);
-}
-
-
 /* ----------
  * RI_FKey_check -
  *
@@ -207,9 +200,12 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 	/*
 	 * Get the relation descriptors of the FK and PK tables and the new
 	 * tuple.
+	 *
+	 * pk_rel is opened in RowShareLock mode since that's what our
+	 * eventual SELECT FOR UPDATE will get on it.
 	 */
 	fk_rel = trigdata->tg_relation;
-	pk_rel = kluge_openr(tgargs[RI_PK_RELNAME_ARGNO], NoLock);
+	pk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
 	if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
 	{
 		old_row = trigdata->tg_trigtuple;
@@ -221,16 +217,17 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 		new_row = trigdata->tg_trigtuple;
 	}
 
-        /*
-         * We should not even consider checking the row if it is no longer
-         * valid since it was either deleted (doesn't matter) or updated
-         * (in which case it'll be checked with its final values).
-         */
-        if (new_row) {
-                if (!HeapTupleSatisfiesItself(new_row->t_data)) {
-                        return PointerGetDatum(NULL);
-                }
-        }
+	/*
+	 * We should not even consider checking the row if it is no longer
+	 * valid since it was either deleted (doesn't matter) or updated
+	 * (in which case it'll be checked with its final values).
+	 */
+	if (new_row) {
+		if (!HeapTupleSatisfiesItself(new_row->t_data)) {
+			heap_close(pk_rel, RowShareLock);
+			return PointerGetDatum(NULL);
+		}
+	}
 
 	/* ----------
 	 * SQL3 11.9 <referential constraint definition>
@@ -252,16 +249,17 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 
 		if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 		{
-			char		querystr[8192];
+			char		querystr[MAX_QUOTED_REL_NAME_LEN + 100];
+			char		pkrelname[MAX_QUOTED_REL_NAME_LEN];
 
 			/* ---------
 			 * The query string built is
 			 *	SELECT 1 FROM ONLY <pktable>
 			 * ----------
 			 */
-			sprintf(querystr, "SELECT 1 FROM ONLY \"%s\" FOR UPDATE OF \"%s\"",
-					tgargs[RI_PK_RELNAME_ARGNO],
-					tgargs[RI_PK_RELNAME_ARGNO]);
+			quoteRelationName(pkrelname, pk_rel);
+			sprintf(querystr, "SELECT 1 FROM ONLY %s x FOR UPDATE OF x",
+					pkrelname);
 
 			/*
 			 * Prepare, save and remember the new plan.
@@ -278,8 +276,6 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 			elog(WARNING, "SPI_connect() failed in RI_FKey_check()");
 
 		SetUserId(RelationGetForm(pk_rel)->relowner);
-		/* pk_rel is no longer neede OK ? */
-		heap_close(pk_rel, NoLock);
 
 		if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
 			elog(ERROR, "SPI_execp() failed in RI_FKey_check()");
@@ -290,11 +286,13 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 			elog(ERROR, "%s referential integrity violation - "
 				 "no rows found in %s",
 				 tgargs[RI_CONSTRAINT_NAME_ARGNO],
-				 tgargs[RI_PK_RELNAME_ARGNO]);
+				 RelationGetRelationName(pk_rel));
 
 		if (SPI_finish() != SPI_OK_FINISH)
 			elog(WARNING, "SPI_finish() failed in RI_FKey_check()");
 
+		heap_close(pk_rel, RowShareLock);
+
 		return PointerGetDatum(NULL);
 
 	}
@@ -322,7 +320,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 			 * This is true for MATCH FULL, MATCH PARTIAL, and MATCH
 			 * <unspecified>
 			 */
-			heap_close(pk_rel, NoLock);
+			heap_close(pk_rel, RowShareLock);
 			return PointerGetDatum(NULL);
 
 		case RI_KEYS_SOME_NULL:
@@ -343,7 +341,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 						 "MATCH FULL doesn't allow mixing of NULL "
 						 "and NON-NULL key values",
 						 tgargs[RI_CONSTRAINT_NAME_ARGNO]);
-					heap_close(pk_rel, NoLock);
+					heap_close(pk_rel, RowShareLock);
 					return PointerGetDatum(NULL);
 
 				case RI_MATCH_TYPE_UNSPECIFIED:
@@ -352,7 +350,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 					 * MATCH <unspecified> - if ANY column is null, we
 					 * have a match.
 					 */
-					heap_close(pk_rel, NoLock);
+					heap_close(pk_rel, RowShareLock);
 					return PointerGetDatum(NULL);
 
 				case RI_MATCH_TYPE_PARTIAL:
@@ -364,7 +362,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 					 * writing a special version here)
 					 */
 					elog(ERROR, "MATCH PARTIAL not yet implemented");
-					heap_close(pk_rel, NoLock);
+					heap_close(pk_rel, RowShareLock);
 					return PointerGetDatum(NULL);
 			}
 
@@ -393,9 +391,11 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 	 */
 	if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 	{
-		char		buf[256];
-		char		querystr[8192];
-		char	   *querysep;
+		char		querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
+							(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+		char		pkrelname[MAX_QUOTED_REL_NAME_LEN];
+		char		attname[MAX_QUOTED_NAME_LEN];
+		const char *querysep;
 		Oid			queryoids[RI_MAX_NUMKEYS];
 
 		/* ----------
@@ -407,21 +407,20 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 		 * how to compare these two types by '='.
 		 * ----------
 		 */
-		sprintf(querystr, "SELECT 1 FROM ONLY \"%s\"",
-				tgargs[RI_PK_RELNAME_ARGNO]);
+		quoteRelationName(pkrelname, pk_rel);
+		sprintf(querystr, "SELECT 1 FROM ONLY %s x", pkrelname);
 		querysep = "WHERE";
 		for (i = 0; i < qkey.nkeypairs; i++)
 		{
-			sprintf(buf, " %s \"%s\" = $%d", querysep,
-					tgargs[5 + i * 2], i + 1);
-			strcat(querystr, buf);
+			quoteOneName(attname,
+						 tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_PK_IDX]);
+			sprintf(querystr + strlen(querystr), " %s %s = $%d",
+					querysep, attname, i+1);
 			querysep = "AND";
 			queryoids[i] = SPI_gettypeid(fk_rel->rd_att,
 									 qkey.keypair[i][RI_KEYPAIR_FK_IDX]);
 		}
-		sprintf(buf, " FOR UPDATE OF \"%s\"",
-				tgargs[RI_PK_RELNAME_ARGNO]);
-		strcat(querystr, buf);
+		strcat(querystr, " FOR UPDATE OF x");
 
 		/*
 		 * Prepare, save and remember the new plan.
@@ -460,8 +459,6 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 	 */
 
 	SetUserId(RelationGetForm(pk_rel)->relowner);
-	/* pk_rel is no longer needed OK ? */
-	heap_close(pk_rel, NoLock);
 
 	if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
 		elog(ERROR, "SPI_execp() failed in RI_FKey_check()");
@@ -472,12 +469,14 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 		elog(ERROR, "%s referential integrity violation - "
 			 "key referenced from %s not found in %s",
 			 tgargs[RI_CONSTRAINT_NAME_ARGNO],
-			 tgargs[RI_FK_RELNAME_ARGNO],
-			 tgargs[RI_PK_RELNAME_ARGNO]);
+			 RelationGetRelationName(fk_rel),
+			 RelationGetRelationName(pk_rel));
 
 	if (SPI_finish() != SPI_OK_FINISH)
 		elog(WARNING, "SPI_finish() failed in RI_FKey_check()");
 
+	heap_close(pk_rel, RowShareLock);
+
 	return PointerGetDatum(NULL);
 
 	/*
@@ -575,8 +574,11 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 	/*
 	 * Get the relation descriptors of the FK and PK tables and the old
 	 * tuple.
+	 *
+	 * fk_rel is opened in RowShareLock mode since that's what our
+	 * eventual SELECT FOR UPDATE will get on it.
 	 */
-	fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
+	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
 	pk_rel = trigdata->tg_relation;
 	old_row = trigdata->tg_trigtuple;
 
@@ -605,7 +607,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 					 * No check - MATCH FULL means there cannot be any
 					 * reference to old key if it contains NULL
 					 */
-					heap_close(fk_rel, NoLock);
+					heap_close(fk_rel, RowShareLock);
 					return PointerGetDatum(NULL);
 
 				case RI_KEYS_NONE_NULL:
@@ -615,7 +617,6 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 					 */
 					break;
 			}
-			heap_close(fk_rel, NoLock);
 
 			if (SPI_connect() != SPI_OK_CONNECT)
 				elog(WARNING, "SPI_connect() failed in RI_FKey_noaction_del()");
@@ -626,9 +627,11 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 			 */
 			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
-				char		buf[256];
-				char		querystr[8192];
-				char	   *querysep;
+				char		querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
+									(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
+				char		attname[MAX_QUOTED_NAME_LEN];
+				const char *querysep;
 				Oid			queryoids[RI_MAX_NUMKEYS];
 
 				/* ----------
@@ -640,21 +643,20 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 				 * how to compare these two types by '='.
 				 * ----------
 				 */
-				sprintf(querystr, "SELECT 1 FROM ONLY \"%s\"",
-						tgargs[RI_FK_RELNAME_ARGNO]);
+				quoteRelationName(fkrelname, fk_rel);
+				sprintf(querystr, "SELECT 1 FROM ONLY %s x", fkrelname);
 				querysep = "WHERE";
 				for (i = 0; i < qkey.nkeypairs; i++)
 				{
-					sprintf(buf, " %s \"%s\" = $%d", querysep,
-							tgargs[4 + i * 2], i + 1);
-					strcat(querystr, buf);
+					quoteOneName(attname,
+								 tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
+					sprintf(querystr + strlen(querystr), " %s %s = $%d",
+							querysep, attname, i+1);
 					querysep = "AND";
 					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
 									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
 				}
-				sprintf(buf, " FOR UPDATE OF \"%s\"",
-						tgargs[RI_FK_RELNAME_ARGNO]);
-				strcat(querystr, buf);
+				strcat(querystr, " FOR UPDATE OF x");
 
 				/*
 				 * Prepare, save and remember the new plan.
@@ -695,12 +697,14 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 				elog(ERROR, "%s referential integrity violation - "
 					 "key in %s still referenced from %s",
 					 tgargs[RI_CONSTRAINT_NAME_ARGNO],
-					 tgargs[RI_PK_RELNAME_ARGNO],
-					 tgargs[RI_FK_RELNAME_ARGNO]);
+					 RelationGetRelationName(pk_rel),
+					 RelationGetRelationName(fk_rel));
 
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_del()");
 
+			heap_close(fk_rel, RowShareLock);
+
 			return PointerGetDatum(NULL);
 
 			/*
@@ -781,8 +785,11 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 	/*
 	 * Get the relation descriptors of the FK and PK tables and the new
 	 * and old tuple.
+	 *
+	 * fk_rel is opened in RowShareLock mode since that's what our
+	 * eventual SELECT FOR UPDATE will get on it.
 	 */
-	fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
+	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
 	pk_rel = trigdata->tg_relation;
 	new_row = trigdata->tg_newtuple;
 	old_row = trigdata->tg_trigtuple;
@@ -812,7 +819,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 					 * No check - MATCH FULL means there cannot be any
 					 * reference to old key if it contains NULL
 					 */
-					heap_close(fk_rel, NoLock);
+					heap_close(fk_rel, RowShareLock);
 					return PointerGetDatum(NULL);
 
 				case RI_KEYS_NONE_NULL:
@@ -822,14 +829,16 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 					 */
 					break;
 			}
-			heap_close(fk_rel, NoLock);
 
 			/*
 			 * No need to check anything if old and new keys are equal
 			 */
 			if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
 							 RI_KEYPAIR_PK_IDX))
+			{
+				heap_close(fk_rel, RowShareLock);
 				return PointerGetDatum(NULL);
+			}
 
 			if (SPI_connect() != SPI_OK_CONNECT)
 				elog(WARNING, "SPI_connect() failed in RI_FKey_noaction_upd()");
@@ -840,9 +849,11 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 			 */
 			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
-				char		buf[256];
-				char		querystr[8192];
-				char	   *querysep;
+				char		querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
+									(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
+				char		attname[MAX_QUOTED_NAME_LEN];
+				const char *querysep;
 				Oid			queryoids[RI_MAX_NUMKEYS];
 
 				/* ----------
@@ -854,21 +865,20 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 				 * how to compare these two types by '='.
 				 * ----------
 				 */
-				sprintf(querystr, "SELECT 1 FROM ONLY \"%s\"",
-						tgargs[RI_FK_RELNAME_ARGNO]);
+				quoteRelationName(fkrelname, fk_rel);
+				sprintf(querystr, "SELECT 1 FROM ONLY %s x", fkrelname);
 				querysep = "WHERE";
 				for (i = 0; i < qkey.nkeypairs; i++)
 				{
-					sprintf(buf, " %s \"%s\" = $%d", querysep,
-							tgargs[4 + i * 2], i + 1);
-					strcat(querystr, buf);
+					quoteOneName(attname,
+								 tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
+					sprintf(querystr + strlen(querystr), " %s %s = $%d",
+							querysep, attname, i+1);
 					querysep = "AND";
 					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
 									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
 				}
-				sprintf(buf, " FOR UPDATE OF \"%s\"",
-						tgargs[RI_FK_RELNAME_ARGNO]);
-				strcat(querystr, buf);
+				strcat(querystr, " FOR UPDATE OF x");
 
 				/*
 				 * Prepare, save and remember the new plan.
@@ -909,12 +919,14 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 				elog(ERROR, "%s referential integrity violation - "
 					 "key in %s still referenced from %s",
 					 tgargs[RI_CONSTRAINT_NAME_ARGNO],
-					 tgargs[RI_PK_RELNAME_ARGNO],
-					 tgargs[RI_FK_RELNAME_ARGNO]);
+					 RelationGetRelationName(pk_rel),
+					 RelationGetRelationName(fk_rel));
 
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_upd()");
 
+			heap_close(fk_rel, RowShareLock);
+
 			return PointerGetDatum(NULL);
 
 			/*
@@ -991,11 +1003,14 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 	/*
 	 * Get the relation descriptors of the FK and PK tables and the old
 	 * tuple.
+	 *
+	 * fk_rel is opened in RowExclusiveLock mode since that's what our
+	 * eventual DELETE will get on it.
 	 */
-	fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
-	fk_owner = RelationGetForm(fk_rel)->relowner;
+	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
 	old_row = trigdata->tg_trigtuple;
+	fk_owner = RelationGetForm(fk_rel)->relowner;
 
 	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
 	{
@@ -1022,7 +1037,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 					 * No check - MATCH FULL means there cannot be any
 					 * reference to old key if it contains NULL
 					 */
-					heap_close(fk_rel, NoLock);
+					heap_close(fk_rel, RowExclusiveLock);
 					return PointerGetDatum(NULL);
 
 				case RI_KEYS_NONE_NULL:
@@ -1032,7 +1047,6 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 					 */
 					break;
 			}
-			heap_close(fk_rel, NoLock);
 
 			if (SPI_connect() != SPI_OK_CONNECT)
 				elog(WARNING, "SPI_connect() failed in RI_FKey_cascade_del()");
@@ -1042,9 +1056,11 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 			 */
 			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
-				char		buf[256];
-				char		querystr[8192];
-				char	   *querysep;
+				char		querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
+									(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
+				char		attname[MAX_QUOTED_NAME_LEN];
+				const char *querysep;
 				Oid			queryoids[RI_MAX_NUMKEYS];
 
 				/* ----------
@@ -1056,14 +1072,15 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 				 * how to compare these two types by '='.
 				 * ----------
 				 */
-				sprintf(querystr, "DELETE FROM ONLY \"%s\"",
-						tgargs[RI_FK_RELNAME_ARGNO]);
+				quoteRelationName(fkrelname, fk_rel);
+				sprintf(querystr, "DELETE FROM ONLY %s", fkrelname);
 				querysep = "WHERE";
 				for (i = 0; i < qkey.nkeypairs; i++)
 				{
-					sprintf(buf, " %s \"%s\" = $%d", querysep,
-							tgargs[4 + i * 2], i + 1);
-					strcat(querystr, buf);
+					quoteOneName(attname,
+								 tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
+					sprintf(querystr + strlen(querystr), " %s %s = $%d",
+							querysep, attname, i+1);
 					querysep = "AND";
 					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
 									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
@@ -1108,6 +1125,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_del()");
 
+			heap_close(fk_rel, RowExclusiveLock);
+
 			return PointerGetDatum(NULL);
 
 			/*
@@ -1186,12 +1205,15 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 	/*
 	 * Get the relation descriptors of the FK and PK tables and the new
 	 * and old tuple.
+	 *
+	 * fk_rel is opened in RowExclusiveLock mode since that's what our
+	 * eventual UPDATE will get on it.
 	 */
-	fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
-	fk_owner = RelationGetForm(fk_rel)->relowner;
+	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
 	new_row = trigdata->tg_newtuple;
 	old_row = trigdata->tg_trigtuple;
+	fk_owner = RelationGetForm(fk_rel)->relowner;
 
 	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
 	{
@@ -1218,7 +1240,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 					 * No update - MATCH FULL means there cannot be any
 					 * reference to old key if it contains NULL
 					 */
-					heap_close(fk_rel, NoLock);
+					heap_close(fk_rel, RowExclusiveLock);
 					return PointerGetDatum(NULL);
 
 				case RI_KEYS_NONE_NULL:
@@ -1228,14 +1250,16 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 					 */
 					break;
 			}
-			heap_close(fk_rel, NoLock);
 
 			/*
 			 * No need to do anything if old and new keys are equal
 			 */
 			if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
 							 RI_KEYPAIR_PK_IDX))
+			{
+				heap_close(fk_rel, RowExclusiveLock);
 				return PointerGetDatum(NULL);
+			}
 
 			if (SPI_connect() != SPI_OK_CONNECT)
 				elog(WARNING, "SPI_connect() failed in RI_FKey_cascade_upd()");
@@ -1246,11 +1270,13 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 			 */
 			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
-				char		buf[256];
-				char		querystr[8192];
-				char		qualstr[8192];
-				char	   *querysep;
-				char	   *qualsep;
+				char		querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
+									(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2];
+				char		qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
+				char		attname[MAX_QUOTED_NAME_LEN];
+				const char *querysep;
+				const char *qualsep;
 				Oid			queryoids[RI_MAX_NUMKEYS * 2];
 
 				/* ----------
@@ -1263,19 +1289,19 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 				 * how to compare these two types by '='.
 				 * ----------
 				 */
-				sprintf(querystr, "UPDATE ONLY \"%s\" SET",
-						tgargs[RI_FK_RELNAME_ARGNO]);
+				quoteRelationName(fkrelname, fk_rel);
+				sprintf(querystr, "UPDATE ONLY %s SET", fkrelname);
 				qualstr[0] = '\0';
 				querysep = "";
 				qualsep = "WHERE";
 				for (i = 0, j = qkey.nkeypairs; i < qkey.nkeypairs; i++, j++)
 				{
-					sprintf(buf, "%s \"%s\" = $%d", querysep,
-							tgargs[4 + i * 2], i + 1);
-					strcat(querystr, buf);
-					sprintf(buf, " %s \"%s\" = $%d", qualsep,
-							tgargs[4 + i * 2], j + 1);
-					strcat(qualstr, buf);
+					quoteOneName(attname,
+								 tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
+					sprintf(querystr + strlen(querystr), "%s %s = $%d",
+							querysep, attname, i+1);
+					sprintf(qualstr + strlen(qualstr), " %s %s = $%d",
+							qualsep, attname, j+1);
 					querysep = ",";
 					qualsep = "AND";
 					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
@@ -1332,6 +1358,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_upd()");
 
+			heap_close(fk_rel, RowExclusiveLock);
+
 			return PointerGetDatum(NULL);
 
 			/*
@@ -1415,11 +1443,14 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 	/*
 	 * Get the relation descriptors of the FK and PK tables and the old
 	 * tuple.
+	 *
+	 * fk_rel is opened in RowShareLock mode since that's what our
+	 * eventual SELECT FOR UPDATE will get on it.
 	 */
-	fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
-	fk_owner = RelationGetForm(fk_rel)->relowner;
+	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
 	pk_rel = trigdata->tg_relation;
 	old_row = trigdata->tg_trigtuple;
+	fk_owner = RelationGetForm(fk_rel)->relowner;
 
 	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
 	{
@@ -1446,7 +1477,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 					 * No check - MATCH FULL means there cannot be any
 					 * reference to old key if it contains NULL
 					 */
-					heap_close(fk_rel, NoLock);
+					heap_close(fk_rel, RowShareLock);
 					return PointerGetDatum(NULL);
 
 				case RI_KEYS_NONE_NULL:
@@ -1456,7 +1487,6 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 					 */
 					break;
 			}
-			heap_close(fk_rel, NoLock);
 
 			if (SPI_connect() != SPI_OK_CONNECT)
 				elog(WARNING, "SPI_connect() failed in RI_FKey_restrict_del()");
@@ -1467,9 +1497,11 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 			 */
 			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
-				char		buf[256];
-				char		querystr[8192];
-				char	   *querysep;
+				char		querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
+									(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
+				char		attname[MAX_QUOTED_NAME_LEN];
+				const char *querysep;
 				Oid			queryoids[RI_MAX_NUMKEYS];
 
 				/* ----------
@@ -1481,21 +1513,20 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 				 * how to compare these two types by '='.
 				 * ----------
 				 */
-				sprintf(querystr, "SELECT 1 FROM ONLY \"%s\"",
-						tgargs[RI_FK_RELNAME_ARGNO]);
+				quoteRelationName(fkrelname, fk_rel);
+				sprintf(querystr, "SELECT 1 FROM ONLY %s x", fkrelname);
 				querysep = "WHERE";
 				for (i = 0; i < qkey.nkeypairs; i++)
 				{
-					sprintf(buf, " %s \"%s\" = $%d", querysep,
-							tgargs[4 + i * 2], i + 1);
-					strcat(querystr, buf);
+					quoteOneName(attname,
+								 tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
+					sprintf(querystr + strlen(querystr), " %s %s = $%d",
+							querysep, attname, i+1);
 					querysep = "AND";
 					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
 									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
 				}
-				sprintf(buf, " FOR UPDATE OF \"%s\"",
-						tgargs[RI_FK_RELNAME_ARGNO]);
-				strcat(querystr, buf);
+				strcat(querystr, " FOR UPDATE OF x");
 
 				/*
 				 * Prepare, save and remember the new plan.
@@ -1537,12 +1568,14 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 				elog(ERROR, "%s referential integrity violation - "
 					 "key in %s still referenced from %s",
 					 tgargs[RI_CONSTRAINT_NAME_ARGNO],
-					 tgargs[RI_PK_RELNAME_ARGNO],
-					 tgargs[RI_FK_RELNAME_ARGNO]);
+					 RelationGetRelationName(pk_rel),
+					 RelationGetRelationName(fk_rel));
 
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_del()");
 
+			heap_close(fk_rel, RowShareLock);
+
 			return PointerGetDatum(NULL);
 
 			/*
@@ -1627,12 +1660,15 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 	/*
 	 * Get the relation descriptors of the FK and PK tables and the new
 	 * and old tuple.
+	 *
+	 * fk_rel is opened in RowShareLock mode since that's what our
+	 * eventual SELECT FOR UPDATE will get on it.
 	 */
-	fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
-	fk_owner = RelationGetForm(fk_rel)->relowner;
+	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
 	pk_rel = trigdata->tg_relation;
 	new_row = trigdata->tg_newtuple;
 	old_row = trigdata->tg_trigtuple;
+	fk_owner = RelationGetForm(fk_rel)->relowner;
 
 	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
 	{
@@ -1659,7 +1695,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 					 * No check - MATCH FULL means there cannot be any
 					 * reference to old key if it contains NULL
 					 */
-					heap_close(fk_rel, NoLock);
+					heap_close(fk_rel, RowShareLock);
 					return PointerGetDatum(NULL);
 
 				case RI_KEYS_NONE_NULL:
@@ -1669,14 +1705,16 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 					 */
 					break;
 			}
-			heap_close(fk_rel, NoLock);
 
 			/*
 			 * No need to check anything if old and new keys are equal
 			 */
 			if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
 							 RI_KEYPAIR_PK_IDX))
+			{
+				heap_close(fk_rel, RowShareLock);
 				return PointerGetDatum(NULL);
+			}
 
 			if (SPI_connect() != SPI_OK_CONNECT)
 				elog(WARNING, "SPI_connect() failed in RI_FKey_restrict_upd()");
@@ -1687,9 +1725,11 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 			 */
 			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
-				char		buf[256];
-				char		querystr[8192];
-				char	   *querysep;
+				char		querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
+									(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
+				char		attname[MAX_QUOTED_NAME_LEN];
+				const char *querysep;
 				Oid			queryoids[RI_MAX_NUMKEYS];
 
 				/* ----------
@@ -1701,21 +1741,20 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 				 * how to compare these two types by '='.
 				 * ----------
 				 */
-				sprintf(querystr, "SELECT 1 FROM ONLY \"%s\"",
-						tgargs[RI_FK_RELNAME_ARGNO]);
+				quoteRelationName(fkrelname, fk_rel);
+				sprintf(querystr, "SELECT 1 FROM ONLY %s x", fkrelname);
 				querysep = "WHERE";
 				for (i = 0; i < qkey.nkeypairs; i++)
 				{
-					sprintf(buf, " %s \"%s\" = $%d", querysep,
-							tgargs[4 + i * 2], i + 1);
-					strcat(querystr, buf);
+					quoteOneName(attname,
+								 tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
+					sprintf(querystr + strlen(querystr), " %s %s = $%d",
+							querysep, attname, i+1);
 					querysep = "AND";
 					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
 									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
 				}
-				sprintf(buf, " FOR UPDATE OF \"%s\"",
-						tgargs[RI_FK_RELNAME_ARGNO]);
-				strcat(querystr, buf);
+				strcat(querystr, " FOR UPDATE OF x");
 
 				/*
 				 * Prepare, save and remember the new plan.
@@ -1759,12 +1798,14 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 				elog(ERROR, "%s referential integrity violation - "
 					 "key in %s still referenced from %s",
 					 tgargs[RI_CONSTRAINT_NAME_ARGNO],
-					 tgargs[RI_PK_RELNAME_ARGNO],
-					 tgargs[RI_FK_RELNAME_ARGNO]);
+					 RelationGetRelationName(pk_rel),
+					 RelationGetRelationName(fk_rel));
 
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_upd()");
 
+			heap_close(fk_rel, RowShareLock);
+
 			return PointerGetDatum(NULL);
 
 			/*
@@ -1841,11 +1882,14 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 	/*
 	 * Get the relation descriptors of the FK and PK tables and the old
 	 * tuple.
+	 *
+	 * fk_rel is opened in RowExclusiveLock mode since that's what our
+	 * eventual UPDATE will get on it.
 	 */
-	fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
-	fk_owner = RelationGetForm(fk_rel)->relowner;
+	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
 	old_row = trigdata->tg_trigtuple;
+	fk_owner = RelationGetForm(fk_rel)->relowner;
 
 	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
 	{
@@ -1872,7 +1916,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 					 * No update - MATCH FULL means there cannot be any
 					 * reference to old key if it contains NULL
 					 */
-					heap_close(fk_rel, NoLock);
+					heap_close(fk_rel, RowExclusiveLock);
 					return PointerGetDatum(NULL);
 
 				case RI_KEYS_NONE_NULL:
@@ -1882,7 +1926,6 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 					 */
 					break;
 			}
-			heap_close(fk_rel, NoLock);
 
 			if (SPI_connect() != SPI_OK_CONNECT)
 				elog(WARNING, "SPI_connect() failed in RI_FKey_setnull_del()");
@@ -1893,11 +1936,13 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 			 */
 			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
-				char		buf[256];
-				char		querystr[8192];
-				char		qualstr[8192];
-				char	   *querysep;
-				char	   *qualsep;
+				char		querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
+									(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2];
+				char		qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
+				char		attname[MAX_QUOTED_NAME_LEN];
+				const char *querysep;
+				const char *qualsep;
 				Oid			queryoids[RI_MAX_NUMKEYS];
 
 				/* ----------
@@ -1910,19 +1955,19 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 				 * how to compare these two types by '='.
 				 * ----------
 				 */
-				sprintf(querystr, "UPDATE ONLY \"%s\" SET",
-						tgargs[RI_FK_RELNAME_ARGNO]);
+				quoteRelationName(fkrelname, fk_rel);
+				sprintf(querystr, "UPDATE ONLY %s SET", fkrelname);
 				qualstr[0] = '\0';
 				querysep = "";
 				qualsep = "WHERE";
 				for (i = 0; i < qkey.nkeypairs; i++)
 				{
-					sprintf(buf, "%s \"%s\" = NULL", querysep,
-							tgargs[4 + i * 2]);
-					strcat(querystr, buf);
-					sprintf(buf, " %s \"%s\" = $%d", qualsep,
-							tgargs[4 + i * 2], i + 1);
-					strcat(qualstr, buf);
+					quoteOneName(attname,
+								 tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
+					sprintf(querystr + strlen(querystr), "%s %s = NULL",
+							querysep, attname);
+					sprintf(qualstr + strlen(qualstr), " %s %s = $%d",
+							qualsep, attname, i+1);
 					querysep = ",";
 					qualsep = "AND";
 					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
@@ -1969,6 +2014,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_del()");
 
+			heap_close(fk_rel, RowExclusiveLock);
+
 			return PointerGetDatum(NULL);
 
 			/*
@@ -2048,13 +2095,16 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 	/*
 	 * Get the relation descriptors of the FK and PK tables and the old
 	 * tuple.
+	 *
+	 * fk_rel is opened in RowExclusiveLock mode since that's what our
+	 * eventual UPDATE will get on it.
 	 */
-	fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
-	fk_owner = RelationGetForm(fk_rel)->relowner;
+	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
 	new_row = trigdata->tg_newtuple;
 	old_row = trigdata->tg_trigtuple;
 	match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);
+	fk_owner = RelationGetForm(fk_rel)->relowner;
 
 	switch (match_type)
 	{
@@ -2081,7 +2131,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 					 * No update - MATCH FULL means there cannot be any
 					 * reference to old key if it contains NULL
 					 */
-					heap_close(fk_rel, NoLock);
+					heap_close(fk_rel, RowExclusiveLock);
 					return PointerGetDatum(NULL);
 
 				case RI_KEYS_NONE_NULL:
@@ -2091,15 +2141,16 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 					 */
 					break;
 			}
-			heap_close(fk_rel, NoLock);
-
 
 			/*
 			 * No need to do anything if old and new keys are equal
 			 */
 			if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
 							 RI_KEYPAIR_PK_IDX))
+			{
+				heap_close(fk_rel, RowExclusiveLock);
 				return PointerGetDatum(NULL);
+			}
 
 			if (SPI_connect() != SPI_OK_CONNECT)
 				elog(WARNING, "SPI_connect() failed in RI_FKey_setnull_upd()");
@@ -2129,11 +2180,13 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 			if (!use_cached_query ||
 				(qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
 			{
-				char		buf[256];
-				char		querystr[8192];
-				char		qualstr[8192];
-				char	   *querysep;
-				char	   *qualsep;
+				char		querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
+									(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2];
+				char		qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
+				char		attname[MAX_QUOTED_NAME_LEN];
+				const char *querysep;
+				const char *qualsep;
 				Oid			queryoids[RI_MAX_NUMKEYS];
 
 				/* ----------
@@ -2146,13 +2199,15 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 				 * how to compare these two types by '='.
 				 * ----------
 				 */
-				sprintf(querystr, "UPDATE ONLY \"%s\" SET",
-						tgargs[RI_FK_RELNAME_ARGNO]);
+				quoteRelationName(fkrelname, fk_rel);
+				sprintf(querystr, "UPDATE ONLY %s SET", fkrelname);
 				qualstr[0] = '\0';
 				querysep = "";
 				qualsep = "WHERE";
 				for (i = 0; i < qkey.nkeypairs; i++)
 				{
+					quoteOneName(attname,
+								 tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
 					/*
 					 * MATCH <unspecified> - only change columns
 					 * corresponding to changed columns in pk_rel's key
@@ -2161,14 +2216,12 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 					  !ri_OneKeyEqual(pk_rel, i, old_row, new_row, &qkey,
 									  RI_KEYPAIR_PK_IDX))
 					{
-						sprintf(buf, "%s \"%s\" = NULL", querysep,
-								tgargs[4 + i * 2]);
-						strcat(querystr, buf);
+						sprintf(querystr + strlen(querystr), "%s %s = NULL",
+								querysep, attname);
 						querysep = ",";
 					}
-					sprintf(buf, " %s \"%s\" = $%d", qualsep,
-							tgargs[4 + i * 2], i + 1);
-					strcat(qualstr, buf);
+					sprintf(qualstr + strlen(qualstr), " %s %s = $%d",
+							qualsep, attname, i+1);
 					qualsep = "AND";
 					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
 									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
@@ -2222,6 +2275,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_upd()");
 
+			heap_close(fk_rel, RowExclusiveLock);
+
 			return PointerGetDatum(NULL);
 
 			/*
@@ -2298,11 +2353,14 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 	/*
 	 * Get the relation descriptors of the FK and PK tables and the old
 	 * tuple.
+	 *
+	 * fk_rel is opened in RowExclusiveLock mode since that's what our
+	 * eventual UPDATE will get on it.
 	 */
-	fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
-	fk_owner = RelationGetForm(fk_rel)->relowner;
+	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
 	old_row = trigdata->tg_trigtuple;
+	fk_owner = RelationGetForm(fk_rel)->relowner;
 
 	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
 	{
@@ -2329,7 +2387,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 					 * No update - MATCH FULL means there cannot be any
 					 * reference to old key if it contains NULL
 					 */
-					heap_close(fk_rel, NoLock);
+					heap_close(fk_rel, RowExclusiveLock);
 					return PointerGetDatum(NULL);
 
 				case RI_KEYS_NONE_NULL:
@@ -2344,16 +2402,18 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 				elog(WARNING, "SPI_connect() failed in RI_FKey_setdefault_del()");
 
 			/*
-			 * Prepare a plan for the set defalt delete operation.
+			 * Prepare a plan for the set default delete operation.
 			 * Unfortunately we need to do it on every invocation because
 			 * the default value could potentially change between calls.
 			 */
 			{
-				char		buf[256];
-				char		querystr[8192];
-				char		qualstr[8192];
-				char	   *querysep;
-				char	   *qualsep;
+				char		querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
+									(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2];
+				char		qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
+				char		attname[MAX_QUOTED_NAME_LEN];
+				const char *querysep;
+				const char *qualsep;
 				Oid			queryoids[RI_MAX_NUMKEYS];
 				Plan	   *spi_plan;
 				AttrDefault *defval;
@@ -2371,19 +2431,19 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 				 * how to compare these two types by '='.
 				 * ----------
 				 */
-				sprintf(querystr, "UPDATE ONLY \"%s\" SET",
-						tgargs[RI_FK_RELNAME_ARGNO]);
+				quoteRelationName(fkrelname, fk_rel);
+				sprintf(querystr, "UPDATE ONLY %s SET", fkrelname);
 				qualstr[0] = '\0';
 				querysep = "";
 				qualsep = "WHERE";
 				for (i = 0; i < qkey.nkeypairs; i++)
 				{
-					sprintf(buf, "%s \"%s\" = NULL", querysep,
-							tgargs[4 + i * 2]);
-					strcat(querystr, buf);
-					sprintf(buf, " %s \"%s\" = $%d", qualsep,
-							tgargs[4 + i * 2], i + 1);
-					strcat(qualstr, buf);
+					quoteOneName(attname,
+								 tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
+					sprintf(querystr + strlen(querystr), "%s %s = NULL",
+							querysep, attname);
+					sprintf(qualstr + strlen(qualstr), " %s %s = $%d",
+							qualsep, attname, i+1);
 					querysep = ",";
 					qualsep = "AND";
 					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
@@ -2437,8 +2497,6 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 					}
 				}
 			}
-			/* fk_rel is no longer needed OK ? */
-			heap_close(fk_rel, NoLock);
 
 			/*
 			 * We have a plan now. Build up the arguments for SPI_execp()
@@ -2471,6 +2529,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_del()");
 
+			heap_close(fk_rel, RowExclusiveLock);
+
 			return PointerGetDatum(NULL);
 
 			/*
@@ -2549,12 +2609,15 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 	/*
 	 * Get the relation descriptors of the FK and PK tables and the old
 	 * tuple.
+	 *
+	 * fk_rel is opened in RowExclusiveLock mode since that's what our
+	 * eventual UPDATE will get on it.
 	 */
-	fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
-	fk_owner = RelationGetForm(fk_rel)->relowner;
+	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
 	pk_rel = trigdata->tg_relation;
 	new_row = trigdata->tg_newtuple;
 	old_row = trigdata->tg_trigtuple;
+	fk_owner = RelationGetForm(fk_rel)->relowner;
 
 	match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);
 
@@ -2583,7 +2646,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 					 * No update - MATCH FULL means there cannot be any
 					 * reference to old key if it contains NULL
 					 */
-					heap_close(fk_rel, NoLock);
+					heap_close(fk_rel, RowExclusiveLock);
 					return PointerGetDatum(NULL);
 
 				case RI_KEYS_NONE_NULL:
@@ -2599,22 +2662,27 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 			 */
 			if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
 							 RI_KEYPAIR_PK_IDX))
+			{
+				heap_close(fk_rel, RowExclusiveLock);
 				return PointerGetDatum(NULL);
+			}
 
 			if (SPI_connect() != SPI_OK_CONNECT)
 				elog(WARNING, "SPI_connect() failed in RI_FKey_setdefault_upd()");
 
 			/*
-			 * Prepare a plan for the set defalt delete operation.
+			 * Prepare a plan for the set default delete operation.
 			 * Unfortunately we need to do it on every invocation because
 			 * the default value could potentially change between calls.
 			 */
 			{
-				char		buf[256];
-				char		querystr[8192];
-				char		qualstr[8192];
-				char	   *querysep;
-				char	   *qualsep;
+				char		querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
+									(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2];
+				char		qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];
+				char		attname[MAX_QUOTED_NAME_LEN];
+				const char *querysep;
+				const char *qualsep;
 				Oid			queryoids[RI_MAX_NUMKEYS];
 				Plan	   *spi_plan;
 				AttrDefault *defval;
@@ -2632,13 +2700,15 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 				 * how to compare these two types by '='.
 				 * ----------
 				 */
-				sprintf(querystr, "UPDATE ONLY \"%s\" SET",
-						tgargs[RI_FK_RELNAME_ARGNO]);
+				quoteRelationName(fkrelname, fk_rel);
+				sprintf(querystr, "UPDATE ONLY %s SET", fkrelname);
 				qualstr[0] = '\0';
 				querysep = "";
 				qualsep = "WHERE";
 				for (i = 0; i < qkey.nkeypairs; i++)
 				{
+					quoteOneName(attname,
+								 tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
 					/*
 					 * MATCH <unspecified> - only change columns
 					 * corresponding to changed columns in pk_rel's key
@@ -2647,14 +2717,12 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 						!ri_OneKeyEqual(pk_rel, i, old_row,
 									  new_row, &qkey, RI_KEYPAIR_PK_IDX))
 					{
-						sprintf(buf, "%s \"%s\" = NULL", querysep,
-								tgargs[4 + i * 2]);
-						strcat(querystr, buf);
+						sprintf(querystr + strlen(querystr), "%s %s = NULL",
+								querysep, attname);
 						querysep = ",";
 					}
-					sprintf(buf, " %s \"%s\" = $%d", qualsep,
-							tgargs[4 + i * 2], i + 1);
-					strcat(qualstr, buf);
+					sprintf(qualstr + strlen(qualstr), " %s %s = $%d",
+							qualsep, attname, i+1);
 					qualsep = "AND";
 					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
 									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
@@ -2713,8 +2781,6 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 					}
 				}
 			}
-			/* fk_rel is no longer needed OK ? */
-			heap_close(fk_rel, NoLock);
 
 			/*
 			 * We have a plan now. Build up the arguments for SPI_execp()
@@ -2747,6 +2813,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
 			if (SPI_finish() != SPI_OK_FINISH)
 				elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_upd()");
 
+			heap_close(fk_rel, RowExclusiveLock);
+
 			return PointerGetDatum(NULL);
 
 			/*
@@ -2805,8 +2873,10 @@ RI_FKey_keyequal_upd(TriggerData *trigdata)
 	/*
 	 * Get the relation descriptors of the FK and PK tables and the new
 	 * and old tuple.
+	 *
+	 * Use minimal locking for fk_rel here.
 	 */
-	fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
+	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, AccessShareLock);
 	pk_rel = trigdata->tg_relation;
 	new_row = trigdata->tg_newtuple;
 	old_row = trigdata->tg_trigtuple;
@@ -2822,7 +2892,8 @@ RI_FKey_keyequal_upd(TriggerData *trigdata)
 								 0,
 								 fk_rel, pk_rel,
 								 tgnargs, tgargs);
-			heap_close(fk_rel, NoLock);
+
+			heap_close(fk_rel, AccessShareLock);
 
 			/*
 			 * Return if key's are equal
@@ -2855,7 +2926,50 @@ RI_FKey_keyequal_upd(TriggerData *trigdata)
  */
 
 
+/*
+ * quoteOneName --- safely quote a single SQL name
+ *
+ * buffer must be MAX_QUOTED_NAME_LEN long (includes room for \0)
+ */
+static void
+quoteOneName(char *buffer, const char *name)
+{
+	/* Rather than trying to be smart, just always quote it. */
+	*buffer++ = '"';
+	while (*name)
+	{
+		if (*name == '"')
+			*buffer++ = '"';
+		*buffer++ = *name++;
+	}
+	*buffer++ = '"';
+	*buffer = '\0';
+}
 
+/*
+ * quoteRelationName --- safely quote a fully qualified relation name
+ *
+ * buffer must be MAX_QUOTED_REL_NAME_LEN long (includes room for \0)
+ */
+static void
+quoteRelationName(char *buffer, Relation rel)
+{
+	HeapTuple	tuple;
+	char	   *nsname;
+
+	tuple = SearchSysCache(NAMESPACEOID,
+						   ObjectIdGetDatum(RelationGetNamespace(rel)),
+						   0, 0, 0);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "Failed to lookup namespace %u of relation %s",
+			 RelationGetNamespace(rel), RelationGetRelationName(rel));
+	nsname = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname);
+	quoteOneName(buffer, nsname);
+	ReleaseSysCache(tuple);
+	buffer += strlen(buffer);
+	*buffer++ = '.';
+	quoteOneName(buffer, RelationGetRelationName(rel));
+}
 
 
 /* ----------
@@ -2867,11 +2981,11 @@ RI_FKey_keyequal_upd(TriggerData *trigdata)
 static int
 ri_DetermineMatchType(char *str)
 {
-	if (!strcmp(str, "UNSPECIFIED"))
+	if (strcmp(str, "UNSPECIFIED") == 0)
 		return RI_MATCH_TYPE_UNSPECIFIED;
-	if (!strcmp(str, "FULL"))
+	if (strcmp(str, "FULL") == 0)
 		return RI_MATCH_TYPE_FULL;
-	if (!strcmp(str, "PARTIAL"))
+	if (strcmp(str, "PARTIAL") == 0)
 		return RI_MATCH_TYPE_PARTIAL;
 
 	elog(ERROR, "unrecognized referential integrity MATCH type '%s'", str);
@@ -2926,7 +3040,7 @@ ri_BuildQueryKeyFull(RI_QueryKey *key, Oid constr_id, int32 constr_queryno,
 		if (fno == SPI_ERROR_NOATTRIBUTE)
 			elog(ERROR, "constraint %s: table %s does not have an attribute %s",
 				 argv[RI_CONSTRAINT_NAME_ARGNO],
-				 argv[RI_FK_RELNAME_ARGNO],
+				 RelationGetRelationName(fk_rel),
 				 argv[j]);
 		key->keypair[i][RI_KEYPAIR_FK_IDX] = fno;
 
@@ -2934,12 +3048,10 @@ ri_BuildQueryKeyFull(RI_QueryKey *key, Oid constr_id, int32 constr_queryno,
 		if (fno == SPI_ERROR_NOATTRIBUTE)
 			elog(ERROR, "constraint %s: table %s does not have an attribute %s",
 				 argv[RI_CONSTRAINT_NAME_ARGNO],
-				 argv[RI_PK_RELNAME_ARGNO],
+				 RelationGetRelationName(pk_rel),
 				 argv[j + 1]);
 		key->keypair[i][RI_KEYPAIR_PK_IDX] = fno;
 	}
-
-	return;
 }
 
 
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index c08a4151e3a26b5f1b52a3bc9bcfaed4e3da9e24..4166b47bb74dfd649fc3f760d1d94bbf71e8ec4d 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: trigger.h,v 1.33 2002/03/29 22:10:34 tgl Exp $
+ * $Id: trigger.h,v 1.34 2002/04/01 22:36:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -81,6 +81,12 @@ typedef struct TriggerData
  *   constrname\0fkrel\0pkrel\0matchtype\0fkatt\0pkatt\0fkatt\0pkatt\0...
  *
  * There are one or more pairs of fkatt/pkatt names.
+ *
+ * The relation names are no longer of much use since they are not
+ * guaranteed unique; they are present only for backwards compatibility.
+ * Use the tgrelid and tgconstrrelid fields to identify the referenced
+ * relations, instead.  (But note that which is which will depend on which
+ * trigger you are looking at!)
  */
 #define RI_CONSTRAINT_NAME_ARGNO		0
 #define RI_FK_RELNAME_ARGNO				1
@@ -127,9 +133,8 @@ extern void ExecARUpdateTriggers(EState *estate,
 					 HeapTuple newtuple);
 
 
-/* ----------
+/*
  * Deferred trigger stuff
- * ----------
  */
 typedef struct DeferredTriggerStatusData
 {
@@ -139,14 +144,12 @@ typedef struct DeferredTriggerStatusData
 
 typedef struct DeferredTriggerStatusData *DeferredTriggerStatus;
 
-
 typedef struct DeferredTriggerEventItem
 {
 	Oid			dti_tgoid;
 	int32		dti_state;
 } DeferredTriggerEventItem;
 
-
 typedef struct DeferredTriggerEventData *DeferredTriggerEvent;
 
 typedef struct DeferredTriggerEventData
@@ -173,7 +176,6 @@ extern void DeferredTriggerSetState(ConstraintsSetStmt *stmt);
 
 /*
  * in utils/adt/ri_triggers.c
- *
  */
 extern bool RI_FKey_keyequal_upd(TriggerData *trigdata);
 
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 4a3fbd1eb1534ed3bb3ba959399ccc486c1ee125..4c6e88efbde745c804d487d8d65138ab7742f60b 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: rel.h,v 1.58 2002/03/31 06:26:32 tgl Exp $
+ * $Id: rel.h,v 1.59 2002/04/01 22:36:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,12 +49,14 @@ typedef LockInfoData *LockInfo;
  */
 typedef struct Trigger
 {
-	Oid			tgoid;
+	Oid			tgoid;			/* OID of trigger (pg_trigger row) */
+	/* Remaining fields are copied from pg_trigger, see pg_trigger.h */
 	char	   *tgname;
 	Oid			tgfoid;
 	int16		tgtype;
 	bool		tgenabled;
 	bool		tgisconstraint;
+	Oid			tgconstrrelid;
 	bool		tgdeferrable;
 	bool		tginitdeferred;
 	int16		tgnargs;