diff --git a/contrib/userlock/user_locks.c b/contrib/userlock/user_locks.c
index c27dc253d8..394a4fe190 100644
--- a/contrib/userlock/user_locks.c
+++ b/contrib/userlock/user_locks.c
@@ -35,8 +35,7 @@ user_lock(uint32 id1, uint32 id2, LOCKMODE lockmode)
 
 	SET_LOCKTAG_USERLOCK(tag, id1, id2);
 
-	return (LockAcquire(&tag, false,
-						lockmode, true, true) != LOCKACQUIRE_NOT_AVAIL);
+	return (LockAcquire(&tag, lockmode, true, true) != LOCKACQUIRE_NOT_AVAIL);
 }
 
 int
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index dfb6348e1d..80ed7d829b 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.128 2006/07/27 08:30:41 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.129 2006/07/31 20:08:55 tgl Exp $ -->
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
  -->
@@ -401,13 +401,6 @@
       <entry>Can index storage data type differ from column data type?</entry>
      </row>
 
-     <row>
-      <entry><structfield>amconcurrent</structfield></entry>
-      <entry><type>bool</type></entry>
-      <entry></entry>
-      <entry>Does the access method support concurrent updates?</entry>
-     </row>
-
      <row>
       <entry><structfield>amclusterable</structfield></entry>
       <entry><type>bool</type></entry>
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
index 7febd0c907..1afa120766 100644
--- a/doc/src/sgml/indexam.sgml
+++ b/doc/src/sgml/indexam.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.15 2006/07/03 22:45:36 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.16 2006/07/31 20:08:59 tgl Exp $ -->
 
 <chapter id="indexam">
  <title>Index Access Method Interface Definition</title>
@@ -94,8 +94,7 @@
   <para>
    Some of the flag columns of <structname>pg_am</structname> have nonobvious
    implications.  The requirements of <structfield>amcanunique</structfield>
-   are discussed in <xref linkend="index-unique-checks">, and those of
-   <structfield>amconcurrent</structfield> in <xref linkend="index-locking">.
+   are discussed in <xref linkend="index-unique-checks">.
    The <structfield>amcanmulticol</structfield> flag asserts that the
    access method supports multicolumn indexes, while
    <structfield>amoptionalkey</structfield> asserts that it allows scans
@@ -474,11 +473,7 @@ amrestrpos (IndexScanDesc scan);
    a concurrent delete may or may not be reflected in the results of a scan.
    What is important is that insertions or deletions not cause the scan to
    miss or multiply return entries that were not themselves being inserted or
-   deleted.  (For an index type that does not set
-   <structname>pg_am</>.<structfield>amconcurrent</>, it is sufficient to
-   handle these cases for insertions or deletions performed by the same
-   backend that's doing the scan.  But when <structfield>amconcurrent</> is
-   true, insertions or deletions from other backends must be handled as well.)
+   deleted.
   </para>
 
   <para>
@@ -506,31 +501,16 @@ amrestrpos (IndexScanDesc scan);
   <title>Index Locking Considerations</title>
 
   <para>
-   An index access method can choose whether it supports concurrent updates
-   of the index by multiple processes.  If the method's
-   <structname>pg_am</>.<structfield>amconcurrent</> flag is true, then
-   the core <productname>PostgreSQL</productname> system obtains
+   Index access methods must handle concurrent updates
+   of the index by multiple processes.
+   The core <productname>PostgreSQL</productname> system obtains
    <literal>AccessShareLock</> on the index during an index scan, and
-   <literal>RowExclusiveLock</> when updating the index.  Since these lock
+   <literal>RowExclusiveLock</> when updating the index (including plain
+   <command>VACUUM</>).  Since these lock
    types do not conflict, the access method is responsible for handling any
    fine-grained locking it may need.  An exclusive lock on the index as a whole
-   will be taken only during index creation, destruction, or
-   <literal>REINDEX</>.  When <structfield>amconcurrent</> is false,
-   <productname>PostgreSQL</productname> still obtains
-   <literal>AccessShareLock</> during index scans, but it obtains
-   <literal>AccessExclusiveLock</> during any update.  This ensures that
-   updaters have sole use of the index.  Note that this implicitly assumes
-   that index scans are read-only; an access method that might modify the
-   index during a scan will still have to do its own locking to handle the
-   case of concurrent scans.
-  </para>
-
-  <para>
-   Recall that a backend's own locks never conflict; therefore, even a
-   non-concurrent index type must be prepared to handle the case where
-   a backend is inserting or deleting entries in an index that it is itself
-   scanning.  (This is of course necessary to support an <command>UPDATE</>
-   that uses the index to find the rows to be updated.)
+   will be taken only during index creation, destruction,
+   <command>REINDEX</>, or <command>VACUUM FULL</>.
   </para>
 
   <para>
@@ -567,7 +547,7 @@ amrestrpos (IndexScanDesc scan);
      </listitem>
      <listitem>
       <para>
-       For concurrent index types, an index scan must maintain a pin
+       An index scan must maintain a pin
        on the index page holding the item last returned by
        <function>amgettuple</>, and <function>ambulkdelete</> cannot delete
        entries from pages that are pinned by other backends.  The need
@@ -576,11 +556,10 @@ amrestrpos (IndexScanDesc scan);
      </listitem>
     </itemizedlist>
 
-   If an index is concurrent then it is possible for an index reader to
+   Without the third rule, it is possible for an index reader to
    see an index entry just before it is removed by <command>VACUUM</>, and
    then to arrive at the corresponding heap entry after that was removed by
-   <command>VACUUM</>.  (With a nonconcurrent index, this is not possible
-   because of the conflicting index-level locks that will be taken out.)
+   <command>VACUUM</>.
    This creates no serious problems if that item
    number is still unused when the reader reaches it, since an empty
    item slot will be ignored by <function>heap_fetch()</>.  But what if a
diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c
index c6551939a2..2bc80a2643 100644
--- a/src/backend/access/gin/ginvacuum.c
+++ b/src/backend/access/gin/ginvacuum.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *          $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.4 2006/07/14 14:52:16 momjian Exp $
+ *          $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.5 2006/07/31 20:08:59 tgl Exp $
  *-------------------------------------------------------------------------
  */
 
@@ -572,7 +572,7 @@ ginvacuumcleanup(PG_FUNCTION_ARGS) {
 	IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
 	IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
 	Relation    index = info->index;
-	bool	 needLock = !RELATION_IS_LOCAL(index);
+	bool	 needLock;
     BlockNumber npages,
 				blkno;
 	BlockNumber nFreePages,
@@ -591,10 +591,14 @@ ginvacuumcleanup(PG_FUNCTION_ARGS) {
 	 */
 	stats->num_index_tuples = info->num_heap_tuples;
 
-	if (info->vacuum_full) {
-		LockRelation(index, AccessExclusiveLock);
+	/*
+	 * If vacuum full, we already have exclusive lock on the index.
+	 * Otherwise, need lock unless it's local to this backend.
+	 */
+	if (info->vacuum_full)
 		needLock = false;
-	}
+	else
+		needLock = !RELATION_IS_LOCAL(index);
 
 	if (needLock)
 		LockRelationForExtension(index, ExclusiveLock);
@@ -653,9 +657,6 @@ ginvacuumcleanup(PG_FUNCTION_ARGS) {
 	if (needLock)
 		UnlockRelationForExtension(index, ExclusiveLock);
 
-	if (info->vacuum_full)
-		UnlockRelation(index, AccessExclusiveLock);
-
 	PG_RETURN_POINTER(stats);
 }
 
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c
index e3bff5147c..37b5631b28 100644
--- a/src/backend/access/gist/gistvacuum.c
+++ b/src/backend/access/gist/gistvacuum.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.25 2006/07/14 14:52:16 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.26 2006/07/31 20:08:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -517,7 +517,7 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
 		GistVacuum	gv;
 		ArrayTuple	res;
 
-		LockRelation(rel, AccessExclusiveLock);
+		/* note: vacuum.c already acquired AccessExclusiveLock on index */
 
 		gv.index = rel;
 		initGISTstate(&(gv.giststate), rel);
@@ -543,8 +543,12 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
 				(errmsg("index \"%s\" needs VACUUM FULL or REINDEX to finish crash recovery",
 						RelationGetRelationName(rel))));
 
+	/*
+	 * If vacuum full, we already have exclusive lock on the index.
+	 * Otherwise, need lock unless it's local to this backend.
+	 */
 	if (info->vacuum_full)
-		needLock = false;		/* relation locked with AccessExclusiveLock */
+		needLock = false;
 	else
 		needLock = !RELATION_IS_LOCAL(rel);
 
@@ -613,9 +617,6 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
 	if (needLock)
 		UnlockRelationForExtension(rel, ExclusiveLock);
 
-	if (info->vacuum_full)
-		UnlockRelation(rel, AccessExclusiveLock);
-
 	PG_RETURN_POINTER(stats);
 }
 
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 91460410bf..f29407e8e5 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.217 2006/07/14 14:52:17 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.218 2006/07/31 20:08:59 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -51,6 +51,7 @@
 #include "pgstat.h"
 #include "storage/procarray.h"
 #include "utils/inval.h"
+#include "utils/lsyscache.h"
 #include "utils/relcache.h"
 
 
@@ -687,15 +688,16 @@ relation_open(Oid relationId, LOCKMODE lockmode)
 
 	Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES);
 
+	/* Get the lock before trying to open the relcache entry */
+	if (lockmode != NoLock)
+		LockRelationOid(relationId, lockmode);
+
 	/* The relcache does all the real work... */
 	r = RelationIdGetRelation(relationId);
 
 	if (!RelationIsValid(r))
 		elog(ERROR, "could not open relation with OID %u", relationId);
 
-	if (lockmode != NoLock)
-		LockRelation(r, lockmode);
-
 	return r;
 }
 
@@ -713,26 +715,38 @@ conditional_relation_open(Oid relationId, LOCKMODE lockmode, bool nowait)
 
 	Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES);
 
+	/* Get the lock before trying to open the relcache entry */
+	if (lockmode != NoLock)
+	{
+		if (nowait)
+		{
+			if (!ConditionalLockRelationOid(relationId, lockmode))
+			{
+				/* try to throw error by name; relation could be deleted... */
+				char   *relname = get_rel_name(relationId);
+
+				if (relname)
+					ereport(ERROR,
+							(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+							 errmsg("could not obtain lock on relation \"%s\"",
+									relname)));
+				else
+					ereport(ERROR,
+							(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+							 errmsg("could not obtain lock on relation with OID %u",
+									relationId)));
+			}
+		}
+		else
+			LockRelationOid(relationId, lockmode);
+	}
+
 	/* The relcache does all the real work... */
 	r = RelationIdGetRelation(relationId);
 
 	if (!RelationIsValid(r))
 		elog(ERROR, "could not open relation with OID %u", relationId);
 
-	if (lockmode != NoLock)
-	{
-		if (nowait)
-		{
-			if (!ConditionalLockRelation(r, lockmode))
-				ereport(ERROR,
-						(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
-						 errmsg("could not obtain lock on relation \"%s\"",
-								RelationGetRelationName(r))));
-		}
-		else
-			LockRelation(r, lockmode);
-	}
-
 	return r;
 }
 
@@ -749,12 +763,12 @@ relation_openrv(const RangeVar *relation, LOCKMODE lockmode)
 
 	/*
 	 * Check for shared-cache-inval messages before trying to open the
-	 * relation.  This is needed to cover the case where the name identifies a
-	 * rel that has been dropped and recreated since the start of our
+	 * relation.  This is needed to cover the case where the name identifies
+	 * a rel that has been dropped and recreated since the start of our
 	 * transaction: if we don't flush the old syscache entry then we'll latch
-	 * onto that entry and suffer an error when we do LockRelation. Note that
-	 * relation_open does not need to do this, since a relation's OID never
-	 * changes.
+	 * onto that entry and suffer an error when we do RelationIdGetRelation.
+	 * Note that relation_open does not need to do this, since a relation's
+	 * OID never changes.
 	 *
 	 * We skip this if asked for NoLock, on the assumption that the caller has
 	 * already ensured some appropriate lock is held.
@@ -772,7 +786,7 @@ relation_openrv(const RangeVar *relation, LOCKMODE lockmode)
 /* ----------------
  *		relation_close - close any relation
  *
- *		If lockmode is not "NoLock", we first release the specified lock.
+ *		If lockmode is not "NoLock", we then release the specified lock.
  *
  *		Note that it is often sensible to hold a lock beyond relation_close;
  *		in that case, the lock is released automatically at xact end.
@@ -781,13 +795,15 @@ relation_openrv(const RangeVar *relation, LOCKMODE lockmode)
 void
 relation_close(Relation relation, LOCKMODE lockmode)
 {
-	Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES);
+	LockRelId	relid = relation->rd_lockInfo.lockRelId;
 
-	if (lockmode != NoLock)
-		UnlockRelation(relation, lockmode);
+	Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES);
 
 	/* The relcache does the real work... */
 	RelationClose(relation);
+
+	if (lockmode != NoLock)
+		UnlockRelationId(&relid, lockmode);
 }
 
 
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 11adf165c4..43ca366f0a 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.62 2006/07/14 14:52:17 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.63 2006/07/31 20:08:59 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1004,7 +1004,7 @@ toast_save_datum(Relation rel, Datum value)
 	 */
 	toastrel = heap_open(rel->rd_rel->reltoastrelid, RowExclusiveLock);
 	toasttupDesc = toastrel->rd_att;
-	toastidx = index_open(toastrel->rd_rel->reltoastidxid);
+	toastidx = index_open(toastrel->rd_rel->reltoastidxid, RowExclusiveLock);
 
 	/*
 	 * Create the varattrib reference
@@ -1043,12 +1043,6 @@ toast_save_datum(Relation rel, Datum value)
 	data_p = VARATT_DATA(value);
 	data_todo = VARATT_SIZE(value) - VARHDRSZ;
 
-	/*
-	 * We must explicitly lock the toast index because we aren't using an
-	 * index scan here.
-	 */
-	LockRelation(toastidx, RowExclusiveLock);
-
 	/*
 	 * Split up the item into chunks
 	 */
@@ -1098,8 +1092,7 @@ toast_save_datum(Relation rel, Datum value)
 	/*
 	 * Done - close toast relation and return the reference
 	 */
-	UnlockRelation(toastidx, RowExclusiveLock);
-	index_close(toastidx);
+	index_close(toastidx, RowExclusiveLock);
 	heap_close(toastrel, RowExclusiveLock);
 
 	return PointerGetDatum(result);
@@ -1130,7 +1123,7 @@ toast_delete_datum(Relation rel, Datum value)
 	 */
 	toastrel = heap_open(attr->va_content.va_external.va_toastrelid,
 						 RowExclusiveLock);
-	toastidx = index_open(toastrel->rd_rel->reltoastidxid);
+	toastidx = index_open(toastrel->rd_rel->reltoastidxid, RowExclusiveLock);
 
 	/*
 	 * Setup a scan key to fetch from the index by va_valueid (we don't
@@ -1144,7 +1137,7 @@ toast_delete_datum(Relation rel, Datum value)
 	/*
 	 * Find the chunks by index
 	 */
-	toastscan = index_beginscan(toastrel, toastidx, true,
+	toastscan = index_beginscan(toastrel, toastidx,
 								SnapshotToast, 1, &toastkey);
 	while ((toasttup = index_getnext(toastscan, ForwardScanDirection)) != NULL)
 	{
@@ -1158,7 +1151,7 @@ toast_delete_datum(Relation rel, Datum value)
 	 * End scan and close relations
 	 */
 	index_endscan(toastscan);
-	index_close(toastidx);
+	index_close(toastidx, RowExclusiveLock);
 	heap_close(toastrel, RowExclusiveLock);
 }
 
@@ -1202,7 +1195,7 @@ toast_fetch_datum(varattrib *attr)
 	toastrel = heap_open(attr->va_content.va_external.va_toastrelid,
 						 AccessShareLock);
 	toasttupDesc = toastrel->rd_att;
-	toastidx = index_open(toastrel->rd_rel->reltoastidxid);
+	toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock);
 
 	/*
 	 * Setup a scan key to fetch from the index by va_valueid
@@ -1221,7 +1214,7 @@ toast_fetch_datum(varattrib *attr)
 	 */
 	nextidx = 0;
 
-	toastscan = index_beginscan(toastrel, toastidx, true,
+	toastscan = index_beginscan(toastrel, toastidx,
 								SnapshotToast, 1, &toastkey);
 	while ((ttup = index_getnext(toastscan, ForwardScanDirection)) != NULL)
 	{
@@ -1282,7 +1275,7 @@ toast_fetch_datum(varattrib *attr)
 	 * End scan and close relations
 	 */
 	index_endscan(toastscan);
-	index_close(toastidx);
+	index_close(toastidx, AccessShareLock);
 	heap_close(toastrel, AccessShareLock);
 
 	return result;
@@ -1355,7 +1348,7 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
 	toastrel = heap_open(attr->va_content.va_external.va_toastrelid,
 						 AccessShareLock);
 	toasttupDesc = toastrel->rd_att;
-	toastidx = index_open(toastrel->rd_rel->reltoastidxid);
+	toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock);
 
 	/*
 	 * Setup a scan key to fetch from the index. This is either two keys or
@@ -1396,7 +1389,7 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
 	 * The index is on (valueid, chunkidx) so they will come in order
 	 */
 	nextidx = startchunk;
-	toastscan = index_beginscan(toastrel, toastidx, true,
+	toastscan = index_beginscan(toastrel, toastidx,
 								SnapshotToast, nscankeys, toastkey);
 	while ((ttup = index_getnext(toastscan, ForwardScanDirection)) != NULL)
 	{
@@ -1461,7 +1454,7 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
 	 * End scan and close relations
 	 */
 	index_endscan(toastscan);
-	index_close(toastidx);
+	index_close(toastidx, AccessShareLock);
 	heap_close(toastrel, AccessShareLock);
 
 	return result;
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 399386c856..347d2b5365 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.57 2006/07/03 22:45:37 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.58 2006/07/31 20:08:59 tgl Exp $
  *
  * NOTES
  *	  many of the old access method routines have been turned into
@@ -87,7 +87,6 @@ RelationGetIndexScan(Relation indexRelation,
 		scan->keyData = NULL;
 
 	scan->is_multiscan = false;			/* caller may change this */
-	scan->have_lock = false;			/* ditto */
 	scan->kill_prior_tuple = false;
 	scan->ignore_killed_tuples = true;	/* default setting */
 
@@ -182,7 +181,7 @@ systable_beginscan(Relation heapRelation,
 	if (indexOK &&
 		!IgnoreSystemIndexes &&
 		!ReindexIsProcessingIndex(indexId))
-		irel = index_open(indexId);
+		irel = index_open(indexId, AccessShareLock);
 	else
 		irel = NULL;
 
@@ -207,7 +206,7 @@ systable_beginscan(Relation heapRelation,
 			key[i].sk_attno = i + 1;
 		}
 
-		sysscan->iscan = index_beginscan(heapRelation, irel, true,
+		sysscan->iscan = index_beginscan(heapRelation, irel,
 										 snapshot, nkeys, key);
 		sysscan->scan = NULL;
 	}
@@ -253,7 +252,7 @@ systable_endscan(SysScanDesc sysscan)
 	if (sysscan->irel)
 	{
 		index_endscan(sysscan->iscan);
-		index_close(sysscan->irel);
+		index_close(sysscan->irel, AccessShareLock);
 	}
 	else
 		heap_endscan(sysscan->scan);
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 900b34263d..2663876f49 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -8,11 +8,10 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.93 2006/05/07 01:21:30 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.94 2006/07/31 20:08:59 tgl Exp $
  *
  * INTERFACE ROUTINES
  *		index_open		- open an index relation by relation OID
- *		index_openrv	- open an index relation specified by a RangeVar
  *		index_close		- close an index relation
  *		index_beginscan - start a scan of an index with amgettuple
  *		index_beginscan_multi - start a scan of an index with amgetmulti
@@ -111,7 +110,6 @@ do { \
 } while(0)
 
 static IndexScanDesc index_beginscan_internal(Relation indexRelation,
-						 bool need_index_lock,
 						 int nkeys, ScanKey key);
 
 
@@ -123,26 +121,23 @@ static IndexScanDesc index_beginscan_internal(Relation indexRelation,
 /* ----------------
  *		index_open - open an index relation by relation OID
  *
- *		Note: we acquire no lock on the index.	A lock is not needed when
- *		simply examining the index reldesc; the index's schema information
- *		is considered to be protected by the lock that the caller had better
- *		be holding on the parent relation.	Some type of lock should be
- *		obtained on the index before physically accessing it, however.
- *		This is handled automatically for most uses by index_beginscan
- *		and index_endscan for scan cases, or by ExecOpenIndices and
- *		ExecCloseIndices for update cases.	Other callers will need to
- *		obtain their own locks.
+ *		If lockmode is not "NoLock", the specified kind of lock is
+ *		obtained on the index.  (Generally, NoLock should only be
+ *		used if the caller knows it has some appropriate lock on the
+ *		index already.)
+ *
+ *		An error is raised if the index does not exist.
  *
  *		This is a convenience routine adapted for indexscan use.
  *		Some callers may prefer to use relation_open directly.
  * ----------------
  */
 Relation
-index_open(Oid relationId)
+index_open(Oid relationId, LOCKMODE lockmode)
 {
 	Relation	r;
 
-	r = relation_open(relationId, NoLock);
+	r = relation_open(relationId, lockmode);
 
 	if (r->rd_rel->relkind != RELKIND_INDEX)
 		ereport(ERROR,
@@ -156,41 +151,26 @@ index_open(Oid relationId)
 }
 
 /* ----------------
- *		index_openrv - open an index relation specified
- *		by a RangeVar node
+ *		index_close - close an index relation
  *
- *		As above, but relation is specified by a RangeVar.
- * ----------------
- */
-Relation
-index_openrv(const RangeVar *relation)
-{
-	Relation	r;
-
-	r = relation_openrv(relation, NoLock);
-
-	if (r->rd_rel->relkind != RELKIND_INDEX)
-		ereport(ERROR,
-				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("\"%s\" is not an index",
-						RelationGetRelationName(r))));
-
-	pgstat_initstats(&r->pgstat_info, r);
-
-	return r;
-}
-
-/* ----------------
- *		index_close - close a index relation
+ *		If lockmode is not "NoLock", we then release the specified lock.
  *
- *		presently the relcache routines do all the work we need
- *		to open/close index relations.
+ *		Note that it is often sensible to hold a lock beyond index_close;
+ *		in that case, the lock is released automatically at xact end.
  * ----------------
  */
 void
-index_close(Relation relation)
+index_close(Relation relation, LOCKMODE lockmode)
 {
+	LockRelId	relid = relation->rd_lockInfo.lockRelId;
+
+	Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES);
+
+	/* The relcache does the real work... */
 	RelationClose(relation);
+
+	if (lockmode != NoLock)
+		UnlockRelationId(&relid, lockmode);
 }
 
 /* ----------------
@@ -229,24 +209,18 @@ index_insert(Relation indexRelation,
  * index_getnext on this scan; index_getnext_indexitem will not use the
  * heapRelation link (nor the snapshot).  However, the caller had better
  * be holding some kind of lock on the heap relation in any case, to ensure
- * no one deletes it (or the index) out from under us.
- *
- * Most callers should pass need_index_lock = true to cause the index code
- * to take AccessShareLock on the index for the duration of the scan.  But
- * if it is known that a lock is already held on the index, pass false to
- * skip taking an unnecessary lock.
+ * no one deletes it (or the index) out from under us.  Caller must also
+ * be holding a lock on the index.
  */
 IndexScanDesc
 index_beginscan(Relation heapRelation,
 				Relation indexRelation,
-				bool need_index_lock,
 				Snapshot snapshot,
 				int nkeys, ScanKey key)
 {
 	IndexScanDesc scan;
 
-	scan = index_beginscan_internal(indexRelation, need_index_lock,
-									nkeys, key);
+	scan = index_beginscan_internal(indexRelation, nkeys, key);
 
 	/*
 	 * Save additional parameters into the scandesc.  Everything else was set
@@ -267,14 +241,12 @@ index_beginscan(Relation heapRelation,
  */
 IndexScanDesc
 index_beginscan_multi(Relation indexRelation,
-					  bool need_index_lock,
 					  Snapshot snapshot,
 					  int nkeys, ScanKey key)
 {
 	IndexScanDesc scan;
 
-	scan = index_beginscan_internal(indexRelation, need_index_lock,
-									nkeys, key);
+	scan = index_beginscan_internal(indexRelation, nkeys, key);
 
 	/*
 	 * Save additional parameters into the scandesc.  Everything else was set
@@ -291,34 +263,19 @@ index_beginscan_multi(Relation indexRelation,
  */
 static IndexScanDesc
 index_beginscan_internal(Relation indexRelation,
-						 bool need_index_lock,
 						 int nkeys, ScanKey key)
 {
 	IndexScanDesc scan;
 	FmgrInfo   *procedure;
 
 	RELATION_CHECKS;
-
-	RelationIncrementReferenceCount(indexRelation);
-
-	/*
-	 * Acquire AccessShareLock for the duration of the scan, unless caller
-	 * says it already has lock on the index.
-	 *
-	 * Note: we could get an SI inval message here and consequently have to
-	 * rebuild the relcache entry.	The refcount increment above ensures that
-	 * we will rebuild it and not just flush it...
-	 */
-	if (need_index_lock)
-		LockRelation(indexRelation, AccessShareLock);
-
-	/*
-	 * LockRelation can clean rd_aminfo structure, so fill procedure after
-	 * LockRelation
-	 */
-
 	GET_REL_PROCEDURE(ambeginscan);
 
+	/*
+	 * We hold a reference count to the relcache entry throughout the scan.
+	 */
+	RelationIncrementReferenceCount(indexRelation);
+
 	/*
 	 * Tell the AM to open a scan.
 	 */
@@ -328,9 +285,6 @@ index_beginscan_internal(Relation indexRelation,
 									  Int32GetDatum(nkeys),
 									  PointerGetDatum(key)));
 
-	/* Save flag to tell index_endscan whether to release lock */
-	scan->have_lock = need_index_lock;
-
 	return scan;
 }
 
@@ -390,11 +344,7 @@ index_endscan(IndexScanDesc scan)
 	/* End the AM's scan */
 	FunctionCall1(procedure, PointerGetDatum(scan));
 
-	/* Release index lock and refcount acquired by index_beginscan */
-
-	if (scan->have_lock)
-		UnlockRelation(scan->indexRelation, AccessShareLock);
-
+	/* Release index refcount acquired by index_beginscan */
 	RelationDecrementReferenceCount(scan->indexRelation);
 
 	/* Release the scan data structure itself */
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 9399b6d052..08189bf07b 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.222 2006/07/31 01:16:36 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.223 2006/07/31 20:09:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1239,12 +1239,13 @@ build_indices(void)
 		Relation	heap;
 		Relation	ind;
 
+		/* need not bother with locks during bootstrap */
 		heap = heap_open(ILHead->il_heap, NoLock);
-		ind = index_open(ILHead->il_ind);
+		ind = index_open(ILHead->il_ind, NoLock);
 
 		index_build(heap, ind, ILHead->il_info, false);
 
-		index_close(ind);
+		index_close(ind, NoLock);
 		heap_close(heap, NoLock);
 	}
 }
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index a7b50df950..5d99acafc9 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -1,7 +1,8 @@
 /*-------------------------------------------------------------------------
  *
  * catalog.c
- *		routines concerned with catalog naming conventions
+ *		routines concerned with catalog naming conventions and other
+ *		bits of hard-wired knowledge
  *
  *
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
@@ -9,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.66 2006/03/05 15:58:22 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.67 2006/07/31 20:09:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,8 +23,16 @@
 #include "access/genam.h"
 #include "access/transam.h"
 #include "catalog/catalog.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_auth_members.h"
+#include "catalog/pg_authid.h"
+#include "catalog/pg_database.h"
 #include "catalog/pg_namespace.h"
+#include "catalog/pg_pltemplate.h"
+#include "catalog/pg_shdepend.h"
+#include "catalog/pg_shdescription.h"
 #include "catalog/pg_tablespace.h"
+#include "catalog/toasting.h"
 #include "miscadmin.h"
 #include "storage/fd.h"
 #include "utils/fmgroids.h"
@@ -217,6 +226,64 @@ IsReservedName(const char *name)
 }
 
 
+/*
+ * IsSharedRelation
+ *		Given the OID of a relation, determine whether it's supposed to be
+ *		shared across an entire database cluster.
+ *
+ * Hard-wiring this list is pretty grotty, but we really need it so that
+ * we can compute the locktag for a relation (and then lock it) without
+ * having already read its pg_class entry.  If we try to retrieve relisshared
+ * from pg_class with no pre-existing lock, there is a race condition against
+ * anyone who is concurrently committing a change to the pg_class entry:
+ * since we read system catalog entries under SnapshotNow, it's possible
+ * that both the old and new versions of the row are invalid at the instants
+ * we scan them.  We fix this by insisting that updaters of a pg_class
+ * row must hold exclusive lock on the corresponding rel, and that users
+ * of a relation must hold at least AccessShareLock on the rel *before*
+ * trying to open its relcache entry.  But to lock a rel, you have to
+ * know if it's shared.  Fortunately, the set of shared relations is
+ * fairly static, so a hand-maintained list of their OIDs isn't completely
+ * impractical.
+ */
+bool
+IsSharedRelation(Oid relationId)
+{
+	/* These are the shared catalogs (look for BKI_SHARED_RELATION) */
+	if (relationId == AuthIdRelationId ||
+		relationId == AuthMemRelationId ||
+		relationId == DatabaseRelationId ||
+		relationId == PLTemplateRelationId ||
+		relationId == SharedDescriptionRelationId ||
+		relationId == SharedDependRelationId ||
+		relationId == TableSpaceRelationId)
+		return true;
+	/* These are their indexes (see indexing.h) */
+	if (relationId == AuthIdRolnameIndexId ||
+		relationId == AuthIdOidIndexId ||
+		relationId == AuthMemRoleMemIndexId ||
+		relationId == AuthMemMemRoleIndexId ||
+		relationId == DatabaseNameIndexId ||
+		relationId == DatabaseOidIndexId ||
+		relationId == PLTemplateNameIndexId ||
+		relationId == SharedDescriptionObjIndexId ||
+		relationId == SharedDependDependerIndexId ||
+		relationId == SharedDependReferenceIndexId ||
+		relationId == TablespaceOidIndexId ||
+		relationId == TablespaceNameIndexId)
+		return true;
+	/* These are their toast tables and toast indexes (see toasting.h) */
+	if (relationId == PgAuthidToastTable ||
+		relationId == PgAuthidToastIndex ||
+		relationId == PgDatabaseToastTable ||
+		relationId == PgDatabaseToastIndex ||
+		relationId == PgShdescriptionToastTable ||
+		relationId == PgShdescriptionToastIndex)
+		return true;
+	return false;
+}
+
+
 /*
  * GetNewOid
  *		Generate a new OID that is unique within the given relation.
@@ -271,9 +338,9 @@ GetNewOid(Relation relation)
 	}
 
 	/* Otherwise, use the index to find a nonconflicting OID */
-	indexrel = index_open(oidIndex);
+	indexrel = index_open(oidIndex, AccessShareLock);
 	newOid = GetNewOidWithIndex(relation, indexrel);
-	index_close(indexrel);
+	index_close(indexrel, AccessShareLock);
 
 	return newOid;
 }
@@ -309,7 +376,7 @@ GetNewOidWithIndex(Relation relation, Relation indexrel)
 					ObjectIdGetDatum(newOid));
 
 		/* see notes above about using SnapshotDirty */
-		scan = index_beginscan(relation, indexrel, true,
+		scan = index_beginscan(relation, indexrel,
 							   SnapshotDirty, 1, &key);
 
 		collides = HeapTupleIsValid(index_getnext(scan, ForwardScanDirection));
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 361f680087..922a0cebf7 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.310 2006/07/31 01:16:36 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.311 2006/07/31 20:09:00 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1971,24 +1971,17 @@ RemoveStatistics(Oid relid, AttrNumber attnum)
 
 
 /*
- * RelationTruncateIndexes - truncate all
- * indexes associated with the heap relation to zero tuples.
+ * RelationTruncateIndexes - truncate all indexes associated
+ * with the heap relation to zero tuples.
  *
  * The routine will truncate and then reconstruct the indexes on
- * the relation specified by the heapId parameter.
+ * the specified relation.  Caller must hold exclusive lock on rel.
  */
 static void
-RelationTruncateIndexes(Oid heapId)
+RelationTruncateIndexes(Relation heapRelation)
 {
-	Relation	heapRelation;
 	ListCell   *indlist;
 
-	/*
-	 * Open the heap rel.  We need grab no lock because we assume
-	 * heap_truncate is holding an exclusive lock on the heap rel.
-	 */
-	heapRelation = heap_open(heapId, NoLock);
-
 	/* Ask the relcache to produce a list of the indexes of the rel */
 	foreach(indlist, RelationGetIndexList(heapRelation))
 	{
@@ -1996,11 +1989,8 @@ RelationTruncateIndexes(Oid heapId)
 		Relation	currentIndex;
 		IndexInfo  *indexInfo;
 
-		/* Open the index relation */
-		currentIndex = index_open(indexId);
-
-		/* Obtain exclusive lock on it, just to be sure */
-		LockRelation(currentIndex, AccessExclusiveLock);
+		/* Open the index relation; use exclusive lock, just to be sure */
+		currentIndex = index_open(indexId, AccessExclusiveLock);
 
 		/* Fetch info needed for index_build */
 		indexInfo = BuildIndexInfo(currentIndex);
@@ -2013,11 +2003,8 @@ RelationTruncateIndexes(Oid heapId)
 		index_build(heapRelation, currentIndex, indexInfo, false);
 
 		/* We're done with this index */
-		index_close(currentIndex);
+		index_close(currentIndex, NoLock);
 	}
-
-	/* And now done with the heap; but keep lock until commit */
-	heap_close(heapRelation, NoLock);
 }
 
 /*
@@ -2066,7 +2053,7 @@ heap_truncate(List *relids)
 		RelationTruncate(rel, 0);
 
 		/* If this relation has indexes, truncate the indexes too */
-		RelationTruncateIndexes(RelationGetRelid(rel));
+		RelationTruncateIndexes(rel);
 
 		/*
 		 * Close the relation, but keep exclusive lock on it until commit.
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 092c9dcda1..0da209ff21 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.271 2006/07/31 01:16:36 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.272 2006/07/31 20:09:00 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -483,12 +483,8 @@ index_create(Oid heapRelationId,
 	/*
 	 * We cannot allow indexing a shared relation after initdb (because
 	 * there's no way to make the entry in other databases' pg_class).
-	 * Unfortunately we can't distinguish initdb from a manually started
-	 * standalone backend (toasting of shared rels happens after the bootstrap
-	 * phase, so checking IsBootstrapProcessingMode() won't work).  However,
-	 * we can at least prevent this mistake under normal multi-user operation.
 	 */
-	if (shared_relation && IsUnderPostmaster)
+	if (shared_relation && !IsBootstrapProcessingMode())
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("shared indexes cannot be created after initdb")));
@@ -753,7 +749,7 @@ index_create(Oid heapRelationId,
 	 * the exclusive lock on the index that we acquired above, until end of
 	 * transaction.
 	 */
-	index_close(indexRelation);
+	index_close(indexRelation, NoLock);
 	heap_close(heapRelation, NoLock);
 
 	return indexRelationId;
@@ -789,8 +785,7 @@ index_drop(Oid indexId)
 	heapId = IndexGetRelation(indexId);
 	userHeapRelation = heap_open(heapId, AccessExclusiveLock);
 
-	userIndexRelation = index_open(indexId);
-	LockRelation(userIndexRelation, AccessExclusiveLock);
+	userIndexRelation = index_open(indexId, AccessExclusiveLock);
 
 	/*
 	 * Schedule physical removal of the file
@@ -804,7 +799,7 @@ index_drop(Oid indexId)
 	 * try to rebuild it while we're deleting catalog entries. We keep the
 	 * lock though.
 	 */
-	index_close(userIndexRelation);
+	index_close(userIndexRelation, NoLock);
 
 	RelationForgetRelation(indexId);
 
@@ -982,11 +977,11 @@ FormIndexDatum(IndexInfo *indexInfo,
 
 
 /*
- * index_update_stats --- update pg_class entry after CREATE INDEX
+ * index_update_stats --- update pg_class entry after CREATE INDEX or REINDEX
  *
  * This routine updates the pg_class row of either an index or its parent
- * relation after CREATE INDEX.  Its rather bizarre API is designed to
- * ensure we can do all the necessary work in just one update.
+ * relation after CREATE INDEX or REINDEX.  Its rather bizarre API is designed
+ * to ensure we can do all the necessary work in just one update.
  *
  * hasindex: set relhasindex to this value
  * isprimary: if true, set relhaspkey true; else no change
@@ -1013,33 +1008,50 @@ index_update_stats(Relation rel, bool hasindex, bool isprimary,
 	Relation	pg_class;
 	HeapTuple	tuple;
 	Form_pg_class rd_rel;
-	bool		in_place_upd;
 	bool		dirty;
 
 	/*
-	 * Find the tuple to update in pg_class.  Normally we make a copy of the
-	 * tuple using the syscache, modify it, and apply heap_update. But in
-	 * bootstrap mode we can't use heap_update, so we use a nontransactional
-	 * update, ie, overwrite the tuple in-place.
+	 * We always update the pg_class row using a non-transactional,
+	 * overwrite-in-place update.  There are several reasons for this:
 	 *
-	 * We also must use an in-place update if reindexing pg_class itself,
-	 * because the target index may presently not be part of the set of
-	 * indexes that CatalogUpdateIndexes would update (see reindex_relation).
+	 * 1. In bootstrap mode, we have no choice --- UPDATE wouldn't work.
+	 *
+	 * 2. We could be reindexing pg_class itself, in which case we can't
+	 * move its pg_class row because CatalogUpdateIndexes might not know
+	 * about all the indexes yet (see reindex_relation).
+	 *
+	 * 3. Because we execute CREATE INDEX with just share lock on the parent
+	 * rel (to allow concurrent index creations), an ordinary update could
+	 * suffer a tuple-concurrently-updated failure against another CREATE
+	 * INDEX committing at about the same time.  We can avoid that by having
+	 * them both do nontransactional updates (we assume they will both be
+	 * trying to change the pg_class row to the same thing, so it doesn't
+	 * matter which goes first).
+	 *
+	 * 4. Even with just a single CREATE INDEX, there's a risk factor because
+	 * someone else might be trying to open the rel while we commit, and this
+	 * creates a race condition as to whether he will see both or neither of
+	 * the pg_class row versions as valid.  Again, a non-transactional update
+	 * avoids the risk.  It is indeterminate which state of the row the other
+	 * process will see, but it doesn't matter (if he's only taking
+	 * AccessShareLock, then it's not critical that he see relhasindex true).
+	 *
+	 * It is safe to use a non-transactional update even though our
+	 * transaction could still fail before committing.  Setting relhasindex
+	 * true is safe even if there are no indexes (VACUUM will eventually fix
+	 * it), and of course the relpages and reltuples counts are correct (or
+	 * at least more so than the old values) regardless.
 	 */
+
 	pg_class = heap_open(RelationRelationId, RowExclusiveLock);
 
-	in_place_upd = IsBootstrapProcessingMode() ||
-		ReindexIsProcessingHeap(RelationRelationId);
-
-restart:
-
-	if (!in_place_upd)
-	{
-		tuple = SearchSysCacheCopy(RELOID,
-								   ObjectIdGetDatum(relid),
-								   0, 0, 0);
-	}
-	else
+	/*
+	 * Make a copy of the tuple to update.  Normally we use the syscache,
+	 * but we can't rely on that during bootstrap or while reindexing
+	 * pg_class itself.
+	 */
+	if (IsBootstrapProcessingMode() ||
+		ReindexIsProcessingHeap(RelationRelationId))
 	{
 		/* don't assume syscache will work */
 		HeapScanDesc pg_class_scan;
@@ -1055,6 +1067,13 @@ restart:
 		tuple = heap_copytuple(tuple);
 		heap_endscan(pg_class_scan);
 	}
+	else
+	{
+		/* normal case, use syscache */
+		tuple = SearchSysCacheCopy(RELOID,
+								   ObjectIdGetDatum(relid),
+								   0, 0, 0);
+	}
 
 	if (!HeapTupleIsValid(tuple))
 		elog(ERROR, "could not find tuple for relation %u", relid);
@@ -1101,53 +1120,8 @@ restart:
 	 */
 	if (dirty)
 	{
-		if (in_place_upd)
-		{
-			heap_inplace_update(pg_class, tuple);
-		}
-		else
-		{
-			/*
-			 * Because PG allows concurrent CREATE INDEX commands, it's
-			 * possible that someone else tries to update the pg_class
-			 * row at about the same time we do.  Hence, instead of using
-			 * simple_heap_update(), we must use full heap_update() and
-			 * cope with HeapTupleUpdated result.  If we see that, just
-			 * go back and try the whole update again.
-			 */
-			HTSU_Result result;
-			ItemPointerData update_ctid;
-			TransactionId update_xmax;
-
-			result = heap_update(pg_class, &tuple->t_self, tuple,
-								 &update_ctid, &update_xmax,
-								 GetCurrentCommandId(), InvalidSnapshot,
-								 true /* wait for commit */ );
-			switch (result)
-			{
-				case HeapTupleSelfUpdated:
-					/* Tuple was already updated in current command? */
-					elog(ERROR, "tuple already updated by self");
-					break;
-
-				case HeapTupleMayBeUpdated:
-					/* done successfully */
-					break;
-
-				case HeapTupleUpdated:
-					heap_freetuple(tuple);
-					/* Must do CCI so we can see the updated tuple */
-					CommandCounterIncrement();
-					goto restart;
-
-				default:
-					elog(ERROR, "unrecognized heap_update status: %u", result);
-					break;
-			}
-
-			/* Keep the catalog indexes up to date */
-			CatalogUpdateIndexes(pg_class, tuple);
-		}
+		heap_inplace_update(pg_class, tuple);
+		/* the above sends a cache inval message */
 	}
 	else
 	{
@@ -1571,8 +1545,7 @@ reindex_index(Oid indexId)
 	 * Open the target index relation and get an exclusive lock on it, to
 	 * ensure that no one else is touching this particular index.
 	 */
-	iRel = index_open(indexId);
-	LockRelation(iRel, AccessExclusiveLock);
+	iRel = index_open(indexId, AccessExclusiveLock);
 
 	/*
 	 * If it's a shared index, we must do inplace processing (because we have
@@ -1628,7 +1601,7 @@ reindex_index(Oid indexId)
 	ResetReindexProcessing();
 
 	/* Close rels, but keep locks */
-	index_close(iRel);
+	index_close(iRel, NoLock);
 	heap_close(heapRelation, NoLock);
 }
 
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index e16afa0559..f0cbaefd60 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.151 2006/07/31 01:16:37 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.152 2006/07/31 20:09:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -314,8 +314,7 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck)
 {
 	Relation	OldIndex;
 
-	OldIndex = index_open(indexOid);
-	LockRelation(OldIndex, AccessExclusiveLock);
+	OldIndex = index_open(indexOid, AccessExclusiveLock);
 
 	/*
 	 * Check that index is in fact an index on the given relation
@@ -406,7 +405,7 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck)
 			   errmsg("cannot cluster temporary tables of other sessions")));
 
 	/* Drop relcache refcnt on OldIndex, but keep lock */
-	index_close(OldIndex);
+	index_close(OldIndex, NoLock);
 }
 
 /*
@@ -649,7 +648,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
 	 */
 	NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
 	OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
-	OldIndex = index_open(OIDOldIndex);
+	OldIndex = index_open(OIDOldIndex, AccessExclusiveLock);
 
 	/*
 	 * Their tuple descriptors should be exactly alike, but here we only need
@@ -669,7 +668,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
 	 * Scan through the OldHeap on the OldIndex and copy each tuple into the
 	 * NewHeap.
 	 */
-	scan = index_beginscan(OldHeap, OldIndex, true,
+	scan = index_beginscan(OldHeap, OldIndex,
 						   SnapshotNow, 0, (ScanKey) NULL);
 
 	while ((tuple = index_getnext(scan, ForwardScanDirection)) != NULL)
@@ -724,7 +723,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
 	pfree(values);
 	pfree(nulls);
 
-	index_close(OldIndex);
+	index_close(OldIndex, NoLock);
 	heap_close(OldHeap, NoLock);
 	heap_close(NewHeap, NoLock);
 }
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index a3b35a640e..6154a4ed3d 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.137 2006/07/14 14:52:18 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.138 2006/07/31 20:09:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -79,7 +79,7 @@ static SeqTable seqtab = NULL;	/* Head of list of SeqTable items */
 static SeqTableData *last_used_seq = NULL;
 
 static int64 nextval_internal(Oid relid);
-static void acquire_share_lock(Relation seqrel, SeqTable seq);
+static Relation open_share_lock(SeqTable seq);
 static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
 static Form_pg_sequence read_info(SeqTable elm, Relation rel, Buffer *buf);
 static void init_params(List *options, Form_pg_sequence new, bool isInit);
@@ -650,8 +650,7 @@ lastval(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("lastval is not yet defined in this session")));
 
-	seqrel = relation_open(last_used_seq->relid, NoLock);
-	acquire_share_lock(seqrel, last_used_seq);
+	seqrel = open_share_lock(last_used_seq);
 
 	/* nextval() must have already been called for this sequence */
 	Assert(last_used_seq->increment != 0);
@@ -802,16 +801,19 @@ setval3_oid(PG_FUNCTION_ARGS)
 
 
 /*
+ * Open the sequence and acquire AccessShareLock if needed
+ *
  * If we haven't touched the sequence already in this transaction,
  * we need to acquire AccessShareLock.	We arrange for the lock to
  * be owned by the top transaction, so that we don't need to do it
  * more than once per xact.
  */
-static void
-acquire_share_lock(Relation seqrel, SeqTable seq)
+static Relation
+open_share_lock(SeqTable seq)
 {
 	TransactionId thisxid = GetTopTransactionId();
 
+	/* Get the lock if not already held in this xact */
 	if (seq->xid != thisxid)
 	{
 		ResourceOwner currentOwner;
@@ -820,7 +822,7 @@ acquire_share_lock(Relation seqrel, SeqTable seq)
 		PG_TRY();
 		{
 			CurrentResourceOwner = TopTransactionResourceOwner;
-			LockRelation(seqrel, AccessShareLock);
+			LockRelationOid(seq->relid, AccessShareLock);
 		}
 		PG_CATCH();
 		{
@@ -831,9 +833,12 @@ acquire_share_lock(Relation seqrel, SeqTable seq)
 		PG_END_TRY();
 		CurrentResourceOwner = currentOwner;
 
-		/* Flag that we have a lock in the current xact. */
+		/* Flag that we have a lock in the current xact */
 		seq->xid = thisxid;
 	}
+
+	/* We now know we have AccessShareLock, and can safely open the rel */
+	return relation_open(seq->relid, NoLock);
 }
 
 /*
@@ -843,19 +848,8 @@ acquire_share_lock(Relation seqrel, SeqTable seq)
 static void
 init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 {
+	SeqTable elm;
 	Relation	seqrel;
-	volatile SeqTable elm;
-
-	/*
-	 * Open the sequence relation.
-	 */
-	seqrel = relation_open(relid, NoLock);
-
-	if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
-		ereport(ERROR,
-				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("\"%s\" is not a sequence",
-						RelationGetRelationName(seqrel))));
 
 	/* Look to see if we already have a seqtable entry for relation */
 	for (elm = seqtab; elm != NULL; elm = elm->next)
@@ -890,7 +884,16 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 		seqtab = elm;
 	}
 
-	acquire_share_lock(seqrel, elm);
+	/*
+	 * Open the sequence relation.
+	 */
+	seqrel = open_share_lock(elm);
+
+	if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("\"%s\" is not a sequence",
+						RelationGetRelationName(seqrel))));
 
 	*p_elm = elm;
 	*p_rel = seqrel;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 53382ff3d4..3b408b411a 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.197 2006/07/31 01:16:37 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.198 2006/07/31 20:09:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -5863,7 +5863,10 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace)
 	HeapTuple	tuple;
 	Form_pg_class rd_rel;
 
-	rel = relation_open(tableOid, NoLock);
+	/*
+	 * Need lock here in case we are recursing to toast table or index
+	 */
+	rel = relation_open(tableOid, AccessExclusiveLock);
 
 	/*
 	 * We can never allow moving of shared or nailed-in-cache relations,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 158e783c57..13e7cab721 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
- *	  $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.204 2006/07/14 14:52:18 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.205 2006/07/31 20:09:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,7 +36,6 @@
 #include "utils/inval.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
-#include "utils/relcache.h"
 #include "utils/syscache.h"
 
 
@@ -2986,7 +2985,6 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
 				while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
 				{
 					Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
-					Relation constraintRel;
 					Oid constraintNamespaceId;
 
 					/*
@@ -3010,13 +3008,9 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
 						pg_trigger->tgfoid == F_RI_FKEY_SETNULL_DEL ||
 						pg_trigger->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
 						pg_trigger->tgfoid == F_RI_FKEY_SETDEFAULT_DEL)
-					{
-						constraintRel = RelationIdGetRelation(pg_trigger->tgconstrrelid);
-					} else {
-						constraintRel = RelationIdGetRelation(pg_trigger->tgrelid);
-					}
-					constraintNamespaceId = RelationGetNamespace(constraintRel);
-					RelationClose(constraintRel);
+						constraintNamespaceId = get_rel_namespace(pg_trigger->tgconstrrelid);
+					else
+						constraintNamespaceId = get_rel_namespace(pg_trigger->tgrelid);
 
 					/*
 					 * If this constraint is not in the schema we're
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 1304e68681..6dfa6296d5 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.95 2006/07/14 14:52:18 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.96 2006/07/31 20:09:00 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -1625,6 +1625,8 @@ get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
 	SysScanDesc depScan;
 	HeapTuple	depTup;
 
+	Assert(lockmode != NoLock);
+
 	/*
 	 * We scan pg_depend to find those things that depend on the domain. (We
 	 * assume we can ignore refobjsubid for a domain.)
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index eb0fce72ed..c21be2f478 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.336 2006/07/30 02:07:18 alvherre Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.337 2006/07/31 20:09:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1138,7 +1138,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
 	 * same process.
 	 */
 	onerelid = onerel->rd_lockInfo.lockRelId;
-	LockRelationForSession(&onerelid, onerel->rd_istemp, lmode);
+	LockRelationIdForSession(&onerelid, lmode);
 
 	/*
 	 * Remember the relation's TOAST relation for later
@@ -1175,7 +1175,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
 	/*
 	 * Now release the session-level lock on the master table.
 	 */
-	UnlockRelationForSession(&onerelid, lmode);
+	UnlockRelationIdForSession(&onerelid, lmode);
 
 	return;
 }
@@ -3476,6 +3476,8 @@ vac_open_indexes(Relation relation, LOCKMODE lockmode,
 	ListCell   *indexoidscan;
 	int			i;
 
+	Assert(lockmode != NoLock);
+
 	indexoidlist = RelationGetIndexList(relation);
 
 	*nindexes = list_length(indexoidlist);
@@ -3489,11 +3491,8 @@ vac_open_indexes(Relation relation, LOCKMODE lockmode,
 	foreach(indexoidscan, indexoidlist)
 	{
 		Oid			indexoid = lfirst_oid(indexoidscan);
-		Relation	ind;
 
-		ind = index_open(indexoid);
-		(*Irel)[i++] = ind;
-		LockRelation(ind, lockmode);
+		(*Irel)[i++] = index_open(indexoid, lockmode);
 	}
 
 	list_free(indexoidlist);
@@ -3513,9 +3512,7 @@ vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode)
 	{
 		Relation	ind = Irel[nindexes];
 
-		if (lockmode != NoLock)
-			UnlockRelation(ind, lockmode);
-		index_close(ind);
+		index_close(ind, lockmode);
 	}
 	pfree(Irel);
 }
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index d2727c4a47..2202e7b7e9 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -31,7 +31,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.75 2006/07/14 14:52:18 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.76 2006/07/31 20:09:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -164,7 +164,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
 	vacrelstats->minxid = RecentXmin;
 
 	/* Open all indexes of the relation */
-	vac_open_indexes(onerel, ShareUpdateExclusiveLock, &nindexes, &Irel);
+	vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel);
 	hasindex = (nindexes > 0);
 
 	/* Do the vacuuming */
@@ -621,15 +621,6 @@ lazy_vacuum_index(Relation indrel,
 
 	pg_rusage_init(&ru0);
 
-	/*
-	 * Acquire appropriate type of lock on index: must be exclusive if index
-	 * AM isn't concurrent-safe.
-	 */
-	if (indrel->rd_am->amconcurrent)
-		LockRelation(indrel, RowExclusiveLock);
-	else
-		LockRelation(indrel, AccessExclusiveLock);
-
 	ivinfo.index = indrel;
 	ivinfo.vacuum_full = false;
 	ivinfo.message_level = elevel;
@@ -640,14 +631,6 @@ lazy_vacuum_index(Relation indrel,
 	*stats = index_bulk_delete(&ivinfo, *stats,
 							   lazy_tid_reaped, (void *) vacrelstats);
 
-	/*
-	 * Release lock acquired above.
-	 */
-	if (indrel->rd_am->amconcurrent)
-		UnlockRelation(indrel, RowExclusiveLock);
-	else
-		UnlockRelation(indrel, AccessExclusiveLock);
-
 	ereport(elevel,
 			(errmsg("scanned index \"%s\" to remove %d row versions",
 					RelationGetRelationName(indrel),
@@ -668,15 +651,6 @@ lazy_cleanup_index(Relation indrel,
 
 	pg_rusage_init(&ru0);
 
-	/*
-	 * Acquire appropriate type of lock on index: must be exclusive if index
-	 * AM isn't concurrent-safe.
-	 */
-	if (indrel->rd_am->amconcurrent)
-		LockRelation(indrel, RowExclusiveLock);
-	else
-		LockRelation(indrel, AccessExclusiveLock);
-
 	ivinfo.index = indrel;
 	ivinfo.vacuum_full = false;
 	ivinfo.message_level = elevel;
@@ -684,14 +658,6 @@ lazy_cleanup_index(Relation indrel,
 
 	stats = index_vacuum_cleanup(&ivinfo, stats);
 
-	/*
-	 * Release lock acquired above.
-	 */
-	if (indrel->rd_am->amconcurrent)
-		UnlockRelation(indrel, RowExclusiveLock);
-	else
-		UnlockRelation(indrel, AccessExclusiveLock);
-
 	if (!stats)
 		return;
 
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 11f2ae20f5..c879de3bbc 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.137 2006/07/14 14:52:19 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.138 2006/07/31 20:09:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -795,12 +795,6 @@ ExecCloseScanRelation(Relation scanrel)
  *
  *		At entry, caller has already opened and locked
  *		resultRelInfo->ri_RelationDesc.
- *
- *		This used to be horribly ugly code, and slow too because it
- *		did a sequential scan of pg_index.	Now we rely on the relcache
- *		to cache a list of the OIDs of the indices associated with any
- *		specific relation, and we use the pg_index syscache to get the
- *		entries we need from pg_index.
  * ----------------------------------------------------------------
  */
 void
@@ -840,6 +834,7 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo)
 
 	/*
 	 * For each index, open the index relation and save pg_index info.
+	 * We acquire RowExclusiveLock, signifying we will update the index.
 	 */
 	i = 0;
 	foreach(l, indexoidlist)
@@ -848,31 +843,7 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo)
 		Relation	indexDesc;
 		IndexInfo  *ii;
 
-		/*
-		 * Open and lock the index relation
-		 *
-		 * If the index AM supports concurrent updates, obtain
-		 * RowExclusiveLock to signify that we are updating the index.	This
-		 * locks out only operations that need exclusive access, such as
-		 * relocating the index to a new tablespace.
-		 *
-		 * If the index AM is not safe for concurrent updates, obtain an
-		 * exclusive lock on the index to lock out other updaters as well as
-		 * readers (index_beginscan places AccessShareLock on the index).
-		 *
-		 * If there are multiple not-concurrent-safe indexes, all backends
-		 * must lock the indexes in the same order or we will get deadlocks
-		 * here. This is guaranteed by RelationGetIndexList(), which promises
-		 * to return the index list in OID order.
-		 *
-		 * The locks will be released in ExecCloseIndices.
-		 */
-		indexDesc = index_open(indexOid);
-
-		if (indexDesc->rd_am->amconcurrent)
-			LockRelation(indexDesc, RowExclusiveLock);
-		else
-			LockRelation(indexDesc, AccessExclusiveLock);
+		indexDesc = index_open(indexOid, RowExclusiveLock);
 
 		/* extract index key information from the index's pg_index info */
 		ii = BuildIndexInfo(indexDesc);
@@ -907,12 +878,7 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo)
 			continue;			/* shouldn't happen? */
 
 		/* Drop lock acquired by ExecOpenIndices */
-		if (indexDescs[i]->rd_am->amconcurrent)
-			UnlockRelation(indexDescs[i], RowExclusiveLock);
-		else
-			UnlockRelation(indexDescs[i], AccessExclusiveLock);
-
-		index_close(indexDescs[i]);
+		index_close(indexDescs[i], RowExclusiveLock);
 	}
 
 	/*
diff --git a/src/backend/executor/nodeBitmapIndexscan.c b/src/backend/executor/nodeBitmapIndexscan.c
index 7555b5a22c..6a0303cddd 100644
--- a/src/backend/executor/nodeBitmapIndexscan.c
+++ b/src/backend/executor/nodeBitmapIndexscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/nodeBitmapIndexscan.c,v 1.19 2006/05/30 14:01:58 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeBitmapIndexscan.c,v 1.20 2006/07/31 20:09:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -201,7 +201,7 @@ ExecEndBitmapIndexScan(BitmapIndexScanState *node)
 	 * close the index relation
 	 */
 	index_endscan(indexScanDesc);
-	index_close(indexRelationDesc);
+	index_close(indexRelationDesc, NoLock);
 }
 
 /* ----------------------------------------------------------------
@@ -258,8 +258,14 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags)
 
 	/*
 	 * Open the index relation.
+	 *
+	 * If the parent table is one of the target relations of the query, then
+	 * InitPlan already opened and write-locked the index, so we can avoid
+	 * taking another lock here.  Otherwise we need a normal reader's lock.
 	 */
-	indexstate->biss_RelationDesc = index_open(node->indexid);
+	relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid);
+	indexstate->biss_RelationDesc = index_open(node->indexid,
+									relistarget ? NoLock : AccessShareLock);
 
 	/*
 	 * Initialize index-specific scan state
@@ -303,18 +309,9 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags)
 
 	/*
 	 * Initialize scan descriptor.
-	 *
-	 * Note we acquire no locks here; the index machinery does its own locks
-	 * and unlocks.  (We rely on having a lock on the parent table to
-	 * ensure the index won't go away!)  Furthermore, if the parent table
-	 * is one of the target relations of the query, then InitPlan already
-	 * opened and write-locked the index, so we can tell the index machinery
-	 * not to bother getting an extra lock.
 	 */
-	relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid);
 	indexstate->biss_ScanDesc =
 		index_beginscan_multi(indexstate->biss_RelationDesc,
-							  !relistarget,
 							  estate->es_snapshot,
 							  indexstate->biss_NumScanKeys,
 							  indexstate->biss_ScanKeys);
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 02f83667a4..84ee56beb0 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.115 2006/07/14 14:52:19 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.116 2006/07/31 20:09:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -415,7 +415,7 @@ ExecEndIndexScan(IndexScanState *node)
 	 * close the index relation
 	 */
 	index_endscan(indexScanDesc);
-	index_close(indexRelationDesc);
+	index_close(indexRelationDesc, NoLock);
 
 	/*
 	 * close the heap relation.
@@ -517,8 +517,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 
 	/*
 	 * Open the index relation.
+	 *
+	 * If the parent table is one of the target relations of the query, then
+	 * InitPlan already opened and write-locked the index, so we can avoid
+	 * taking another lock here.  Otherwise we need a normal reader's lock.
 	 */
-	indexstate->iss_RelationDesc = index_open(node->indexid);
+	relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid);
+	indexstate->iss_RelationDesc = index_open(node->indexid,
+									relistarget ? NoLock : AccessShareLock);
 
 	/*
 	 * Initialize index-specific scan state
@@ -561,18 +567,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 
 	/*
 	 * Initialize scan descriptor.
-	 *
-	 * Note we acquire no locks here; the index machinery does its own locks
-	 * and unlocks.  (We rely on having a lock on the parent table to
-	 * ensure the index won't go away!)  Furthermore, if the parent table
-	 * is one of the target relations of the query, then InitPlan already
-	 * opened and write-locked the index, so we can tell the index machinery
-	 * not to bother getting an extra lock.
 	 */
-	relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid);
 	indexstate->iss_ScanDesc = index_beginscan(currentRelation,
 											   indexstate->iss_RelationDesc,
-											   !relistarget,
 											   estate->es_snapshot,
 											   indexstate->iss_NumScanKeys,
 											   indexstate->iss_ScanKeys);
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index ff453336a1..bafe1b6673 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.121 2006/07/14 14:52:21 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.122 2006/07/31 20:09:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,7 +64,7 @@ static List *get_relation_constraints(Oid relationObjectId, RelOptInfo *rel);
  * widths here, and we may as well cache the results for costsize.c.
  */
 void
-get_relation_info(Oid relationObjectId, RelOptInfo *rel)
+get_relation_info(PlannerInfo *root, Oid relationObjectId, RelOptInfo *rel)
 {
 	Index		varno = rel->relid;
 	Relation	relation;
@@ -105,9 +105,23 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel)
 	{
 		List	   *indexoidlist;
 		ListCell   *l;
+		LOCKMODE	lmode;
 
 		indexoidlist = RelationGetIndexList(relation);
 
+		/*
+		 * For each index, we get the same type of lock that the executor will
+		 * need, and do not release it.  This saves a couple of trips to the
+		 * shared lock manager while not creating any real loss of
+		 * concurrency, because no schema changes could be happening on the
+		 * index while we hold lock on the parent rel, and neither lock type
+		 * blocks any other kind of index operation.
+		 */
+		if (rel->relid == root->parse->resultRelation)
+			lmode = RowExclusiveLock;
+		else
+			lmode = AccessShareLock;
+
 		foreach(l, indexoidlist)
 		{
 			Oid			indexoid = lfirst_oid(l);
@@ -120,13 +134,8 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel)
 
 			/*
 			 * Extract info from the relation descriptor for the index.
-			 *
-			 * Note that we take no lock on the index; we assume our lock on
-			 * the parent table will protect the index's schema information.
-			 * When and if the executor actually uses the index, it will take
-			 * a lock as needed to protect the access to the index contents.
 			 */
-			indexRelation = index_open(indexoid);
+			indexRelation = index_open(indexoid, lmode);
 			index = indexRelation->rd_index;
 
 			info = makeNode(IndexOptInfo);
@@ -203,7 +212,7 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel)
 					info->tuples = rel->tuples;
 			}
 
-			index_close(indexRelation);
+			index_close(indexRelation, NoLock);
 
 			indexinfos = lcons(info, indexinfos);
 		}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index d20a815461..8d06254a9f 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.79 2006/07/14 14:52:21 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.80 2006/07/31 20:09:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -92,7 +92,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
 	{
 		case RTE_RELATION:
 			/* Table --- retrieve statistics from the system catalogs */
-			get_relation_info(rte->relid, rel);
+			get_relation_info(root, rte->relid, rel);
 			break;
 		case RTE_SUBQUERY:
 		case RTE_FUNCTION:
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 90d47b09dc..25a19b2b24 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.25 2006/07/14 14:52:22 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.26 2006/07/31 20:09:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -47,7 +47,6 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/ps_status.h"
-#include "utils/relcache.h"
 #include "utils/syscache.h"
 
 
@@ -764,7 +763,6 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
 					 List **vacuum_tables,
 					 List **toast_table_ids)
 {
-	Relation	rel;
 	float4		reltuples;		/* pg_class.reltuples */
 
 	/* constants from pg_autovacuum or GUC variables */
@@ -799,12 +797,7 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
 	if (!PointerIsValid(tabentry))
 		return;
 
-	rel = RelationIdGetRelation(relid);
-	/* The table was recently dropped? */
-	if (!PointerIsValid(rel))
-		return;
-
-	reltuples = rel->rd_rel->reltuples;
+	reltuples = classForm->reltuples;
 	vactuples = tabentry->n_dead_tuples;
 	anltuples = tabentry->n_live_tuples + tabentry->n_dead_tuples -
 		tabentry->last_anl_tuples;
@@ -861,7 +854,7 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
 	 */
 
 	elog(DEBUG3, "%s: vac: %.0f (threshold %.0f), anl: %.0f (threshold %.0f)",
-		 RelationGetRelationName(rel),
+		 NameStr(classForm->relname),
 		 vactuples, vacthresh, anltuples, anlthresh);
 
 	/* Determine if this table needs vacuum or analyze. */
@@ -880,7 +873,7 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
 			elog(DEBUG2, "autovac: will%s%s %s",
 				 (dovacuum ? " VACUUM" : ""),
 				 (doanalyze ? " ANALYZE" : ""),
-				 RelationGetRelationName(rel));
+				 NameStr(classForm->relname));
 
 		/*
 		 * we must record tables that have a toast table, even if we currently
@@ -907,8 +900,6 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
 		if (dovacuum)
 			*toast_table_ids = lappend_oid(*toast_table_ids, relid);
 	}
-
-	RelationClose(rel);
 }
 
 /*
@@ -966,10 +957,10 @@ autovacuum_do_vac_analyze(List *relids, bool dovacuum, bool doanalyze,
  * done with the current one, and exiting right after the last one, so we don't
  * bother to report "<IDLE>" or some such.
  */
-#define MAX_AUTOVAC_ACTIV_LEN (NAMEDATALEN * 2 + 32)
 static void
 autovac_report_activity(VacuumStmt *vacstmt, List *relids)
 {
+#define MAX_AUTOVAC_ACTIV_LEN (NAMEDATALEN * 2 + 32)
 	char		activity[MAX_AUTOVAC_ACTIV_LEN];
 
 	/*
@@ -982,33 +973,32 @@ autovac_report_activity(VacuumStmt *vacstmt, List *relids)
 	/* Report the command and possible options */
 	if (vacstmt->vacuum)
 		snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,
-					   "VACUUM%s%s%s",
-					   vacstmt->full ? " FULL" : "",
-					   vacstmt->freeze ? " FREEZE" : "",
-					   vacstmt->analyze ? " ANALYZE" : "");
+				 "VACUUM%s%s%s",
+				 vacstmt->full ? " FULL" : "",
+				 vacstmt->freeze ? " FREEZE" : "",
+				 vacstmt->analyze ? " ANALYZE" : "");
 	else if (vacstmt->analyze)
 		snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,
-					   "ANALYZE");
+				 "ANALYZE");
 
 	/* Report the qualified name of the first relation, if any */
-	if (list_length(relids) > 0)
+	if (relids)
 	{
 		Oid			relid = linitial_oid(relids);
-		Relation	rel;
+		char	   *relname = get_rel_name(relid);
+		char	   *nspname = get_namespace_name(get_rel_namespace(relid));
 
-		rel = RelationIdGetRelation(relid);
-		if (rel == NULL)
-			elog(WARNING, "cache lookup failed for relation %u", relid);
-		else
+		/*
+		 * Paranoia is appropriate here in case relation was recently
+		 * dropped --- the lsyscache routines we just invoked will return
+		 * NULL rather than failing.
+		 */
+		if (relname && nspname)
 		{
-			char   *nspname = get_namespace_name(RelationGetNamespace(rel));
 			int		len = strlen(activity);
 
 			snprintf(activity + len, MAX_AUTOVAC_ACTIV_LEN - len,
-					 " %s.%s", nspname, RelationGetRelationName(rel));
-
-			pfree(nspname);
-			RelationClose(rel);
+					 " %s.%s", nspname, relname);
 		}
 	}
 
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index fd6ae5abc3..582eabdb2a 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -17,7 +17,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.118 2006/07/14 14:52:23 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.119 2006/07/31 20:09:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -69,7 +69,7 @@ open_lo_relation(void)
 		if (lo_heap_r == NULL)
 			lo_heap_r = heap_open(LargeObjectRelationId, RowExclusiveLock);
 		if (lo_index_r == NULL)
-			lo_index_r = index_open(LargeObjectLOidPNIndexId);
+			lo_index_r = index_open(LargeObjectLOidPNIndexId, RowExclusiveLock);
 	}
 	PG_CATCH();
 	{
@@ -103,7 +103,7 @@ close_lo_relation(bool isCommit)
 				CurrentResourceOwner = TopTransactionResourceOwner;
 
 				if (lo_index_r)
-					index_close(lo_index_r);
+					index_close(lo_index_r, NoLock);
 				if (lo_heap_r)
 					heap_close(lo_heap_r, NoLock);
 			}
@@ -314,7 +314,7 @@ inv_getsize(LargeObjectDesc *obj_desc)
 				BTEqualStrategyNumber, F_OIDEQ,
 				ObjectIdGetDatum(obj_desc->id));
 
-	sd = index_beginscan(lo_heap_r, lo_index_r, true,
+	sd = index_beginscan(lo_heap_r, lo_index_r,
 						 obj_desc->snapshot, 1, skey);
 
 	/*
@@ -425,7 +425,7 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 				BTGreaterEqualStrategyNumber, F_INT4GE,
 				Int32GetDatum(pageno));
 
-	sd = index_beginscan(lo_heap_r, lo_index_r, true,
+	sd = index_beginscan(lo_heap_r, lo_index_r,
 						 obj_desc->snapshot, 2, skey);
 
 	while ((tuple = index_getnext(sd, ForwardScanDirection)) != NULL)
@@ -541,7 +541,7 @@ inv_write(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 				BTGreaterEqualStrategyNumber, F_INT4GE,
 				Int32GetDatum(pageno));
 
-	sd = index_beginscan(lo_heap_r, lo_index_r, false /* got lock */,
+	sd = index_beginscan(lo_heap_r, lo_index_r,
 						 obj_desc->snapshot, 2, skey);
 
 	oldtuple = NULL;
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
index 8e99d4be48..ad7ee3e601 100644
--- a/src/backend/storage/lmgr/lmgr.c
+++ b/src/backend/storage/lmgr/lmgr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.85 2006/07/14 16:59:19 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.86 2006/07/31 20:09:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,10 +18,13 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/xact.h"
+#include "catalog/catalog.h"
+#include "catalog/namespace.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
 #include "storage/procarray.h"
 #include "utils/inval.h"
+#include "utils/lsyscache.h"
 
 
 /*
@@ -44,8 +47,106 @@ RelationInitLockInfo(Relation relation)
 		relation->rd_lockInfo.lockRelId.dbId = MyDatabaseId;
 }
 
+/*
+ * SetLocktagRelationOid
+ *		Set up a locktag for a relation, given only relation OID
+ */
+static inline void
+SetLocktagRelationOid(LOCKTAG *tag, Oid relid)
+{
+	Oid		dbid;
+
+	if (IsSharedRelation(relid))
+		dbid = InvalidOid;
+	else
+		dbid = MyDatabaseId;
+
+	SET_LOCKTAG_RELATION(*tag, dbid, relid);
+}
+
+/*
+ *		LockRelationOid
+ *
+ * Lock a relation given only its OID.  This should generally be used
+ * before attempting to open the relation's relcache entry.
+ */
+void
+LockRelationOid(Oid relid, LOCKMODE lockmode)
+{
+	LOCKTAG		tag;
+	LockAcquireResult res;
+
+	SetLocktagRelationOid(&tag, relid);
+
+	res = LockAcquire(&tag, lockmode, false, false);
+
+	/*
+	 * Now that we have the lock, check for invalidation messages, so that
+	 * we will update or flush any stale relcache entry before we try to use
+	 * it.  We can skip this in the not-uncommon case that we already had
+	 * the same type of lock being requested, since then no one else could
+	 * have modified the relcache entry in an undesirable way.  (In the
+	 * case where our own xact modifies the rel, the relcache update happens
+	 * via CommandCounterIncrement, not here.)
+	 */
+	if (res != LOCKACQUIRE_ALREADY_HELD)
+		AcceptInvalidationMessages();
+}
+
+/*
+ *		ConditionalLockRelationOid
+ *
+ * As above, but only lock if we can get the lock without blocking.
+ * Returns TRUE iff the lock was acquired.
+ *
+ * NOTE: we do not currently need conditional versions of all the
+ * LockXXX routines in this file, but they could easily be added if needed.
+ */
+bool
+ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
+{
+	LOCKTAG		tag;
+	LockAcquireResult res;
+
+	SetLocktagRelationOid(&tag, relid);
+
+	res = LockAcquire(&tag, lockmode, false, true);
+
+	if (res == LOCKACQUIRE_NOT_AVAIL)
+		return false;
+
+	/*
+	 * Now that we have the lock, check for invalidation messages; see
+	 * notes in LockRelationOid.
+	 */
+	if (res != LOCKACQUIRE_ALREADY_HELD)
+		AcceptInvalidationMessages();
+
+	return true;
+}
+
+/*
+ *		UnlockRelationId
+ *
+ * Note: we don't supply UnlockRelationOid since it's normally easy for
+ * callers to provide the LockRelId info from a relcache entry.
+ */
+void
+UnlockRelationId(LockRelId *relid, LOCKMODE lockmode)
+{
+	LOCKTAG		tag;
+
+	SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId);
+
+	LockRelease(&tag, lockmode, false);
+}
+
 /*
  *		LockRelation
+ *
+ * This is a convenience routine for acquiring an additional lock on an
+ * already-open relation.  Never try to do "relation_open(foo, NoLock)"
+ * and then lock with this.
  */
 void
 LockRelation(Relation relation, LOCKMODE lockmode)
@@ -57,31 +158,22 @@ LockRelation(Relation relation, LOCKMODE lockmode)
 						 relation->rd_lockInfo.lockRelId.dbId,
 						 relation->rd_lockInfo.lockRelId.relId);
 
-	res = LockAcquire(&tag, relation->rd_istemp, lockmode, false, false);
+	res = LockAcquire(&tag, lockmode, false, false);
 
 	/*
-	 * Check to see if the relcache entry has been invalidated while we were
-	 * waiting to lock it.	If so, rebuild it, or ereport() trying. Increment
-	 * the refcount to ensure that RelationFlushRelation will rebuild it and
-	 * not just delete it.	We can skip this if the lock was already held,
-	 * however.
+	 * Now that we have the lock, check for invalidation messages; see
+	 * notes in LockRelationOid.
 	 */
 	if (res != LOCKACQUIRE_ALREADY_HELD)
-	{
-		RelationIncrementReferenceCount(relation);
 		AcceptInvalidationMessages();
-		RelationDecrementReferenceCount(relation);
-	}
 }
 
 /*
  *		ConditionalLockRelation
  *
- * As above, but only lock if we can get the lock without blocking.
- * Returns TRUE iff the lock was acquired.
- *
- * NOTE: we do not currently need conditional versions of all the
- * LockXXX routines in this file, but they could easily be added if needed.
+ * This is a convenience routine for acquiring an additional lock on an
+ * already-open relation.  Never try to do "relation_open(foo, NoLock)"
+ * and then lock with this.
  */
 bool
 ConditionalLockRelation(Relation relation, LOCKMODE lockmode)
@@ -93,30 +185,26 @@ ConditionalLockRelation(Relation relation, LOCKMODE lockmode)
 						 relation->rd_lockInfo.lockRelId.dbId,
 						 relation->rd_lockInfo.lockRelId.relId);
 
-	res = LockAcquire(&tag, relation->rd_istemp, lockmode, false, true);
+	res = LockAcquire(&tag, lockmode, false, true);
 
 	if (res == LOCKACQUIRE_NOT_AVAIL)
 		return false;
 
 	/*
-	 * Check to see if the relcache entry has been invalidated while we were
-	 * waiting to lock it.	If so, rebuild it, or ereport() trying. Increment
-	 * the refcount to ensure that RelationFlushRelation will rebuild it and
-	 * not just delete it.	We can skip this if the lock was already held,
-	 * however.
+	 * Now that we have the lock, check for invalidation messages; see
+	 * notes in LockRelationOid.
 	 */
 	if (res != LOCKACQUIRE_ALREADY_HELD)
-	{
-		RelationIncrementReferenceCount(relation);
 		AcceptInvalidationMessages();
-		RelationDecrementReferenceCount(relation);
-	}
 
 	return true;
 }
 
 /*
  *		UnlockRelation
+ *
+ * This is a convenience routine for unlocking a relation without also
+ * closing it.
  */
 void
 UnlockRelation(Relation relation, LOCKMODE lockmode)
@@ -131,11 +219,11 @@ UnlockRelation(Relation relation, LOCKMODE lockmode)
 }
 
 /*
- *		LockRelationForSession
+ *		LockRelationIdForSession
  *
  * This routine grabs a session-level lock on the target relation.	The
  * session lock persists across transaction boundaries.  It will be removed
- * when UnlockRelationForSession() is called, or if an ereport(ERROR) occurs,
+ * when UnlockRelationIdForSession() is called, or if an ereport(ERROR) occurs,
  * or if the backend exits.
  *
  * Note that one should also grab a transaction-level lock on the rel
@@ -143,20 +231,20 @@ UnlockRelation(Relation relation, LOCKMODE lockmode)
  * relcache entry is up to date.
  */
 void
-LockRelationForSession(LockRelId *relid, bool istemprel, LOCKMODE lockmode)
+LockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
 {
 	LOCKTAG		tag;
 
 	SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId);
 
-	(void) LockAcquire(&tag, istemprel, lockmode, true, false);
+	(void) LockAcquire(&tag, lockmode, true, false);
 }
 
 /*
- *		UnlockRelationForSession
+ *		UnlockRelationIdForSession
  */
 void
-UnlockRelationForSession(LockRelId *relid, LOCKMODE lockmode)
+UnlockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
 {
 	LOCKTAG		tag;
 
@@ -184,7 +272,7 @@ LockRelationForExtension(Relation relation, LOCKMODE lockmode)
 								relation->rd_lockInfo.lockRelId.dbId,
 								relation->rd_lockInfo.lockRelId.relId);
 
-	(void) LockAcquire(&tag, relation->rd_istemp, lockmode, false, false);
+	(void) LockAcquire(&tag, lockmode, false, false);
 }
 
 /*
@@ -218,7 +306,7 @@ LockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
 					 relation->rd_lockInfo.lockRelId.relId,
 					 blkno);
 
-	(void) LockAcquire(&tag, relation->rd_istemp, lockmode, false, false);
+	(void) LockAcquire(&tag, lockmode, false, false);
 }
 
 /*
@@ -237,8 +325,7 @@ ConditionalLockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
 					 relation->rd_lockInfo.lockRelId.relId,
 					 blkno);
 
-	return (LockAcquire(&tag, relation->rd_istemp,
-						lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
+	return (LockAcquire(&tag, lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
 }
 
 /*
@@ -275,7 +362,7 @@ LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
 					  ItemPointerGetBlockNumber(tid),
 					  ItemPointerGetOffsetNumber(tid));
 
-	(void) LockAcquire(&tag, relation->rd_istemp, lockmode, false, false);
+	(void) LockAcquire(&tag, lockmode, false, false);
 }
 
 /*
@@ -295,8 +382,7 @@ ConditionalLockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
 					  ItemPointerGetBlockNumber(tid),
 					  ItemPointerGetOffsetNumber(tid));
 
-	return (LockAcquire(&tag, relation->rd_istemp,
-						lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
+	return (LockAcquire(&tag, lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
 }
 
 /*
@@ -330,7 +416,7 @@ XactLockTableInsert(TransactionId xid)
 
 	SET_LOCKTAG_TRANSACTION(tag, xid);
 
-	(void) LockAcquire(&tag, false, ExclusiveLock, false, false);
+	(void) LockAcquire(&tag, ExclusiveLock, false, false);
 }
 
 /*
@@ -375,7 +461,7 @@ XactLockTableWait(TransactionId xid)
 
 		SET_LOCKTAG_TRANSACTION(tag, xid);
 
-		(void) LockAcquire(&tag, false, ShareLock, false, false);
+		(void) LockAcquire(&tag, ShareLock, false, false);
 
 		LockRelease(&tag, ShareLock, false);
 
@@ -403,8 +489,7 @@ ConditionalXactLockTableWait(TransactionId xid)
 
 		SET_LOCKTAG_TRANSACTION(tag, xid);
 
-		if (LockAcquire(&tag, false,
-						ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL)
+		if (LockAcquire(&tag, ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL)
 			return false;
 
 		LockRelease(&tag, ShareLock, false);
@@ -423,9 +508,7 @@ ConditionalXactLockTableWait(TransactionId xid)
  * Obtain a lock on a general object of the current database.  Don't use
  * this for shared objects (such as tablespaces).  It's unwise to apply it
  * to relations, also, since a lock taken this way will NOT conflict with
- * LockRelation, and also may be wrongly marked if the relation is temp.
- * (If we ever invent temp objects that aren't tables, we'll want to extend
- * the API of this routine to include an isTempObject flag.)
+ * locks taken via LockRelation and friends.
  */
 void
 LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,
@@ -439,7 +522,7 @@ LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,
 					   objid,
 					   objsubid);
 
-	(void) LockAcquire(&tag, false, lockmode, false, false);
+	(void) LockAcquire(&tag, lockmode, false, false);
 }
 
 /*
@@ -477,7 +560,7 @@ LockSharedObject(Oid classid, Oid objid, uint16 objsubid,
 					   objid,
 					   objsubid);
 
-	(void) LockAcquire(&tag, false, lockmode, false, false);
+	(void) LockAcquire(&tag, lockmode, false, false);
 
 	/* Make sure syscaches are up-to-date with any changes we waited for */
 	AcceptInvalidationMessages();
@@ -500,3 +583,39 @@ UnlockSharedObject(Oid classid, Oid objid, uint16 objsubid,
 
 	LockRelease(&tag, lockmode, false);
 }
+
+
+/*
+ * LockTagIsTemp
+ *		Determine whether a locktag is for a lock on a temporary object
+ *
+ * We need this because 2PC cannot deal with temp objects
+ */
+bool
+LockTagIsTemp(const LOCKTAG *tag)
+{
+	switch (tag->locktag_type)
+	{
+		case LOCKTAG_RELATION:
+		case LOCKTAG_RELATION_EXTEND:
+		case LOCKTAG_PAGE:
+		case LOCKTAG_TUPLE:
+			/* check for lock on a temp relation */
+			/* field1 is dboid, field2 is reloid for all of these */
+			if ((Oid) tag->locktag_field1 == InvalidOid)
+				return false;	/* shared, so not temp */
+			if (isTempNamespace(get_rel_namespace((Oid) tag->locktag_field2)))
+				return true;
+			break;
+		case LOCKTAG_TRANSACTION:
+			/* there are no temp transactions */
+			break;
+		case LOCKTAG_OBJECT:
+			/* there are currently no non-table temp objects */
+			break;
+		case LOCKTAG_USERLOCK:
+			/* assume these aren't temp */
+			break;
+	}
+	return false;				/* default case */
+}
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index a0bc2869c0..10049d593a 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.169 2006/07/24 16:32:45 petere Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.170 2006/07/31 20:09:05 tgl Exp $
  *
  * NOTES
  *	  A lock table is a shared memory hash table.  When
@@ -36,6 +36,7 @@
 #include "access/twophase.h"
 #include "access/twophase_rmgr.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
 #include "utils/memutils.h"
 #include "utils/ps_status.h"
 #include "utils/resowner.h"
@@ -449,8 +450,6 @@ ProcLockHashCode(const PROCLOCKTAG *proclocktag, uint32 hashcode)
  *
  * Inputs:
  *	locktag: unique identifier for the lockable object
- *	isTempObject: is the lockable object a temporary object?  (Under 2PC,
- *		such locks cannot be persisted)
  *	lockmode: lock mode to acquire
  *	sessionLock: if true, acquire lock for session not current transaction
  *	dontWait: if true, don't wait to acquire lock
@@ -471,7 +470,6 @@ ProcLockHashCode(const PROCLOCKTAG *proclocktag, uint32 hashcode)
  */
 LockAcquireResult
 LockAcquire(const LOCKTAG *locktag,
-			bool isTempObject,
 			LOCKMODE lockmode,
 			bool sessionLock,
 			bool dontWait)
@@ -528,7 +526,6 @@ LockAcquire(const LOCKTAG *locktag,
 	{
 		locallock->lock = NULL;
 		locallock->proclock = NULL;
-		locallock->isTempObject = isTempObject;
 		locallock->hashcode = LockTagHashCode(&(localtag.lock));
 		locallock->nLocks = 0;
 		locallock->numLockOwners = 0;
@@ -540,8 +537,6 @@ LockAcquire(const LOCKTAG *locktag,
 	}
 	else
 	{
-		Assert(locallock->isTempObject == isTempObject);
-
 		/* Make sure there will be room to remember the lock */
 		if (locallock->numLockOwners >= locallock->maxLockOwners)
 		{
@@ -1733,7 +1728,7 @@ AtPrepare_Locks(void)
 		}
 
 		/* Can't handle it if the lock is on a temporary object */
-		if (locallock->isTempObject)
+		if (LockTagIsTemp(&locallock->tag.lock))
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("cannot PREPARE a transaction that has operated on temporary tables")));
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index f78db61968..20b83e196a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.131 2006/07/14 14:52:25 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.132 2006/07/31 20:09:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -904,12 +904,7 @@ CatalogCacheInitializeCache(CatCache *cache)
 
 	CatalogCacheInitializeCache_DEBUG1;
 
-	/*
-	 * Open the relation without locking --- we only need the tupdesc, which
-	 * we assume will never change ...
-	 */
-	relation = heap_open(cache->cc_reloid, NoLock);
-	Assert(RelationIsValid(relation));
+	relation = heap_open(cache->cc_reloid, AccessShareLock);
 
 	/*
 	 * switch to the cache context so our allocations do not vanish at the end
@@ -936,7 +931,7 @@ CatalogCacheInitializeCache(CatCache *cache)
 	 */
 	MemoryContextSwitchTo(oldcxt);
 
-	heap_close(relation, NoLock);
+	heap_close(relation, AccessShareLock);
 
 	CACHE3_elog(DEBUG2, "CatalogCacheInitializeCache: %s, %d keys",
 				cache->cc_relname, cache->cc_nkeys);
@@ -1012,8 +1007,8 @@ InitCatCachePhase2(CatCache *cache)
 	{
 		Relation	idesc;
 
-		idesc = index_open(cache->cc_indexoid);
-		index_close(idesc);
+		idesc = index_open(cache->cc_indexoid, AccessShareLock);
+		index_close(idesc, AccessShareLock);
 	}
 }
 
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 0fe1f29b25..08697d5036 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.246 2006/07/14 14:52:25 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.247 2006/07/31 20:09:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,7 +17,6 @@
  *		RelationCacheInitialize			- initialize relcache (to empty)
  *		RelationCacheInitializePhase2	- finish initializing relcache
  *		RelationIdGetRelation			- get a reldesc by relation id
- *		RelationIdCacheGetRelation		- get a cached reldesc by relid
  *		RelationClose					- close an open relation
  *
  * NOTES
@@ -34,6 +33,7 @@
 #include "access/heapam.h"
 #include "access/reloptions.h"
 #include "access/xact.h"
+#include "catalog/catalog.h"
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_amop.h"
@@ -763,6 +763,10 @@ equalRuleLocks(RuleLock *rlock1, RuleLock *rlock2)
  *		recycling the given old relation object.  The latter case
  *		supports rebuilding a relcache entry without invalidating
  *		pointers to it.
+ *
+ *		Returns NULL if no pg_class row could be found for the given relid
+ *		(suggesting we are trying to access a just-deleted relation).
+ *		Any other error is reported via elog.
  * --------------------------------
  */
 static Relation
@@ -1387,41 +1391,18 @@ formrdesc(const char *relationName, Oid relationReltype,
  * ----------------------------------------------------------------
  */
 
-/*
- *		RelationIdCacheGetRelation
- *
- *		Lookup an existing reldesc by OID.
- *
- *		Only try to get the reldesc by looking in the cache,
- *		do not go to the disk if it's not present.
- *
- *		NB: relation ref count is incremented if successful.
- *		Caller should eventually decrement count.  (Usually,
- *		that happens by calling RelationClose().)
- */
-Relation
-RelationIdCacheGetRelation(Oid relationId)
-{
-	Relation	rd;
-
-	RelationIdCacheLookup(relationId, rd);
-
-	if (RelationIsValid(rd))
-	{
-		RelationIncrementReferenceCount(rd);
-		/* revalidate nailed index if necessary */
-		if (!rd->rd_isvalid)
-			RelationReloadClassinfo(rd);
-	}
-
-	return rd;
-}
-
 /*
  *		RelationIdGetRelation
  *
  *		Lookup a reldesc by OID; make one if not already in cache.
  *
+ *		Returns NULL if no pg_class row could be found for the given relid
+ *		(suggesting we are trying to access a just-deleted relation).
+ *		Any other error is reported via elog.
+ *
+ *		NB: caller should already have at least AccessShareLock on the
+ *		relation ID, else there are nasty race conditions.
+ *
  *		NB: relation ref count is incremented, or set to 1 if new entry.
  *		Caller should eventually decrement count.  (Usually,
  *		that happens by calling RelationClose().)
@@ -1432,11 +1413,18 @@ RelationIdGetRelation(Oid relationId)
 	Relation	rd;
 
 	/*
-	 * first try and get a reldesc from the cache
+	 * first try to find reldesc in the cache
 	 */
-	rd = RelationIdCacheGetRelation(relationId);
+	RelationIdCacheLookup(relationId, rd);
+
 	if (RelationIsValid(rd))
+	{
+		RelationIncrementReferenceCount(rd);
+		/* revalidate nailed index if necessary */
+		if (!rd->rd_isvalid)
+			RelationReloadClassinfo(rd);
 		return rd;
+	}
 
 	/*
 	 * no reldesc in the cache, so have RelationBuildDesc() build one and add
@@ -2133,6 +2121,16 @@ RelationBuildLocalRelation(const char *relname,
 			break;
 	}
 
+	/*
+	 * check that hardwired list of shared rels matches what's in the
+	 * bootstrap .bki file.  If you get a failure here during initdb,
+	 * you probably need to fix IsSharedRelation() to match whatever
+	 * you've done to the set of shared relations.
+	 */
+	if (shared_relation != IsSharedRelation(relid))
+		elog(ERROR, "shared_relation flag for \"%s\" does not match IsSharedRelation(%u)",
+			 relname, relid);
+
 	/*
 	 * switch to the cache context to create the relcache entry.
 	 */
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 9cd9162c26..ddad174527 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.64 2006/07/13 17:47:01 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.65 2006/07/31 20:09:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,7 @@
 #include "access/relscan.h"
 #include "access/sdir.h"
 #include "nodes/primnodes.h"
+#include "storage/lock.h"
 
 /*
  * Struct for statistics returned by ambuild
@@ -84,9 +85,9 @@ typedef SysScanDescData *SysScanDesc;
 /*
  * generalized index_ interface routines (in indexam.c)
  */
-extern Relation index_open(Oid relationId);
-extern Relation index_openrv(const RangeVar *relation);
-extern void index_close(Relation relation);
+extern Relation index_open(Oid relationId, LOCKMODE lockmode);
+extern void index_close(Relation relation, LOCKMODE lockmode);
+
 extern bool index_insert(Relation indexRelation,
 			 Datum *values, bool *isnull,
 			 ItemPointer heap_t_ctid,
@@ -95,11 +96,9 @@ extern bool index_insert(Relation indexRelation,
 
 extern IndexScanDesc index_beginscan(Relation heapRelation,
 				Relation indexRelation,
-				bool need_index_lock,
 				Snapshot snapshot,
 				int nkeys, ScanKey key);
 extern IndexScanDesc index_beginscan_multi(Relation indexRelation,
-					  bool need_index_lock,
 					  Snapshot snapshot,
 					  int nkeys, ScanKey key);
 extern void index_rescan(IndexScanDesc scan, ScanKey key);
diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h
index cbe7b0c244..d32ab6d524 100644
--- a/src/include/access/relscan.h
+++ b/src/include/access/relscan.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/relscan.h,v 1.48 2006/07/13 18:01:01 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/access/relscan.h,v 1.49 2006/07/31 20:09:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -62,7 +62,6 @@ typedef struct IndexScanDescData
 	int			numberOfKeys;	/* number of scan keys */
 	ScanKey		keyData;		/* array of scan key descriptors */
 	bool		is_multiscan;	/* TRUE = using amgetmulti */
-	bool		have_lock;		/* TRUE = holding AccessShareLock for scan */
 
 	/* signaling to index AM about killing index tuples */
 	bool		kill_prior_tuple;		/* last-returned tuple is dead */
diff --git a/src/include/catalog/catalog.h b/src/include/catalog/catalog.h
index 6136a33f24..512ea9adbf 100644
--- a/src/include/catalog/catalog.h
+++ b/src/include/catalog/catalog.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catalog.h,v 1.35 2006/03/05 15:58:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catalog.h,v 1.36 2006/07/31 20:09:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,6 +31,8 @@ extern bool IsToastNamespace(Oid namespaceId);
 
 extern bool IsReservedName(const char *name);
 
+extern bool IsSharedRelation(Oid relationId);
+
 extern Oid	GetNewOid(Relation relation);
 extern Oid	GetNewOidWithIndex(Relation relation, Relation indexrel);
 extern Oid GetNewRelFileNode(Oid reltablespace, bool relisshared,
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 052d2a19cf..23b0ac9ce4 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.344 2006/07/31 01:16:37 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.345 2006/07/31 20:09:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200607301
+#define CATALOG_VERSION_NO	200607311
 
 #endif
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index 048e3e97f7..7b155bf828 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.45 2006/07/03 22:45:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.46 2006/07/31 20:09:05 tgl Exp $
  *
  * NOTES
  *		the genbki.sh script reads this file and generates .bki
@@ -51,7 +51,6 @@ CATALOG(pg_am,2601)
 	bool		amoptionalkey;	/* can query omit key for the first column? */
 	bool		amindexnulls;	/* does AM support NULL index entries? */
 	bool		amstorage;		/* can storage type differ from column type? */
-	bool		amconcurrent;	/* does AM support concurrent updates? */
 	bool		amclusterable;	/* does AM support cluster command? */
 	regproc		aminsert;		/* "insert this tuple" function */
 	regproc		ambeginscan;	/* "start new scan" function */
@@ -79,7 +78,7 @@ typedef FormData_pg_am *Form_pg_am;
  *		compiler constants for pg_am
  * ----------------
  */
-#define Natts_pg_am						24
+#define Natts_pg_am						23
 #define Anum_pg_am_amname				1
 #define Anum_pg_am_amstrategies			2
 #define Anum_pg_am_amsupport			3
@@ -89,37 +88,36 @@ typedef FormData_pg_am *Form_pg_am;
 #define Anum_pg_am_amoptionalkey		7
 #define Anum_pg_am_amindexnulls			8
 #define Anum_pg_am_amstorage			9
-#define Anum_pg_am_amconcurrent			10
-#define Anum_pg_am_amclusterable		11
-#define Anum_pg_am_aminsert				12
-#define Anum_pg_am_ambeginscan			13
-#define Anum_pg_am_amgettuple			14
-#define Anum_pg_am_amgetmulti			15
-#define Anum_pg_am_amrescan				16
-#define Anum_pg_am_amendscan			17
-#define Anum_pg_am_ammarkpos			18
-#define Anum_pg_am_amrestrpos			19
-#define Anum_pg_am_ambuild				20
-#define Anum_pg_am_ambulkdelete			21
-#define Anum_pg_am_amvacuumcleanup		22
-#define Anum_pg_am_amcostestimate		23
-#define Anum_pg_am_amoptions			24
+#define Anum_pg_am_amclusterable		10
+#define Anum_pg_am_aminsert				11
+#define Anum_pg_am_ambeginscan			12
+#define Anum_pg_am_amgettuple			13
+#define Anum_pg_am_amgetmulti			14
+#define Anum_pg_am_amrescan				15
+#define Anum_pg_am_amendscan			16
+#define Anum_pg_am_ammarkpos			17
+#define Anum_pg_am_amrestrpos			18
+#define Anum_pg_am_ambuild				19
+#define Anum_pg_am_ambulkdelete			20
+#define Anum_pg_am_amvacuumcleanup		21
+#define Anum_pg_am_amcostestimate		22
+#define Anum_pg_am_amoptions			23
 
 /* ----------------
  *		initial contents of pg_am
  * ----------------
  */
 
-DATA(insert OID = 403 (  btree	5 1 1 t t t t f t t btinsert btbeginscan btgettuple btgetmulti btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoptions ));
+DATA(insert OID = 403 (  btree	5 1 1 t t t t f t btinsert btbeginscan btgettuple btgetmulti btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoptions ));
 DESCR("b-tree index access method");
 #define BTREE_AM_OID 403
-DATA(insert OID = 405 (  hash	1 1 0 f f f f f t f hashinsert hashbeginscan hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions ));
+DATA(insert OID = 405 (  hash	1 1 0 f f f f f f hashinsert hashbeginscan hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions ));
 DESCR("hash index access method");
 #define HASH_AM_OID 405
-DATA(insert OID = 783 (  gist	100 7 0 f t t t t t t gistinsert gistbeginscan gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions ));
+DATA(insert OID = 783 (  gist	100 7 0 f t t t t t gistinsert gistbeginscan gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions ));
 DESCR("GiST index access method");
 #define GIST_AM_OID 783
-DATA(insert OID = 2742 (  gin	100 4 0 f f f f t t f gininsert ginbeginscan gingettuple gingetmulti ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
+DATA(insert OID = 2742 (  gin	100 4 0 f f f f t f gininsert ginbeginscan gingettuple gingetmulti ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
 DESCR("GIN index access method");
 #define GIN_AM_OID 2742
 
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h
index c0e86cb80e..322ae97741 100644
--- a/src/include/optimizer/plancat.h
+++ b/src/include/optimizer/plancat.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/plancat.h,v 1.39 2006/03/05 15:58:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/plancat.h,v 1.40 2006/07/31 20:09:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,7 +17,7 @@
 #include "nodes/relation.h"
 
 
-extern void get_relation_info(Oid relationObjectId, RelOptInfo *rel);
+extern void get_relation_info(PlannerInfo *root, Oid relationObjectId, RelOptInfo *rel);
 
 extern bool relation_excluded_by_constraints(RelOptInfo *rel,
 											 RangeTblEntry *rte);
diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h
index 1eaede10a0..d0f9ba2b9c 100644
--- a/src/include/storage/lmgr.h
+++ b/src/include/storage/lmgr.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.54 2006/03/05 15:58:59 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.55 2006/07/31 20:09:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,13 +21,16 @@
 extern void RelationInitLockInfo(Relation relation);
 
 /* Lock a relation */
+extern void LockRelationOid(Oid relid, LOCKMODE lockmode);
+extern bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode);
+extern void UnlockRelationId(LockRelId *relid, LOCKMODE lockmode);
+
 extern void LockRelation(Relation relation, LOCKMODE lockmode);
 extern bool ConditionalLockRelation(Relation relation, LOCKMODE lockmode);
 extern void UnlockRelation(Relation relation, LOCKMODE lockmode);
 
-extern void LockRelationForSession(LockRelId *relid, bool istemprel,
-					   LOCKMODE lockmode);
-extern void UnlockRelationForSession(LockRelId *relid, LOCKMODE lockmode);
+extern void LockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode);
+extern void UnlockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode);
 
 /* Lock a relation for extension */
 extern void LockRelationForExtension(Relation relation, LOCKMODE lockmode);
@@ -62,4 +65,7 @@ extern void LockSharedObject(Oid classid, Oid objid, uint16 objsubid,
 extern void UnlockSharedObject(Oid classid, Oid objid, uint16 objsubid,
 				   LOCKMODE lockmode);
 
+/* Knowledge about which locktags describe temp objects */
+extern bool LockTagIsTemp(const LOCKTAG *tag);
+
 #endif   /* LMGR_H */
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index 7a2651cd28..208b4a93cc 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.96 2006/07/23 23:08:46 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.97 2006/07/31 20:09:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -349,7 +349,6 @@ typedef struct LOCALLOCK
 	/* data */
 	LOCK	   *lock;			/* associated LOCK object in shared mem */
 	PROCLOCK   *proclock;		/* associated PROCLOCK object in shmem */
-	bool		isTempObject;	/* true if lock is on a temporary object */
 	uint32		hashcode;		/* copy of LOCKTAG's hash value */
 	int			nLocks;			/* total number of times lock is held */
 	int			numLockOwners;	/* # of relevant ResourceOwners */
@@ -405,7 +404,6 @@ extern void InitLocks(void);
 extern LockMethod GetLocksMethodTable(const LOCK *lock);
 extern uint32 LockTagHashCode(const LOCKTAG *locktag);
 extern LockAcquireResult LockAcquire(const LOCKTAG *locktag,
-			bool isTempObject,
 			LOCKMODE lockmode,
 			bool sessionLock,
 			bool dontWait);
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index b30387a7ef..aa2b1608c1 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.54 2006/05/04 18:51:36 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.55 2006/07/31 20:09:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,13 +17,9 @@
 #include "utils/rel.h"
 
 /*
- * relation lookup routines
+ * Routines to open (lookup) and close a relcache entry
  */
 extern Relation RelationIdGetRelation(Oid relationId);
-
-/* finds an existing cache entry, but won't make a new one */
-extern Relation RelationIdCacheGetRelation(Oid relationId);
-
 extern void RelationClose(Relation relation);
 
 /*