diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 5c3a6dc81ce890f514bcfba8f124a5a71668b9ca..e36a1450db38f08b61c8b2a5075c71e126da53ff 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.30 2001/10/28 06:25:41 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.31 2002/02/19 20:11:10 tgl Exp $
  *
  * NOTES
  *	  many of the old access method routines have been turned into
@@ -44,12 +44,14 @@
  *		next item pointer using the flags.
  * ----------------------------------------------------------------
  */
-
 #include "postgres.h"
-#include "access/genam.h"
 
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "miscadmin.h"
 #include "pgstat.h"
 
+
 /* ----------------------------------------------------------------
  *		general access method routines
  *
@@ -242,3 +244,156 @@ IndexScanRestorePosition(IndexScanDesc scan)
 }
 
 #endif
+
+
+/* ----------------------------------------------------------------
+ *		heap-or-index-scan access to system catalogs
+ *
+ *		These functions support system catalog accesses that normally use
+ *		an index but need to be capable of being switched to heap scans
+ *		if the system indexes are unavailable.  The interface is
+ *		as easy to use as a heap scan, and hides all the extra cruft of
+ *		the present indexscan API.
+ *
+ *		The specified scan keys must be compatible with the named index.
+ *		Generally this means that they must constrain either all columns
+ *		of the index, or the first K columns of an N-column index.
+ *
+ *		These routines would work fine with non-system tables, actually,
+ *		but they're only useful when there is a known index to use with
+ *		the given scan keys, so in practice they're only good for
+ *		predetermined types of scans of system catalogs.
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * systable_beginscan --- set up for heap-or-index scan
+ *
+ *	rel: catalog to scan, already opened and suitably locked
+ *	indexRelname: name of index to conditionally use
+ *	indexOK: if false, forces a heap scan (see notes below)
+ *	snapshot: time qual to use (usually should be SnapshotNow)
+ *	nkeys, key: scan keys
+ *
+ * The attribute numbers in the scan key should be set for the heap case.
+ * If we choose to index, we reset them to 1..n to reference the index
+ * columns.  Note this means there must be one scankey qualification per
+ * index column!  This is checked by the Asserts in the normal, index-using
+ * case, but won't be checked if the heapscan path is taken.
+ *
+ * The routine checks the normal cases for whether an indexscan is safe,
+ * but caller can make additional checks and pass indexOK=false if needed.
+ * In standard case indexOK can simply be constant TRUE.
+ */
+SysScanDesc
+systable_beginscan(Relation rel,
+				   const char *indexRelname,
+				   bool indexOK,
+				   Snapshot snapshot,
+				   unsigned nkeys, ScanKey key)
+{
+	SysScanDesc sysscan;
+
+	sysscan = (SysScanDesc) palloc(sizeof(SysScanDescData));
+	sysscan->heap_rel = rel;
+	sysscan->snapshot = snapshot;
+	sysscan->tuple.t_datamcxt = NULL;
+	sysscan->tuple.t_data = NULL;
+	sysscan->buffer = InvalidBuffer;
+
+	if (indexOK &&
+		rel->rd_rel->relhasindex &&
+		!IsIgnoringSystemIndexes())
+	{
+		Relation	irel;
+		unsigned	i;
+
+		sysscan->irel = irel = index_openr(indexRelname);
+		/*
+		 * Change attribute numbers to be index column numbers.
+		 *
+		 * This code could be generalized to search for the index key numbers
+		 * to substitute, but for now there's no need.
+		 */
+		for (i = 0; i < nkeys; i++)
+		{
+			Assert(key[i].sk_attno == irel->rd_index->indkey[i]);
+			key[i].sk_attno = i+1;
+		}
+		sysscan->iscan = index_beginscan(irel, false, nkeys, key);
+		sysscan->scan = NULL;
+	}
+	else
+	{
+		sysscan->irel = (Relation) NULL;
+		sysscan->scan = heap_beginscan(rel, false, snapshot, nkeys, key);
+		sysscan->iscan = NULL;
+	}
+
+	return sysscan;
+}
+
+/*
+ * systable_getnext --- get next tuple in a heap-or-index scan
+ *
+ * Returns NULL if no more tuples available.
+ *
+ * Note that returned tuple is a reference to data in a disk buffer;
+ * it must not be modified, and should be presumed inaccessible after
+ * next getnext() or endscan() call.
+ */
+HeapTuple
+systable_getnext(SysScanDesc sysscan)
+{
+	HeapTuple	htup = (HeapTuple) NULL;
+
+	if (sysscan->irel)
+	{
+		RetrieveIndexResult indexRes;
+
+		if (BufferIsValid(sysscan->buffer))
+		{
+			ReleaseBuffer(sysscan->buffer);
+			sysscan->buffer = InvalidBuffer;
+		}
+
+		while ((indexRes = index_getnext(sysscan->iscan, ForwardScanDirection)) != NULL)
+		{
+			sysscan->tuple.t_self = indexRes->heap_iptr;
+			pfree(indexRes);
+			heap_fetch(sysscan->heap_rel, sysscan->snapshot,
+					   &sysscan->tuple, &sysscan->buffer,
+					   sysscan->iscan);
+			if (sysscan->tuple.t_data != NULL)
+			{
+				htup = &sysscan->tuple;
+				break;
+			}
+		}
+	}
+	else
+		htup = heap_getnext(sysscan->scan, 0);
+
+	return htup;
+}
+
+/*
+ * systable_endscan --- close scan, release resources
+ *
+ * Note that it's still up to the caller to close the heap relation.
+ */
+void
+systable_endscan(SysScanDesc sysscan)
+{
+	if (sysscan->irel)
+	{
+		if (BufferIsValid(sysscan->buffer))
+			ReleaseBuffer(sysscan->buffer);
+		index_endscan(sysscan->iscan);
+		index_close(sysscan->irel);
+	}
+	else
+		heap_endscan(sysscan->scan);
+
+	pfree(sysscan);
+}
diff --git a/src/backend/access/index/istrat.c b/src/backend/access/index/istrat.c
index 9e0fb8fe13371fb5ec5d823c8bb971022c6caf43..9366534bbac439bdefac895cde9828a1ef410e7b 100644
--- a/src/backend/access/index/istrat.c
+++ b/src/backend/access/index/istrat.c
@@ -9,29 +9,19 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/index/Attic/istrat.c,v 1.56 2001/11/05 17:46:24 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/index/Attic/istrat.c,v 1.57 2002/02/19 20:11:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
-#include "access/heapam.h"
 #include "access/istrat.h"
-#include "catalog/catname.h"
-#include "catalog/pg_amop.h"
-#include "catalog/pg_amproc.h"
-#include "catalog/pg_index.h"
-#include "catalog/pg_operator.h"
-#include "miscadmin.h"
-#include "utils/fmgroids.h"
-#include "utils/syscache.h"
+
 
 #ifdef USE_ASSERT_CHECKING
 static bool StrategyEvaluationIsValid(StrategyEvaluation evaluation);
 static bool StrategyExpressionIsValid(StrategyExpression expression,
 						  StrategyNumber maxStrategy);
-static ScanKey StrategyMapGetScanKeyEntry(StrategyMap map,
-						   StrategyNumber strategyNumber);
 static bool StrategyOperatorIsValid(StrategyOperator operator,
 						StrategyNumber maxStrategy);
 static bool StrategyTermIsValid(StrategyTerm term,
@@ -63,7 +53,7 @@ static bool StrategyTermIsValid(StrategyTerm term,
  *		Assumes that the index strategy number is valid.
  *		Bounds checking should be done outside this routine.
  */
-static ScanKey
+ScanKey
 StrategyMapGetScanKeyEntry(StrategyMap map,
 						   StrategyNumber strategyNumber)
 {
@@ -453,161 +443,6 @@ RelationInvokeStrategy(Relation relation,
 }
 #endif
 
-/* ----------------
- *		FillScanKeyEntry
- *
- * Initialize a ScanKey entry for the given operator OID.
- * ----------------
- */
-static void
-FillScanKeyEntry(Oid operatorObjectId, ScanKey entry)
-{
-	HeapTuple	tuple;
-
-	tuple = SearchSysCache(OPEROID,
-						   ObjectIdGetDatum(operatorObjectId),
-						   0, 0, 0);
-
-	if (!HeapTupleIsValid(tuple))
-		elog(ERROR, "FillScanKeyEntry: unknown operator %u",
-			 operatorObjectId);
-
-	MemSet(entry, 0, sizeof(*entry));
-	entry->sk_flags = 0;
-	entry->sk_procedure = ((Form_pg_operator) GETSTRUCT(tuple))->oprcode;
-
-	ReleaseSysCache(tuple);
-
-	if (!RegProcedureIsValid(entry->sk_procedure))
-		elog(ERROR, "FillScanKeyEntry: no procedure for operator %u",
-			 operatorObjectId);
-
-	/*
-	 * Mark entry->sk_func invalid, until and unless someone sets it up.
-	 */
-	entry->sk_func.fn_oid = InvalidOid;
-}
-
-
-/*
- * IndexSupportInitialize
- *		Initializes an index strategy and associated support procedures.
- *
- * Data is returned into *indexStrategy, *indexSupport, and *isUnique,
- * all of which are objects allocated by the caller.
- *
- * The primary input keys are indexObjectId and accessMethodObjectId.
- * The caller also passes maxStrategyNumber, maxSupportNumber, and
- * maxAttributeNumber, since these indicate the size of the indexStrategy
- * and indexSupport arrays it has allocated --- but in practice these
- * numbers must always match those obtainable from the system catalog
- * entries for the index and access method.
- */
-void
-IndexSupportInitialize(IndexStrategy indexStrategy,
-					   RegProcedure *indexSupport,
-					   bool *isUnique,
-					   Oid indexObjectId,
-					   Oid accessMethodObjectId,
-					   StrategyNumber maxStrategyNumber,
-					   StrategyNumber maxSupportNumber,
-					   AttrNumber maxAttributeNumber)
-{
-	HeapTuple	tuple;
-	Form_pg_index iform;
-	int			attIndex;
-	Oid			operatorClassObjectId[INDEX_MAX_KEYS];
-
-	maxStrategyNumber = AMStrategies(maxStrategyNumber);
-
-	tuple = SearchSysCache(INDEXRELID,
-						   ObjectIdGetDatum(indexObjectId),
-						   0, 0, 0);
-	if (!HeapTupleIsValid(tuple))
-		elog(ERROR, "IndexSupportInitialize: no pg_index entry for index %u",
-			 indexObjectId);
-	iform = (Form_pg_index) GETSTRUCT(tuple);
-
-	*isUnique = iform->indisunique;
-
-	/*
-	 * XXX note that the following assumes the INDEX tuple is well formed
-	 * and that the *key and *class are 0 terminated.
-	 */
-	for (attIndex = 0; attIndex < maxAttributeNumber; attIndex++)
-	{
-		if (iform->indkey[attIndex] == InvalidAttrNumber ||
-			!OidIsValid(iform->indclass[attIndex]))
-			elog(ERROR, "IndexSupportInitialize: bogus pg_index tuple");
-		operatorClassObjectId[attIndex] = iform->indclass[attIndex];
-	}
-
-	ReleaseSysCache(tuple);
-
-	/* if support routines exist for this access method, load them */
-	if (maxSupportNumber > 0)
-	{
-		for (attIndex = 0; attIndex < maxAttributeNumber; attIndex++)
-		{
-			Oid			opclass = operatorClassObjectId[attIndex];
-			RegProcedure *loc;
-			StrategyNumber support;
-
-			loc = &indexSupport[attIndex * maxSupportNumber];
-
-			for (support = 0; support < maxSupportNumber; ++support)
-			{
-				tuple = SearchSysCache(AMPROCNUM,
-									   ObjectIdGetDatum(opclass),
-									   Int16GetDatum(support + 1),
-									   0, 0);
-				if (HeapTupleIsValid(tuple))
-				{
-					Form_pg_amproc amprocform;
-
-					amprocform = (Form_pg_amproc) GETSTRUCT(tuple);
-					loc[support] = amprocform->amproc;
-					ReleaseSysCache(tuple);
-				}
-				else
-					loc[support] = InvalidOid;
-			}
-		}
-	}
-
-	/* Now load the strategy information for the index operators */
-	for (attIndex = 0; attIndex < maxAttributeNumber; attIndex++)
-	{
-		Oid			opclass = operatorClassObjectId[attIndex];
-		StrategyMap map;
-		StrategyNumber strategy;
-
-		map = IndexStrategyGetStrategyMap(indexStrategy,
-										  maxStrategyNumber,
-										  attIndex + 1);
-
-		for (strategy = 1; strategy <= maxStrategyNumber; strategy++)
-		{
-			ScanKey		mapentry = StrategyMapGetScanKeyEntry(map, strategy);
-
-			tuple = SearchSysCache(AMOPSTRATEGY,
-								   ObjectIdGetDatum(opclass),
-								   Int16GetDatum(strategy),
-								   0, 0);
-			if (HeapTupleIsValid(tuple))
-			{
-				Form_pg_amop amopform;
-
-				amopform = (Form_pg_amop) GETSTRUCT(tuple);
-				FillScanKeyEntry(amopform->amopopr, mapentry);
-				ReleaseSysCache(tuple);
-			}
-			else
-				ScanKeyEntrySetIllegal(mapentry);
-		}
-	}
-}
-
 /* ----------------
  *		IndexStrategyDisplay
  * ----------------
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 809702ddec900014d89c9e1fa7295dcf4964341b..ad192dca5082372ad8a74ac650ce74b088bedea7 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.181 2001/11/12 00:00:55 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.182 2002/02/19 20:11:11 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -912,32 +912,21 @@ RelationRemoveInheritance(Relation relation)
 static void
 RelationRemoveIndexes(Relation relation)
 {
-	Relation	indexRelation;
-	HeapTuple	tuple;
-	HeapScanDesc scan;
-	ScanKeyData entry;
-
-	indexRelation = heap_openr(IndexRelationName, RowExclusiveLock);
+	List	   *indexoidlist,
+			   *indexoidscan;
 
-	ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_index_indrelid,
-						   F_OIDEQ,
-						   ObjectIdGetDatum(RelationGetRelid(relation)));
-
-	scan = heap_beginscan(indexRelation,
-						  false,
-						  SnapshotNow,
-						  1,
-						  &entry);
+	indexoidlist = RelationGetIndexList(relation);
 
-	while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
+	foreach(indexoidscan, indexoidlist)
 	{
-		index_drop(((Form_pg_index) GETSTRUCT(tuple))->indexrelid);
+		Oid			indexoid = lfirsti(indexoidscan);
+
+		index_drop(indexoid);
 		/* advance cmd counter to make catalog changes visible */
 		CommandCounterIncrement();
 	}
 
-	heap_endscan(scan);
-	heap_close(indexRelation, RowExclusiveLock);
+	freeList(indexoidlist);
 }
 
 /* --------------------------------
@@ -984,17 +973,21 @@ RelationTruncateIndexes(Oid heapId)
 {
 	Relation	indexRelation;
 	ScanKeyData entry;
-	HeapScanDesc scan;
+	SysScanDesc	scan;
 	HeapTuple	indexTuple;
 
 	/* Scan pg_index to find indexes on specified heap */
 	indexRelation = heap_openr(IndexRelationName, AccessShareLock);
-	ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indrelid, F_OIDEQ,
+	ScanKeyEntryInitialize(&entry, 0,
+						   Anum_pg_index_indrelid,
+						   F_OIDEQ,
 						   ObjectIdGetDatum(heapId));
-	scan = heap_beginscan(indexRelation, false, SnapshotNow, 1, &entry);
+	scan = systable_beginscan(indexRelation, IndexIndrelidIndex, true,
+							  SnapshotNow, 1, &entry);
 
-	while (HeapTupleIsValid(indexTuple = heap_getnext(scan, 0)))
+	while (HeapTupleIsValid(indexTuple = systable_getnext(scan)))
 	{
+		Form_pg_index indexform = (Form_pg_index) GETSTRUCT(indexTuple);
 		Oid			indexId;
 		IndexInfo  *indexInfo;
 		Relation	heapRelation,
@@ -1003,8 +996,8 @@ RelationTruncateIndexes(Oid heapId)
 		/*
 		 * For each index, fetch info needed for index_build
 		 */
-		indexId = ((Form_pg_index) GETSTRUCT(indexTuple))->indexrelid;
-		indexInfo = BuildIndexInfo(indexTuple);
+		indexId = indexform->indexrelid;
+		indexInfo = BuildIndexInfo(indexform);
 
 		/*
 		 * We have to re-open the heap rel each time through this loop
@@ -1041,7 +1034,7 @@ RelationTruncateIndexes(Oid heapId)
 	}
 
 	/* Complete the scan and close pg_index */
-	heap_endscan(scan);
+	systable_endscan(scan);
 	heap_close(indexRelation, AccessShareLock);
 }
 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index ed57db68707e1c346840cceccdf47d75c4b8feea..bd6bd1de38aa56675c768a17bae1b2c761fc3af9 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.171 2002/01/06 00:37:44 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.172 2002/02/19 20:11:11 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -333,67 +333,6 @@ ConstructTupleDescriptor(Relation heapRelation,
 	return indexTupDesc;
 }
 
-/* ----------------------------------------------------------------
- * AccessMethodObjectIdGetForm
- *		Returns an access method tuple given its object identifier,
- *		or NULL if no such AM tuple can be found.
- *
- * Scanning is done using CurrentMemoryContext as working storage,
- * but the returned tuple will be allocated in resultCxt (which is
- * typically CacheMemoryContext).
- *
- * There was a note here about adding indexing, but I don't see a need
- * for it.	There are so few tuples in pg_am that an indexscan would
- * surely be slower.
- * ----------------------------------------------------------------
- */
-Form_pg_am
-AccessMethodObjectIdGetForm(Oid accessMethodObjectId,
-							MemoryContext resultCxt)
-{
-	Relation	pg_am_desc;
-	HeapScanDesc pg_am_scan;
-	HeapTuple	pg_am_tuple;
-	ScanKeyData key;
-	Form_pg_am	aform;
-
-	/*
-	 * form a scan key for the pg_am relation
-	 */
-	ScanKeyEntryInitialize(&key, 0, ObjectIdAttributeNumber,
-						   F_OIDEQ,
-						   ObjectIdGetDatum(accessMethodObjectId));
-
-	/*
-	 * fetch the desired access method tuple
-	 */
-	pg_am_desc = heap_openr(AccessMethodRelationName, AccessShareLock);
-	pg_am_scan = heap_beginscan(pg_am_desc, 0, SnapshotNow, 1, &key);
-
-	pg_am_tuple = heap_getnext(pg_am_scan, 0);
-
-	/*
-	 * return NULL if not found
-	 */
-	if (!HeapTupleIsValid(pg_am_tuple))
-	{
-		heap_endscan(pg_am_scan);
-		heap_close(pg_am_desc, AccessShareLock);
-		return NULL;
-	}
-
-	/*
-	 * if found AM tuple, then copy it into resultCxt and return the copy
-	 */
-	aform = (Form_pg_am) MemoryContextAlloc(resultCxt, sizeof *aform);
-	memcpy(aform, GETSTRUCT(pg_am_tuple), sizeof *aform);
-
-	heap_endscan(pg_am_scan);
-	heap_close(pg_am_desc, AccessShareLock);
-
-	return aform;
-}
-
 /* ----------------------------------------------------------------
  *		ConstructIndexReldesc
  * ----------------------------------------------------------------
@@ -401,12 +340,6 @@ AccessMethodObjectIdGetForm(Oid accessMethodObjectId,
 static void
 ConstructIndexReldesc(Relation indexRelation, Oid amoid)
 {
-	/*
-	 * Fill in a copy of relevant pg_am entry
-	 */
-	indexRelation->rd_am = AccessMethodObjectIdGetForm(amoid,
-													 CacheMemoryContext);
-
 	/*
 	 * Set up some additional fields of the index' pg_class entry. In
 	 * particular, initialize knowledge of whether the index is shared.
@@ -953,9 +886,8 @@ index_drop(Oid indexId)
  * ----------------
  */
 IndexInfo *
-BuildIndexInfo(HeapTuple indexTuple)
+BuildIndexInfo(Form_pg_index indexStruct)
 {
-	Form_pg_index indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
 	IndexInfo  *ii = makeNode(IndexInfo);
 	int			i;
 	int			numKeys;
@@ -1337,9 +1269,6 @@ setNewRelfilenode(Relation relation)
 	}
 	/* schedule unlinking old relfilenode */
 	smgrunlink(DEFAULT_SMGR, relation);
-	/* cleanup pg_internal.init if necessary */
-	if (relation->rd_isnailed)
-		unlink(RELCACHE_INIT_FILENAME);
 	/* create another storage file. Is it a little ugly ? */
 	memcpy((char *) &workrel, relation, sizeof(RelationData));
 	workrel.rd_node.relNode = newrelfilenode;
@@ -1863,11 +1792,7 @@ bool
 reindex_index(Oid indexId, bool force, bool inplace)
 {
 	Relation	iRel,
-				indexRelation,
 				heapRelation;
-	ScanKeyData entry;
-	HeapScanDesc scan;
-	HeapTuple	indexTuple;
 	IndexInfo  *indexInfo;
 	Oid			heapId;
 	bool		old;
@@ -1899,23 +1824,10 @@ reindex_index(Oid indexId, bool force, bool inplace)
 
 	old = SetReindexProcessing(true);
 
-	/* Scan pg_index to find the index's pg_index entry */
-	indexRelation = heap_openr(IndexRelationName, AccessShareLock);
-	ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indexrelid, F_OIDEQ,
-						   ObjectIdGetDatum(indexId));
-	scan = heap_beginscan(indexRelation, false, SnapshotNow, 1, &entry);
-	indexTuple = heap_getnext(scan, 0);
-	if (!HeapTupleIsValid(indexTuple))
-		elog(ERROR, "reindex_index: index %u not found in pg_index", indexId);
-
 	/* Get OID of index's parent table */
-	heapId = ((Form_pg_index) GETSTRUCT(indexTuple))->indrelid;
+	heapId = iRel->rd_index->indrelid;
 	/* Fetch info needed for index_build */
-	indexInfo = BuildIndexInfo(indexTuple);
-
-	/* Complete the scan and close pg_index */
-	heap_endscan(scan);
-	heap_close(indexRelation, AccessShareLock);
+	indexInfo = BuildIndexInfo(iRel->rd_index);
 
 	/* Open the parent heap relation */
 	heapRelation = heap_open(heapId, ExclusiveLock);
diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
index 28b8516947354dcb2c02cad2b391265fdf9ad505..e600eab6a30e3e0ac640b20410c6bda26f019f95 100644
--- a/src/backend/catalog/indexing.c
+++ b/src/backend/catalog/indexing.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.82 2001/08/21 16:36:01 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.83 2002/02/19 20:11:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -80,12 +80,6 @@ char	   *Name_pg_description_indices[Num_pg_description_indices] =
 
 
 
-static HeapTuple CatalogIndexFetchTuple(Relation heapRelation,
-					   Relation idesc,
-					   ScanKey skey,
-					   int16 num_keys);
-
-
 /*
  * Changes (appends) to catalogs can and do happen at various places
  * throughout the code.  We need a generic routine that will open all of
@@ -151,18 +145,10 @@ CatalogIndexInsert(Relation *idescs,
 
 	for (i = 0; i < nIndices; i++)
 	{
-		HeapTuple	index_tup;
 		IndexInfo  *indexInfo;
 		InsertIndexResult indexRes;
 
-		index_tup = SearchSysCache(INDEXRELID,
-								   ObjectIdGetDatum(idescs[i]->rd_id),
-								   0, 0, 0);
-		if (!HeapTupleIsValid(index_tup))
-			elog(ERROR, "CatalogIndexInsert: index %u not found",
-				 idescs[i]->rd_id);
-		indexInfo = BuildIndexInfo(index_tup);
-		ReleaseSysCache(index_tup);
+		indexInfo = BuildIndexInfo(idescs[i]->rd_index);
 
 		FormIndexDatum(indexInfo,
 					   heapTuple,
@@ -178,127 +164,3 @@ CatalogIndexInsert(Relation *idescs,
 		pfree(indexInfo);
 	}
 }
-
-
-/*
- *	CatalogIndexFetchTuple() -- Get a tuple that satisfies a scan key
- *								from a catalog relation.
- *
- *		Since the index may contain pointers to dead tuples, we need to
- *		iterate until we find a tuple that's valid and satisfies the scan
- *		key.
- */
-static HeapTuple
-CatalogIndexFetchTuple(Relation heapRelation,
-					   Relation idesc,
-					   ScanKey skey,
-					   int16 num_keys)
-{
-	IndexScanDesc sd;
-	RetrieveIndexResult indexRes;
-	HeapTupleData tuple;
-	HeapTuple	result = NULL;
-	Buffer		buffer;
-
-	sd = index_beginscan(idesc, false, num_keys, skey);
-	tuple.t_datamcxt = CurrentMemoryContext;
-	tuple.t_data = NULL;
-	while ((indexRes = index_getnext(sd, ForwardScanDirection)))
-	{
-		tuple.t_self = indexRes->heap_iptr;
-		heap_fetch(heapRelation, SnapshotNow, &tuple, &buffer, sd);
-		pfree(indexRes);
-		if (tuple.t_data != NULL)
-			break;
-	}
-
-	if (tuple.t_data != NULL)
-	{
-		result = heap_copytuple(&tuple);
-		ReleaseBuffer(buffer);
-	}
-
-	index_endscan(sd);
-
-	return result;
-}
-
-
-/*---------------------------------------------------------------------
- *						 Class-specific index lookups
- *---------------------------------------------------------------------
- */
-
-/*
- * The remainder of the file is for individual index scan routines.
- * These routines provide canned scanning code for certain widely-used
- * indexes.  Most indexes don't need one of these.
- */
-
-
-HeapTuple
-AttributeRelidNumIndexScan(Relation heapRelation,
-						   Datum relid,
-						   Datum attnum)
-{
-	Relation	idesc;
-	ScanKeyData skey[2];
-	HeapTuple	tuple;
-
-	ScanKeyEntryInitialize(&skey[0],
-						   (bits16) 0x0,
-						   (AttrNumber) 1,
-						   (RegProcedure) F_OIDEQ,
-						   relid);
-
-	ScanKeyEntryInitialize(&skey[1],
-						   (bits16) 0x0,
-						   (AttrNumber) 2,
-						   (RegProcedure) F_INT2EQ,
-						   attnum);
-
-	idesc = index_openr(AttributeRelidNumIndex);
-	tuple = CatalogIndexFetchTuple(heapRelation, idesc, skey, 2);
-	index_close(idesc);
-	return tuple;
-}
-
-
-HeapTuple
-ClassNameIndexScan(Relation heapRelation, Datum relName)
-{
-	Relation	idesc;
-	ScanKeyData skey[1];
-	HeapTuple	tuple;
-
-	ScanKeyEntryInitialize(&skey[0],
-						   (bits16) 0x0,
-						   (AttrNumber) 1,
-						   (RegProcedure) F_NAMEEQ,
-						   relName);
-
-	idesc = index_openr(ClassNameIndex);
-	tuple = CatalogIndexFetchTuple(heapRelation, idesc, skey, 1);
-	index_close(idesc);
-	return tuple;
-}
-
-
-HeapTuple
-ClassOidIndexScan(Relation heapRelation, Datum relId)
-{
-	Relation	idesc;
-	ScanKeyData skey[1];
-	HeapTuple	tuple;
-
-	ScanKeyEntryInitialize(&skey[0],
-						   (bits16) 0x0,
-						   (AttrNumber) 1,
-						   (RegProcedure) F_OIDEQ,
-						   relId);
-
-	idesc = index_openr(ClassOidIndex);
-	tuple = CatalogIndexFetchTuple(heapRelation, idesc, skey, 1);
-	index_close(idesc);
-	return tuple;
-}
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 663a2b22008b4c8cf4fbc12c6feb43394a2c5082..f37394874264a6279565bf3a96cd3b01b08dea52 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.71 2002/01/06 00:37:44 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.72 2002/02/19 20:11:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -61,7 +61,6 @@ cluster(char *oldrelname, char *oldindexname)
 				OIDNewHeap;
 	Relation	OldHeap,
 				OldIndex;
-	HeapTuple	tuple;
 	bool		istemp;
 	char		NewHeapName[NAMEDATALEN];
 	char		NewIndexName[NAMEDATALEN];
@@ -90,16 +89,9 @@ cluster(char *oldrelname, char *oldindexname)
 	/*
 	 * Check that index is in fact an index on the given relation
 	 */
-	tuple = SearchSysCache(INDEXRELID,
-						   ObjectIdGetDatum(OIDOldIndex),
-						   0, 0, 0);
-	if (!HeapTupleIsValid(tuple))
-		elog(ERROR, "CLUSTER: no pg_index entry for index %u",
-			 OIDOldIndex);
-	if (((Form_pg_index) GETSTRUCT(tuple))->indrelid != OIDOldHeap)
+	if (OldIndex->rd_index->indrelid != OIDOldHeap)
 		elog(ERROR, "CLUSTER: \"%s\" is not an index for table \"%s\"",
 			 saveoldindexname, saveoldrelname);
-	ReleaseSysCache(tuple);
 
 	/* Drop relcache refcnts, but do NOT give up the locks */
 	heap_close(OldHeap, NoLock);
@@ -188,10 +180,6 @@ copy_index(Oid OIDOldIndex, Oid OIDNewHeap, char *NewIndexName)
 {
 	Relation	OldIndex,
 				NewHeap;
-	HeapTuple	Old_pg_index_Tuple,
-				Old_pg_index_relation_Tuple;
-	Form_pg_index Old_pg_index_Form;
-	Form_pg_class Old_pg_index_relation_Form;
 	IndexInfo  *indexInfo;
 
 	NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
@@ -206,33 +194,18 @@ copy_index(Oid OIDOldIndex, Oid OIDNewHeap, char *NewIndexName)
 	 * its parent table is, so we don't need to do anything special for
 	 * the temp-table case here.
 	 */
-	Old_pg_index_Tuple = SearchSysCache(INDEXRELID,
-										ObjectIdGetDatum(OIDOldIndex),
-										0, 0, 0);
-	Assert(Old_pg_index_Tuple);
-	Old_pg_index_Form = (Form_pg_index) GETSTRUCT(Old_pg_index_Tuple);
-
-	indexInfo = BuildIndexInfo(Old_pg_index_Tuple);
-
-	Old_pg_index_relation_Tuple = SearchSysCache(RELOID,
-										   ObjectIdGetDatum(OIDOldIndex),
-												 0, 0, 0);
-	Assert(Old_pg_index_relation_Tuple);
-	Old_pg_index_relation_Form = (Form_pg_class) GETSTRUCT(Old_pg_index_relation_Tuple);
+	indexInfo = BuildIndexInfo(OldIndex->rd_index);
 
 	index_create(RelationGetRelationName(NewHeap),
 				 NewIndexName,
 				 indexInfo,
-				 Old_pg_index_relation_Form->relam,
-				 Old_pg_index_Form->indclass,
-				 Old_pg_index_Form->indisprimary,
+				 OldIndex->rd_rel->relam,
+				 OldIndex->rd_index->indclass,
+				 OldIndex->rd_index->indisprimary,
 				 allowSystemTableMods);
 
 	setRelhasindex(OIDNewHeap, true,
-				   Old_pg_index_Form->indisprimary, InvalidOid);
-
-	ReleaseSysCache(Old_pg_index_Tuple);
-	ReleaseSysCache(Old_pg_index_relation_Tuple);
+				   OldIndex->rd_index->indisprimary, InvalidOid);
 
 	index_close(OldIndex);
 	heap_close(NewHeap, NoLock);
diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
index 9b1a33673c737eb2b7012f92ee8018bed7ef136c..f01cf4498fdd88bcdb8f95d3e257543394436429 100644
--- a/src/backend/commands/command.c
+++ b/src/backend/commands/command.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.153 2002/02/14 15:24:06 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.154 2002/02/19 20:11:12 tgl Exp $
  *
  * NOTES
  *	  The PerformAddAttribute() code, like most of the relation
@@ -29,6 +29,7 @@
 #include "catalog/pg_attrdef.h"
 #include "catalog/pg_index.h"
 #include "catalog/pg_opclass.h"
+#include "catalog/pg_relcheck.h"
 #include "catalog/pg_type.h"
 #include "commands/command.h"
 #include "commands/trigger.h"
@@ -820,92 +821,8 @@ AlterTableAlterColumnStatistics(const char *relationName,
 #ifdef	_DROP_COLUMN_HACK__
 /*
  *	ALTER TABLE DROP COLUMN trial implementation
- *
  */
 
-/*
- *	system table scan(index scan/sequential scan)
- */
-typedef struct SysScanDescData
-{
-	Relation	heap_rel;
-	Relation	irel;
-	HeapScanDesc scan;
-	IndexScanDesc iscan;
-	HeapTupleData tuple;
-	Buffer		buffer;
-}	SysScanDescData, *SysScanDesc;
-
-static void *
-systable_beginscan(Relation rel, const char *indexRelname, int nkeys, ScanKey entry)
-{
-	bool		hasindex = (rel->rd_rel->relhasindex && !IsIgnoringSystemIndexes());
-	SysScanDesc sysscan;
-
-	sysscan = (SysScanDesc) palloc(sizeof(SysScanDescData));
-	sysscan->heap_rel = rel;
-	sysscan->irel = (Relation) NULL;
-	sysscan->tuple.t_datamcxt = NULL;
-	sysscan->tuple.t_data = NULL;
-	sysscan->buffer = InvalidBuffer;
-	if (hasindex)
-	{
-		sysscan->irel = index_openr((char *) indexRelname);
-		sysscan->iscan = index_beginscan(sysscan->irel, false, nkeys, entry);
-	}
-	else
-		sysscan->scan = heap_beginscan(rel, false, SnapshotNow, nkeys, entry);
-	return (void *) sysscan;
-}
-
-static void
-systable_endscan(void *scan)
-{
-	SysScanDesc sysscan = (SysScanDesc) scan;
-
-	if (sysscan->irel)
-	{
-		if (BufferIsValid(sysscan->buffer))
-			ReleaseBuffer(sysscan->buffer);
-		index_endscan(sysscan->iscan);
-		index_close(sysscan->irel);
-	}
-	else
-		heap_endscan(sysscan->scan);
-	pfree(scan);
-}
-
-static HeapTuple
-systable_getnext(void *scan)
-{
-	SysScanDesc sysscan = (SysScanDesc) scan;
-	HeapTuple	htup = (HeapTuple) NULL;
-	RetrieveIndexResult indexRes;
-
-	if (sysscan->irel)
-	{
-		if (BufferIsValid(sysscan->buffer))
-		{
-			ReleaseBuffer(sysscan->buffer);
-			sysscan->buffer = InvalidBuffer;
-		}
-		while (indexRes = index_getnext(sysscan->iscan, ForwardScanDirection), indexRes != NULL)
-		{
-			sysscan->tuple.t_self = indexRes->heap_iptr;
-			heap_fetch(sysscan->heap_rel, SnapshotNow, &sysscan->tuple, &(sysscan->buffer));
-			pfree(indexRes);
-			if (sysscan->tuple.t_data != NULL)
-			{
-				htup = &sysscan->tuple;
-				break;
-			}
-		}
-	}
-	else
-		htup = heap_getnext(sysscan->scan, 0);
-	return htup;
-}
-
 /*
  *	find a specified attribute in a node entry
  */
@@ -957,10 +874,15 @@ RemoveColumnReferences(Oid reloid, int attnum, bool checkonly, HeapTuple reltup)
 	/*
 	 * Remove/check constraints here
 	 */
-	ScanKeyEntryInitialize(&entry, (bits16) 0x0, Anum_pg_relcheck_rcrelid,
-					   (RegProcedure) F_OIDEQ, ObjectIdGetDatum(reloid));
+	ScanKeyEntryInitialize(&entry, (bits16) 0x0,
+						   Anum_pg_relcheck_rcrelid,
+						   (RegProcedure) F_OIDEQ,
+						   ObjectIdGetDatum(reloid));
+
 	rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock);
-	sysscan = systable_beginscan(rcrel, RelCheckIndex, 1, &entry);
+	sysscan = systable_beginscan(rcrel, RelCheckIndex, true,
+								 SnapshotNow,
+								 1, &entry);
 
 	while (HeapTupleIsValid(htup = systable_getnext(sysscan)))
 	{
@@ -987,6 +909,7 @@ RemoveColumnReferences(Oid reloid, int attnum, bool checkonly, HeapTuple reltup)
 			}
 		}
 	}
+
 	systable_endscan(sysscan);
 	heap_close(rcrel, NoLock);
 
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 528e53ee47ddd7bf5f38ebb30be28bec22cfbca2..402ff51e4bc267ac8e643fda1afb8902a275c06a 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.101 2002/01/15 16:52:47 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.102 2002/02/19 20:11:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,7 +56,7 @@ CreateTrigger(CreateTrigStmt *stmt)
 	char		nulls[Natts_pg_trigger];
 	Relation	rel;
 	Relation	tgrel;
-	HeapScanDesc tgscan;
+	SysScanDesc	tgscan;
 	ScanKeyData key;
 	Relation	pgrel;
 	HeapTuple	tuple;
@@ -151,8 +151,9 @@ CreateTrigger(CreateTrigStmt *stmt)
 	ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
 						   F_OIDEQ,
 						   ObjectIdGetDatum(RelationGetRelid(rel)));
-	tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key);
-	while (HeapTupleIsValid(tuple = heap_getnext(tgscan, 0)))
+	tgscan = systable_beginscan(tgrel, TriggerRelidIndex, true,
+								SnapshotNow, 1, &key);
+	while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
 	{
 		Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
 
@@ -161,7 +162,7 @@ CreateTrigger(CreateTrigStmt *stmt)
 				 stmt->trigname, stmt->relname);
 		found++;
 	}
-	heap_endscan(tgscan);
+	systable_endscan(tgscan);
 
 	/*
 	 * Find and validate the trigger function.
@@ -311,7 +312,7 @@ DropTrigger(DropTrigStmt *stmt)
 {
 	Relation	rel;
 	Relation	tgrel;
-	HeapScanDesc tgscan;
+	SysScanDesc	tgscan;
 	ScanKeyData key;
 	Relation	pgrel;
 	HeapTuple	tuple;
@@ -343,8 +344,9 @@ DropTrigger(DropTrigStmt *stmt)
 	ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
 						   F_OIDEQ,
 						   ObjectIdGetDatum(RelationGetRelid(rel)));
-	tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key);
-	while (HeapTupleIsValid(tuple = heap_getnext(tgscan, 0)))
+	tgscan = systable_beginscan(tgrel, TriggerRelidIndex, true,
+								SnapshotNow, 1, &key);
+	while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
 	{
 		Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
 
@@ -359,14 +361,15 @@ DropTrigger(DropTrigStmt *stmt)
 		else
 			found++;
 	}
+	systable_endscan(tgscan);
+	heap_close(tgrel, RowExclusiveLock);
+
 	if (tgfound == 0)
 		elog(ERROR, "DropTrigger: there is no trigger %s on relation %s",
 			 stmt->trigname, stmt->relname);
 	if (tgfound > 1)
 		elog(NOTICE, "DropTrigger: found (and deleted) %d triggers %s on relation %s",
 			 tgfound, stmt->trigname, stmt->relname);
-	heap_endscan(tgscan);
-	heap_close(tgrel, RowExclusiveLock);
 
 	/*
 	 * Update relation's pg_class entry.  Crucial side-effect: other
@@ -406,7 +409,7 @@ void
 RelationRemoveTriggers(Relation rel)
 {
 	Relation	tgrel;
-	HeapScanDesc tgscan;
+	SysScanDesc	tgscan;
 	ScanKeyData key;
 	HeapTuple	tup;
 	bool		found = false;
@@ -415,10 +418,10 @@ RelationRemoveTriggers(Relation rel)
 	ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
 						   F_OIDEQ,
 						   ObjectIdGetDatum(RelationGetRelid(rel)));
+	tgscan = systable_beginscan(tgrel, TriggerRelidIndex, true,
+								SnapshotNow, 1, &key);
 
-	tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key);
-
-	while (HeapTupleIsValid(tup = heap_getnext(tgscan, 0)))
+	while (HeapTupleIsValid(tup = systable_getnext(tgscan)))
 	{
 		/* Delete any comments associated with this trigger */
 		DeleteComments(tup->t_data->t_oid, RelationGetRelid(tgrel));
@@ -428,7 +431,7 @@ RelationRemoveTriggers(Relation rel)
 		found = true;
 	}
 
-	heap_endscan(tgscan);
+	systable_endscan(tgscan);
 
 	/*
 	 * If we deleted any triggers, must update pg_class entry and advance
@@ -468,9 +471,10 @@ RelationRemoveTriggers(Relation rel)
 	ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgconstrrelid,
 						   F_OIDEQ,
 						   ObjectIdGetDatum(RelationGetRelid(rel)));
+	tgscan = systable_beginscan(tgrel, TriggerConstrRelidIndex, true,
+								SnapshotNow, 1, &key);
 
-	tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key);
-	while (HeapTupleIsValid(tup = heap_getnext(tgscan, 0)))
+	while (HeapTupleIsValid(tup = systable_getnext(tgscan)))
 	{
 		Form_pg_trigger pg_trigger;
 		Relation	refrel;
@@ -500,7 +504,7 @@ RelationRemoveTriggers(Relation rel)
 		pfree(stmt.relname);
 		pfree(stmt.trigname);
 	}
-	heap_endscan(tgscan);
+	systable_endscan(tgscan);
 
 	heap_close(tgrel, RowExclusiveLock);
 }
@@ -518,69 +522,33 @@ RelationBuildTriggers(Relation relation)
 	TriggerDesc *trigdesc;
 	int			ntrigs = relation->rd_rel->reltriggers;
 	Trigger    *triggers = NULL;
-	Trigger    *build;
+	int			found = 0;
 	Relation	tgrel;
-	Form_pg_trigger pg_trigger;
-	Relation	irel = (Relation) NULL;
 	ScanKeyData skey;
-	HeapTupleData tuple;
-	IndexScanDesc sd = (IndexScanDesc) NULL;
-	HeapScanDesc tgscan = (HeapScanDesc) NULL;
+	SysScanDesc	tgscan;
 	HeapTuple	htup;
-	RetrieveIndexResult indexRes;
-	Buffer		buffer;
 	struct varlena *val;
 	bool		isnull;
-	int			found;
-	bool		hasindex;
-
-	trigdesc = (TriggerDesc *) MemoryContextAlloc(CacheMemoryContext,
-												  sizeof(TriggerDesc));
-	MemSet(trigdesc, 0, sizeof(TriggerDesc));
 
 	ScanKeyEntryInitialize(&skey,
 						   (bits16) 0x0,
-						   (AttrNumber) 1,
+						   (AttrNumber) Anum_pg_trigger_tgrelid,
 						   (RegProcedure) F_OIDEQ,
 						   ObjectIdGetDatum(RelationGetRelid(relation)));
 
 	tgrel = heap_openr(TriggerRelationName, AccessShareLock);
-	hasindex = (tgrel->rd_rel->relhasindex && !IsIgnoringSystemIndexes());
-	if (hasindex)
-	{
-		irel = index_openr(TriggerRelidIndex);
-		sd = index_beginscan(irel, false, 1, &skey);
-	}
-	else
-		tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &skey);
+	tgscan = systable_beginscan(tgrel, TriggerRelidIndex, true,
+								SnapshotNow, 1, &skey);
 
-	for (found = 0;;)
+	while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
 	{
-		if (hasindex)
-		{
-			indexRes = index_getnext(sd, ForwardScanDirection);
-			if (!indexRes)
-				break;
+		Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
+		Trigger    *build;
 
-			tuple.t_self = indexRes->heap_iptr;
-			heap_fetch(tgrel, SnapshotNow, &tuple, &buffer, sd);
-			pfree(indexRes);
-			if (!tuple.t_data)
-				continue;
-			htup = &tuple;
-		}
-		else
-		{
-			htup = heap_getnext(tgscan, 0);
-			if (!HeapTupleIsValid(htup))
-				break;
-		}
 		if (found == ntrigs)
 			elog(ERROR, "RelationBuildTriggers: unexpected record found for rel %s",
 				 RelationGetRelationName(relation));
 
-		pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
-
 		if (triggers == NULL)
 			triggers = (Trigger *) MemoryContextAlloc(CacheMemoryContext,
 													  sizeof(Trigger));
@@ -628,25 +596,20 @@ RelationBuildTriggers(Relation relation)
 			build->tgargs = NULL;
 
 		found++;
-		if (hasindex)
-			ReleaseBuffer(buffer);
 	}
 
-	if (found < ntrigs)
+	systable_endscan(tgscan);
+	heap_close(tgrel, AccessShareLock);
+
+	if (found != ntrigs)
 		elog(ERROR, "RelationBuildTriggers: %d record(s) not found for rel %s",
 			 ntrigs - found,
 			 RelationGetRelationName(relation));
 
-	if (hasindex)
-	{
-		index_endscan(sd);
-		index_close(irel);
-	}
-	else
-		heap_endscan(tgscan);
-	heap_close(tgrel, AccessShareLock);
-
 	/* Build trigdesc */
+	trigdesc = (TriggerDesc *) MemoryContextAlloc(CacheMemoryContext,
+												  sizeof(TriggerDesc));
+	MemSet(trigdesc, 0, sizeof(TriggerDesc));
 	trigdesc->triggers = triggers;
 	trigdesc->numtriggers = ntrigs;
 	for (found = 0; found < ntrigs; found++)
@@ -1741,14 +1704,12 @@ void
 DeferredTriggerSetState(ConstraintsSetStmt *stmt)
 {
 	Relation	tgrel;
-	Relation	irel = (Relation) NULL;
 	List	   *l;
 	List	   *ls;
 	List	   *loid = NIL;
 	MemoryContext oldcxt;
 	bool		found;
 	DeferredTriggerStatus state;
-	bool		hasindex;
 
 	/*
 	 * Handle SET CONSTRAINTS ALL ...
@@ -1817,21 +1778,12 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt)
 	 * ----------
 	 */
 	tgrel = heap_openr(TriggerRelationName, AccessShareLock);
-	hasindex = (tgrel->rd_rel->relhasindex && !IsIgnoringSystemIndexes());
-	if (hasindex)
-		irel = index_openr(TriggerConstrNameIndex);
 
 	foreach(l, stmt->constraints)
 	{
 		ScanKeyData skey;
-		HeapTupleData tuple;
-		IndexScanDesc sd = (IndexScanDesc) NULL;
-		HeapScanDesc tgscan = (HeapScanDesc) NULL;
+		SysScanDesc	tgscan;
 		HeapTuple	htup;
-		RetrieveIndexResult indexRes;
-		Buffer		buffer;
-		Form_pg_trigger pg_trigger;
-		Oid			constr_oid;
 
 		/*
 		 * Check that only named constraints are set explicitly
@@ -1844,47 +1796,28 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt)
 		 */
 		ScanKeyEntryInitialize(&skey,
 							   (bits16) 0x0,
-							   (AttrNumber) 1,
+							   (AttrNumber) Anum_pg_trigger_tgconstrname,
 							   (RegProcedure) F_NAMEEQ,
 							   PointerGetDatum((char *) lfirst(l)));
 
-		if (hasindex)
-			sd = index_beginscan(irel, false, 1, &skey);
-		else
-			tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &skey);
+		tgscan = systable_beginscan(tgrel, TriggerConstrNameIndex, true,
+									SnapshotNow, 1, &skey);
 
 		/*
 		 * ... and search for the constraint trigger row
 		 */
 		found = false;
-		for (;;)
-		{
-			if (hasindex)
-			{
-				indexRes = index_getnext(sd, ForwardScanDirection);
-				if (!indexRes)
-					break;
 
-				tuple.t_self = indexRes->heap_iptr;
-				heap_fetch(tgrel, SnapshotNow, &tuple, &buffer, sd);
-				pfree(indexRes);
-				if (!tuple.t_data)
-					continue;
-				htup = &tuple;
-			}
-			else
-			{
-				htup = heap_getnext(tgscan, 0);
-				if (!HeapTupleIsValid(htup))
-					break;
-			}
+		while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
+		{
+			Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
+			Oid			constr_oid;
 
 			/*
 			 * If we found some, check that they fit the deferrability but
 			 * skip ON <event> RESTRICT ones, since they are silently
 			 * never deferrable.
 			 */
-			pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
 			if (stmt->deferred && !pg_trigger->tgdeferrable &&
 				pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_UPD &&
 				pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_DEL)
@@ -1894,24 +1827,16 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt)
 			constr_oid = htup->t_data->t_oid;
 			loid = lappendi(loid, constr_oid);
 			found = true;
-
-			if (hasindex)
-				ReleaseBuffer(buffer);
 		}
 
+		systable_endscan(tgscan);
+
 		/*
 		 * Not found ?
 		 */
 		if (!found)
 			elog(ERROR, "Constraint '%s' does not exist", (char *) lfirst(l));
-
-		if (hasindex)
-			index_endscan(sd);
-		else
-			heap_endscan(tgscan);
 	}
-	if (hasindex)
-		index_close(irel);
 	heap_close(tgrel, AccessShareLock);
 
 	if (!IsTransactionBlock())
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 894a7e328559092b43b94eabfe35f2cc6874a260..a36ae25b3f840302bd1e40824acb45bcef3ce27e 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.213 2002/01/06 00:37:44 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.214 2002/02/19 20:11:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -315,20 +315,6 @@ vacuum_shutdown(VacuumStmt *vacstmt)
 		vac_truncate_clog(initialOldestXmin, initialFreezeLimit);
 	}
 
-	/*
-	 * If we did a complete vacuum or analyze, then flush the init file
-	 * that relcache.c uses to save startup time. The next backend startup
-	 * will rebuild the init file with up-to-date information from
-	 * pg_class. This lets the optimizer see the stats that we've
-	 * collected for certain critical system indexes.  See relcache.c for
-	 * more details.
-	 *
-	 * Ignore any failure to unlink the file, since it might not be there if
-	 * no backend has been started since the last vacuum.
-	 */
-	if (vacstmt->vacrel == NULL)
-		unlink(RELCACHE_INIT_FILENAME);
-
 	/*
 	 * Clean up working storage --- note we must do this after
 	 * StartTransactionCommand, else we might be trying to delete the
@@ -535,8 +521,15 @@ vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples,
 	if (!hasindex)
 		pgcform->relhaspkey = false;
 
-	/* invalidate the tuple in the cache and write the buffer */
+	/*
+	 * Invalidate the tuple in the catcaches; this also arranges to flush
+	 * the relation's relcache entry.  (If we fail to commit for some reason,
+	 * no flush will occur, but no great harm is done since there are no
+	 * noncritical state updates here.)
+	 */
 	RelationInvalidateHeapTuple(rd, &rtup);
+
+	/* Write the buffer */
 	WriteBuffer(buffer);
 
 	heap_close(rd, RowExclusiveLock);
@@ -2816,10 +2809,6 @@ vac_close_indexes(int nindexes, Relation *Irel)
 bool
 vac_is_partial_index(Relation indrel)
 {
-	bool		result;
-	HeapTuple	cachetuple;
-	Form_pg_index indexStruct;
-
 	/*
 	 * If the index's AM doesn't support nulls, it's partial for our
 	 * purposes
@@ -2828,18 +2817,7 @@ vac_is_partial_index(Relation indrel)
 		return true;
 
 	/* Otherwise, look to see if there's a partial-index predicate */
-	cachetuple = SearchSysCache(INDEXRELID,
-							  ObjectIdGetDatum(RelationGetRelid(indrel)),
-								0, 0, 0);
-	if (!HeapTupleIsValid(cachetuple))
-		elog(ERROR, "vac_is_partial_index: index %u not found",
-			 RelationGetRelid(indrel));
-	indexStruct = (Form_pg_index) GETSTRUCT(cachetuple);
-
-	result = (VARSIZE(&indexStruct->indpred) > VARHDRSZ);
-
-	ReleaseSysCache(cachetuple);
-	return result;
+	return (VARSIZE(&indrel->rd_index->indpred) > VARHDRSZ);
 }
 
 
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index af0e190d396d42a65a773e62c9d5e9c38766183d..e7a335916bdfe99a922b65c5028ec761f9803b1d 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -6,25 +6,12 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Id: execAmi.c,v 1.60 2001/10/25 05:49:27 momjian Exp $
+ *	$Id: execAmi.c,v 1.61 2002/02/19 20:11:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-/*
- *	 INTERFACE ROUTINES
- *
- *		ExecOpenScanR	\							  / amopen
- *		ExecBeginScan	 \							 /	ambeginscan
- *		ExecCloseR		  \							/	amclose
- *		ExecInsert		   \  executor interface   /	aminsert
- *		ExecReScanR		   /  to access methods    \	amrescan
- *		ExecMarkPos		  /							\	ammarkpos
- *		ExecRestrPos	 /							 \	amrestpos
- */
-
 #include "postgres.h"
 
-
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "catalog/heap.h"
@@ -50,202 +37,6 @@
 #include "executor/nodeSubqueryscan.h"
 #include "executor/nodeUnique.h"
 
-static Pointer ExecBeginScan(Relation relation, int nkeys, ScanKey skeys,
-			  bool isindex, ScanDirection dir, Snapshot snapshot);
-
-/* ----------------------------------------------------------------
- *		ExecOpenScanR
- *
- * old comments:
- *		Parameters:
- *		  relation -- relation to be opened and scanned.
- *		  nkeys    -- number of keys
- *		  skeys    -- keys to restrict scanning
- *			 isindex  -- if this is true, the relation is the relid of
- *						 an index relation, else it is a heap relation.
- *		Returns the relation as(relDesc scanDesc)
- * ----------------------------------------------------------------
- */
-void
-ExecOpenScanR(Oid relOid,
-			  int nkeys,
-			  ScanKey skeys,
-			  bool isindex,
-			  ScanDirection dir,
-			  Snapshot snapshot,
-			  Relation *returnRelation, /* return */
-			  Pointer *returnScanDesc)	/* return */
-{
-	Relation	relation;
-	Pointer		scanDesc;
-
-	/*
-	 * note: scanDesc returned by ExecBeginScan can be either a
-	 * HeapScanDesc or an IndexScanDesc so for now we make it a Pointer.
-	 * There should be a better scan abstraction someday -cim 9/9/89
-	 */
-
-	/*
-	 * Open the relation with the correct call depending on whether this
-	 * is a heap relation or an index relation.
-	 *
-	 * For a table, acquire AccessShareLock for the duration of the query
-	 * execution.  For indexes, acquire no lock here; the index machinery
-	 * does its own locks and unlocks.	(We rely on having some kind of
-	 * lock on the parent table to ensure the index won't go away!)
-	 */
-	if (isindex)
-		relation = index_open(relOid);
-	else
-		relation = heap_open(relOid, AccessShareLock);
-
-	scanDesc = ExecBeginScan(relation,
-							 nkeys,
-							 skeys,
-							 isindex,
-							 dir,
-							 snapshot);
-
-	if (returnRelation != NULL)
-		*returnRelation = relation;
-	if (scanDesc != NULL)
-		*returnScanDesc = scanDesc;
-}
-
-/* ----------------------------------------------------------------
- *		ExecBeginScan
- *
- *		beginscans a relation in current direction.
- *
- *		XXX fix parameters to AMbeginscan (and btbeginscan)
- *				currently we need to pass a flag stating whether
- *				or not the scan should begin at an endpoint of
- *				the relation.. Right now we always pass false
- *				-cim 9/14/89
- * ----------------------------------------------------------------
- */
-static Pointer
-ExecBeginScan(Relation relation,
-			  int nkeys,
-			  ScanKey skeys,
-			  bool isindex,
-			  ScanDirection dir,
-			  Snapshot snapshot)
-{
-	Pointer		scanDesc;
-
-	/*
-	 * open the appropriate type of scan.
-	 *
-	 * Note: ambeginscan()'s second arg is a boolean indicating that the scan
-	 * should be done in reverse..	That is, if you pass it true, then the
-	 * scan is backward.
-	 */
-	if (isindex)
-	{
-		scanDesc = (Pointer) index_beginscan(relation,
-											 false,		/* see above comment */
-											 nkeys,
-											 skeys);
-	}
-	else
-	{
-		scanDesc = (Pointer) heap_beginscan(relation,
-											ScanDirectionIsBackward(dir),
-											snapshot,
-											nkeys,
-											skeys);
-	}
-
-	if (scanDesc == NULL)
-		elog(DEBUG, "ExecBeginScan: scanDesc = NULL, heap_beginscan failed.");
-
-	return scanDesc;
-}
-
-/* ----------------------------------------------------------------
- *		ExecCloseR
- *
- *		closes the relation and scan descriptor for a scan node.
- *		Also closes index relations and scans for index scans.
- * ----------------------------------------------------------------
- */
-void
-ExecCloseR(Plan *node)
-{
-	CommonScanState *state;
-	Relation	relation;
-	HeapScanDesc scanDesc;
-
-	/*
-	 * get state for node and shut down the heap scan, if any
-	 */
-	switch (nodeTag(node))
-	{
-		case T_SeqScan:
-			state = ((SeqScan *) node)->scanstate;
-			break;
-
-		case T_IndexScan:
-			state = ((IndexScan *) node)->scan.scanstate;
-			break;
-
-		case T_TidScan:
-			state = ((TidScan *) node)->scan.scanstate;
-			break;
-
-		default:
-			elog(DEBUG, "ExecCloseR: not a scan node!");
-			return;
-	}
-
-	relation = state->css_currentRelation;
-	scanDesc = state->css_currentScanDesc;
-
-	if (scanDesc != NULL)
-		heap_endscan(scanDesc);
-
-	/*
-	 * if this is an index scan then we have to take care of the index
-	 * relations as well.
-	 */
-	if (IsA(node, IndexScan))
-	{
-		IndexScan  *iscan = (IndexScan *) node;
-		IndexScanState *indexstate = iscan->indxstate;
-		int			numIndices;
-		RelationPtr indexRelationDescs;
-		IndexScanDescPtr indexScanDescs;
-		int			i;
-
-		numIndices = indexstate->iss_NumIndices;
-		indexRelationDescs = indexstate->iss_RelationDescs;
-		indexScanDescs = indexstate->iss_ScanDescs;
-
-		for (i = 0; i < numIndices; i++)
-		{
-			/*
-			 * shut down each of the index scans and close each of the
-			 * index relations
-			 */
-			if (indexScanDescs[i] != NULL)
-				index_endscan(indexScanDescs[i]);
-
-			if (indexRelationDescs[i] != NULL)
-				index_close(indexRelationDescs[i]);
-		}
-	}
-
-	/*
-	 * Finally, close the heap relation.
-	 *
-	 * Currently, we do not release the AccessShareLock acquired by
-	 * ExecOpenScanR.  This lock should be held till end of transaction.
-	 * (There is a faction that considers this too much locking, however.)
-	 */
-	if (relation != NULL)
-		heap_close(relation, NoLock);
-}
 
 /* ----------------------------------------------------------------
  *		ExecReScan
@@ -374,27 +165,6 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
 	}
 }
 
-/* ----------------------------------------------------------------
- *		ExecReScanR
- *
- *		XXX this does not do the right thing with indices yet.
- * ----------------------------------------------------------------
- */
-HeapScanDesc
-ExecReScanR(Relation relDesc,	/* LLL relDesc unused  */
-			HeapScanDesc scanDesc,
-			ScanDirection direction,
-			int nkeys,			/* LLL nkeys unused  */
-			ScanKey skeys)
-{
-	if (scanDesc != NULL)
-		heap_rescan(scanDesc,	/* scan desc */
-					ScanDirectionIsBackward(direction), /* backward flag */
-					skeys);		/* scan keys */
-
-	return scanDesc;
-}
-
 /* ----------------------------------------------------------------
  *		ExecMarkPos
  *
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index d710c8270334cb0350702e055fae22537c630dee..5b0719487792960068bd816bf28cd616abd0c163 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.78 2001/10/25 05:49:27 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.79 2002/02/19 20:11:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -496,7 +496,6 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo)
 	{
 		Oid			indexOid = lfirsti(indexoidscan);
 		Relation	indexDesc;
-		HeapTuple	indexTuple;
 		IndexInfo  *ii;
 
 		/*
@@ -525,20 +524,9 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo)
 			LockRelation(indexDesc, AccessExclusiveLock);
 
 		/*
-		 * Get the pg_index tuple for the index
+		 * extract index key information from the index's pg_index tuple
 		 */
-		indexTuple = SearchSysCache(INDEXRELID,
-									ObjectIdGetDatum(indexOid),
-									0, 0, 0);
-		if (!HeapTupleIsValid(indexTuple))
-			elog(ERROR, "ExecOpenIndices: index %u not found", indexOid);
-
-		/*
-		 * extract the index key information from the tuple
-		 */
-		ii = BuildIndexInfo(indexTuple);
-
-		ReleaseSysCache(indexTuple);
+		ii = BuildIndexInfo(indexDesc->rd_index);
 
 		relationDescs[i] = indexDesc;
 		indexInfoArray[i] = ii;
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index bde5bce334dbc9b1d2aa66379a1ef79bef3fd795..3ab73f62f9a9746d77e6eb0d4eea8dda256f3643 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.66 2002/02/11 20:10:48 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.67 2002/02/19 20:11:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -359,14 +359,20 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
 			int			n_keys;
 			ScanKey		scan_keys;
 			int		   *run_keys;
+			List	   *listscan;
 
 			indxqual = lnext(indxqual);
 			n_keys = numScanKeys[i];
 			scan_keys = scanKeys[i];
 			run_keys = runtimeKeyInfo[i];
 
+			listscan = qual;
 			for (j = 0; j < n_keys; j++)
 			{
+				Expr	   *clause = lfirst(listscan);
+
+				listscan = lnext(listscan);
+
 				/*
 				 * If we have a run-time key, then extract the run-time
 				 * expression and evaluate it with respect to the current
@@ -382,7 +388,6 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
 				 */
 				if (run_keys[j] != NO_OP)
 				{
-					Expr	   *clause = nth(j, qual);
 					Node	   *scanexpr;
 					Datum		scanvalue;
 					bool		isNull;
@@ -448,6 +453,9 @@ ExecEndIndexScan(IndexScan *node)
 	List	   *indxqual;
 	int		   *numScanKeys;
 	int			numIndices;
+	Relation	relation;
+	RelationPtr indexRelationDescs;
+	IndexScanDescPtr indexScanDescs;
 	int			i;
 
 	scanstate = node->scan.scanstate;
@@ -461,6 +469,9 @@ ExecEndIndexScan(IndexScan *node)
 	numIndices = indexstate->iss_NumIndices;
 	scanKeys = indexstate->iss_ScanKeys;
 	numScanKeys = indexstate->iss_NumScanKeys;
+	indexRelationDescs = indexstate->iss_RelationDescs;
+	indexScanDescs = indexstate->iss_ScanDescs;
+	relation = scanstate->css_currentRelation;
 
 	/*
 	 * Free the projection info and the scan attribute info
@@ -475,9 +486,25 @@ ExecEndIndexScan(IndexScan *node)
 		FreeExprContext(indexstate->iss_RuntimeContext);
 
 	/*
-	 * close the heap and index relations
+	 * close the index relations
+	 */
+	for (i = 0; i < numIndices; i++)
+	{
+		if (indexScanDescs[i] != NULL)
+			index_endscan(indexScanDescs[i]);
+
+		if (indexRelationDescs[i] != NULL)
+			index_close(indexRelationDescs[i]);
+	}
+
+	/*
+	 * close the heap relation.
+	 *
+	 * Currently, we do not release the AccessShareLock acquired by
+	 * ExecInitIndexScan.  This lock should be held till end of transaction.
+	 * (There is a faction that considers this too much locking, however.)
 	 */
-	ExecCloseR((Plan *) node);
+	heap_close(relation, NoLock);
 
 	/*
 	 * free the scan keys used in scanning the indices
@@ -589,6 +616,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	CommonScanState *scanstate;
 	List	   *indxqual;
 	List	   *indxid;
+	List	   *listscan;
 	int			i;
 	int			numIndices;
 	int			indexPtr;
@@ -603,7 +631,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	Index		relid;
 	Oid			reloid;
 	Relation	currentRelation;
-	HeapScanDesc currentScanDesc;
 	ScanDirection direction;
 
 	/*
@@ -709,6 +736,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 		 * for each opclause in the given qual, convert each qual's
 		 * opclause into a single scan key
 		 */
+		listscan = qual;
 		for (j = 0; j < n_keys; j++)
 		{
 			Expr	   *clause; /* one clause of index qual */
@@ -725,7 +753,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 			/*
 			 * extract clause information from the qualification
 			 */
-			clause = nth(j, qual);
+			clause = lfirst(listscan);
+			listscan = lnext(listscan);
 
 			op = (Oper *) clause->oper;
 			if (!IsA(clause, Expr) ||!IsA(op, Oper))
@@ -989,25 +1018,19 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	direction = estate->es_direction;
 
 	/*
-	 * open the base relation
+	 * open the base relation and acquire AccessShareLock on it.
 	 */
 	relid = node->scan.scanrelid;
 	rtentry = rt_fetch(relid, rangeTable);
 	reloid = rtentry->relid;
 
-	ExecOpenScanR(reloid,		/* relation */
-				  0,			/* nkeys */
-				  (ScanKey) NULL,		/* scan key */
-				  false,		/* is index */
-				  direction,	/* scan direction */
-				  estate->es_snapshot,	/* */
-				  &currentRelation,		/* return: rel desc */
-				  (Pointer *) &currentScanDesc);		/* return: scan desc */
+	currentRelation = heap_open(reloid, AccessShareLock);
 
 	if (!RelationGetForm(currentRelation)->relhasindex)
 		elog(ERROR, "indexes of the relation %u was inactivated", reloid);
+
 	scanstate->css_currentRelation = currentRelation;
-	scanstate->css_currentScanDesc = currentScanDesc;
+	scanstate->css_currentScanDesc = NULL; /* no heap scan here */
 
 	/*
 	 * get the scan type from the relation descriptor.
@@ -1017,24 +1040,30 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 
 	/*
 	 * open the index relations and initialize relation and scan
-	 * descriptors.
+	 * descriptors.  Note we acquire no locks here; the index machinery
+	 * does its own locks and unlocks.	(We rely on having AccessShareLock
+	 * on the parent table to ensure the index won't go away!)
 	 */
+	listscan = indxid;
 	for (i = 0; i < numIndices; i++)
 	{
-		Oid			indexOid = (Oid) nthi(i, indxid);
+		Oid			indexOid = (Oid) lfirsti(listscan);
 
 		if (indexOid != 0)
 		{
-			ExecOpenScanR(indexOid,		/* relation */
-						  numScanKeys[i],		/* nkeys */
-						  scanKeys[i],	/* scan key */
-						  true, /* is index */
-						  direction,	/* scan direction */
-						  estate->es_snapshot,
-						  &(relationDescs[i]),	/* return: rel desc */
-						  (Pointer *) &(scanDescs[i]));
-			/* return: scan desc */
+			relationDescs[i] = index_open(indexOid);
+
+			/*
+			 * Note: index_beginscan()'s second arg is a boolean indicating
+			 * that the scan should be done in reverse.  That is, if you pass
+			 * it true, then the scan is backward.
+			 */
+			scanDescs[i] = index_beginscan(relationDescs[i],
+										   false, /* see above comment */
+										   numScanKeys[i],
+										   scanKeys[i]);
 		}
+		listscan = lnext(listscan);
 	}
 
 	indexstate->iss_RelationDescs = relationDescs;
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index d7ebec4985f8d80783d9f4d3ff84f4fea0b77d31..957a1bb2b722813b2f888cc0969616ed7a4b7d46 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.33 2001/10/28 06:25:43 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.34 2002/02/19 20:11:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -154,7 +154,9 @@ InitScanRelation(SeqScan *node, EState *estate,
 
 	/*
 	 * get the relation object id from the relid'th entry in the range
-	 * table, open that relation and initialize the scan state...
+	 * table, open that relation and initialize the scan state.
+	 *
+	 * We acquire AccessShareLock for the duration of the scan.
 	 */
 	relid = node->scanrelid;
 	rangeTable = estate->es_range_table;
@@ -162,14 +164,13 @@ InitScanRelation(SeqScan *node, EState *estate,
 	reloid = rtentry->relid;
 	direction = estate->es_direction;
 
-	ExecOpenScanR(reloid,		/* relation */
-				  0,			/* nkeys */
-				  NULL,			/* scan key */
-				  false,		/* is index */
-				  direction,	/* scan direction */
-				  estate->es_snapshot,
-				  &currentRelation,		/* return: rel desc */
-				  (Pointer *) &currentScanDesc);		/* return: scan desc */
+	currentRelation = heap_open(reloid, AccessShareLock);
+
+	currentScanDesc = heap_beginscan(currentRelation,
+									 ScanDirectionIsBackward(direction),
+									 estate->es_snapshot,
+									 0,
+									 NULL);
 
 	scanstate->css_currentRelation = currentRelation;
 	scanstate->css_currentScanDesc = currentScanDesc;
@@ -189,7 +190,6 @@ ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
 {
 	CommonScanState *scanstate;
 	Oid			reloid;
-	HeapScanDesc scandesc;
 
 	/*
 	 * Once upon a time it was possible to have an outerPlan of a SeqScan,
@@ -229,7 +229,6 @@ ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
 	 */
 	reloid = InitScanRelation(node, estate, scanstate);
 
-	scandesc = scanstate->css_currentScanDesc;
 	scanstate->cstate.cs_TupFromTlist = false;
 
 	/*
@@ -259,11 +258,15 @@ void
 ExecEndSeqScan(SeqScan *node)
 {
 	CommonScanState *scanstate;
+	Relation	relation;
+	HeapScanDesc scanDesc;
 
 	/*
 	 * get information from node
 	 */
 	scanstate = node->scanstate;
+	relation = scanstate->css_currentRelation;
+	scanDesc = scanstate->css_currentScanDesc;
 
 	/*
 	 * Free the projection info and the scan attribute info
@@ -276,9 +279,18 @@ ExecEndSeqScan(SeqScan *node)
 	ExecFreeExprContext(&scanstate->cstate);
 
 	/*
-	 * close scan relation
+	 * close heap scan
+	 */
+	heap_endscan(scanDesc);
+
+	/*
+	 * close the heap relation.
+	 *
+	 * Currently, we do not release the AccessShareLock acquired by
+	 * InitScanRelation.  This lock should be held till end of transaction.
+	 * (There is a faction that considers this too much locking, however.)
 	 */
-	ExecCloseR((Plan *) node);
+	heap_close(relation, NoLock);
 
 	/*
 	 * clean out the tuple table
@@ -303,7 +315,6 @@ ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent)
 {
 	CommonScanState *scanstate;
 	EState	   *estate;
-	Relation	rel;
 	HeapScanDesc scan;
 	ScanDirection direction;
 
@@ -317,11 +328,13 @@ ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent)
 		estate->es_evTupleNull[node->scanrelid - 1] = false;
 		return;
 	}
-	rel = scanstate->css_currentRelation;
+
 	scan = scanstate->css_currentScanDesc;
 	direction = estate->es_direction;
-	scan = ExecReScanR(rel, scan, direction, 0, NULL);
-	scanstate->css_currentScanDesc = scan;
+
+	heap_rescan(scan,			/* scan desc */
+				ScanDirectionIsBackward(direction), /* backward flag */
+				NULL);			/* new scan keys */
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 08685571cab4acc4863db757e637f285af50f945..5ee222744d3b64d35d2763c043759233a13f932b 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.22 2002/02/11 20:10:50 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.23 2002/02/19 20:11:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -300,15 +300,14 @@ ExecEndTidScan(TidScan *node)
 	CommonScanState *scanstate;
 	TidScanState *tidstate;
 
+	/*
+	 * extract information from the node
+	 */
 	scanstate = node->scan.scanstate;
 	tidstate = node->tidstate;
 	if (tidstate && tidstate->tss_TidList)
 		pfree(tidstate->tss_TidList);
 
-	/*
-	 * extract information from the node
-	 */
-
 	/*
 	 * Free the projection info and the scan attribute info
 	 *
@@ -320,9 +319,13 @@ ExecEndTidScan(TidScan *node)
 	ExecFreeExprContext(&scanstate->cstate);
 
 	/*
-	 * close the heap and tid relations
+	 * close the heap relation.
+	 *
+	 * Currently, we do not release the AccessShareLock acquired by
+	 * ExecInitTidScan.  This lock should be held till end of transaction.
+	 * (There is a faction that considers this too much locking, however.)
 	 */
-	ExecCloseR((Plan *) node);
+	heap_close(scanstate->css_currentRelation, NoLock);
 
 	/*
 	 * clear out tuple table slots
@@ -460,14 +463,17 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
 
 	/*
 	 * open the base relation
+	 *
+	 * We acquire AccessShareLock for the duration of the scan.
 	 */
 	relid = node->scan.scanrelid;
 	rtentry = rt_fetch(relid, rangeTable);
 	reloid = rtentry->relid;
 
 	currentRelation = heap_open(reloid, AccessShareLock);
+
 	scanstate->css_currentRelation = currentRelation;
-	scanstate->css_currentScanDesc = 0;
+	scanstate->css_currentScanDesc = NULL; /* no heap scan here */
 
 	/*
 	 * get the scan type from the relation descriptor.
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 86ebb09ff677eb38749965668e9217a1b3fac0eb..447b3e61f2b8b9ccd871edb491bfc3c22c986023 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.69 2001/10/25 05:49:34 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.70 2002/02/19 20:11:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -98,20 +98,15 @@ find_secondary_indexes(Oid relationObjectId)
 	foreach(indexoidscan, indexoidlist)
 	{
 		Oid			indexoid = lfirsti(indexoidscan);
-		HeapTuple	indexTuple;
+		Relation	indexRelation;
 		Form_pg_index index;
 		IndexOptInfo *info;
 		int			i;
-		Relation	indexRelation;
 		int16		amorderstrategy;
 
-		indexTuple = SearchSysCache(INDEXRELID,
-									ObjectIdGetDatum(indexoid),
-									0, 0, 0);
-		if (!HeapTupleIsValid(indexTuple))
-			elog(ERROR, "find_secondary_indexes: index %u not found",
-				 indexoid);
-		index = (Form_pg_index) GETSTRUCT(indexTuple);
+		/* Extract info from the relation descriptor for the index */
+		indexRelation = index_open(indexoid);
+
 		info = makeNode(IndexOptInfo);
 
 		/*
@@ -123,6 +118,7 @@ find_secondary_indexes(Oid relationObjectId)
 		info->ordering = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1));
 
 		/* Extract info from the pg_index tuple */
+		index = indexRelation->rd_index;
 		info->indexoid = index->indexrelid;
 		info->indproc = index->indproc; /* functional index ?? */
 		if (VARSIZE(&index->indpred) > VARHDRSZ)		/* partial index ?? */
@@ -156,14 +152,11 @@ find_secondary_indexes(Oid relationObjectId)
 		info->indexkeys[i] = 0;
 		info->nkeys = i;
 
-		/* Extract info from the relation descriptor for the index */
-		indexRelation = index_open(index->indexrelid);
 		info->relam = indexRelation->rd_rel->relam;
 		info->pages = indexRelation->rd_rel->relpages;
 		info->tuples = indexRelation->rd_rel->reltuples;
 		info->amcostestimate = index_cost_estimator(indexRelation);
 		amorderstrategy = indexRelation->rd_am->amorderstrategy;
-		index_close(indexRelation);
 
 		/*
 		 * Fetch the ordering operators associated with the index, if any.
@@ -171,26 +164,16 @@ find_secondary_indexes(Oid relationObjectId)
 		MemSet(info->ordering, 0, sizeof(Oid) * (INDEX_MAX_KEYS + 1));
 		if (amorderstrategy != 0)
 		{
+			int		oprindex = amorderstrategy - 1;
+
 			for (i = 0; i < info->ncolumns; i++)
 			{
-				HeapTuple	amopTuple;
-				Form_pg_amop amop;
-
-				amopTuple =
-					SearchSysCache(AMOPSTRATEGY,
-								   ObjectIdGetDatum(index->indclass[i]),
-								   Int16GetDatum(amorderstrategy),
-								   0, 0);
-				if (!HeapTupleIsValid(amopTuple))
-					elog(ERROR, "find_secondary_indexes: no amop %u %d",
-						 index->indclass[i], (int) amorderstrategy);
-				amop = (Form_pg_amop) GETSTRUCT(amopTuple);
-				info->ordering[i] = amop->amopopr;
-				ReleaseSysCache(amopTuple);
+				info->ordering[i] = indexRelation->rd_operator[oprindex];
+				oprindex += indexRelation->rd_am->amstrategies;
 			}
 		}
 
-		ReleaseSysCache(indexTuple);
+		index_close(indexRelation);
 
 		indexinfos = lcons(info, indexinfos);
 	}
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 7739185133874b4f3b948c1e289ade2370e7c715..766a5daad55b8fcaa5780896c20324535ec984f9 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.115 2001/12/12 03:28:49 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.116 2002/02/19 20:11:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -547,11 +547,9 @@ agg_get_candidates(char *aggname,
 				   Oid typeId,
 				   CandidateList *candidates)
 {
-	CandidateList current_candidate;
 	Relation	pg_aggregate_desc;
-	HeapScanDesc pg_aggregate_scan;
+	SysScanDesc	pg_aggregate_scan;
 	HeapTuple	tup;
-	Form_pg_aggregate agg;
 	int			ncandidates = 0;
 	ScanKeyData aggKey[1];
 
@@ -563,15 +561,15 @@ agg_get_candidates(char *aggname,
 						   NameGetDatum(aggname));
 
 	pg_aggregate_desc = heap_openr(AggregateRelationName, AccessShareLock);
-	pg_aggregate_scan = heap_beginscan(pg_aggregate_desc,
-									   0,
-									   SnapshotSelf,	/* ??? */
-									   1,
-									   aggKey);
+	pg_aggregate_scan = systable_beginscan(pg_aggregate_desc,
+										   AggregateNameTypeIndex, true,
+										   SnapshotNow,
+										   1, aggKey);
 
-	while (HeapTupleIsValid(tup = heap_getnext(pg_aggregate_scan, 0)))
+	while (HeapTupleIsValid(tup = systable_getnext(pg_aggregate_scan)))
 	{
-		agg = (Form_pg_aggregate) GETSTRUCT(tup);
+		Form_pg_aggregate agg = (Form_pg_aggregate) GETSTRUCT(tup);
+		CandidateList current_candidate;
 
 		current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
 		current_candidate->args = (Oid *) palloc(sizeof(Oid));
@@ -582,7 +580,7 @@ agg_get_candidates(char *aggname,
 		ncandidates++;
 	}
 
-	heap_endscan(pg_aggregate_scan);
+	systable_endscan(pg_aggregate_scan);
 	heap_close(pg_aggregate_desc, AccessShareLock);
 
 	return ncandidates;
@@ -680,62 +678,46 @@ static CandidateList
 func_get_candidates(char *funcname, int nargs)
 {
 	Relation	heapRelation;
-	Relation	idesc;
-	ScanKeyData skey;
-	HeapTupleData tuple;
-	IndexScanDesc sd;
-	RetrieveIndexResult indexRes;
-	Form_pg_proc pgProcP;
+	ScanKeyData skey[2];
+	HeapTuple	tuple;
+	SysScanDesc	funcscan;
 	CandidateList candidates = NULL;
-	CandidateList current_candidate;
 	int			i;
 
 	heapRelation = heap_openr(ProcedureRelationName, AccessShareLock);
-	ScanKeyEntryInitialize(&skey,
+
+	ScanKeyEntryInitialize(&skey[0],
 						   (bits16) 0x0,
 						   (AttrNumber) Anum_pg_proc_proname,
 						   (RegProcedure) F_NAMEEQ,
 						   PointerGetDatum(funcname));
+	ScanKeyEntryInitialize(&skey[1],
+						   (bits16) 0x0,
+						   (AttrNumber) Anum_pg_proc_pronargs,
+						   (RegProcedure) F_INT2EQ,
+						   Int16GetDatum(nargs));
 
-	idesc = index_openr(ProcedureNameIndex);
-
-	sd = index_beginscan(idesc, false, 1, &skey);
+	funcscan = systable_beginscan(heapRelation, ProcedureNameIndex, true,
+								  SnapshotNow, 2, skey);
 
-	do
+	while (HeapTupleIsValid(tuple = systable_getnext(funcscan)))
 	{
-		indexRes = index_getnext(sd, ForwardScanDirection);
-		if (indexRes)
-		{
-			Buffer		buffer;
-
-			tuple.t_datamcxt = NULL;
-			tuple.t_data = NULL;
-			tuple.t_self = indexRes->heap_iptr;
-			heap_fetch(heapRelation, SnapshotNow, &tuple, &buffer, sd);
-			pfree(indexRes);
-			if (tuple.t_data != NULL)
-			{
-				pgProcP = (Form_pg_proc) GETSTRUCT(&tuple);
-				if (pgProcP->pronargs == nargs)
-				{
-					current_candidate = (CandidateList)
-						palloc(sizeof(struct _CandidateList));
-					current_candidate->args = (Oid *)
-						palloc(FUNC_MAX_ARGS * sizeof(Oid));
-					MemSet(current_candidate->args, 0, FUNC_MAX_ARGS * sizeof(Oid));
-					for (i = 0; i < nargs; i++)
-						current_candidate->args[i] = pgProcP->proargtypes[i];
-
-					current_candidate->next = candidates;
-					candidates = current_candidate;
-				}
-				ReleaseBuffer(buffer);
-			}
-		}
-	} while (indexRes);
+		Form_pg_proc pgProcP = (Form_pg_proc) GETSTRUCT(tuple);
+		CandidateList current_candidate;
+
+		current_candidate = (CandidateList)
+			palloc(sizeof(struct _CandidateList));
+		current_candidate->args = (Oid *)
+			palloc(FUNC_MAX_ARGS * sizeof(Oid));
+		MemSet(current_candidate->args, 0, FUNC_MAX_ARGS * sizeof(Oid));
+		for (i = 0; i < nargs; i++)
+			current_candidate->args[i] = pgProcP->proargtypes[i];
+
+		current_candidate->next = candidates;
+		candidates = current_candidate;
+	}
 
-	index_endscan(sd);
-	index_close(idesc);
+	systable_endscan(funcscan);
 	heap_close(heapRelation, AccessShareLock);
 
 	return candidates;
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 15a39dc84bc4a99aa9a13fb4b32e844ede30c8c0..318f1b9eb7e26f6bc2a23b01205437a6c7de6d0d 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -8,15 +8,17 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.51 2001/10/25 05:49:40 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.52 2002/02/19 20:11:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
+#include "access/genam.h"
 #include "access/heapam.h"
 #include "catalog/catname.h"
+#include "catalog/indexing.h"
 #include "catalog/pg_operator.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_func.h"
@@ -80,13 +82,11 @@ static int
 binary_oper_get_candidates(char *opname,
 						   CandidateList *candidates)
 {
-	CandidateList current_candidate;
 	Relation	pg_operator_desc;
-	HeapScanDesc pg_operator_scan;
+	SysScanDesc	pg_operator_scan;
 	HeapTuple	tup;
-	Form_pg_operator oper;
 	int			ncandidates = 0;
-	ScanKeyData opKey[2];
+	ScanKeyData opKey[1];
 
 	*candidates = NULL;
 
@@ -95,38 +95,94 @@ binary_oper_get_candidates(char *opname,
 						   F_NAMEEQ,
 						   NameGetDatum(opname));
 
-	ScanKeyEntryInitialize(&opKey[1], 0,
-						   Anum_pg_operator_oprkind,
-						   F_CHAREQ,
-						   CharGetDatum('b'));
-
 	pg_operator_desc = heap_openr(OperatorRelationName, AccessShareLock);
-	pg_operator_scan = heap_beginscan(pg_operator_desc,
-									  0,
-									  SnapshotSelf,		/* ??? */
-									  2,
-									  opKey);
+	pg_operator_scan = systable_beginscan(pg_operator_desc,
+										  OperatorNameIndex, true,
+										  SnapshotNow,
+										  1, opKey);
 
-	while (HeapTupleIsValid(tup = heap_getnext(pg_operator_scan, 0)))
+	while (HeapTupleIsValid(tup = systable_getnext(pg_operator_scan)))
 	{
-		oper = (Form_pg_operator) GETSTRUCT(tup);
+		Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tup);
+
+		if (oper->oprkind == 'b')
+		{
+			CandidateList current_candidate;
 
-		current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
-		current_candidate->args = (Oid *) palloc(2 * sizeof(Oid));
+			current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
+			current_candidate->args = (Oid *) palloc(2 * sizeof(Oid));
 
-		current_candidate->args[0] = oper->oprleft;
-		current_candidate->args[1] = oper->oprright;
-		current_candidate->next = *candidates;
-		*candidates = current_candidate;
-		ncandidates++;
+			current_candidate->args[0] = oper->oprleft;
+			current_candidate->args[1] = oper->oprright;
+			current_candidate->next = *candidates;
+			*candidates = current_candidate;
+			ncandidates++;
+		}
 	}
 
-	heap_endscan(pg_operator_scan);
+	systable_endscan(pg_operator_scan);
 	heap_close(pg_operator_desc, AccessShareLock);
 
 	return ncandidates;
 }	/* binary_oper_get_candidates() */
 
+/* unary_oper_get_candidates()
+ *	given opname, find all possible types for which
+ *	a right/left unary operator named opname exists.
+ *	Build a list of the candidate input types.
+ *	Returns number of candidates found.
+ */
+static int
+unary_oper_get_candidates(char *opname,
+						  CandidateList *candidates,
+						  char rightleft)
+{
+	Relation	pg_operator_desc;
+	SysScanDesc	pg_operator_scan;
+	HeapTuple	tup;
+	int			ncandidates = 0;
+	ScanKeyData opKey[1];
+
+	*candidates = NULL;
+
+	ScanKeyEntryInitialize(&opKey[0], 0,
+						   Anum_pg_operator_oprname,
+						   F_NAMEEQ,
+						   NameGetDatum(opname));
+
+	pg_operator_desc = heap_openr(OperatorRelationName, AccessShareLock);
+	pg_operator_scan = systable_beginscan(pg_operator_desc,
+										  OperatorNameIndex, true,
+										  SnapshotNow,
+										  1, opKey);
+
+	while (HeapTupleIsValid(tup = systable_getnext(pg_operator_scan)))
+	{
+		Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tup);
+
+		if (oper->oprkind == rightleft)
+		{
+			CandidateList current_candidate;
+
+			current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
+			current_candidate->args = (Oid *) palloc(sizeof(Oid));
+
+			if (rightleft == 'r')
+				current_candidate->args[0] = oper->oprleft;
+			else
+				current_candidate->args[0] = oper->oprright;
+			current_candidate->next = *candidates;
+			*candidates = current_candidate;
+			ncandidates++;
+		}
+	}
+
+	systable_endscan(pg_operator_scan);
+	heap_close(pg_operator_desc, AccessShareLock);
+
+	return ncandidates;
+}	/* unary_oper_get_candidates() */
+
 
 /* oper_select_candidate()
  * Given the input argtype array and one or more candidates
@@ -739,66 +795,6 @@ compatible_oper_funcid(char *op, Oid arg1, Oid arg2, bool noError)
 	return InvalidOid;
 }
 
-/* unary_oper_get_candidates()
- *	given opname, find all possible types for which
- *	a right/left unary operator named opname exists.
- *	Build a list of the candidate input types.
- *	Returns number of candidates found.
- */
-static int
-unary_oper_get_candidates(char *opname,
-						  CandidateList *candidates,
-						  char rightleft)
-{
-	CandidateList current_candidate;
-	Relation	pg_operator_desc;
-	HeapScanDesc pg_operator_scan;
-	HeapTuple	tup;
-	Form_pg_operator oper;
-	int			ncandidates = 0;
-	ScanKeyData opKey[2];
-
-	*candidates = NULL;
-
-	ScanKeyEntryInitialize(&opKey[0], 0,
-						   Anum_pg_operator_oprname,
-						   F_NAMEEQ,
-						   NameGetDatum(opname));
-
-	ScanKeyEntryInitialize(&opKey[1], 0,
-						   Anum_pg_operator_oprkind,
-						   F_CHAREQ,
-						   CharGetDatum(rightleft));
-
-	pg_operator_desc = heap_openr(OperatorRelationName, AccessShareLock);
-	pg_operator_scan = heap_beginscan(pg_operator_desc,
-									  0,
-									  SnapshotSelf,		/* ??? */
-									  2,
-									  opKey);
-
-	while (HeapTupleIsValid(tup = heap_getnext(pg_operator_scan, 0)))
-	{
-		oper = (Form_pg_operator) GETSTRUCT(tup);
-
-		current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
-		current_candidate->args = (Oid *) palloc(sizeof(Oid));
-
-		if (rightleft == 'r')
-			current_candidate->args[0] = oper->oprleft;
-		else
-			current_candidate->args[0] = oper->oprright;
-		current_candidate->next = *candidates;
-		*candidates = current_candidate;
-		ncandidates++;
-	}
-
-	heap_endscan(pg_operator_scan);
-	heap_close(pg_operator_desc, AccessShareLock);
-
-	return ncandidates;
-}	/* unary_oper_get_candidates() */
-
 
 /* Given unary right operator (operator on right), return oper struct
  *
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 27abf6ace505ed9bc418a642dfdff1b3584819d2..95e217b9a4fae4de388ab63a6dbdf999ba0d4ae0 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
  *				back to source text
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.89 2001/11/26 21:15:14 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.90 2002/02/19 20:11:16 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -341,6 +341,7 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
 	HeapTuple	ht_idx;
 	HeapTuple	ht_idxrel;
 	HeapTuple	ht_indrel;
+	HeapTuple	ht_am;
 	Form_pg_index idxrec;
 	Form_pg_class idxrelrec;
 	Form_pg_class indrelrec;
@@ -383,13 +384,13 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
 
 	/*
 	 * Fetch the pg_am tuple of the index' access method
-	 *
-	 * There is no syscache for this, so use index.c subroutine.
 	 */
-	amrec = AccessMethodObjectIdGetForm(idxrelrec->relam,
-										CurrentMemoryContext);
-	if (!amrec)
-		elog(ERROR, "lookup for AM %u failed", idxrelrec->relam);
+	ht_am = SearchSysCache(AMOID,
+						   ObjectIdGetDatum(idxrelrec->relam),
+						   0, 0, 0);
+	if (!HeapTupleIsValid(ht_am))
+		elog(ERROR, "syscache lookup for AM %u failed", idxrelrec->relam);
+	amrec = (Form_pg_am) GETSTRUCT(ht_am);
 
 	/*
 	 * Start the index definition
@@ -504,10 +505,11 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
 
 	pfree(buf.data);
 	pfree(keybuf.data);
-	pfree(amrec);
+
 	ReleaseSysCache(ht_idx);
 	ReleaseSysCache(ht_idxrel);
 	ReleaseSysCache(ht_indrel);
+	ReleaseSysCache(ht_am);
 
 	PG_RETURN_TEXT_P(indexdef);
 }
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index cab6291acd08a6b3af97b560150826e611b736ac..47ab410cfc235c5be152d071cd8248b7a8fbb67b 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.86 2001/11/05 17:46:30 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.87 2002/02/19 20:11:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,9 +24,13 @@
 #include "catalog/catname.h"
 #include "catalog/indexing.h"
 #include "miscadmin.h"
+#ifdef CATCACHE_STATS
+#include "storage/ipc.h"		/* for on_proc_exit */
+#endif
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/catcache.h"
+#include "utils/relcache.h"
 #include "utils/syscache.h"
 
 
@@ -94,6 +98,9 @@ static Index CatalogCacheComputeTupleHashIndex(CatCache *cache,
 								  HeapTuple tuple);
 static void CatalogCacheInitializeCache(CatCache *cache);
 static Datum cc_hashname(PG_FUNCTION_ARGS);
+#ifdef CATCACHE_STATS
+static void CatCachePrintStats(void);
+#endif
 
 
 /*
@@ -147,6 +154,46 @@ cc_hashname(PG_FUNCTION_ARGS)
 }
 
 
+#ifdef CATCACHE_STATS
+
+static void
+CatCachePrintStats(void)
+{
+	CatCache   *cache;
+	long		cc_searches = 0;
+	long		cc_hits = 0;
+	long		cc_newloads = 0;
+
+	elog(DEBUG, "Catcache stats dump: %d/%d tuples in catcaches",
+		 CacheHdr->ch_ntup, CacheHdr->ch_maxtup);
+
+	for (cache = CacheHdr->ch_caches; cache; cache = cache->cc_next)
+	{
+		if (cache->cc_ntup == 0 && cache->cc_searches == 0)
+			continue;			/* don't print unused caches */
+		elog(DEBUG, "Catcache %s/%s: %d tup, %ld srch, %ld hits, %ld loads, %ld not found",
+			 cache->cc_relname,
+			 cache->cc_indname,
+			 cache->cc_ntup,
+			 cache->cc_searches,
+			 cache->cc_hits,
+			 cache->cc_newloads,
+			 cache->cc_searches - cache->cc_hits - cache->cc_newloads);
+		cc_searches += cache->cc_searches;
+		cc_hits += cache->cc_hits;
+		cc_newloads += cache->cc_newloads;
+	}
+	elog(DEBUG, "Catcache totals: %d tup, %ld srch, %ld hits, %ld loads, %ld not found",
+		 CacheHdr->ch_ntup,
+		 cc_searches,
+		 cc_hits,
+		 cc_newloads,
+		 cc_searches - cc_hits - cc_newloads);
+}
+
+#endif /* CATCACHE_STATS */
+
+
 /*
  * Standard routine for creating cache context if it doesn't exist yet
  *
@@ -289,6 +336,30 @@ CatalogCacheInitializeCache(CatCache *cache)
 	cache->cc_tupdesc = tupdesc;
 }
 
+/*
+ * InitCatCachePhase2 -- external interface for CatalogCacheInitializeCache
+ *
+ * The only reason to call this routine is to ensure that the relcache
+ * has created entries for all the catalogs and indexes referenced by
+ * catcaches.  Therefore, open the index too.  An exception is the indexes
+ * on pg_am, which we don't use (cf. IndexScanOK).
+ */
+void
+InitCatCachePhase2(CatCache *cache)
+{
+	if (cache->cc_tupdesc == NULL)
+		CatalogCacheInitializeCache(cache);
+
+	if (cache->id != AMOID &&
+		cache->id != AMNAME)
+	{
+		Relation	idesc;
+
+		idesc = index_openr(cache->cc_indname);
+		index_close(idesc);
+	}
+}
+
 /*
  *		CatalogCacheComputeHashIndex
  */
@@ -644,11 +715,11 @@ CatalogCacheFlushRelation(Oid relId)
 				{
 					bool		isNull;
 
-					tupRelid = DatumGetObjectId(
-												fastgetattr(&ct->tuple,
-													cache->cc_reloidattr,
-													   cache->cc_tupdesc,
-															&isNull));
+					tupRelid =
+						DatumGetObjectId(fastgetattr(&ct->tuple,
+													 cache->cc_reloidattr,
+													 cache->cc_tupdesc,
+													 &isNull));
 					Assert(!isNull);
 				}
 
@@ -717,20 +788,19 @@ InitCatCache(int id,
 		CacheHdr->ch_ntup = 0;
 		CacheHdr->ch_maxtup = MAXCCTUPLES;
 		DLInitList(&CacheHdr->ch_lrulist);
+#ifdef CATCACHE_STATS
+		on_proc_exit(CatCachePrintStats, 0);
+#endif
 	}
 
 	/*
 	 * allocate a new cache structure
+	 *
+	 * Note: we assume zeroing initializes the bucket headers correctly
 	 */
 	cp = (CatCache *) palloc(sizeof(CatCache) + NCCBUCKETS * sizeof(Dllist));
 	MemSet((char *) cp, 0, sizeof(CatCache) + NCCBUCKETS * sizeof(Dllist));
 
-	/*
-	 * initialize the cache buckets (each bucket is a list header)
-	 */
-	for (i = 0; i < NCCBUCKETS; ++i)
-		DLInitList(&cp->cc_bucket[i]);
-
 	/*
 	 * initialize the cache's relation information for the relation
 	 * corresponding to this cache, and initialize some of the new cache's
@@ -777,57 +847,44 @@ InitCatCache(int id,
  *		certain system indexes that support critical syscaches.
  *		We can't use an indexscan to fetch these, else we'll get into
  *		infinite recursion.  A plain heap scan will work, however.
+ *
+ *		Once we have completed relcache initialization (signaled by
+ *		criticalRelcachesBuilt), we don't have to worry anymore.
  */
 static bool
 IndexScanOK(CatCache *cache, ScanKey cur_skey)
 {
 	if (cache->id == INDEXRELID)
 	{
-		static Oid	indexSelfOid = InvalidOid;
-
-		/* One-time lookup of the OID of pg_index_indexrelid_index */
-		if (!OidIsValid(indexSelfOid))
-		{
-			Relation	rel;
-			ScanKeyData key;
-			HeapScanDesc sd;
-			HeapTuple	ntp;
-
-			rel = heap_openr(RelationRelationName, AccessShareLock);
-			ScanKeyEntryInitialize(&key, 0, Anum_pg_class_relname,
-								   F_NAMEEQ,
-								   PointerGetDatum(IndexRelidIndex));
-			sd = heap_beginscan(rel, false, SnapshotNow, 1, &key);
-			ntp = heap_getnext(sd, 0);
-			if (!HeapTupleIsValid(ntp))
-				elog(ERROR, "IndexScanOK: %s not found in %s",
-					 IndexRelidIndex, RelationRelationName);
-			indexSelfOid = ntp->t_data->t_oid;
-			heap_endscan(sd);
-			heap_close(rel, AccessShareLock);
-		}
-
-		/* Looking for pg_index_indexrelid_index? */
-		if (DatumGetObjectId(cur_skey[0].sk_argument) == indexSelfOid)
+		/*
+		 * Since the OIDs of indexes aren't hardwired, it's painful to
+		 * figure out which is which.  Just force all pg_index searches
+		 * to be heap scans while building the relcaches.
+		 */
+		if (!criticalRelcachesBuilt)
 			return false;
 	}
-	else if (cache->id == AMOPSTRATEGY ||
-			 cache->id == AMPROCNUM)
+	else if (cache->id == AMOID ||
+			 cache->id == AMNAME)
 	{
-		/* Looking for an OID or INT2 btree operator or function? */
-		Oid			lookup_oid = DatumGetObjectId(cur_skey[0].sk_argument);
-
-		if (lookup_oid == OID_BTREE_OPS_OID ||
-			lookup_oid == INT2_BTREE_OPS_OID)
-			return false;
+		/*
+		 * Always do heap scans in pg_am, because it's so small there's
+		 * not much point in an indexscan anyway.  We *must* do this when
+		 * initially building critical relcache entries, but we might as
+		 * well just always do it.
+		 */
+		return false;
 	}
 	else if (cache->id == OPEROID)
 	{
-		/* Looking for an OID comparison function? */
-		Oid			lookup_oid = DatumGetObjectId(cur_skey[0].sk_argument);
+		if (!criticalRelcachesBuilt)
+		{
+			/* Looking for an OID comparison function? */
+			Oid		lookup_oid = DatumGetObjectId(cur_skey[0].sk_argument);
 
-		if (lookup_oid >= MIN_OIDCMP && lookup_oid <= MAX_OIDCMP)
-			return false;
+			if (lookup_oid >= MIN_OIDCMP && lookup_oid <= MAX_OIDCMP)
+				return false;
+		}
 	}
 
 	/* Normal case, allow index scan */
@@ -861,6 +918,10 @@ SearchCatCache(CatCache *cache,
 	if (cache->cc_tupdesc == NULL)
 		CatalogCacheInitializeCache(cache);
 
+#ifdef CATCACHE_STATS
+	cache->cc_searches++;
+#endif
+
 	/*
 	 * initialize the search key information
 	 */
@@ -919,6 +980,10 @@ SearchCatCache(CatCache *cache,
 					cache->cc_relname, hash);
 #endif   /* CACHEDEBUG */
 
+#ifdef CATCACHE_STATS
+		cache->cc_hits++;
+#endif
+
 		return &ct->tuple;
 	}
 
@@ -1046,6 +1111,10 @@ SearchCatCache(CatCache *cache,
 	DLAddHead(&CacheHdr->ch_lrulist, &ct->lrulist_elem);
 	DLAddHead(&cache->cc_bucket[hash], &ct->cache_elem);
 
+#ifdef CATCACHE_STATS
+	cache->cc_newloads++;
+#endif
+
 	/*
 	 * If we've exceeded the desired size of the caches, try to throw away
 	 * the least recently used entry.
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
index 40d79942d5e0203e137d5bed7ff14e0207c9cb28..0e383b32c8417bfd9e1c123d7b8f33081cba92a8 100644
--- a/src/backend/utils/cache/inval.c
+++ b/src/backend/utils/cache/inval.c
@@ -48,6 +48,12 @@
  *	(XXX is it worth testing likewise for duplicate catcache flush entries?
  *	Probably not.)
  *
+ *	If a relcache flush is issued for a system relation that we preload
+ *	from the relcache init file, we must also delete the init file so that
+ *	it will be rebuilt during the next backend restart.  The actual work of
+ *	manipulating the init file is in relcache.c, but we keep track of the
+ *	need for it here.
+ *
  *	All the request lists are kept in TopTransactionContext memory, since
  *	they need not live beyond the end of the current transaction.
  *
@@ -56,7 +62,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.47 2001/11/16 23:30:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.48 2002/02/19 20:11:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -107,6 +113,8 @@ typedef struct InvalidationListHeader
  */
 static InvalidationListHeader GlobalInvalidMsgs;
 
+static bool RelcacheInitFileInval; /* init file must be invalidated? */
+
 /*
  * head of invalidation message list for the current command
  * eaten by AtCommit_LocalCache() in CommandCounterIncrement()
@@ -339,6 +347,13 @@ RegisterRelcacheInvalidation(Oid dbId, Oid relId)
 								   dbId, relId);
 	AddRelcacheInvalidationMessage(&LocalInvalidMsgs,
 								   dbId, relId);
+
+	/*
+	 * If the relation being invalidated is one of those cached in the
+	 * relcache init file, mark that we need to zap that file at commit.
+	 */
+	if (RelationIdIsInInitFile(relId))
+		RelcacheInitFileInval = true;
 }
 
 /*
@@ -418,8 +433,8 @@ InvalidateSystemCaches(void)
 
 /*
  * PrepareForTupleInvalidation
- *		Invoke functions for the tuple which register invalidation
- *		of catalog/relation cache.
+ *		Detect whether invalidation of this tuple implies invalidation
+ *		of catalog/relation cache entries; if so, register inval events.
  */
 static void
 PrepareForTupleInvalidation(Relation relation, HeapTuple tuple,
@@ -525,8 +540,19 @@ AtEOXactInvalidationMessages(bool isCommit)
 {
 	if (isCommit)
 	{
+		/*
+		 * Relcache init file invalidation requires processing both
+		 * before and after we send the SI messages.  However, we need
+		 * not do anything unless we committed.
+		 */
+		if (RelcacheInitFileInval)
+			RelationCacheInitFileInvalidate(true);
+
 		ProcessInvalidationMessages(&GlobalInvalidMsgs,
 									SendSharedInvalidMessage);
+
+		if (RelcacheInitFileInval)
+			RelationCacheInitFileInvalidate(false);
 	}
 	else
 	{
@@ -534,6 +560,8 @@ AtEOXactInvalidationMessages(bool isCommit)
 									LocalExecuteInvalidationMessage);
 	}
 
+	RelcacheInitFileInval = false;
+
 	DiscardInvalidationMessages(&GlobalInvalidMsgs, false);
 	DiscardInvalidationMessages(&LocalInvalidMsgs, false);
 	DiscardInvalidationMessages(&RollbackMsgs, false);
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 16a38df83d3cd60d894569e2be43d2521e0ed0f0..cd5162ac81c2e12579e2e76a9433075190a72fed 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.151 2002/01/16 17:34:42 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.152 2002/02/19 20:11:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,8 +41,12 @@
 #include "catalog/catname.h"
 #include "catalog/index.h"
 #include "catalog/indexing.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_amproc.h"
 #include "catalog/pg_attrdef.h"
+#include "catalog/pg_attribute.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_relcheck.h"
 #include "catalog/pg_rewrite.h"
@@ -53,12 +57,18 @@
 #include "utils/builtins.h"
 #include "utils/catcache.h"
 #include "utils/fmgroids.h"
-#include "utils/hsearch.h"
-#include "utils/memutils.h"
+#include "utils/inval.h"
+#include "utils/lsyscache.h"
 #include "utils/relcache.h"
+#include "utils/syscache.h"
 #include "utils/temprel.h"
 
 
+/*
+ * name of relcache init file, used to speed up backend startup
+ */
+#define RELCACHE_INIT_FILENAME	"pg_internal.init"
+
 /*
  *		hardcoded tuple descriptors.  see lib/backend/catalog/pg_attribute.h
  */
@@ -88,14 +98,33 @@ static HTAB *RelationNodeCache;
  *	  relations created during this transaction. We need to keep track of
  *	  these.
  */
-static List *newlyCreatedRelns = NULL;
+static List *newlyCreatedRelns = NIL;
 
 /*
  * This flag is false until we have prepared the critical relcache entries
  * that are needed to do indexscans on the tables read by relcache building.
  */
-static bool criticalRelcachesBuilt = false;
+bool criticalRelcachesBuilt = false;
+
+/*
+ * This flag is set if we discover that we need to write a new relcache
+ * cache file at the end of startup.
+ */
+static bool needNewCacheFile = false;
+
+/*
+ * This counter counts relcache inval events received since backend startup
+ * (but only for rels that are actually in cache).  Presently, we use it only
+ * to detect whether data about to be written by write_relcache_init_file()
+ * might already be obsolete.
+ */
+static long relcacheInvalsReceived = 0L;
 
+/*
+ * This list remembers the OIDs of the relations cached in the relcache
+ * init file.
+ */
+static List *initFileRelationIds = NIL;
 
 /*
  *		RelationBuildDescInfo exists so code can be shared
@@ -220,6 +249,24 @@ do { \
 		elog(NOTICE, "trying to delete a reldesc that does not exist."); \
 } while(0)
 
+
+/*
+ * Special cache for opclass-related information
+ */
+typedef struct opclasscacheent
+{
+	Oid			opclassoid;		/* lookup key: OID of opclass */
+	bool		valid;			/* set TRUE after successful fill-in */
+	StrategyNumber numStrats;	/* max # of strategies (from pg_am) */
+	StrategyNumber numSupport;	/* max # of support procs (from pg_am) */
+	Oid		   *operatorOids;	/* strategy operators' OIDs */
+	RegProcedure *operatorProcs; /* strategy operators' procs */
+	RegProcedure *supportProcs;	/* support procs */
+} OpClassCacheEnt;
+
+static HTAB *OpClassCache = NULL;
+
+
 /* non-export function prototypes */
 
 static void RelationClearRelation(Relation relation, bool rebuildIt);
@@ -229,28 +276,31 @@ static void RelationReloadClassinfo(Relation relation);
 #endif   /* ENABLE_REINDEX_NAILED_RELATIONS */
 static void RelationFlushRelation(Relation relation);
 static Relation RelationNameCacheGetRelation(const char *relationName);
-static void init_irels(void);
-static void write_irels(void);
+static bool load_relcache_init_file(void);
+static void write_relcache_init_file(void);
 
 static void formrdesc(char *relationName, int natts,
 		  FormData_pg_attribute *att);
-static void fixrdesc(char *relationName);
 
 static HeapTuple ScanPgRelation(RelationBuildDescInfo buildinfo);
-static HeapTuple scan_pg_rel_seq(RelationBuildDescInfo buildinfo);
-static HeapTuple scan_pg_rel_ind(RelationBuildDescInfo buildinfo);
 static Relation AllocateRelationDesc(Relation relation, Form_pg_class relp);
 static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
 					   Relation relation);
-static void build_tupdesc_seq(RelationBuildDescInfo buildinfo,
-				  Relation relation);
-static void build_tupdesc_ind(RelationBuildDescInfo buildinfo,
-				  Relation relation);
 static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo,
 				  Relation oldrelation);
 static void AttrDefaultFetch(Relation relation);
 static void RelCheckFetch(Relation relation);
 static List *insert_ordered_oid(List *list, Oid datum);
+static void IndexSupportInitialize(Form_pg_index iform,
+								   IndexStrategy indexStrategy,
+								   Oid *indexOperator,
+								   RegProcedure *indexSupport,
+								   StrategyNumber maxStrategyNumber,
+								   StrategyNumber maxSupportNumber,
+								   AttrNumber maxAttributeNumber);
+static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid,
+										  StrategyNumber numStrats,
+										  StrategyNumber numSupport);
 
 
 /*
@@ -271,26 +321,11 @@ static List *insert_ordered_oid(List *list, Oid datum);
  */
 static HeapTuple
 ScanPgRelation(RelationBuildDescInfo buildinfo)
-{
-	/*
-	 * If this is bootstrap time (initdb), then we can't use the system
-	 * catalog indices, because they may not exist yet.  Otherwise, we
-	 * can, and do.
-	 */
-
-	if (IsIgnoringSystemIndexes() || !criticalRelcachesBuilt)
-		return scan_pg_rel_seq(buildinfo);
-	else
-		return scan_pg_rel_ind(buildinfo);
-}
-
-static HeapTuple
-scan_pg_rel_seq(RelationBuildDescInfo buildinfo)
 {
 	HeapTuple	pg_class_tuple;
-	HeapTuple	return_tuple;
 	Relation	pg_class_desc;
-	HeapScanDesc pg_class_scan;
+	const char *indexRelname;
+	SysScanDesc pg_class_scan;
 	ScanKeyData key;
 
 	/*
@@ -303,6 +338,7 @@ scan_pg_rel_seq(RelationBuildDescInfo buildinfo)
 								   ObjectIdAttributeNumber,
 								   F_OIDEQ,
 								   ObjectIdGetDatum(buildinfo.i.info_id));
+			indexRelname = ClassOidIndex;
 			break;
 
 		case INFO_RELNAME:
@@ -310,83 +346,38 @@ scan_pg_rel_seq(RelationBuildDescInfo buildinfo)
 								   Anum_pg_class_relname,
 								   F_NAMEEQ,
 								   NameGetDatum(buildinfo.i.info_name));
+			indexRelname = ClassNameIndex;
 			break;
 
 		default:
 			elog(ERROR, "ScanPgRelation: bad buildinfo");
-			return NULL;
+			return NULL;		/* keep compiler quiet */
 	}
 
 	/*
-	 * open pg_class and fetch a tuple
+	 * Open pg_class and fetch a tuple.  Force heap scan if we haven't
+	 * yet built the critical relcache entries (this includes initdb
+	 * and startup without a pg_internal.init file).
 	 */
 	pg_class_desc = heap_openr(RelationRelationName, AccessShareLock);
-	pg_class_scan = heap_beginscan(pg_class_desc, 0, SnapshotNow, 1, &key);
-	pg_class_tuple = heap_getnext(pg_class_scan, 0);
-
-	/*
-	 * get set to return tuple
-	 */
-	if (!HeapTupleIsValid(pg_class_tuple))
-		return_tuple = pg_class_tuple;
-	else
-	{
-		/*
-		 * a satanic bug used to live here: pg_class_tuple used to be
-		 * returned here without having the corresponding buffer pinned.
-		 * so when the buffer gets replaced, all hell breaks loose. this
-		 * bug is discovered and killed by wei on 9/27/91.
-		 */
-		return_tuple = heap_copytuple(pg_class_tuple);
-	}
-
-	/* all done */
-	heap_endscan(pg_class_scan);
-	heap_close(pg_class_desc, AccessShareLock);
-
-	return return_tuple;
-}
+	pg_class_scan = systable_beginscan(pg_class_desc, indexRelname,
+									   criticalRelcachesBuilt,
+									   SnapshotNow,
+									   1, &key);
 
-static HeapTuple
-scan_pg_rel_ind(RelationBuildDescInfo buildinfo)
-{
-	Relation	pg_class_desc;
-	HeapTuple	return_tuple;
-
-	pg_class_desc = heap_openr(RelationRelationName, AccessShareLock);
+	pg_class_tuple = systable_getnext(pg_class_scan);
 
 	/*
-	 * If the indexes of pg_class are deactivated we have to call
-	 * scan_pg_rel_seq() instead.
+	 * Must copy tuple before releasing buffer.
 	 */
-	if (!pg_class_desc->rd_rel->relhasindex)
-	{
-		heap_close(pg_class_desc, AccessShareLock);
-		return scan_pg_rel_seq(buildinfo);
-	}
-
-	switch (buildinfo.infotype)
-	{
-		case INFO_RELID:
-			return_tuple = ClassOidIndexScan(pg_class_desc,
-								  ObjectIdGetDatum(buildinfo.i.info_id));
-			break;
-
-		case INFO_RELNAME:
-			return_tuple = ClassNameIndexScan(pg_class_desc,
-								 PointerGetDatum(buildinfo.i.info_name));
-			break;
-
-		default:
-			elog(ERROR, "ScanPgRelation: bad buildinfo");
-			return_tuple = NULL;	/* keep compiler quiet */
-	}
+	if (HeapTupleIsValid(pg_class_tuple))
+		pg_class_tuple = heap_copytuple(pg_class_tuple);
 
+	/* all done */
+	systable_endscan(pg_class_scan);
 	heap_close(pg_class_desc, AccessShareLock);
 
-	/* The xxxIndexScan routines will have returned a palloc'd tuple. */
-
-	return return_tuple;
+	return pg_class_tuple;
 }
 
 /*
@@ -452,75 +443,16 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp)
  *		RelationBuildTupleDesc
  *
  *		Form the relation's tuple descriptor from information in
- *		the pg_attribute, pg_attrdef & pg_relcheck system cataloges.
+ *		the pg_attribute, pg_attrdef & pg_relcheck system catalogs.
  */
 static void
 RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
 					   Relation relation)
-{
-	/*
-	 * If this is bootstrap time (initdb), then we can't use the system
-	 * catalog indices, because they may not exist yet.  Otherwise, we
-	 * can, and do.
-	 */
-
-	if (IsIgnoringSystemIndexes() || !criticalRelcachesBuilt)
-		build_tupdesc_seq(buildinfo, relation);
-	else
-		build_tupdesc_ind(buildinfo, relation);
-}
-
-static void
-SetConstrOfRelation(Relation relation,
-					TupleConstr *constr,
-					int ndef,
-					AttrDefault *attrdef)
-{
-	if (constr->has_not_null || ndef > 0 || relation->rd_rel->relchecks)
-	{
-		relation->rd_att->constr = constr;
-
-		if (ndef > 0)			/* DEFAULTs */
-		{
-			if (ndef < relation->rd_rel->relnatts)
-				constr->defval = (AttrDefault *)
-					repalloc(attrdef, ndef * sizeof(AttrDefault));
-			else
-				constr->defval = attrdef;
-			constr->num_defval = ndef;
-			AttrDefaultFetch(relation);
-		}
-		else
-			constr->num_defval = 0;
-
-		if (relation->rd_rel->relchecks > 0)	/* CHECKs */
-		{
-			constr->num_check = relation->rd_rel->relchecks;
-			constr->check = (ConstrCheck *)
-				MemoryContextAlloc(CacheMemoryContext,
-								constr->num_check * sizeof(ConstrCheck));
-			MemSet(constr->check, 0, constr->num_check * sizeof(ConstrCheck));
-			RelCheckFetch(relation);
-		}
-		else
-			constr->num_check = 0;
-	}
-	else
-	{
-		pfree(constr);
-		relation->rd_att->constr = NULL;
-	}
-}
-
-static void
-build_tupdesc_seq(RelationBuildDescInfo buildinfo,
-				  Relation relation)
 {
 	HeapTuple	pg_attribute_tuple;
 	Relation	pg_attribute_desc;
-	HeapScanDesc pg_attribute_scan;
-	Form_pg_attribute attp;
-	ScanKeyData key;
+	SysScanDesc pg_attribute_scan;
+	ScanKeyData skey[2];
 	int			need;
 	TupleConstr *constr;
 	AttrDefault *attrdef = NULL;
@@ -531,73 +463,89 @@ build_tupdesc_seq(RelationBuildDescInfo buildinfo,
 	constr->has_not_null = false;
 
 	/*
-	 * form a scan key
+	 * Form a scan key that selects only user attributes (attnum > 0).
+	 * (Eliminating system attribute rows at the index level is lots
+	 * faster than fetching them.)
 	 */
-	ScanKeyEntryInitialize(&key, 0,
+	ScanKeyEntryInitialize(&skey[0], 0,
 						   Anum_pg_attribute_attrelid,
 						   F_OIDEQ,
 						   ObjectIdGetDatum(RelationGetRelid(relation)));
+	ScanKeyEntryInitialize(&skey[1], 0,
+						   Anum_pg_attribute_attnum,
+						   F_INT2GT,
+						   Int16GetDatum(0));
 
 	/*
-	 * open pg_attribute and begin a scan
+	 * Open pg_attribute and begin a scan.  Force heap scan if we haven't
+	 * yet built the critical relcache entries (this includes initdb
+	 * and startup without a pg_internal.init file).
 	 */
 	pg_attribute_desc = heap_openr(AttributeRelationName, AccessShareLock);
-	pg_attribute_scan = heap_beginscan(pg_attribute_desc, 0, SnapshotNow, 1, &key);
+	pg_attribute_scan = systable_beginscan(pg_attribute_desc,
+										   AttributeRelidNumIndex,
+										   criticalRelcachesBuilt,
+										   SnapshotNow,
+										   2, skey);
 
 	/*
 	 * add attribute data to relation->rd_att
 	 */
 	need = relation->rd_rel->relnatts;
 
-	pg_attribute_tuple = heap_getnext(pg_attribute_scan, 0);
-	while (HeapTupleIsValid(pg_attribute_tuple) && need > 0)
+	while (HeapTupleIsValid(pg_attribute_tuple = systable_getnext(pg_attribute_scan)))
 	{
+		Form_pg_attribute attp;
+
 		attp = (Form_pg_attribute) GETSTRUCT(pg_attribute_tuple);
 
-		if (attp->attnum > 0)
-		{
-			relation->rd_att->attrs[attp->attnum - 1] =
-				(Form_pg_attribute) MemoryContextAlloc(CacheMemoryContext,
+		if (attp->attnum <= 0 ||
+			attp->attnum > relation->rd_rel->relnatts)
+			elog(ERROR, "Bogus attribute number %d for %s",
+				 attp->attnum, RelationGetRelationName(relation));
+
+		relation->rd_att->attrs[attp->attnum - 1] =
+			(Form_pg_attribute) MemoryContextAlloc(CacheMemoryContext,
 												   ATTRIBUTE_TUPLE_SIZE);
 
-			memcpy((char *) (relation->rd_att->attrs[attp->attnum - 1]),
-				   (char *) attp,
-				   ATTRIBUTE_TUPLE_SIZE);
-			need--;
+		memcpy((char *) (relation->rd_att->attrs[attp->attnum - 1]),
+			   (char *) attp,
+			   ATTRIBUTE_TUPLE_SIZE);
 
-			/* Update if this attribute have a constraint */
-			if (attp->attnotnull)
-				constr->has_not_null = true;
+		/* Update constraint/default info */
+		if (attp->attnotnull)
+			constr->has_not_null = true;
 
-			if (attp->atthasdef)
+		if (attp->atthasdef)
+		{
+			if (attrdef == NULL)
 			{
-				if (attrdef == NULL)
-				{
-					attrdef = (AttrDefault *)
-						MemoryContextAlloc(CacheMemoryContext,
-										   relation->rd_rel->relnatts *
-										   sizeof(AttrDefault));
-					MemSet(attrdef, 0,
+				attrdef = (AttrDefault *)
+					MemoryContextAlloc(CacheMemoryContext,
+									   relation->rd_rel->relnatts *
+									   sizeof(AttrDefault));
+				MemSet(attrdef, 0,
 					   relation->rd_rel->relnatts * sizeof(AttrDefault));
-				}
-				attrdef[ndef].adnum = attp->attnum;
-				attrdef[ndef].adbin = NULL;
-				ndef++;
 			}
+			attrdef[ndef].adnum = attp->attnum;
+			attrdef[ndef].adbin = NULL;
+			ndef++;
 		}
-		pg_attribute_tuple = heap_getnext(pg_attribute_scan, 0);
+		need--;
+		if (need == 0)
+			break;
 	}
 
-	if (need > 0)
-		elog(ERROR, "catalog is missing %d attribute%s for relid %u",
-			 need, (need == 1 ? "" : "s"), RelationGetRelid(relation));
-
 	/*
 	 * end the scan and close the attribute relation
 	 */
-	heap_endscan(pg_attribute_scan);
+	systable_endscan(pg_attribute_scan);
 	heap_close(pg_attribute_desc, AccessShareLock);
 
+	if (need != 0)
+		elog(ERROR, "catalog is missing %d attribute(s) for relid %u",
+			 need, RelationGetRelid(relation));
+
 	/*
 	 * The attcacheoff values we read from pg_attribute should all be -1
 	 * ("unknown").  Verify this if assert checking is on.	They will be
@@ -620,110 +568,43 @@ build_tupdesc_seq(RelationBuildDescInfo buildinfo,
 	 */
 	relation->rd_att->attrs[0]->attcacheoff = 0;
 
-	SetConstrOfRelation(relation, constr, ndef, attrdef);
-}
-
-static void
-build_tupdesc_ind(RelationBuildDescInfo buildinfo,
-				  Relation relation)
-{
-	Relation	attrel;
-	HeapTuple	atttup;
-	Form_pg_attribute attp;
-	TupleConstr *constr;
-	AttrDefault *attrdef = NULL;
-	int			ndef = 0;
-	int			i;
-
-	constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext,
-												sizeof(TupleConstr));
-	constr->has_not_null = false;
-
-	attrel = heap_openr(AttributeRelationName, AccessShareLock);
-
-	for (i = 1; i <= relation->rd_rel->relnatts; i++)
+	/*
+	 * Set up constraint/default info
+	 */
+	if (constr->has_not_null || ndef > 0 || relation->rd_rel->relchecks)
 	{
-#ifdef	_DROP_COLUMN_HACK__
-		bool		columnDropped = false;
-#endif   /* _DROP_COLUMN_HACK__ */
-
-		atttup = AttributeRelidNumIndexScan(attrel,
-							ObjectIdGetDatum(RelationGetRelid(relation)),
-											Int32GetDatum(i));
+		relation->rd_att->constr = constr;
 
-		if (!HeapTupleIsValid(atttup))
+		if (ndef > 0)			/* DEFAULTs */
 		{
-#ifdef	_DROP_COLUMN_HACK__
-			atttup = AttributeRelidNumIndexScan(attrel,
-							ObjectIdGetDatum(RelationGetRelid(relation)),
-								 Int32GetDatum(DROPPED_COLUMN_INDEX(i)));
-			if (!HeapTupleIsValid(atttup))
-#endif   /* _DROP_COLUMN_HACK__ */
-				elog(ERROR, "cannot find attribute %d of relation %s", i,
-					 RelationGetRelationName(relation));
-#ifdef	_DROP_COLUMN_HACK__
-			columnDropped = true;
-#endif   /* _DROP_COLUMN_HACK__ */
+			if (ndef < relation->rd_rel->relnatts)
+				constr->defval = (AttrDefault *)
+					repalloc(attrdef, ndef * sizeof(AttrDefault));
+			else
+				constr->defval = attrdef;
+			constr->num_defval = ndef;
+			AttrDefaultFetch(relation);
 		}
+		else
+			constr->num_defval = 0;
 
-		relation->rd_att->attrs[i - 1] = attp =
-			(Form_pg_attribute) MemoryContextAlloc(CacheMemoryContext,
-												   ATTRIBUTE_TUPLE_SIZE);
-
-		memcpy((char *) attp,
-			   (char *) (Form_pg_attribute) GETSTRUCT(atttup),
-			   ATTRIBUTE_TUPLE_SIZE);
-
-		/* don't forget to free the tuple returned from xxxIndexScan */
-		heap_freetuple(atttup);
-
-#ifdef	_DROP_COLUMN_HACK__
-		if (columnDropped)
-			continue;
-#endif   /* _DROP_COLUMN_HACK__ */
-
-		/* Update if this attribute have a constraint */
-		if (attp->attnotnull)
-			constr->has_not_null = true;
-
-		if (attp->atthasdef)
+		if (relation->rd_rel->relchecks > 0)	/* CHECKs */
 		{
-			if (attrdef == NULL)
-			{
-				attrdef = (AttrDefault *)
-					MemoryContextAlloc(CacheMemoryContext,
-									   relation->rd_rel->relnatts *
-									   sizeof(AttrDefault));
-				MemSet(attrdef, 0,
-					   relation->rd_rel->relnatts * sizeof(AttrDefault));
-			}
-			attrdef[ndef].adnum = i;
-			attrdef[ndef].adbin = NULL;
-			ndef++;
+			constr->num_check = relation->rd_rel->relchecks;
+			constr->check = (ConstrCheck *)
+				MemoryContextAlloc(CacheMemoryContext,
+								constr->num_check * sizeof(ConstrCheck));
+			MemSet(constr->check, 0, constr->num_check * sizeof(ConstrCheck));
+			RelCheckFetch(relation);
 		}
+		else
+			constr->num_check = 0;
+	}
+	else
+	{
+		pfree(constr);
+		relation->rd_att->constr = NULL;
 	}
-
-	heap_close(attrel, AccessShareLock);
-
-	/*
-	 * The attcacheoff values we read from pg_attribute should all be -1
-	 * ("unknown").  Verify this if assert checking is on.	They will be
-	 * computed when and if needed during tuple access.
-	 */
-#ifdef USE_ASSERT_CHECKING
-	for (i = 0; i < relation->rd_rel->relnatts; i++)
-		Assert(relation->rd_att->attrs[i]->attcacheoff == -1);
-#endif
-
-	/*
-	 * However, we can easily set the attcacheoff value for the first
-	 * attribute: it must be zero.	This eliminates the need for special
-	 * cases for attnum=1 that used to exist in fastgetattr() and
-	 * index_getattr().
-	 */
-	relation->rd_att->attrs[0]->attcacheoff = 0;
-
-	SetConstrOfRelation(relation, constr, ndef, attrdef);
 }
 
 /*
@@ -749,7 +630,7 @@ RelationBuildRuleLock(Relation relation)
 	HeapTuple	pg_rewrite_tuple;
 	Relation	pg_rewrite_desc;
 	TupleDesc	pg_rewrite_tupdesc;
-	HeapScanDesc pg_rewrite_scan;
+	SysScanDesc pg_rewrite_scan;
 	ScanKeyData key;
 	RuleLock   *rulelock;
 	int			numlocks;
@@ -786,12 +667,16 @@ RelationBuildRuleLock(Relation relation)
 
 	/*
 	 * open pg_rewrite and begin a scan
+	 *
+	 * XXX: there is no suitable index for this scan.  FIXME.
 	 */
 	pg_rewrite_desc = heap_openr(RewriteRelationName, AccessShareLock);
-	pg_rewrite_scan = heap_beginscan(pg_rewrite_desc, 0, SnapshotNow, 1, &key);
 	pg_rewrite_tupdesc = RelationGetDescr(pg_rewrite_desc);
+	pg_rewrite_scan = systable_beginscan(pg_rewrite_desc, NULL, false,
+										 SnapshotNow,
+										 1, &key);
 
-	while (HeapTupleIsValid(pg_rewrite_tuple = heap_getnext(pg_rewrite_scan, 0)))
+	while (HeapTupleIsValid(pg_rewrite_tuple = systable_getnext(pg_rewrite_scan)))
 	{
 		bool		isnull;
 		Datum		ruleaction;
@@ -854,7 +739,7 @@ RelationBuildRuleLock(Relation relation)
 	/*
 	 * end the scan and close the attribute relation
 	 */
-	heap_endscan(pg_rewrite_scan);
+	systable_endscan(pg_rewrite_scan);
 	heap_close(pg_rewrite_desc, AccessShareLock);
 
 	/*
@@ -930,25 +815,6 @@ equalRuleLocks(RuleLock *rlock1, RuleLock *rlock2)
  *		recycling the given old relation object.  The latter case
  *		supports rebuilding a relcache entry without invalidating
  *		pointers to it.
- *
- *		To build a relation descriptor, we have to allocate space,
- *		open the underlying unix file and initialize the following
- *		fields:
- *
- *	File				   rd_fd;		 open file descriptor
- *	BlockNumber			   rd_nblocks;	 number of blocks in rel
- *										 it will be set in ambeginscan()
- *	int					   rd_refcnt;	 reference count
- *	Form_pg_am			   rd_am;		 AM tuple
- *	Form_pg_class		   rd_rel;		 RELATION tuple
- *	Oid					   rd_id;		 relation's object id
- *	LockInfoData		   rd_lockInfo;  lock manager's info
- *	TupleDesc			   rd_att;		 tuple descriptor
- *
- *		Note: rd_ismem (rel is in-memory only) is currently unused
- *		by any part of the system.	someday this will indicate that
- *		the relation lives only in the main-memory buffer pool
- *		-cim 2/4/91
  * --------------------------------
  */
 static Relation
@@ -957,7 +823,6 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
 {
 	Relation	relation;
 	Oid			relid;
-	Oid			relam;
 	HeapTuple	pg_class_tuple;
 	Form_pg_class relp;
 	MemoryContext oldcxt;
@@ -1005,14 +870,6 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
 	 */
 	relation->rd_isnailed = false;
 
-	/*
-	 * initialize the access method information (relation->rd_am)
-	 */
-	relam = relation->rd_rel->relam;
-	if (OidIsValid(relam))
-		relation->rd_am = AccessMethodObjectIdGetForm(relam,
-													  CacheMemoryContext);
-
 	/*
 	 * initialize the tuple descriptor (relation->rd_att).
 	 */
@@ -1035,9 +892,9 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
 		relation->trigdesc = NULL;
 
 	/*
-	 * initialize index strategy and support information for this relation
+	 * if it's an index, initialize index-related information
 	 */
-	if (OidIsValid(relam))
+	if (OidIsValid(relation->rd_rel->relam))
 		RelationInitIndexAccessInfo(relation);
 
 	/*
@@ -1051,24 +908,8 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
 		relation->rd_node.tblNode = MyDatabaseId;
 	relation->rd_node.relNode = relation->rd_rel->relfilenode;
 
-	/*
-	 * Open the relation and assign the file descriptor returned by the
-	 * storage manager code to rd_fd.
-	 *
-	 * We do not raise a hard error if we fail to open the relation at this
-	 * point.  If we did, it would be impossible to drop a relation whose
-	 * underlying physical file had disappeared.
-	 */
-	if (relation->rd_rel->relkind != RELKIND_VIEW)
-	{
-		relation->rd_fd = smgropen(DEFAULT_SMGR, relation, true);
-		Assert(relation->rd_fd >= -1);
-		if (relation->rd_fd == -1)
-			elog(NOTICE, "RelationBuildDesc: can't open %s: %m",
-				 RelationGetRelationName(relation));
-	}
-	else
-		relation->rd_fd = -1;
+	/* make sure relation is marked as having no open file yet */
+	relation->rd_fd = -1;
 
 	/*
 	 * insert newly created relation into proper relcaches, restore memory
@@ -1087,18 +928,55 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
 void
 RelationInitIndexAccessInfo(Relation relation)
 {
+	HeapTuple	tuple;
+	Size		iformsize;
+	Form_pg_index iform;
+	Form_pg_am	aform;
 	MemoryContext indexcxt;
 	IndexStrategy strategy;
+	Oid		   *operator;
 	RegProcedure *support;
 	FmgrInfo   *supportinfo;
 	int			natts;
 	uint16		amstrategies;
 	uint16		amsupport;
-	Size		stratSize;
+
+	/*
+	 * Make a copy of the pg_index entry for the index.  Note that this
+	 * is a variable-length tuple.
+	 */
+	tuple = SearchSysCache(INDEXRELID,
+						   ObjectIdGetDatum(RelationGetRelid(relation)),
+						   0, 0, 0);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "RelationInitIndexAccessInfo: no pg_index entry for index %u",
+			 RelationGetRelid(relation));
+	iformsize = tuple->t_len - tuple->t_data->t_hoff;
+	iform = (Form_pg_index) MemoryContextAlloc(CacheMemoryContext, iformsize);
+	memcpy(iform, GETSTRUCT(tuple), iformsize);
+	ReleaseSysCache(tuple);
+	relation->rd_index = iform;
+
+	/* this field is now kinda redundant... */
+	relation->rd_uniqueindex = iform->indisunique;
+
+	/*
+	 * Make a copy of the pg_am entry for the index's access method
+	 */
+	tuple = SearchSysCache(AMOID,
+						   ObjectIdGetDatum(relation->rd_rel->relam),
+						   0, 0, 0);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "RelationInitIndexAccessInfo: cache lookup failed for AM %u",
+			 relation->rd_rel->relam);
+	aform = (Form_pg_am) MemoryContextAlloc(CacheMemoryContext, sizeof *aform);
+	memcpy(aform, GETSTRUCT(tuple), sizeof *aform);
+	ReleaseSysCache(tuple);
+	relation->rd_am = aform;
 
 	natts = relation->rd_rel->relnatts;
-	amstrategies = relation->rd_am->amstrategies;
-	amsupport = relation->rd_am->amsupport;
+	amstrategies = aform->amstrategies;
+	amsupport = aform->amsupport;
 
 	/*
 	 * Make the private context to hold index access info.	The reason we
@@ -1118,8 +996,23 @@ RelationInitIndexAccessInfo(Relation relation)
 	/*
 	 * Allocate arrays to hold data
 	 */
-	stratSize = AttributeNumberGetIndexStrategySize(natts, amstrategies);
-	strategy = (IndexStrategy) MemoryContextAlloc(indexcxt, stratSize);
+	if (amstrategies > 0)
+	{
+		int			noperators = natts * amstrategies;
+		Size		stratSize;
+
+		stratSize = AttributeNumberGetIndexStrategySize(natts, amstrategies);
+		strategy = (IndexStrategy) MemoryContextAlloc(indexcxt, stratSize);
+		MemSet(strategy, 0, stratSize);
+		operator = (Oid *)
+			MemoryContextAlloc(indexcxt, noperators * sizeof(Oid));
+		MemSet(operator, 0, noperators * sizeof(Oid));
+	}
+	else
+	{
+		strategy = NULL;
+		operator = NULL;
+	}
 
 	if (amsupport > 0)
 	{
@@ -1127,6 +1020,7 @@ RelationInitIndexAccessInfo(Relation relation)
 
 		support = (RegProcedure *)
 			MemoryContextAlloc(indexcxt, nsupport * sizeof(RegProcedure));
+		MemSet(support, 0, nsupport * sizeof(RegProcedure));
 		supportinfo = (FmgrInfo *)
 			MemoryContextAlloc(indexcxt, nsupport * sizeof(FmgrInfo));
 		MemSet(supportinfo, 0, nsupport * sizeof(FmgrInfo));
@@ -1137,31 +1031,301 @@ RelationInitIndexAccessInfo(Relation relation)
 		supportinfo = NULL;
 	}
 
+	relation->rd_istrat = strategy;
+	relation->rd_operator = operator;
+	relation->rd_support = support;
+	relation->rd_supportinfo = supportinfo;
+
 	/*
 	 * Fill the strategy map and the support RegProcedure arrays.
 	 * (supportinfo is left as zeroes, and is filled on-the-fly when used)
 	 */
-	IndexSupportInitialize(strategy, support,
-						   &relation->rd_uniqueindex,
-						   RelationGetRelid(relation),
-						   relation->rd_rel->relam,
+	IndexSupportInitialize(iform,
+						   strategy, operator, support,
 						   amstrategies, amsupport, natts);
-
-	relation->rd_istrat = strategy;
-	relation->rd_support = support;
-	relation->rd_supportinfo = supportinfo;
 }
 
 /*
- *		formrdesc
+ * IndexSupportInitialize
+ *		Initializes an index strategy and associated support procedures,
+ *		given the index's pg_index tuple.
  *
- *		This is a special cut-down version of RelationBuildDesc()
- *		used by RelationCacheInitialize() in initializing the relcache.
- *		The relation descriptor is built just from the supplied parameters,
+ * Data is returned into *indexStrategy, *indexOperator, and *indexSupport,
+ * all of which are objects allocated by the caller.
+ *
+ * The caller also passes maxStrategyNumber, maxSupportNumber, and
+ * maxAttributeNumber, since these indicate the size of the arrays
+ * it has allocated --- but in practice these numbers must always match
+ * those obtainable from the system catalog entries for the index and
+ * access method.
+ */
+static void
+IndexSupportInitialize(Form_pg_index iform,
+					   IndexStrategy indexStrategy,
+					   Oid *indexOperator,
+					   RegProcedure *indexSupport,
+					   StrategyNumber maxStrategyNumber,
+					   StrategyNumber maxSupportNumber,
+					   AttrNumber maxAttributeNumber)
+{
+	int			attIndex;
+
+	maxStrategyNumber = AMStrategies(maxStrategyNumber);
+
+	/*
+	 * XXX note that the following assumes the INDEX tuple is well formed
+	 * and that the *key and *class are 0 terminated.
+	 */
+	for (attIndex = 0; attIndex < maxAttributeNumber; attIndex++)
+	{
+		OpClassCacheEnt *opcentry;
+
+		if (iform->indkey[attIndex] == InvalidAttrNumber ||
+			!OidIsValid(iform->indclass[attIndex]))
+			elog(ERROR, "IndexSupportInitialize: bogus pg_index tuple");
+
+		/* look up the info for this opclass, using a cache */
+		opcentry = LookupOpclassInfo(iform->indclass[attIndex],
+									 maxStrategyNumber,
+									 maxSupportNumber);
+
+		/* load the strategy information for the index operators */
+		if (maxStrategyNumber > 0)
+		{
+			StrategyMap map;
+			Oid		   *opers;
+			StrategyNumber strategy;
+
+			map = IndexStrategyGetStrategyMap(indexStrategy,
+											  maxStrategyNumber,
+											  attIndex + 1);
+			opers = &indexOperator[attIndex * maxStrategyNumber];
+
+			for (strategy = 0; strategy < maxStrategyNumber; strategy++)
+			{
+				ScanKey		mapentry;
+
+				mapentry = StrategyMapGetScanKeyEntry(map, strategy + 1);
+				if (RegProcedureIsValid(opcentry->operatorProcs[strategy]))
+				{
+					MemSet(mapentry, 0, sizeof(*mapentry));
+					mapentry->sk_flags = 0;
+					mapentry->sk_procedure = opcentry->operatorProcs[strategy];
+					/*
+					 * Mark mapentry->sk_func invalid, until and unless
+					 * someone sets it up.
+					 */
+					mapentry->sk_func.fn_oid = InvalidOid;
+				}
+				else
+					ScanKeyEntrySetIllegal(mapentry);
+				opers[strategy] = opcentry->operatorOids[strategy];
+			}
+		}
+
+		/* if support routines exist for this access method, load them */
+		if (maxSupportNumber > 0)
+		{
+			RegProcedure *procs;
+			StrategyNumber support;
+
+			procs = &indexSupport[attIndex * maxSupportNumber];
+
+			for (support = 0; support < maxSupportNumber; ++support)
+				procs[support] = opcentry->supportProcs[support];
+		}
+	}
+}
+
+/*
+ * LookupOpclassInfo
+ *
+ * This routine maintains a per-opclass cache of the information needed
+ * by IndexSupportInitialize().  This is more efficient than relying on
+ * the catalog cache, because we can load all the info about a particular
+ * opclass in a single indexscan of pg_amproc or pg_amop.
+ *
+ * The information from pg_am about expected range of strategy and support
+ * numbers is passed in, rather than being looked up, mainly because the
+ * caller will have it already.
+ *
+ * XXX There isn't any provision for flushing the cache.  However, there
+ * isn't any provision for flushing relcache entries when opclass info
+ * changes, either :-(
+ */
+static OpClassCacheEnt *
+LookupOpclassInfo(Oid operatorClassOid,
+				  StrategyNumber numStrats,
+				  StrategyNumber numSupport)
+{
+	OpClassCacheEnt *opcentry;
+	bool		found;
+	Relation	pg_amop_desc;
+	Relation	pg_amproc_desc;
+	SysScanDesc pg_amop_scan;
+	SysScanDesc pg_amproc_scan;
+	ScanKeyData key;
+	HeapTuple	htup;
+	bool		indexOK;
+
+	if (OpClassCache == NULL)
+	{
+		/* First time through: initialize the opclass cache */
+		HASHCTL		ctl;
+
+		if (!CacheMemoryContext)
+			CreateCacheMemoryContext();
+
+		MemSet(&ctl, 0, sizeof(ctl));
+		ctl.keysize = sizeof(Oid);
+		ctl.entrysize = sizeof(OpClassCacheEnt);
+		ctl.hash = tag_hash;
+		OpClassCache = hash_create("Operator class cache", 64,
+								   &ctl, HASH_ELEM | HASH_FUNCTION);
+	}
+
+	opcentry = (OpClassCacheEnt *) hash_search(OpClassCache,
+											   (void *) &operatorClassOid,
+											   HASH_ENTER, &found);
+	if (opcentry == NULL)
+		elog(ERROR, "out of memory for operator class cache");
+
+	if (found && opcentry->valid)
+	{
+		/* Already made an entry for it */
+		Assert(numStrats == opcentry->numStrats);
+		Assert(numSupport == opcentry->numSupport);
+		return opcentry;
+	}
+
+	/* Need to fill in new entry */
+	opcentry->valid = false;	/* until known OK */
+	opcentry->numStrats = numStrats;
+	opcentry->numSupport = numSupport;
+
+	if (numStrats > 0)
+	{
+		opcentry->operatorOids = (Oid *)
+			MemoryContextAlloc(CacheMemoryContext,
+							   numStrats * sizeof(Oid));
+		MemSet(opcentry->operatorOids, 0, numStrats * sizeof(Oid));
+		opcentry->operatorProcs = (RegProcedure *)
+			MemoryContextAlloc(CacheMemoryContext,
+							   numStrats * sizeof(RegProcedure));
+		MemSet(opcentry->operatorProcs, 0, numStrats * sizeof(RegProcedure));
+	}
+	else
+	{
+		opcentry->operatorOids = NULL;
+		opcentry->operatorProcs = NULL;
+	}
+
+	if (numSupport > 0)
+	{
+		opcentry->supportProcs = (RegProcedure *)
+			MemoryContextAlloc(CacheMemoryContext,
+							   numSupport * sizeof(RegProcedure));
+		MemSet(opcentry->supportProcs, 0, numSupport * sizeof(RegProcedure));
+	}
+	else
+		opcentry->supportProcs = NULL;
+
+	/*
+	 * To avoid infinite recursion during startup, force a heap scan if
+	 * we're looking up info for the opclasses used by the indexes we
+	 * would like to reference here.
+	 */
+	indexOK = criticalRelcachesBuilt ||
+		(operatorClassOid != OID_BTREE_OPS_OID &&
+		 operatorClassOid != INT2_BTREE_OPS_OID);
+
+	/*
+	 * Scan pg_amop to obtain operators for the opclass
+	 */
+	if (numStrats > 0)
+	{
+		ScanKeyEntryInitialize(&key, 0,
+							   Anum_pg_amop_amopclaid,
+							   F_OIDEQ,
+							   ObjectIdGetDatum(operatorClassOid));
+		pg_amop_desc = heap_openr(AccessMethodOperatorRelationName,
+								  AccessShareLock);
+		pg_amop_scan = systable_beginscan(pg_amop_desc,
+										  AccessMethodStrategyIndex,
+										  indexOK,
+										  SnapshotNow,
+										  1, &key);
+
+		while (HeapTupleIsValid(htup = systable_getnext(pg_amop_scan)))
+		{
+			Form_pg_amop amopform = (Form_pg_amop) GETSTRUCT(htup);
+
+			if (amopform->amopstrategy <= 0 ||
+				(StrategyNumber) amopform->amopstrategy > numStrats)
+				elog(ERROR, "Bogus amopstrategy number %d for opclass %u",
+					 amopform->amopstrategy, operatorClassOid);
+			opcentry->operatorOids[amopform->amopstrategy - 1] =
+				amopform->amopopr;
+			opcentry->operatorProcs[amopform->amopstrategy - 1] =
+				get_opcode(amopform->amopopr);
+		}
+
+		systable_endscan(pg_amop_scan);
+		heap_close(pg_amop_desc, AccessShareLock);
+	}
+
+	/*
+	 * Scan pg_amproc to obtain support procs for the opclass
+	 */
+	if (numSupport > 0)
+	{
+		ScanKeyEntryInitialize(&key, 0,
+							   Anum_pg_amproc_amopclaid,
+							   F_OIDEQ,
+							   ObjectIdGetDatum(operatorClassOid));
+		pg_amproc_desc = heap_openr(AccessMethodProcedureRelationName,
+									AccessShareLock);
+		pg_amproc_scan = systable_beginscan(pg_amproc_desc,
+											AccessMethodProcedureIndex,
+											indexOK,
+											SnapshotNow,
+											1, &key);
+
+		while (HeapTupleIsValid(htup = systable_getnext(pg_amproc_scan)))
+		{
+			Form_pg_amproc amprocform = (Form_pg_amproc) GETSTRUCT(htup);
+
+			if (amprocform->amprocnum <= 0 ||
+				(StrategyNumber) amprocform->amprocnum > numSupport)
+				elog(ERROR, "Bogus amproc number %d for opclass %u",
+					 amprocform->amprocnum, operatorClassOid);
+
+			opcentry->supportProcs[amprocform->amprocnum - 1] =
+				amprocform->amproc;
+		}
+
+		systable_endscan(pg_amproc_scan);
+		heap_close(pg_amproc_desc, AccessShareLock);
+	}
+
+	opcentry->valid = true;
+	return opcentry;
+}
+
+
+/*
+ *		formrdesc
+ *
+ *		This is a special cut-down version of RelationBuildDesc()
+ *		used by RelationCacheInitialize() in initializing the relcache.
+ *		The relation descriptor is built just from the supplied parameters,
  *		without actually looking at any system table entries.  We cheat
  *		quite a lot since we only need to work for a few basic system
  *		catalogs...
  *
+ * Note that these catalogs can't have constraints, default values,
+ * rules, or triggers, since we don't cope with any of that.
+ *
  * NOTE: we assume we are already switched into CacheMemoryContext.
  */
 static void
@@ -1223,6 +1387,10 @@ formrdesc(char *relationName,
 
 	/*
 	 * initialize attribute tuple form
+	 *
+	 * Unlike the case with the relation tuple, this data had better be
+	 * right because it will never be replaced.  The input values must be
+	 * correctly defined by macros in src/include/catalog/ headers.
 	 */
 	relation->rd_att = CreateTemplateTupleDesc(natts);
 
@@ -1235,8 +1403,13 @@ formrdesc(char *relationName,
 		memcpy((char *) relation->rd_att->attrs[i],
 			   (char *) &att[i],
 			   ATTRIBUTE_TUPLE_SIZE);
+		/* make sure attcacheoff is valid */
+		relation->rd_att->attrs[i]->attcacheoff = -1;
 	}
 
+	/* initialize first attribute's attcacheoff, cf RelationBuildTupleDesc */
+	relation->rd_att->attrs[0]->attcacheoff = 0;
+
 	/*
 	 * initialize relation id from info in att array (my, this is ugly)
 	 */
@@ -1280,52 +1453,6 @@ formrdesc(char *relationName,
 }
 
 
-/*
- *		fixrdesc
- *
- *		Update the phony data inserted by formrdesc() with real info
- *		from pg_class.
- */
-static void
-fixrdesc(char *relationName)
-{
-	RelationBuildDescInfo buildinfo;
-	HeapTuple	pg_class_tuple;
-	Form_pg_class relp;
-	Relation	relation;
-
-	/*
-	 * find the tuple in pg_class corresponding to the given relation name
-	 */
-	buildinfo.infotype = INFO_RELNAME;
-	buildinfo.i.info_name = relationName;
-
-	pg_class_tuple = ScanPgRelation(buildinfo);
-
-	if (!HeapTupleIsValid(pg_class_tuple))
-		elog(FATAL, "fixrdesc: no pg_class entry for %s",
-			 relationName);
-	relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
-
-	/*
-	 * find the pre-made relcache entry (better be there!)
-	 */
-	relation = RelationNameCacheGetRelation(relationName);
-	if (!RelationIsValid(relation))
-		elog(FATAL, "fixrdesc: no existing relcache entry for %s",
-			 relationName);
-
-	/*
-	 * and copy pg_class_tuple to relation->rd_rel. (See notes in
-	 * AllocateRelationDesc())
-	 */
-	Assert(relation->rd_rel != NULL);
-	memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
-
-	heap_freetuple(pg_class_tuple);
-}
-
-
 /* ----------------------------------------------------------------
  *				 Relation Descriptor Lookup Interface
  * ----------------------------------------------------------------
@@ -1351,13 +1478,7 @@ RelationIdCacheGetRelation(Oid relationId)
 	RelationIdCacheLookup(relationId, rd);
 
 	if (RelationIsValid(rd))
-	{
-		/* re-open files if necessary */
-		if (rd->rd_fd == -1 && rd->rd_rel->relkind != RELKIND_VIEW)
-			rd->rd_fd = smgropen(DEFAULT_SMGR, rd, false);
-
 		RelationIncrementReferenceCount(rd);
-	}
 
 	return rd;
 }
@@ -1381,13 +1502,7 @@ RelationNameCacheGetRelation(const char *relationName)
 	RelationNameCacheLookup(NameStr(name), rd);
 
 	if (RelationIsValid(rd))
-	{
-		/* re-open files if necessary */
-		if (rd->rd_fd == -1 && rd->rd_rel->relkind != RELKIND_VIEW)
-			rd->rd_fd = smgropen(DEFAULT_SMGR, rd, false);
-
 		RelationIncrementReferenceCount(rd);
-	}
 
 	return rd;
 }
@@ -1400,13 +1515,7 @@ RelationNodeCacheGetRelation(RelFileNode rnode)
 	RelationNodeCacheLookup(rnode, rd);
 
 	if (RelationIsValid(rd))
-	{
-		/* re-open files if necessary */
-		if (rd->rd_fd == -1 && rd->rd_rel->relkind != RELKIND_VIEW)
-			rd->rd_fd = smgropen(DEFAULT_SMGR, rd, false);
-
 		RelationIncrementReferenceCount(rd);
-	}
 
 	return rd;
 }
@@ -1619,6 +1728,8 @@ RelationClearRelation(Relation relation, bool rebuildIt)
 	 * manager might have pointers into the rewrite rules. So to begin
 	 * with, we can only get rid of these fields:
 	 */
+	if (relation->rd_index)
+		pfree(relation->rd_index);
 	if (relation->rd_am)
 		pfree(relation->rd_am);
 	if (relation->rd_rel)
@@ -1805,32 +1916,11 @@ RelationIdInvalidateRelationCacheByRelationId(Oid relationId)
 	RelationIdCacheLookup(relationId, relation);
 
 	if (PointerIsValid(relation))
-		RelationFlushRelation(relation);
-}
-
-#if NOT_USED
-/* only used by RelationIdInvalidateRelationCacheByAccessMethodId,
- * which is dead code.
- */
-static void
-RelationFlushIndexes(Relation *r,
-					 Oid accessMethodId)
-{
-	Relation	relation = *r;
-
-	if (!RelationIsValid(relation))
 	{
-		elog(NOTICE, "inval call to RFI");
-		return;
-	}
-
-	if (relation->rd_rel->relkind == RELKIND_INDEX &&	/* XXX style */
-		(!OidIsValid(accessMethodId) ||
-		 relation->rd_rel->relam == accessMethodId))
+		relcacheInvalsReceived++;
 		RelationFlushRelation(relation);
+	}
 }
-#endif
-
 
 /*
  * RelationCacheInvalidate
@@ -1873,6 +1963,8 @@ RelationCacheInvalidate(void)
 		if (relation->rd_myxactonly)
 			continue;
 
+		relcacheInvalsReceived++;
+
 		if (RelationHasReferenceCountZero(relation))
 		{
 			/* Delete this entry immediately */
@@ -2071,7 +2163,12 @@ RelationPurgeLocalRelation(bool xactCommitted)
 /*
  *		RelationCacheInitialize
  *
- *		This initializes the relation descriptor cache.
+ *		This initializes the relation descriptor cache.  At the time
+ *		that this is invoked, we can't do database access yet (mainly
+ *		because the transaction subsystem is not up), so we can't get
+ *		"real" info.  However it's okay to read the pg_internal.init
+ *		cache file, if one is available.  Otherwise we make phony
+ *		entries for the minimum set of nailed-in-cache relations.
  */
 
 #define INITRELCACHESIZE		400
@@ -2091,7 +2188,7 @@ RelationCacheInitialize(void)
 	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
 
 	/*
-	 * create global caches
+	 * create hashtables that index the relcache
 	 */
 	MemSet(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(NameData);
@@ -2112,22 +2209,24 @@ RelationCacheInitialize(void)
 									&ctl, HASH_ELEM | HASH_FUNCTION);
 
 	/*
-	 * initialize the cache with pre-made relation descriptors for some of
-	 * the more important system relations.  These relations should always
-	 * be in the cache.
-	 *
-	 * NB: see also the list in RelationCacheInitializePhase2().
-	 */
-	formrdesc(RelationRelationName, Natts_pg_class, Desc_pg_class);
-	formrdesc(AttributeRelationName, Natts_pg_attribute, Desc_pg_attribute);
-	formrdesc(ProcedureRelationName, Natts_pg_proc, Desc_pg_proc);
-	formrdesc(TypeRelationName, Natts_pg_type, Desc_pg_type);
-
-	/*
-	 * init_irels() used to be called here. It is changed to be called in
-	 * RelationCacheInitializePhase2() now so that transactional control
-	 * could guarantee the consistency.
+	 * Try to load the relcache cache file.  If successful, we're done
+	 * for now.  Otherwise, initialize the cache with pre-made descriptors
+	 * for the critical "nailed-in" system catalogs.
 	 */
+	if (IsBootstrapProcessingMode() ||
+		! load_relcache_init_file())
+	{
+		formrdesc(RelationRelationName,
+				  Natts_pg_class, Desc_pg_class);
+		formrdesc(AttributeRelationName,
+				  Natts_pg_attribute, Desc_pg_attribute);
+		formrdesc(ProcedureRelationName,
+				  Natts_pg_proc, Desc_pg_proc);
+		formrdesc(TypeRelationName,
+				  Natts_pg_type, Desc_pg_type);
+
+#define NUM_CRITICAL_RELS	4	/* fix if you change list above */
+	}
 
 	MemoryContextSwitchTo(oldcxt);
 }
@@ -2135,40 +2234,145 @@ RelationCacheInitialize(void)
 /*
  *		RelationCacheInitializePhase2
  *
- *		This completes initialization of the relcache after catcache
- *		is functional and we are able to actually load data from pg_class.
+ *		This is called as soon as the catcache and transaction system
+ *		are functional.  At this point we can actually read data from
+ *		the system catalogs.  Update the relcache entries made during
+ *		RelationCacheInitialize, and make sure we have entries for the
+ *		critical system indexes.
  */
 void
 RelationCacheInitializePhase2(void)
 {
+	HASH_SEQ_STATUS status;
+	RelNameCacheEnt *namehentry;
+
+	if (IsBootstrapProcessingMode())
+		return;
+
 	/*
-	 * Get the real pg_class tuple for each nailed-in-cache relcache entry
-	 * that was made by RelationCacheInitialize(), and replace the phony
-	 * rd_rel entry made by formrdesc().  This is necessary so that we
-	 * have, for example, the correct toast-table info for tables that
-	 * have such.
+	 * If we didn't get the critical system indexes loaded into relcache,
+	 * do so now.  These are critical because the catcache depends on them
+	 * for catcache fetches that are done during relcache load.  Thus, we
+	 * have an infinite-recursion problem.  We can break the recursion
+	 * by doing heapscans instead of indexscans at certain key spots.
+	 * To avoid hobbling performance, we only want to do that until we
+	 * have the critical indexes loaded into relcache.  Thus, the flag
+	 * criticalRelcachesBuilt is used to decide whether to do heapscan
+	 * or indexscan at the key spots, and we set it true after we've loaded
+	 * the critical indexes.
+	 *
+	 * The critical indexes are marked as "nailed in cache", partly to make
+	 * it easy for load_relcache_init_file to count them, but mainly
+	 * because we cannot flush and rebuild them once we've set
+	 * criticalRelcachesBuilt to true.  (NOTE: perhaps it would be possible
+	 * to reload them by temporarily setting criticalRelcachesBuilt to
+	 * false again.  For now, though, we just nail 'em in.)
+	 */
+	if (! criticalRelcachesBuilt)
+	{
+		RelationBuildDescInfo buildinfo;
+		Relation	ird;
+
+#define LOAD_CRIT_INDEX(indname) \
+		do { \
+			buildinfo.infotype = INFO_RELNAME; \
+			buildinfo.i.info_name = (indname); \
+			ird = RelationBuildDesc(buildinfo, NULL); \
+			ird->rd_isnailed = true; \
+			RelationSetReferenceCount(ird, 1); \
+		} while (0)
+
+		LOAD_CRIT_INDEX(ClassNameIndex);
+		LOAD_CRIT_INDEX(ClassOidIndex);
+		LOAD_CRIT_INDEX(AttributeRelidNumIndex);
+		LOAD_CRIT_INDEX(IndexRelidIndex);
+		LOAD_CRIT_INDEX(AccessMethodStrategyIndex);
+		LOAD_CRIT_INDEX(AccessMethodProcedureIndex);
+		LOAD_CRIT_INDEX(OperatorOidIndex);
+
+#define NUM_CRITICAL_INDEXES	7	/* fix if you change list above */
+
+		criticalRelcachesBuilt = true;
+	}
+
+	/*
+	 * Now, scan all the relcache entries and update anything that might
+	 * be wrong in the results from formrdesc or the relcache cache file.
+	 * If we faked up relcache entries using formrdesc, then read
+	 * the real pg_class rows and replace the fake entries with them.
+	 * Also, if any of the relcache entries have rules or triggers,
+	 * load that info the hard way since it isn't recorded in the cache file.
 	 */
-	if (!IsBootstrapProcessingMode())
+	hash_seq_init(&status, RelationNameCache);
+
+	while ((namehentry = (RelNameCacheEnt *) hash_seq_search(&status)) != NULL)
 	{
+		Relation	relation = namehentry->reldesc;
+
 		/*
-		 * Initialize critical system index relation descriptors, first.
-		 * They are to make building relation descriptors fast.
-		 * init_irels() used to be called in RelationCacheInitialize(). It
-		 * is changed to be called here to be transaction safe.
+		 * If it's a faked-up entry, read the real pg_class tuple.
 		 */
-		MemoryContext oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+		if (needNewCacheFile && relation->rd_isnailed)
+		{
+			HeapTuple	htup;
+			Form_pg_class relp;
 
-		init_irels();
-		MemoryContextSwitchTo(oldcxt);
+			htup = SearchSysCache(RELOID,
+								  ObjectIdGetDatum(RelationGetRelid(relation)),
+								  0, 0, 0);
+			if (!HeapTupleIsValid(htup))
+				elog(FATAL, "RelationCacheInitializePhase2: no pg_class entry for %s",
+					 RelationGetRelationName(relation));
+			relp = (Form_pg_class) GETSTRUCT(htup);
+			/*
+			 * Copy tuple to relation->rd_rel. (See notes in
+			 * AllocateRelationDesc())
+			 */
+			Assert(relation->rd_rel != NULL);
+			memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
 
-		/* fix nailed-in-cache relations */
-		fixrdesc(RelationRelationName);
-		fixrdesc(AttributeRelationName);
-		fixrdesc(ProcedureRelationName);
-		fixrdesc(TypeRelationName);
+			ReleaseSysCache(htup);
+		}
+
+		/*
+		 * Fix data that isn't saved in relcache cache file.
+		 */
+		if (relation->rd_rel->relhasrules && relation->rd_rules == NULL)
+			RelationBuildRuleLock(relation);
+		if (relation->rd_rel->reltriggers > 0 && relation->trigdesc == NULL)
+			RelationBuildTriggers(relation);
 	}
 }
 
+/*
+ *		RelationCacheInitializePhase3
+ *
+ *		Final step of relcache initialization: write out a new relcache
+ *		cache file if one is needed.
+ */
+void
+RelationCacheInitializePhase3(void)
+{
+	if (IsBootstrapProcessingMode())
+		return;
+
+	if (needNewCacheFile)
+	{
+		/*
+		 * Force all the catcaches to finish initializing and thereby
+		 * open the catalogs and indexes they use.  This will preload
+		 * the relcache with entries for all the most important system
+		 * catalogs and indexes, so that the init file will be most
+		 * useful for future backends.
+		 */
+		InitCatalogCachePhase2();
+
+		/* now write the file */
+		write_relcache_init_file();
+	}
+}
+
+
 /* used by XLogInitCache */
 void		CreateDummyCaches(void);
 void		DestroyDummyCaches(void);
@@ -2233,63 +2437,31 @@ AttrDefaultFetch(Relation relation)
 	AttrDefault *attrdef = relation->rd_att->constr->defval;
 	int			ndef = relation->rd_att->constr->num_defval;
 	Relation	adrel;
-	Relation	irel = (Relation) NULL;
+	SysScanDesc adscan;
 	ScanKeyData skey;
-	HeapTupleData tuple;
 	HeapTuple	htup;
-	Form_pg_attrdef adform;
-	IndexScanDesc sd = (IndexScanDesc) NULL;
-	HeapScanDesc adscan = (HeapScanDesc) NULL;
-	RetrieveIndexResult indexRes;
 	Datum		val;
 	bool		isnull;
 	int			found;
 	int			i;
-	bool		hasindex;
 
 	ScanKeyEntryInitialize(&skey,
 						   (bits16) 0x0,
-						   (AttrNumber) 1,
+						   (AttrNumber) Anum_pg_attrdef_adrelid,
 						   (RegProcedure) F_OIDEQ,
 						   ObjectIdGetDatum(RelationGetRelid(relation)));
 
 	adrel = heap_openr(AttrDefaultRelationName, AccessShareLock);
-	hasindex = (adrel->rd_rel->relhasindex && !IsIgnoringSystemIndexes());
-	if (hasindex)
-	{
-		irel = index_openr(AttrDefaultIndex);
-		sd = index_beginscan(irel, false, 1, &skey);
-	}
-	else
-		adscan = heap_beginscan(adrel, false, SnapshotNow, 1, &skey);
-	tuple.t_datamcxt = NULL;
-	tuple.t_data = NULL;
+	adscan = systable_beginscan(adrel, AttrDefaultIndex, true,
+								SnapshotNow,
+								1, &skey);
+	found = 0;
 
-	for (found = 0;;)
+	while (HeapTupleIsValid(htup = systable_getnext(adscan)))
 	{
-		Buffer		buffer;
+		Form_pg_attrdef adform = (Form_pg_attrdef) GETSTRUCT(htup);
 
-		if (hasindex)
-		{
-			indexRes = index_getnext(sd, ForwardScanDirection);
-			if (!indexRes)
-				break;
-
-			tuple.t_self = indexRes->heap_iptr;
-			heap_fetch(adrel, SnapshotNow, &tuple, &buffer, sd);
-			pfree(indexRes);
-			if (tuple.t_data == NULL)
-				continue;
-			htup = &tuple;
-		}
-		else
-		{
-			htup = heap_getnext(adscan, 0);
-			if (!HeapTupleIsValid(htup))
-				break;
-		}
 		found++;
-		adform = (Form_pg_attrdef) GETSTRUCT(htup);
 		for (i = 0; i < ndef; i++)
 		{
 			if (adform->adnum != attrdef[i].adnum)
@@ -2312,8 +2484,6 @@ AttrDefaultFetch(Relation relation)
 																 val)));
 			break;
 		}
-		if (hasindex)
-			ReleaseBuffer(buffer);
 
 		if (i >= ndef)
 			elog(NOTICE, "AttrDefaultFetch: unexpected record found for attr %d in rel %s",
@@ -2321,18 +2491,12 @@ AttrDefaultFetch(Relation relation)
 				 RelationGetRelationName(relation));
 	}
 
-	if (found < ndef)
-		elog(NOTICE, "AttrDefaultFetch: %d record not found for rel %s",
-			 ndef - found, RelationGetRelationName(relation));
-
-	if (hasindex)
-	{
-		index_endscan(sd);
-		index_close(irel);
-	}
-	else
-		heap_endscan(adscan);
+	systable_endscan(adscan);
 	heap_close(adrel, AccessShareLock);
+
+	if (found != ndef)
+		elog(NOTICE, "AttrDefaultFetch: %d record(s) not found for rel %s",
+			 ndef - found, RelationGetRelationName(relation));
 }
 
 static void
@@ -2341,60 +2505,28 @@ RelCheckFetch(Relation relation)
 	ConstrCheck *check = relation->rd_att->constr->check;
 	int			ncheck = relation->rd_att->constr->num_check;
 	Relation	rcrel;
-	Relation	irel = (Relation) NULL;
+	SysScanDesc rcscan;
 	ScanKeyData skey;
-	HeapTupleData tuple;
 	HeapTuple	htup;
-	IndexScanDesc sd = (IndexScanDesc) NULL;
-	HeapScanDesc rcscan = (HeapScanDesc) NULL;
-	RetrieveIndexResult indexRes;
 	Name		rcname;
 	Datum		val;
 	bool		isnull;
 	int			found;
-	bool		hasindex;
 
 	ScanKeyEntryInitialize(&skey,
 						   (bits16) 0x0,
-						   (AttrNumber) 1,
+						   (AttrNumber) Anum_pg_relcheck_rcrelid,
 						   (RegProcedure) F_OIDEQ,
 						   ObjectIdGetDatum(RelationGetRelid(relation)));
 
 	rcrel = heap_openr(RelCheckRelationName, AccessShareLock);
-	hasindex = (rcrel->rd_rel->relhasindex && !IsIgnoringSystemIndexes());
-	if (hasindex)
-	{
-		irel = index_openr(RelCheckIndex);
-		sd = index_beginscan(irel, false, 1, &skey);
-	}
-	else
-		rcscan = heap_beginscan(rcrel, false, SnapshotNow, 1, &skey);
-	tuple.t_datamcxt = NULL;
-	tuple.t_data = NULL;
+	rcscan = systable_beginscan(rcrel, RelCheckIndex, true,
+								SnapshotNow,
+								1, &skey);
+	found = 0;
 
-	for (found = 0;;)
+	while (HeapTupleIsValid(htup = systable_getnext(rcscan)))
 	{
-		Buffer		buffer;
-
-		if (hasindex)
-		{
-			indexRes = index_getnext(sd, ForwardScanDirection);
-			if (!indexRes)
-				break;
-
-			tuple.t_self = indexRes->heap_iptr;
-			heap_fetch(rcrel, SnapshotNow, &tuple, &buffer, sd);
-			pfree(indexRes);
-			if (tuple.t_data == NULL)
-				continue;
-			htup = &tuple;
-		}
-		else
-		{
-			htup = heap_getnext(rcscan, 0);
-			if (!HeapTupleIsValid(htup))
-				break;
-		}
 		if (found == ncheck)
 			elog(ERROR, "RelCheckFetch: unexpected record found for rel %s",
 				 RelationGetRelationName(relation));
@@ -2417,22 +2549,14 @@ RelCheckFetch(Relation relation)
 							 DatumGetCString(DirectFunctionCall1(textout,
 																 val)));
 		found++;
-		if (hasindex)
-			ReleaseBuffer(buffer);
 	}
 
-	if (found < ncheck)
-		elog(ERROR, "RelCheckFetch: %d record not found for rel %s",
-			 ncheck - found, RelationGetRelationName(relation));
-
-	if (hasindex)
-	{
-		index_endscan(sd);
-		index_close(irel);
-	}
-	else
-		heap_endscan(rcscan);
+	systable_endscan(rcscan);
 	heap_close(rcrel, AccessShareLock);
+
+	if (found != ncheck)
+		elog(ERROR, "RelCheckFetch: %d record(s) not found for rel %s",
+			 ncheck - found, RelationGetRelationName(relation));
 }
 
 /*
@@ -2461,11 +2585,9 @@ List *
 RelationGetIndexList(Relation relation)
 {
 	Relation	indrel;
-	Relation	irel = (Relation) NULL;
+	SysScanDesc	indscan;
 	ScanKeyData skey;
-	IndexScanDesc sd = (IndexScanDesc) NULL;
-	HeapScanDesc hscan = (HeapScanDesc) NULL;
-	bool		hasindex;
+	HeapTuple	htup;
 	List	   *result;
 	MemoryContext oldcxt;
 
@@ -2473,29 +2595,6 @@ RelationGetIndexList(Relation relation)
 	if (relation->rd_indexfound)
 		return listCopy(relation->rd_indexlist);
 
-	/* Prepare to scan pg_index for entries having indrelid = this rel. */
-	indrel = heap_openr(IndexRelationName, AccessShareLock);
-	hasindex = (indrel->rd_rel->relhasindex && !IsIgnoringSystemIndexes());
-	if (hasindex)
-	{
-		irel = index_openr(IndexIndrelidIndex);
-		ScanKeyEntryInitialize(&skey,
-							   (bits16) 0x0,
-							   (AttrNumber) 1,
-							   (RegProcedure) F_OIDEQ,
-						   ObjectIdGetDatum(RelationGetRelid(relation)));
-		sd = index_beginscan(irel, false, 1, &skey);
-	}
-	else
-	{
-		ScanKeyEntryInitialize(&skey,
-							   (bits16) 0x0,
-							   (AttrNumber) Anum_pg_index_indrelid,
-							   (RegProcedure) F_OIDEQ,
-						   ObjectIdGetDatum(RelationGetRelid(relation)));
-		hscan = heap_beginscan(indrel, false, SnapshotNow, 1, &skey);
-	}
-
 	/*
 	 * We build the list we intend to return (in the caller's context)
 	 * while doing the scan.  After successfully completing the scan, we
@@ -2504,51 +2603,26 @@ RelationGetIndexList(Relation relation)
 	 */
 	result = NIL;
 
-	for (;;)
-	{
-		HeapTupleData tuple;
-		HeapTuple	htup;
-		Buffer		buffer;
-		Form_pg_index index;
+	/* Prepare to scan pg_index for entries having indrelid = this rel. */
+	ScanKeyEntryInitialize(&skey,
+						   (bits16) 0x0,
+						   (AttrNumber) Anum_pg_index_indrelid,
+						   (RegProcedure) F_OIDEQ,
+						   ObjectIdGetDatum(RelationGetRelid(relation)));
 
-		if (hasindex)
-		{
-			RetrieveIndexResult indexRes;
-
-			indexRes = index_getnext(sd, ForwardScanDirection);
-			if (!indexRes)
-				break;
-			tuple.t_self = indexRes->heap_iptr;
-			tuple.t_datamcxt = NULL;
-			tuple.t_data = NULL;
-			heap_fetch(indrel, SnapshotNow, &tuple, &buffer, sd);
-			pfree(indexRes);
-			if (tuple.t_data == NULL)
-				continue;
-			htup = &tuple;
-		}
-		else
-		{
-			htup = heap_getnext(hscan, 0);
-			if (!HeapTupleIsValid(htup))
-				break;
-		}
+	indrel = heap_openr(IndexRelationName, AccessShareLock);
+	indscan = systable_beginscan(indrel, IndexIndrelidIndex, true,
+								 SnapshotNow,
+								 1, &skey);
 
-		index = (Form_pg_index) GETSTRUCT(htup);
+	while (HeapTupleIsValid(htup = systable_getnext(indscan)))
+	{
+		Form_pg_index index = (Form_pg_index) GETSTRUCT(htup);
 
 		result = insert_ordered_oid(result, index->indexrelid);
-
-		if (hasindex)
-			ReleaseBuffer(buffer);
 	}
 
-	if (hasindex)
-	{
-		index_endscan(sd);
-		index_close(irel);
-	}
-	else
-		heap_endscan(hscan);
+	systable_endscan(indscan);
 	heap_close(indrel, AccessShareLock);
 
 	/* Now save a copy of the completed list in the relcache entry. */
@@ -2594,8 +2668,7 @@ insert_ordered_oid(List *list, Oid datum)
 
 
 /*
- *	init_irels(), write_irels() -- handle special-case initialization of
- *								   index relation descriptors.
+ *	load_relcache_init_file, write_relcache_init_file
  *
  *		In late 1992, we started regularly having databases with more than
  *		a thousand classes in them.  With this number of classes, it became
@@ -2609,7 +2682,7 @@ insert_ordered_oid(List *list, Oid datum)
  *		In order to get around the problem, we do the following:
  *
  *		   +  When the database system is initialized (at initdb time), we
- *			  don't use indices on pg_attribute.  We do sequential scans.
+ *			  don't use indexes.  We do sequential scans.
  *
  *		   +  When the backend is started up in normal mode, we load an image
  *			  of the appropriate relation descriptors, in internal format,
@@ -2621,177 +2694,308 @@ insert_ordered_oid(List *list, Oid datum)
  *
  *		We could dispense with the initialization file and just build the
  *		critical reldescs the hard way on every backend startup, but that
- *		slows down backend startup noticeably if pg_class is large.
+ *		slows down backend startup noticeably.
+ *
+ *		We can in fact go further, and save more relcache entries than
+ *		just the ones that are absolutely critical; this allows us to speed
+ *		up backend startup by not having to build such entries the hard way.
+ *		Presently, all the catalog and index entries that are referred to
+ *		by catcaches are stored in the initialization file.
  *
  *		As of v6.5, vacuum.c deletes the initialization file at completion
  *		of a VACUUM, so that it will be rebuilt at the next backend startup.
- *		This ensures that vacuum-collected stats for the system indexes
- *		will eventually get used by the optimizer --- otherwise the relcache
- *		entries for these indexes will show zero sizes forever, since the
- *		relcache entries are pinned in memory and will never be reloaded
- *		from pg_class.
+ *		This ensures that vacuum-collected stats for the system catalogs
+ *		and indexes will be seen by backends started later.
  */
 
-/* pg_attnumind, pg_classnameind, pg_classoidind */
-#define Num_indices_bootstrap	3
-
-static void
-init_irels(void)
+/*
+ * load_relcache_init_file -- attempt to load cache from the init file
+ *
+ * If successful, return TRUE and set criticalRelcachesBuilt to true.
+ * If not successful, return FALSE and set needNewCacheFile to true.
+ *
+ * NOTE: we assume we are already switched into CacheMemoryContext.
+ */
+static bool
+load_relcache_init_file(void)
 {
-	Size		len;
-	int			nread;
-	File		fd;
-	Relation	irel[Num_indices_bootstrap];
-	Relation	ird;
-	Form_pg_am	am;
-	Form_pg_class relform;
-	MemoryContext indexcxt;
-	IndexStrategy strat;
-	RegProcedure *support;
-	int			nstrategies,
-				nsupport;
+	FILE	   *fp;
+	char		initfilename[MAXPGPATH];
+	Relation   *rels;
+	int			relno,
+				num_rels,
+				max_rels,
+				nailed_rels,
+				nailed_indexes;
 	int			i;
-	int			relno;
 
-	if ((fd = FileNameOpenFile(RELCACHE_INIT_FILENAME, O_RDONLY | PG_BINARY, 0600)) < 0)
+	snprintf(initfilename, sizeof(initfilename), "%s/%s",
+			 DatabasePath, RELCACHE_INIT_FILENAME);
+
+	fp = AllocateFile(initfilename, PG_BINARY_R);
+	if (fp == NULL)
 	{
-		write_irels();
-		return;
+		needNewCacheFile = true;
+		return false;
 	}
 
-	for (relno = 0; relno < Num_indices_bootstrap; relno++)
+	/*
+	 * Read the index relcache entries from the file.  Note we will not
+	 * enter any of them into the cache if the read fails partway through;
+	 * this helps to guard against broken init files.
+	 */
+	max_rels = 100;
+	rels = (Relation *) palloc(max_rels * sizeof(Relation));
+	num_rels = 0;
+	nailed_rels = nailed_indexes = 0;
+	initFileRelationIds = NIL;
+
+	for (relno = 0; ; relno++)
 	{
+		Size		len;
+		size_t		nread;
+		Relation	rel;
+		Form_pg_class relform;
+
 		/* first read the relation descriptor length */
-		if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len))
+		if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
+		{
+			if (nread == 0)
+				break;			/* end of file */
 			goto read_failed;
+		}
 
 		/* safety check for incompatible relcache layout */
 		if (len != sizeof(RelationData))
 			goto read_failed;
 
-		ird = irel[relno] = (Relation) palloc(len);
-		MemSet(ird, 0, len);
-
-		/* then, read the Relation structure */
-		if ((nread = FileRead(fd, (char *) ird, len)) != len)
-			goto read_failed;
-
-		/* reset transient fields */
-		ird->rd_targblock = InvalidBlockNumber;
-		ird->rd_fd = -1;
-		ird->rd_refcnt = 0;
+		/* allocate another relcache header */
+		if (num_rels >= max_rels)
+		{
+			max_rels *= 2;
+			rels = (Relation *) repalloc(rels, max_rels * sizeof(Relation));
+		}
 
-		ird->rd_node.tblNode = MyDatabaseId;
+		rel = rels[num_rels++] = (Relation) palloc(len);
 
-		/* next, read the access method tuple form */
-		if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len))
-			goto read_failed;
-
-		am = (Form_pg_am) palloc(len);
-		if ((nread = FileRead(fd, (char *) am, len)) != len)
+		/* then, read the Relation structure */
+		if ((nread = fread(rel, 1, len, fp)) != len)
 			goto read_failed;
 
-		ird->rd_am = am;
-
 		/* next read the relation tuple form */
-		if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len))
+		if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
 			goto read_failed;
 
 		relform = (Form_pg_class) palloc(len);
-		if ((nread = FileRead(fd, (char *) relform, len)) != len)
+		if ((nread = fread(relform, 1, len, fp)) != len)
 			goto read_failed;
 
-		ird->rd_rel = relform;
+		rel->rd_rel = relform;
 
 		/* initialize attribute tuple forms */
-		ird->rd_att = CreateTemplateTupleDesc(relform->relnatts);
+		rel->rd_att = CreateTemplateTupleDesc(relform->relnatts);
 
 		/* next read all the attribute tuple form data entries */
-		len = ATTRIBUTE_TUPLE_SIZE;
 		for (i = 0; i < relform->relnatts; i++)
 		{
-			if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len))
+			if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
 				goto read_failed;
 
-			ird->rd_att->attrs[i] = (Form_pg_attribute) palloc(len);
+			rel->rd_att->attrs[i] = (Form_pg_attribute) palloc(len);
 
-			if ((nread = FileRead(fd, (char *) ird->rd_att->attrs[i], len)) != len)
+			if ((nread = fread(rel->rd_att->attrs[i], 1, len, fp)) != len)
 				goto read_failed;
 		}
 
-		/*
-		 * prepare index info context --- parameters should match
-		 * RelationInitIndexAccessInfo
-		 */
-		indexcxt = AllocSetContextCreate(CacheMemoryContext,
-										 RelationGetRelationName(ird),
-										 0,		/* minsize */
-										 512,	/* initsize */
-										 1024); /* maxsize */
-		ird->rd_indexcxt = indexcxt;
-
-		/* next, read the index strategy map */
-		if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len))
-			goto read_failed;
+		/* If it's an index, there's more to do */
+		if (rel->rd_rel->relkind == RELKIND_INDEX)
+		{
+			Form_pg_am	am;
+			MemoryContext indexcxt;
+			IndexStrategy strat;
+			Oid		   *operator;
+			RegProcedure *support;
+			int			nstrategies,
+						nsupport;
+
+			/* Count nailed indexes to ensure we have 'em all */
+			if (rel->rd_isnailed)
+				nailed_indexes++;
+
+			/* next, read the pg_index tuple form */
+			if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
+				goto read_failed;
 
-		strat = (IndexStrategy) MemoryContextAlloc(indexcxt, len);
-		if ((nread = FileRead(fd, (char *) strat, len)) != len)
-			goto read_failed;
+			rel->rd_index = (Form_pg_index) palloc(len);
+			if ((nread = fread(rel->rd_index, 1, len, fp)) != len)
+				goto read_failed;
 
-		/* have to invalidate any FmgrInfo data in the strategy maps */
-		nstrategies = am->amstrategies * relform->relnatts;
-		for (i = 0; i < nstrategies; i++)
-			strat->strategyMapData[i].entry[0].sk_func.fn_oid = InvalidOid;
+			/* next, read the access method tuple form */
+			if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
+				goto read_failed;
 
-		ird->rd_istrat = strat;
+			am = (Form_pg_am) palloc(len);
+			if ((nread = fread(am, 1, len, fp)) != len)
+				goto read_failed;
+			rel->rd_am = am;
 
-		/* finally, read the vector of support procedures */
-		if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len))
-			goto read_failed;
-		support = (RegProcedure *) MemoryContextAlloc(indexcxt, len);
-		if ((nread = FileRead(fd, (char *) support, len)) != len)
-			goto read_failed;
+			/*
+			 * prepare index info context --- parameters should match
+			 * RelationInitIndexAccessInfo
+			 */
+			indexcxt = AllocSetContextCreate(CacheMemoryContext,
+											 RelationGetRelationName(rel),
+											 0,		/* minsize */
+											 512,	/* initsize */
+											 1024); /* maxsize */
+			rel->rd_indexcxt = indexcxt;
+
+			/* next, read the index strategy map */
+			if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
+				goto read_failed;
 
-		ird->rd_support = support;
+			strat = (IndexStrategy) MemoryContextAlloc(indexcxt, len);
+			if ((nread = fread(strat, 1, len, fp)) != len)
+				goto read_failed;
 
-		nsupport = relform->relnatts * am->amsupport;
-		ird->rd_supportinfo = (FmgrInfo *)
-			MemoryContextAlloc(indexcxt, nsupport * sizeof(FmgrInfo));
-		MemSet(ird->rd_supportinfo, 0, nsupport * sizeof(FmgrInfo));
+			/* have to invalidate any FmgrInfo data in the strategy maps */
+			nstrategies = am->amstrategies * relform->relnatts;
+			for (i = 0; i < nstrategies; i++)
+				strat->strategyMapData[i].entry[0].sk_func.fn_oid = InvalidOid;
+
+			rel->rd_istrat = strat;
+
+			/* next, read the vector of operator OIDs */
+			if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
+				goto read_failed;
+
+			operator = (Oid *) MemoryContextAlloc(indexcxt, len);
+			if ((nread = fread(operator, 1, len, fp)) != len)
+				goto read_failed;
+
+			rel->rd_operator = operator;
 
-		RelationInitLockInfo(ird);
+			/* finally, read the vector of support procedures */
+			if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
+				goto read_failed;
+			support = (RegProcedure *) MemoryContextAlloc(indexcxt, len);
+			if ((nread = fread(support, 1, len, fp)) != len)
+				goto read_failed;
+
+			rel->rd_support = support;
+
+			/* add a zeroed support-fmgr-info vector */
+			nsupport = relform->relnatts * am->amsupport;
+			rel->rd_supportinfo = (FmgrInfo *)
+				MemoryContextAlloc(indexcxt, nsupport * sizeof(FmgrInfo));
+			MemSet(rel->rd_supportinfo, 0, nsupport * sizeof(FmgrInfo));
+		}
+		else
+		{
+			/* Count nailed rels to ensure we have 'em all */
+			if (rel->rd_isnailed)
+				nailed_rels++;
+
+			Assert(rel->rd_index == NULL);
+			Assert(rel->rd_am == NULL);
+			Assert(rel->rd_indexcxt == NULL);
+			Assert(rel->rd_istrat == NULL);
+			Assert(rel->rd_operator == NULL);
+			Assert(rel->rd_support == NULL);
+			Assert(rel->rd_supportinfo == NULL);
+		}
+
+		/*
+		 * Rules and triggers are not saved (mainly because the internal
+		 * format is complex and subject to change).  They must be rebuilt
+		 * if needed by RelationCacheInitializePhase2.  This is not expected
+		 * to be a big performance hit since few system catalogs have such.
+		 */
+		rel->rd_rules = NULL;
+		rel->rd_rulescxt = NULL;
+		rel->trigdesc = NULL;
+
+		/*
+		 * Reset transient-state fields in the relcache entry
+		 */
+		rel->rd_fd = -1;
+		rel->rd_targblock = InvalidBlockNumber;
+		if (rel->rd_isnailed)
+			RelationSetReferenceCount(rel, 1);
+		else
+			RelationSetReferenceCount(rel, 0);
+		rel->rd_indexfound = false;
+		rel->rd_indexlist = NIL;
+		MemSet(&rel->pgstat_info, 0, sizeof(rel->pgstat_info));
 
-		RelationCacheInsert(ird);
+		/*
+		 * Make sure database ID is correct.  This is needed in case the
+		 * pg_internal.init file was copied from some other database by
+		 * CREATE DATABASE.
+		 */
+		if (rel->rd_rel->relisshared)
+			rel->rd_node.tblNode = InvalidOid;
+		else
+			rel->rd_node.tblNode = MyDatabaseId;
+
+		RelationInitLockInfo(rel);
+	}
+
+	/*
+	 * We reached the end of the init file without apparent problem.
+	 * Did we get the right number of nailed items?  (This is a useful
+	 * crosscheck in case the set of critical rels or indexes changes.)
+	 */
+	if (nailed_rels != NUM_CRITICAL_RELS ||
+		nailed_indexes != NUM_CRITICAL_INDEXES)
+		goto read_failed;
+
+	/*
+	 * OK, all appears well.
+	 *
+	 * Now insert all the new relcache entries into the cache.
+	 */
+	for (relno = 0; relno < num_rels; relno++)
+	{
+		RelationCacheInsert(rels[relno]);
+		/* also make a list of their OIDs, for RelationIdIsInInitFile */
+		initFileRelationIds = lconsi((int) RelationGetRelid(rels[relno]),
+									 initFileRelationIds);
 	}
 
-	/* successfully read the init file */
-	FileClose(fd);
+	pfree(rels);
+	FreeFile(fp);
+
 	criticalRelcachesBuilt = true;
-	return;
+	return true;
 
-	/* init file is broken, so do it the hard way */
+	/*
+	 * init file is broken, so do it the hard way.  We don't bother
+	 * trying to free the clutter we just allocated; it's not in the
+	 * relcache so it won't hurt.
+	 */
 read_failed:
-	FileClose(fd);
-	write_irels();
+	pfree(rels);
+	FreeFile(fp);
+
+	needNewCacheFile = true;
+	return false;
 }
 
+/*
+ * Write out a new initialization file with the current contents
+ * of the relcache.
+ */
 static void
-write_irels(void)
+write_relcache_init_file(void)
 {
-	Size		len;
-	int			nwritten;
-	File		fd;
-	Relation	irel[Num_indices_bootstrap];
-	Relation	ird;
-	Form_pg_am	am;
-	Form_pg_class relform;
-	IndexStrategy strat;
-	RegProcedure *support;
-	int			i;
-	int			relno;
-	RelationBuildDescInfo bi;
+	FILE	   *fp;
 	char		tempfilename[MAXPGPATH];
 	char		finalfilename[MAXPGPATH];
+	HASH_SEQ_STATUS status;
+	RelNameCacheEnt *namehentry;
+	MemoryContext oldcxt;
+	int			i;
 
 	/*
 	 * We must write a temporary file and rename it into place. Otherwise,
@@ -2803,8 +3007,10 @@ write_irels(void)
 	snprintf(finalfilename, sizeof(finalfilename), "%s/%s",
 			 DatabasePath, RELCACHE_INIT_FILENAME);
 
-	fd = PathNameOpenFile(tempfilename, O_WRONLY | O_CREAT | O_TRUNC | PG_BINARY, 0600);
-	if (fd < 0)
+	unlink(tempfilename);		/* in case it exists w/wrong permissions */
+
+	fp = AllocateFile(tempfilename, PG_BINARY_W);
+	if (fp == NULL)
 	{
 		/*
 		 * We used to consider this a fatal error, but we might as well
@@ -2814,148 +3020,227 @@ write_irels(void)
 		return;
 	}
 
-	FileSeek(fd, 0L, SEEK_SET);
-
 	/*
-	 * Build relation descriptors for the critical system indexes without
-	 * resort to the descriptor cache.	In order to do this, we set
-	 * ProcessingMode to Bootstrap.  The effect of this is to disable
-	 * indexed relation searches -- a necessary step, since we're trying
-	 * to instantiate the index relation descriptors here.	Once we have
-	 * the descriptors, nail them into cache so we never lose them.
-	 */
-
-	/*---------
-	 * Removed the following ProcessingMode change -- inoue
-	 * At this point
-	 * 1) Catalog Cache isn't initialized
-	 * 2) Relation Cache for the following critical indexes aren't built
-	 * oldmode = GetProcessingMode();
-	 * SetProcessingMode(BootstrapProcessing);
-	 *---------
+	 * Write all the reldescs (in no particular order).
 	 */
+	hash_seq_init(&status, RelationNameCache);
 
-	bi.infotype = INFO_RELNAME;
-	bi.i.info_name = AttributeRelidNumIndex;
-	irel[0] = RelationBuildDesc(bi, NULL);
-	irel[0]->rd_isnailed = true;
-
-	bi.i.info_name = ClassNameIndex;
-	irel[1] = RelationBuildDesc(bi, NULL);
-	irel[1]->rd_isnailed = true;
-
-	bi.i.info_name = ClassOidIndex;
-	irel[2] = RelationBuildDesc(bi, NULL);
-	irel[2]->rd_isnailed = true;
-
-	criticalRelcachesBuilt = true;
+	initFileRelationIds = NIL;
 
-	/*
-	 * Removed the following ProcessingMode -- inoue
-	 * SetProcessingMode(oldmode);
-	 */
-
-	/*
-	 * Write out the index reldescs to the special cache file.
-	 */
-	for (relno = 0; relno < Num_indices_bootstrap; relno++)
+	while ((namehentry = (RelNameCacheEnt *) hash_seq_search(&status)) != NULL)
 	{
-		ird = irel[relno];
-
-		/* save the volatile fields in the relation descriptor */
-		am = ird->rd_am;
-		ird->rd_am = (Form_pg_am) NULL;
-		relform = ird->rd_rel;
-		ird->rd_rel = (Form_pg_class) NULL;
-		strat = ird->rd_istrat;
-		support = ird->rd_support;
+		Relation	rel = namehentry->reldesc;
+		Form_pg_class relform = rel->rd_rel;
+		Size		len;
 
 		/*
-		 * first write the relation descriptor , excluding strategy and
-		 * support
+		 * first write the relcache entry proper
 		 */
 		len = sizeof(RelationData);
 
 		/* first, write the relation descriptor length */
-		if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
-			!= sizeof(len))
+		if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len))
 			elog(FATAL, "cannot write init file -- descriptor length");
 
 		/* next, write out the Relation structure */
-		if ((nwritten = FileWrite(fd, (char *) ird, len)) != len)
+		if (fwrite(rel, 1, len, fp) != len)
 			elog(FATAL, "cannot write init file -- reldesc");
 
-		/* next, write the access method tuple form */
-		len = sizeof(FormData_pg_am);
-		if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
-			!= sizeof(len))
-			elog(FATAL, "cannot write init file -- am tuple form length");
-
-		if ((nwritten = FileWrite(fd, (char *) am, len)) != len)
-			elog(FATAL, "cannot write init file -- am tuple form");
-
 		/* next write the relation tuple form */
 		len = sizeof(FormData_pg_class);
-		if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
-			!= sizeof(len))
+		if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len))
 			elog(FATAL, "cannot write init file -- relation tuple form length");
 
-		if ((nwritten = FileWrite(fd, (char *) relform, len)) != len)
+		if (fwrite(relform, 1, len, fp) != len)
 			elog(FATAL, "cannot write init file -- relation tuple form");
 
 		/* next, do all the attribute tuple form data entries */
-		len = ATTRIBUTE_TUPLE_SIZE;
 		for (i = 0; i < relform->relnatts; i++)
 		{
-			if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
-				!= sizeof(len))
+			len = ATTRIBUTE_TUPLE_SIZE;
+			if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len))
 				elog(FATAL, "cannot write init file -- length of attdesc %d", i);
-			if ((nwritten = FileWrite(fd, (char *) ird->rd_att->attrs[i], len))
-				!= len)
+			if (fwrite(rel->rd_att->attrs[i], 1, len, fp) != len)
 				elog(FATAL, "cannot write init file -- attdesc %d", i);
 		}
 
-		/* next, write the index strategy map */
-		len = AttributeNumberGetIndexStrategySize(relform->relnatts,
-												  am->amstrategies);
-		if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
-			!= sizeof(len))
-			elog(FATAL, "cannot write init file -- strategy map length");
-
-		if ((nwritten = FileWrite(fd, (char *) strat, len)) != len)
-			elog(FATAL, "cannot write init file -- strategy map");
-
-		/* finally, write the vector of support procedures */
-		len = relform->relnatts * (am->amsupport * sizeof(RegProcedure));
-		if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
-			!= sizeof(len))
-			elog(FATAL, "cannot write init file -- support vector length");
+		/* If it's an index, there's more to do */
+		if (rel->rd_rel->relkind == RELKIND_INDEX)
+		{
+			Form_pg_am	am = rel->rd_am;
+			HeapTuple	tuple;
 
-		if ((nwritten = FileWrite(fd, (char *) support, len)) != len)
-			elog(FATAL, "cannot write init file -- support vector");
+			/*
+			 * We need to write the index tuple form, but this is a bit
+			 * tricky since it's a variable-length struct.  Rather than
+			 * hoping to intuit the length, fetch the pg_index tuple
+			 * afresh using the syscache, and write that.
+			 */
+			tuple = SearchSysCache(INDEXRELID,
+								   ObjectIdGetDatum(RelationGetRelid(rel)),
+								   0, 0, 0);
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "write_relcache_init_file: no pg_index entry for index %u",
+					 RelationGetRelid(rel));
+			len = tuple->t_len - tuple->t_data->t_hoff;
+			if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len))
+				elog(FATAL, "cannot write init file -- index tuple form length");
+			if (fwrite(GETSTRUCT(tuple), 1, len, fp) != len)
+				elog(FATAL, "cannot write init file -- index tuple form");
+			ReleaseSysCache(tuple);
+
+			/* next, write the access method tuple form */
+			len = sizeof(FormData_pg_am);
+			if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len))
+				elog(FATAL, "cannot write init file -- am tuple form length");
+
+			if (fwrite(am, 1, len, fp) != len)
+				elog(FATAL, "cannot write init file -- am tuple form");
+
+			/* next, write the index strategy map */
+			len = AttributeNumberGetIndexStrategySize(relform->relnatts,
+													  am->amstrategies);
+			if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len))
+				elog(FATAL, "cannot write init file -- strategy map length");
+
+			if (fwrite(rel->rd_istrat, 1, len, fp) != len)
+				elog(FATAL, "cannot write init file -- strategy map");
+
+			/* next, write the vector of operator OIDs */
+			len = relform->relnatts * (am->amstrategies * sizeof(Oid));
+			if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len))
+				elog(FATAL, "cannot write init file -- operator vector length");
+
+			if (fwrite(rel->rd_operator, 1, len, fp) != len)
+				elog(FATAL, "cannot write init file -- operator vector");
+
+			/* finally, write the vector of support procedures */
+			len = relform->relnatts * (am->amsupport * sizeof(RegProcedure));
+			if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len))
+				elog(FATAL, "cannot write init file -- support vector length");
+
+			if (fwrite(rel->rd_support, 1, len, fp) != len)
+				elog(FATAL, "cannot write init file -- support vector");
+		}
 
-		/* restore volatile fields */
-		ird->rd_am = am;
-		ird->rd_rel = relform;
+		/* also make a list of their OIDs, for RelationIdIsInInitFile */
+		oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+		initFileRelationIds = lconsi((int) RelationGetRelid(rel),
+									 initFileRelationIds);
+		MemoryContextSwitchTo(oldcxt);
 	}
 
-	FileClose(fd);
+	FreeFile(fp);
 
 	/*
-	 * And rename the temp file to its final name, deleting any
-	 * previously-existing init file.
+	 * Now we have to check whether the data we've so painstakingly
+	 * accumulated is already obsolete due to someone else's just-committed
+	 * catalog changes.  If so, we just delete the temp file and leave it
+	 * to the next backend to try again.  (Our own relcache entries will be
+	 * updated by SI message processing, but we can't be sure whether what
+	 * we wrote out was up-to-date.)
 	 *
-	 * Note: a failure here is possible under Cygwin, if some other
-	 * backend is holding open an unlinked-but-not-yet-gone init file.
-	 * So treat this as a noncritical failure.
+	 * This mustn't run concurrently with RelationCacheInitFileInvalidate,
+	 * so grab a serialization lock for the duration.
 	 */
-	if (rename(tempfilename, finalfilename) < 0)
+	LWLockAcquire(RelCacheInitLock, LW_EXCLUSIVE);
+
+	/* Make sure we have seen all incoming SI messages */
+	AcceptInvalidationMessages();
+
+	/*
+	 * If we have received any SI relcache invals since backend start,
+	 * assume we may have written out-of-date data.
+	 */
+	if (relcacheInvalsReceived == 0L)
 	{
-		elog(NOTICE, "Cannot rename init file %s to %s: %m\n\tContinuing anyway, but there's something wrong.", tempfilename, finalfilename);
 		/*
-		 * If we fail, try to clean up the useless temp file; don't bother
-		 * to complain if this fails too.
+		 * OK, rename the temp file to its final name, deleting any
+		 * previously-existing init file.
+		 *
+		 * Note: a failure here is possible under Cygwin, if some other
+		 * backend is holding open an unlinked-but-not-yet-gone init file.
+		 * So treat this as a noncritical failure.
 		 */
+		if (rename(tempfilename, finalfilename) < 0)
+		{
+			elog(NOTICE, "Cannot rename init file %s to %s: %m\n\tContinuing anyway, but there's something wrong.", tempfilename, finalfilename);
+			/*
+			 * If we fail, try to clean up the useless temp file; don't bother
+			 * to complain if this fails too.
+			 */
+			unlink(tempfilename);
+		}
+	}
+	else
+	{
+		/* Delete the already-obsolete temp file */
 		unlink(tempfilename);
 	}
+
+	LWLockRelease(RelCacheInitLock);
+}
+
+/*
+ * Detect whether a given relation (identified by OID) is one of the ones
+ * we store in the init file.
+ *
+ * Note that we effectively assume that all backends running in a database
+ * would choose to store the same set of relations in the init file;
+ * otherwise there are cases where we'd fail to detect the need for an init
+ * file invalidation.  This does not seem likely to be a problem in practice.
+ */
+bool
+RelationIdIsInInitFile(Oid relationId)
+{
+	return intMember((int) relationId, initFileRelationIds);
+}
+
+/*
+ * Invalidate (remove) the init file during commit of a transaction that
+ * changed one or more of the relation cache entries that are kept in the
+ * init file.
+ *
+ * We actually need to remove the init file twice: once just before sending
+ * the SI messages that include relcache inval for such relations, and once
+ * just after sending them.  The unlink before ensures that a backend that's
+ * currently starting cannot read the now-obsolete init file and then miss
+ * the SI messages that will force it to update its relcache entries.  (This
+ * works because the backend startup sequence gets into the PROC array before
+ * trying to load the init file.)  The unlink after is to synchronize with a
+ * backend that may currently be trying to write an init file based on data
+ * that we've just rendered invalid.  Such a backend will see the SI messages,
+ * but we can't leave the init file sitting around to fool later backends.
+ *
+ * Ignore any failure to unlink the file, since it might not be there if
+ * no backend has been started since the last removal.
+ */
+void
+RelationCacheInitFileInvalidate(bool beforeSend)
+{
+	char		initfilename[MAXPGPATH];
+
+	snprintf(initfilename, sizeof(initfilename), "%s/%s",
+			 DatabasePath, RELCACHE_INIT_FILENAME);
+
+	if (beforeSend)
+	{
+		/* no interlock needed here */
+		unlink(initfilename);
+	}
+	else
+	{
+		/*
+		 * We need to interlock this against write_relcache_init_file,
+		 * to guard against possibility that someone renames a new-but-
+		 * already-obsolete init file into place just after we unlink.
+		 * With the interlock, it's certain that write_relcache_init_file
+		 * will notice our SI inval message before renaming into place,
+		 * or else that we will execute second and successfully unlink
+		 * the file.
+		 */
+		LWLockAcquire(RelCacheInitLock, LW_EXCLUSIVE);
+		unlink(initfilename);
+		LWLockRelease(RelCacheInitLock);
+	}
 }
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index e629bf48e468aa42a20caa53f5aad1e49588276d..dfbcccffbed250efda87862838f135fc11c352cb 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.66 2001/10/25 05:49:46 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.67 2002/02/19 20:11:18 tgl Exp $
  *
  * NOTES
  *	  These routines allow the parser/planner/executor to perform
@@ -113,6 +113,16 @@ static struct cachedesc cacheinfo[] = {
 			0,
 			0
 	}},
+	{AccessMethodRelationName,	/* AMOID */
+		AmOidIndex,
+		0,
+		1,
+		{
+			ObjectIdAttributeNumber,
+			0,
+			0,
+			0
+	}},
 	{AccessMethodOperatorRelationName,	/* AMOPOPID */
 		AccessMethodOperatorIndex,
 		0,
@@ -365,8 +375,7 @@ static struct cachedesc cacheinfo[] = {
 	}}
 };
 
-static CatCache *SysCache[
-						  lengthof(cacheinfo)];
+static CatCache *SysCache[lengthof(cacheinfo)];
 static int	SysCacheSize = lengthof(cacheinfo);
 static bool CacheInitialized = false;
 
@@ -383,7 +392,7 @@ IsCacheInitialized(void)
  *
  * Note that no database access is done here; we only allocate memory
  * and initialize the cache structure.	Interrogation of the database
- * to complete initialization of a cache happens only upon first use
+ * to complete initialization of a cache happens upon first use
  * of that cache.
  */
 void
@@ -411,6 +420,32 @@ InitCatalogCache(void)
 }
 
 
+/*
+ * InitCatalogCachePhase2 - finish initializing the caches
+ *
+ * Finish initializing all the caches, including necessary database
+ * access.
+ *
+ * This is *not* essential; normally we allow syscaches to be initialized
+ * on first use.  However, it is useful as a mechanism to preload the
+ * relcache with entries for the most-commonly-used system catalogs.
+ * Therefore, we invoke this routine when we need to write a new relcache
+ * init file.
+ */
+void
+InitCatalogCachePhase2(void)
+{
+	int			cacheId;
+
+	Assert(CacheInitialized);
+
+	for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
+	{
+		InitCatCachePhase2(SysCache[cacheId]);
+	}
+}
+
+
 /*
  * SearchSysCache
  *
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 3a48b133bfe8aeb3bf50de83fc858b740d51dd67..222ab6d54ab2aed462512795c63717403842055f 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.97 2001/11/02 16:30:29 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.98 2002/02/19 20:11:18 tgl Exp $
  *
  *
  *-------------------------------------------------------------------------
@@ -287,16 +287,15 @@ InitPostgres(const char *dbname, const char *username)
 	AmiTransactionOverride(bootstrap);
 
 	/*
-	 * Initialize the relation descriptor cache. The pre-allocated
-	 * reldescs are created here.
+	 * Initialize the relation descriptor cache.  This must create
+	 * at least the minimum set of "nailed-in" cache entries.  No
+	 * catalog access happens here.
 	 */
 	RelationCacheInitialize();
 
 	/*
-	 * Initialize all the system catalog caches.
-	 *
-	 * Does not touch files since all routines are builtins (?) - thomas
-	 * 1997-11-01
+	 * Initialize all the system catalog caches.  Note that no catalog
+	 * access happens here; we only set up the cache structure.
 	 */
 	InitCatalogCache();
 
@@ -313,7 +312,11 @@ InitPostgres(const char *dbname, const char *username)
 	if (!bootstrap)
 		StartTransactionCommand();
 
-	/* replace faked-up relcache entries with the real info */
+	/*
+	 * It's now possible to do real access to the system catalogs.
+	 *
+	 * Replace faked-up relcache entries with correct info.
+	 */
 	RelationCacheInitializePhase2();
 
 	/*
@@ -333,8 +336,10 @@ InitPostgres(const char *dbname, const char *username)
 		}
 	}
 	else
+	{
 		/* normal multiuser case */
 		InitializeSessionUserId(username);
+	}
 
 	/*
 	 * Unless we are bootstrapping, double-check that InitMyDatabaseInfo()
@@ -349,6 +354,13 @@ InitPostgres(const char *dbname, const char *username)
 	set_default_client_encoding();
 #endif
 
+	/*
+	 * Final phase of relation cache startup: write a new cache file
+	 * if necessary.  This is done after ReverifyMyDatabase to avoid
+	 * writing a cache file into a dead database.
+	 */
+	RelationCacheInitializePhase3();
+
 	/*
 	 * Set up process-exit callbacks to remove temp relations and then do
 	 * pre-shutdown cleanup.  This should be last because we want
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index db212156c8db9d9cae582ce76d0bf99ad350f528..c8cb4c105863495bfeb423f002a9a475b38c015d 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: genam.h,v 1.31 2001/11/05 17:46:31 momjian Exp $
+ * $Id: genam.h,v 1.32 2002/02/19 20:11:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,9 +31,23 @@ typedef struct IndexBulkDeleteResult
 typedef bool (*IndexBulkDeleteCallback) (ItemPointer itemptr, void *state);
 
 
-/* ----------------
- *		generalized index_ interface routines (in indexam.c)
- * ----------------
+/* Struct for heap-or-index scans of system tables */
+typedef struct SysScanDescData
+{
+	Relation	heap_rel;		/* catalog being scanned */
+	Snapshot	snapshot;		/* time qual (normally SnapshotNow) */
+	Relation	irel;			/* NULL if doing heap scan */
+	HeapScanDesc scan;			/* only valid in heap-scan case */
+	IndexScanDesc iscan;		/* only valid in index-scan case */
+	HeapTupleData tuple;		/* workspace for indexscan */
+	Buffer		buffer;			/* working state for indexscan */
+} SysScanDescData;
+
+typedef SysScanDescData *SysScanDesc;
+
+
+/*
+ * generalized index_ interface routines (in indexam.c)
  */
 extern Relation index_open(Oid relationId);
 extern Relation index_openr(const char *relationName);
@@ -59,9 +73,22 @@ extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum,
 extern struct FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
 				  uint16 procnum);
 
-/* in genam.c */
+/*
+ * index access method support routines (in genam.c)
+ */
 extern IndexScanDesc RelationGetIndexScan(Relation relation, bool scanFromEnd,
 					 uint16 numberOfKeys, ScanKey key);
 extern void IndexScanEnd(IndexScanDesc scan);
 
+/*
+ * heap-or-index access to system catalogs (in genam.c)
+ */
+extern SysScanDesc systable_beginscan(Relation rel,
+									  const char *indexRelname,
+									  bool indexOK,
+									  Snapshot snapshot,
+									  unsigned nkeys, ScanKey key);
+extern HeapTuple systable_getnext(SysScanDesc sysscan);
+extern void systable_endscan(SysScanDesc sysscan);
+
 #endif   /* GENAM_H */
diff --git a/src/include/access/istrat.h b/src/include/access/istrat.h
index c1000cb70b4a258f6926c62fd01a33bcab49cd68..1335863f8cf8df7715cebbbeb747c05f2276fed7 100644
--- a/src/include/access/istrat.h
+++ b/src/include/access/istrat.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: istrat.h,v 1.23 2001/11/05 17:46:31 momjian Exp $
+ * $Id: istrat.h,v 1.24 2002/02/19 20:11:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -47,6 +47,8 @@
  */
 #define IndexStrategyIsValid(s) PointerIsValid(s)
 
+extern ScanKey StrategyMapGetScanKeyEntry(StrategyMap map,
+										  StrategyNumber strategyNumber);
 extern StrategyMap IndexStrategyGetStrategyMap(IndexStrategy indexStrategy,
 					  StrategyNumber maxStrategyNum, AttrNumber attrNum);
 
@@ -55,13 +57,5 @@ extern Size AttributeNumberGetIndexStrategySize(AttrNumber maxAttributeNumber,
 extern StrategyNumber RelationGetStrategy(Relation relation,
 			   AttrNumber attributeNumber, StrategyEvaluation evaluation,
 					RegProcedure procedure);
-extern void IndexSupportInitialize(IndexStrategy indexStrategy,
-					   RegProcedure *indexSupport,
-					   bool *isUnique,
-					   Oid indexObjectId,
-					   Oid accessMethodObjectId,
-					   StrategyNumber maxStrategyNumber,
-					   StrategyNumber maxSupportNumber,
-					   AttrNumber maxAttributeNumber);
 
 #endif   /* ISTRAT_H */
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index fb6facec0a844c24e65087987b5793aa39e0a9b3..a2d47b836d25c09649549551fba73615636fb97b 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: index.h,v 1.43 2001/11/05 17:46:31 momjian Exp $
+ * $Id: index.h,v 1.44 2002/02/19 20:11:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,6 +15,7 @@
 #define INDEX_H
 
 #include "access/itup.h"
+#include "catalog/pg_index.h"
 #include "nodes/execnodes.h"
 
 
@@ -27,9 +28,6 @@ typedef void (*IndexBuildCallback) (Relation index,
 												void *state);
 
 
-extern Form_pg_am AccessMethodObjectIdGetForm(Oid accessMethodObjectId,
-							MemoryContext resultCxt);
-
 extern Oid index_create(char *heapRelationName,
 			 char *indexRelationName,
 			 IndexInfo *indexInfo,
@@ -40,7 +38,7 @@ extern Oid index_create(char *heapRelationName,
 
 extern void index_drop(Oid indexId);
 
-extern IndexInfo *BuildIndexInfo(HeapTuple indexTuple);
+extern IndexInfo *BuildIndexInfo(Form_pg_index indexStruct);
 
 extern void FormIndexDatum(IndexInfo *indexInfo,
 			   HeapTuple heapTuple,
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 2ca7bf8286e5953965b4618e70e7e11daf9d59b7..8d0ba9c4b9c88e33e8dc4a0af028a68a72a1c6cf 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: indexing.h,v 1.56 2001/11/05 17:46:31 momjian Exp $
+ * $Id: indexing.h,v 1.57 2002/02/19 20:11:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -123,15 +123,6 @@ extern void CatalogCloseIndices(int nIndices, Relation *idescs);
 extern void CatalogIndexInsert(Relation *idescs, int nIndices,
 				   Relation heapRelation, HeapTuple heapTuple);
 
-/*
- * Canned functions for indexscans on certain system indexes.
- * All index-value arguments should be passed as Datum for portability!
- */
-extern HeapTuple AttributeRelidNumIndexScan(Relation heapRelation,
-						   Datum relid, Datum attnum);
-extern HeapTuple ClassNameIndexScan(Relation heapRelation, Datum relName);
-extern HeapTuple ClassOidIndexScan(Relation heapRelation, Datum relId);
-
 
 /*
  * These macros are just to keep the C compiler from spitting up on the
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 4e4e07a2c72eece838ef9dfb954b4e8df1dde1f2..c089a47731f0f7c0376666f0f6addb45081330c0 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.61 2001/11/05 17:46:33 momjian Exp $
+ * $Id: executor.h,v 1.62 2002/02/19 20:11:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,13 +30,7 @@
 /*
  * prototypes from functions in execAmi.c
  */
-extern void ExecOpenScanR(Oid relOid, int nkeys, ScanKey skeys, bool isindex,
-			  ScanDirection dir, Snapshot snapshot,
-			  Relation *returnRelation, Pointer *returnScanDesc);
-extern void ExecCloseR(Plan *node);
 extern void ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent);
-extern HeapScanDesc ExecReScanR(Relation relDesc, HeapScanDesc scanDesc,
-			ScanDirection direction, int nkeys, ScanKey skeys);
 extern void ExecMarkPos(Plan *node);
 extern void ExecRestrPos(Plan *node);
 
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 4db2d7e2c0ff8ec6529b54a05018a9c1ad9c6327..91c2084fd9cd76df9d9df0af516fa37be48ebc8f 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: lwlock.h,v 1.4 2001/11/05 17:46:35 momjian Exp $
+ * $Id: lwlock.h,v 1.5 2002/02/19 20:11:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,6 +38,7 @@ typedef enum LWLockId
 	ControlFileLock,
 	CheckpointLock,
 	CLogControlLock,
+	RelCacheInitLock,
 
 	NumFixedLWLocks,			/* must be last except for
 								 * MaxDynamicLWLock */
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index de5957441ac5c39522c3cba9f9c1f1cc3aed1c4c..6ad0d74851c9987a8c44177b358e281a94955698 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catcache.h,v 1.37 2001/11/05 17:46:36 momjian Exp $
+ * $Id: catcache.h,v 1.38 2002/02/19 20:11:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,6 +44,12 @@ typedef struct catcache
 	int			cc_key[4];		/* AttrNumber of each key */
 	PGFunction	cc_hashfunc[4]; /* hash function to use for each key */
 	ScanKeyData cc_skey[4];		/* precomputed key info for heap scans */
+#ifdef CATCACHE_STATS
+	long		cc_searches;	/* total # searches against this cache */
+	long		cc_hits;		/* # of matches against existing entry */
+	long		cc_newloads;	/* # of successful loads of new entry */
+	/* cc_searches - (cc_hits + cc_newloads) is # of failed searches */
+#endif
 	Dllist		cc_bucket[1];	/* hash buckets --- VARIABLE LENGTH ARRAY */
 } CatCache;						/* VARIABLE LENGTH STRUCT */
 
@@ -89,6 +95,7 @@ extern void AtEOXact_CatCache(bool isCommit);
 extern CatCache *InitCatCache(int id, char *relname, char *indname,
 			 int reloidattr,
 			 int nkeys, int *key);
+extern void InitCatCachePhase2(CatCache *cache);
 
 extern HeapTuple SearchCatCache(CatCache *cache,
 			   Datum v1, Datum v2,
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 1bfcaa83de37c81e05a05abaa62040b2ce8116da..49814386ca6adc23a45a6915e829ae21956eddbf 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: rel.h,v 1.55 2001/11/05 17:46:36 momjian Exp $
+ * $Id: rel.h,v 1.56 2002/02/19 20:11:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,6 +18,7 @@
 #include "access/tupdesc.h"
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
+#include "catalog/pg_index.h"
 #include "rewrite/prs2lock.h"
 #include "storage/block.h"
 #include "storage/fd.h"
@@ -113,19 +114,23 @@ typedef struct RelationData
 	bool		rd_isnailed;	/* rel is nailed in cache */
 	bool		rd_indexfound;	/* true if rd_indexlist is valid */
 	bool		rd_uniqueindex; /* true if rel is a UNIQUE index */
-	Form_pg_am	rd_am;			/* AM tuple (if an index) */
 	Form_pg_class rd_rel;		/* RELATION tuple */
+	TupleDesc	rd_att;			/* tuple descriptor */
 	Oid			rd_id;			/* relation's object id */
 	List	   *rd_indexlist;	/* list of OIDs of indexes on relation */
 	LockInfoData rd_lockInfo;	/* lock mgr's info for locking relation */
-	TupleDesc	rd_att;			/* tuple descriptor */
 	RuleLock   *rd_rules;		/* rewrite rules */
 	MemoryContext rd_rulescxt;	/* private memory cxt for rd_rules, if any */
 	TriggerDesc *trigdesc;		/* Trigger info, or NULL if rel has none */
 
+	/* These are non-NULL only for an index relation: */
+	Form_pg_index rd_index;		/* pg_index tuple describing this index */
+	Form_pg_am	rd_am;			/* pg_am tuple for index's AM */
+
 	/* index access support info (used only for an index relation) */
 	MemoryContext rd_indexcxt;	/* private memory cxt for this stuff */
 	IndexStrategy rd_istrat;	/* operator strategy map */
+	Oid		   *rd_operator;	/* OIDs of index operators */
 	RegProcedure *rd_support;	/* OIDs of support procedures */
 	struct FmgrInfo *rd_supportinfo;	/* lookup info for support
 										 * procedures */
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index cd265636155d3caaa5f395e11c84a85490c1624e..e5af1bee61b8e88233e243312aa7e8c2f19ae270 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: relcache.h,v 1.29 2001/11/05 17:46:36 momjian Exp $
+ * $Id: relcache.h,v 1.30 2002/02/19 20:11:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,6 +40,7 @@ extern void RelationInitIndexAccessInfo(Relation relation);
  */
 extern void RelationCacheInitialize(void);
 extern void RelationCacheInitializePhase2(void);
+extern void RelationCacheInitializePhase3(void);
 
 /*
  * Routine to create a relcache entry for an about-to-be-created relation
@@ -62,15 +63,18 @@ extern void RelationPurgeLocalRelation(bool xactComitted);
 
 extern void RelationCacheAbort(void);
 
+/*
+ * Routines to help manage rebuilding of relcache init file
+ */
+extern bool RelationIdIsInInitFile(Oid relationId);
+extern void RelationCacheInitFileInvalidate(bool beforeSend);
 
 /* XLOG support */
 extern void CreateDummyCaches(void);
 extern void DestroyDummyCaches(void);
 
-/*
- * both vacuum.c and relcache.c need to know the name of the relcache init file
- */
 
-#define RELCACHE_INIT_FILENAME	"pg_internal.init"
+/* should be used only by relcache.c and catcache.c */
+extern bool criticalRelcachesBuilt;
 
 #endif   /* RELCACHE_H */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 5e7b2d864ff78b1ad5574777492dc40363682114..6164c02b1525e6320b545250f39518e9a16f82e2 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: syscache.h,v 1.36 2001/11/05 17:46:36 momjian Exp $
+ * $Id: syscache.h,v 1.37 2002/02/19 20:11:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,33 +30,35 @@
 
 #define AGGNAME			0
 #define AMNAME			1
-#define AMOPOPID		2
-#define AMOPSTRATEGY	3
-#define AMPROCNUM		4
-#define ATTNAME			5
-#define ATTNUM			6
-#define CLAAMNAME		7
-#define CLAOID			8
-#define GRONAME			9
-#define GROSYSID		10
-#define INDEXRELID		11
-#define INHRELID		12
-#define LANGNAME		13
-#define LANGOID			14
-#define OPERNAME		15
-#define OPEROID			16
-#define PROCNAME		17
-#define PROCOID			18
-#define RELNAME			19
-#define RELOID			20
-#define RULENAME		21
-#define SHADOWNAME		22
-#define SHADOWSYSID		23
-#define STATRELATT		24
-#define TYPENAME		25
-#define TYPEOID			26
+#define AMOID			2
+#define AMOPOPID		3
+#define AMOPSTRATEGY	4
+#define AMPROCNUM		5
+#define ATTNAME			6
+#define ATTNUM			7
+#define CLAAMNAME		8
+#define CLAOID			9
+#define GRONAME			10
+#define GROSYSID		11
+#define INDEXRELID		12
+#define INHRELID		13
+#define LANGNAME		14
+#define LANGOID			15
+#define OPERNAME		16
+#define OPEROID			17
+#define PROCNAME		18
+#define PROCOID			19
+#define RELNAME			20
+#define RELOID			21
+#define RULENAME		22
+#define SHADOWNAME		23
+#define SHADOWSYSID		24
+#define STATRELATT		25
+#define TYPENAME		26
+#define TYPEOID			27
 
 extern void InitCatalogCache(void);
+extern void InitCatalogCachePhase2(void);
 
 extern HeapTuple SearchSysCache(int cacheId,
 			   Datum key1, Datum key2, Datum key3, Datum key4);