OSDN Git Service

Generalize concept of temporary relations to "relation persistence".
[pg-rex/syncrep.git] / src / backend / utils / cache / relcache.c
index 16a1c08..1509686 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.299 2010/01/12 18:12:18 tgl Exp $
+ *       src/backend/utils/cache/relcache.c
  *
  *-------------------------------------------------------------------------
  */
 #include "access/genam.h"
 #include "access/reloptions.h"
 #include "access/sysattr.h"
+#include "access/transam.h"
 #include "access/xact.h"
 #include "catalog/catalog.h"
 #include "catalog/index.h"
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
-#include "catalog/pg_amop.h"
 #include "catalog/pg_amproc.h"
 #include "catalog/pg_attrdef.h"
 #include "catalog/pg_authid.h"
+#include "catalog/pg_auth_members.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_database.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
 #include "catalog/pg_tablespace.h"
+#include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
 #include "catalog/schemapg.h"
+#include "catalog/storage.h"
 #include "commands/trigger.h"
 #include "miscadmin.h"
 #include "optimizer/clauses.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/relcache.h"
+#include "utils/relmapper.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
 #include "utils/tqual.h"
-#include "utils/typcache.h"
 
 
 /*
  */
 #define RELCACHE_INIT_FILENAME "pg_internal.init"
 
-#define RELCACHE_INIT_FILEMAGIC                0x573265        /* version ID value */
+#define RELCACHE_INIT_FILEMAGIC                0x573266        /* version ID value */
 
 /*
- *             hardcoded tuple descriptors.  see include/catalog/pg_attribute.h
+ *             hardcoded tuple descriptors, contents generated by genbki.pl
  */
 static const FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class};
 static const FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = {Schema_pg_attribute};
 static const FormData_pg_attribute Desc_pg_proc[Natts_pg_proc] = {Schema_pg_proc};
 static const FormData_pg_attribute Desc_pg_type[Natts_pg_type] = {Schema_pg_type};
 static const FormData_pg_attribute Desc_pg_database[Natts_pg_database] = {Schema_pg_database};
+static const FormData_pg_attribute Desc_pg_authid[Natts_pg_authid] = {Schema_pg_authid};
+static const FormData_pg_attribute Desc_pg_auth_members[Natts_pg_auth_members] = {Schema_pg_auth_members};
 static const FormData_pg_attribute Desc_pg_index[Natts_pg_index] = {Schema_pg_index};
 
 /*
@@ -113,7 +118,7 @@ bool                criticalRelcachesBuilt = false;
 
 /*
  * This flag is false until we have prepared the critical relcache entries
- * for shared catalogs (specifically, pg_database and its indexes).
+ * for shared catalogs (which are the tables needed for login).
  */
 bool           criticalSharedRelcachesBuilt = false;
 
@@ -178,19 +183,17 @@ do { \
 /*
  * Special cache for opclass-related information
  *
- * Note: only default operators and support procs get cached, ie, those with
+ * Note: only default support procs get cached, ie, those with
  * lefttype = righttype = opcintype.
  */
 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                     opcfamily;              /* OID of opclass's family */
        Oid                     opcintype;              /* OID of opclass's declared input type */
-       Oid                *operatorOids;       /* strategy operators' OIDs */
-       RegProcedure *supportProcs; /* support procs */
+       RegProcedure *supportProcs; /* OIDs of support procedures */
 } OpClassCacheEnt;
 
 static HTAB *OpClassCache = NULL;
@@ -217,22 +220,19 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
 static void RelationBuildTupleDesc(Relation relation);
 static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
 static void RelationInitPhysicalAddr(Relation relation);
-static void load_critical_index(Oid indexoid);
+static void load_critical_index(Oid indexoid, Oid heapoid);
 static TupleDesc GetPgClassDescriptor(void);
 static TupleDesc GetPgIndexDescriptor(void);
 static void AttrDefaultFetch(Relation relation);
 static void CheckConstraintFetch(Relation relation);
 static List *insert_ordered_oid(List *list, Oid datum);
 static void IndexSupportInitialize(oidvector *indclass,
-                                          Oid *indexOperator,
                                           RegProcedure *indexSupport,
                                           Oid *opFamily,
                                           Oid *opcInType,
-                                          StrategyNumber maxStrategyNumber,
                                           StrategyNumber maxSupportNumber,
                                           AttrNumber maxAttributeNumber);
 static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid,
-                                 StrategyNumber numStrats,
                                  StrategyNumber numSupport);
 static void RelationCacheInitFileRemoveInDir(const char *tblspcpath);
 static void unlink_initfile(const char *initfilename);
@@ -323,13 +323,6 @@ AllocateRelationDesc(Form_pg_class relp)
         */
        relation = (Relation) palloc0(sizeof(RelationData));
 
-       /*
-        * clear fields of reldesc that should initialize to something non-zero
-        */
-       relation->rd_targblock = InvalidBlockNumber;
-       relation->rd_fsm_nblocks = InvalidBlockNumber;
-       relation->rd_vm_nblocks = InvalidBlockNumber;
-
        /* make sure relation is marked as having no open file yet */
        relation->rd_smgr = NULL;
 
@@ -834,6 +827,7 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
         */
        relid = HeapTupleGetOid(pg_class_tuple);
        relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
+       Assert(relid == targetRelId);
 
        /*
         * allocate storage for the relation descriptor, and copy pg_class_tuple
@@ -855,11 +849,31 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
        relation->rd_isnailed = false;
        relation->rd_createSubid = InvalidSubTransactionId;
        relation->rd_newRelfilenodeSubid = InvalidSubTransactionId;
-       relation->rd_istemp = relation->rd_rel->relistemp;
-       if (relation->rd_istemp)
-               relation->rd_islocaltemp = isTempOrToastNamespace(relation->rd_rel->relnamespace);
-       else
-               relation->rd_islocaltemp = false;
+       switch (relation->rd_rel->relpersistence)
+       {
+               case RELPERSISTENCE_PERMANENT:
+                       relation->rd_backend = InvalidBackendId;
+                       break;
+               case RELPERSISTENCE_TEMP:
+                       if (isTempOrToastNamespace(relation->rd_rel->relnamespace))
+                               relation->rd_backend = MyBackendId;
+                       else
+                       {
+                               /*
+                                * If it's a local temp table, but not one of ours, we have to
+                                * use the slow, grotty method to figure out the owning
+                                * backend.
+                                */
+                               relation->rd_backend =
+                                       GetTempNamespaceBackendId(relation->rd_rel->relnamespace);
+                               Assert(relation->rd_backend != InvalidBackendId);
+                       }
+                       break;
+               default:
+                       elog(ERROR, "invalid relpersistence: %c",
+                                relation->rd_rel->relpersistence);
+                       break;
+       }
 
        /*
         * initialize the tuple descriptor (relation->rd_att).
@@ -923,6 +937,10 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 
 /*
  * Initialize the physical addressing info (RelFileNode) for a relcache entry
+ *
+ * Note: at the physical level, relations in the pg_global tablespace must
+ * be treated as shared, even if relisshared isn't set.  Hence we do not
+ * look at relisshared here.
  */
 static void
 RelationInitPhysicalAddr(Relation relation)
@@ -931,11 +949,22 @@ RelationInitPhysicalAddr(Relation relation)
                relation->rd_node.spcNode = relation->rd_rel->reltablespace;
        else
                relation->rd_node.spcNode = MyDatabaseTableSpace;
-       if (relation->rd_rel->relisshared)
+       if (relation->rd_node.spcNode == GLOBALTABLESPACE_OID)
                relation->rd_node.dbNode = InvalidOid;
        else
                relation->rd_node.dbNode = MyDatabaseId;
-       relation->rd_node.relNode = relation->rd_rel->relfilenode;
+       if (relation->rd_rel->relfilenode)
+               relation->rd_node.relNode = relation->rd_rel->relfilenode;
+       else
+       {
+               /* Consult the relation mapper */
+               relation->rd_node.relNode =
+                       RelationMapOidToFilenode(relation->rd_id,
+                                                                        relation->rd_rel->relisshared);
+               if (!OidIsValid(relation->rd_node.relNode))
+                       elog(ERROR, "could not find relation mapping for relation \"%s\", OID %u",
+                                RelationGetRelationName(relation), relation->rd_id);
+       }
 }
 
 /*
@@ -954,7 +983,6 @@ RelationInitIndexAccessInfo(Relation relation)
        MemoryContext indexcxt;
        MemoryContext oldcontext;
        int                     natts;
-       uint16          amstrategies;
        uint16          amsupport;
 
        /*
@@ -962,9 +990,8 @@ RelationInitIndexAccessInfo(Relation relation)
         * contains variable-length and possibly-null fields, we have to do this
         * honestly rather than just treating it as a Form_pg_index struct.
         */
-       tuple = SearchSysCache(INDEXRELID,
-                                                  ObjectIdGetDatum(RelationGetRelid(relation)),
-                                                  0, 0, 0);
+       tuple = SearchSysCache1(INDEXRELID,
+                                                       ObjectIdGetDatum(RelationGetRelid(relation)));
        if (!HeapTupleIsValid(tuple))
                elog(ERROR, "cache lookup failed for index %u",
                         RelationGetRelid(relation));
@@ -977,9 +1004,7 @@ RelationInitIndexAccessInfo(Relation relation)
        /*
         * 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);
+       tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(relation->rd_rel->relam));
        if (!HeapTupleIsValid(tuple))
                elog(ERROR, "cache lookup failed for access method %u",
                         relation->rd_rel->relam);
@@ -992,7 +1017,6 @@ RelationInitIndexAccessInfo(Relation relation)
        if (natts != relation->rd_index->indnatts)
                elog(ERROR, "relnatts disagrees with indnatts for index %u",
                         RelationGetRelid(relation));
-       amstrategies = aform->amstrategies;
        amsupport = aform->amsupport;
 
        /*
@@ -1021,13 +1045,6 @@ RelationInitIndexAccessInfo(Relation relation)
        relation->rd_opcintype = (Oid *)
                MemoryContextAllocZero(indexcxt, natts * sizeof(Oid));
 
-       if (amstrategies > 0)
-               relation->rd_operator = (Oid *)
-                       MemoryContextAllocZero(indexcxt,
-                                                                  natts * amstrategies * sizeof(Oid));
-       else
-               relation->rd_operator = NULL;
-
        if (amsupport > 0)
        {
                int                     nsupport = natts * amsupport;
@@ -1059,14 +1076,13 @@ RelationInitIndexAccessInfo(Relation relation)
        indclass = (oidvector *) DatumGetPointer(indclassDatum);
 
        /*
-        * Fill the operator and support procedure OID arrays, as well as the info
-        * about opfamilies and opclass input types.  (aminfo and supportinfo are
-        * left as zeroes, and are filled on-the-fly when used)
+        * Fill the support procedure OID array, as well as the info about
+        * opfamilies and opclass input types.  (aminfo and supportinfo are left
+        * as zeroes, and are filled on-the-fly when used)
         */
-       IndexSupportInitialize(indclass,
-                                                  relation->rd_operator, relation->rd_support,
+       IndexSupportInitialize(indclass, relation->rd_support,
                                                   relation->rd_opfamily, relation->rd_opcintype,
-                                                  amstrategies, amsupport, natts);
+                                                  amsupport, natts);
 
        /*
         * Similarly extract indoption and copy it to the cache entry
@@ -1095,22 +1111,19 @@ RelationInitIndexAccessInfo(Relation relation)
  *             Initializes an index's cached opclass information,
  *             given the index's pg_index.indclass entry.
  *
- * Data is returned into *indexOperator, *indexSupport, *opFamily, and
- * *opcInType, which are arrays allocated by the caller.
+ * Data is returned into *indexSupport, *opFamily, and *opcInType,
+ * which are arrays 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.
+ * The caller also passes 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(oidvector *indclass,
-                                          Oid *indexOperator,
                                           RegProcedure *indexSupport,
                                           Oid *opFamily,
                                           Oid *opcInType,
-                                          StrategyNumber maxStrategyNumber,
                                           StrategyNumber maxSupportNumber,
                                           AttrNumber maxAttributeNumber)
 {
@@ -1125,16 +1138,11 @@ IndexSupportInitialize(oidvector *indclass,
 
                /* look up the info for this opclass, using a cache */
                opcentry = LookupOpclassInfo(indclass->values[attIndex],
-                                                                        maxStrategyNumber,
                                                                         maxSupportNumber);
 
                /* copy cached data into relcache entry */
                opFamily[attIndex] = opcentry->opcfamily;
                opcInType[attIndex] = opcentry->opcintype;
-               if (maxStrategyNumber > 0)
-                       memcpy(&indexOperator[attIndex * maxStrategyNumber],
-                                  opcentry->operatorOids,
-                                  maxStrategyNumber * sizeof(Oid));
                if (maxSupportNumber > 0)
                        memcpy(&indexSupport[attIndex * maxSupportNumber],
                                   opcentry->supportProcs,
@@ -1148,9 +1156,9 @@ IndexSupportInitialize(oidvector *indclass,
  * 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.
+ * opclass in a single indexscan of pg_amproc.
  *
- * The information from pg_am about expected range of strategy and support
+ * The information from pg_am about expected range of support function
  * numbers is passed in, rather than being looked up, mainly because the
  * caller will have it already.
  *
@@ -1164,7 +1172,6 @@ IndexSupportInitialize(oidvector *indclass,
  */
 static OpClassCacheEnt *
 LookupOpclassInfo(Oid operatorClassOid,
-                                 StrategyNumber numStrats,
                                  StrategyNumber numSupport)
 {
        OpClassCacheEnt *opcentry;
@@ -1200,16 +1207,8 @@ LookupOpclassInfo(Oid operatorClassOid,
        {
                /* Need to allocate memory for new entry */
                opcentry->valid = false;        /* until known OK */
-               opcentry->numStrats = numStrats;
                opcentry->numSupport = numSupport;
 
-               if (numStrats > 0)
-                       opcentry->operatorOids = (Oid *)
-                               MemoryContextAllocZero(CacheMemoryContext,
-                                                                          numStrats * sizeof(Oid));
-               else
-                       opcentry->operatorOids = NULL;
-
                if (numSupport > 0)
                        opcentry->supportProcs = (RegProcedure *)
                                MemoryContextAllocZero(CacheMemoryContext,
@@ -1219,7 +1218,6 @@ LookupOpclassInfo(Oid operatorClassOid,
        }
        else
        {
-               Assert(numStrats == opcentry->numStrats);
                Assert(numSupport == opcentry->numSupport);
        }
 
@@ -1250,7 +1248,7 @@ LookupOpclassInfo(Oid operatorClassOid,
 
        /*
         * We have to fetch the pg_opclass row to determine its opfamily and
-        * opcintype, which are needed to look up the operators and functions.
+        * opcintype, which are needed to look up related operators and functions.
         * It'd be convenient to use the syscache here, but that probably doesn't
         * work while bootstrapping.
         */
@@ -1275,45 +1273,6 @@ LookupOpclassInfo(Oid operatorClassOid,
        systable_endscan(scan);
        heap_close(rel, AccessShareLock);
 
-
-       /*
-        * Scan pg_amop to obtain operators for the opclass.  We only fetch the
-        * default ones (those with lefttype = righttype = opcintype).
-        */
-       if (numStrats > 0)
-       {
-               ScanKeyInit(&skey[0],
-                                       Anum_pg_amop_amopfamily,
-                                       BTEqualStrategyNumber, F_OIDEQ,
-                                       ObjectIdGetDatum(opcentry->opcfamily));
-               ScanKeyInit(&skey[1],
-                                       Anum_pg_amop_amoplefttype,
-                                       BTEqualStrategyNumber, F_OIDEQ,
-                                       ObjectIdGetDatum(opcentry->opcintype));
-               ScanKeyInit(&skey[2],
-                                       Anum_pg_amop_amoprighttype,
-                                       BTEqualStrategyNumber, F_OIDEQ,
-                                       ObjectIdGetDatum(opcentry->opcintype));
-               rel = heap_open(AccessMethodOperatorRelationId, AccessShareLock);
-               scan = systable_beginscan(rel, AccessMethodStrategyIndexId, indexOK,
-                                                                 SnapshotNow, 3, skey);
-
-               while (HeapTupleIsValid(htup = systable_getnext(scan)))
-               {
-                       Form_pg_amop amopform = (Form_pg_amop) GETSTRUCT(htup);
-
-                       if (amopform->amopstrategy <= 0 ||
-                               (StrategyNumber) amopform->amopstrategy > numStrats)
-                               elog(ERROR, "invalid amopstrategy number %d for opclass %u",
-                                        amopform->amopstrategy, operatorClassOid);
-                       opcentry->operatorOids[amopform->amopstrategy - 1] =
-                               amopform->amopopr;
-               }
-
-               systable_endscan(scan);
-               heap_close(rel, AccessShareLock);
-       }
-
        /*
         * Scan pg_amproc to obtain support procs for the opclass.      We only fetch
         * the default ones (those with lefttype = righttype = opcintype).
@@ -1368,8 +1327,9 @@ LookupOpclassInfo(Oid operatorClassOid,
  *             quite a lot since we only need to work for a few basic system
  *             catalogs.
  *
- * formrdesc is currently used for: pg_database, pg_class, pg_attribute,
- * pg_proc, and pg_type (see RelationCacheInitializePhase2/3).
+ * formrdesc is currently used for: pg_database, pg_authid, pg_auth_members,
+ * pg_class, pg_attribute, pg_proc, and pg_type
+ * (see RelationCacheInitializePhase2/3).
  *
  * Note that these catalogs can't have constraints (except attnotnull),
  * default values, rules, or triggers, since we don't cope with any of that.
@@ -1392,9 +1352,6 @@ formrdesc(const char *relationName, Oid relationReltype,
         * allocate new relation desc, clear all fields of reldesc
         */
        relation = (Relation) palloc0(sizeof(RelationData));
-       relation->rd_targblock = InvalidBlockNumber;
-       relation->rd_fsm_nblocks = InvalidBlockNumber;
-       relation->rd_vm_nblocks = InvalidBlockNumber;
 
        /* make sure relation is marked as having no open file yet */
        relation->rd_smgr = NULL;
@@ -1411,17 +1368,16 @@ formrdesc(const char *relationName, Oid relationReltype,
        relation->rd_isnailed = true;
        relation->rd_createSubid = InvalidSubTransactionId;
        relation->rd_newRelfilenodeSubid = InvalidSubTransactionId;
-       relation->rd_istemp = false;
-       relation->rd_islocaltemp = false;
+       relation->rd_backend = InvalidBackendId;
 
        /*
         * initialize relation tuple form
         *
         * The data we insert here is pretty incomplete/bogus, but it'll serve to
         * get us launched.  RelationCacheInitializePhase3() will read the real
-        * data from pg_class and replace what we've done here.  Note in particular
-        * that relowner is left as zero; this cues RelationCacheInitializePhase3
-        * that the real data isn't there yet.
+        * data from pg_class and replace what we've done here.  Note in
+        * particular that relowner is left as zero; this cues
+        * RelationCacheInitializePhase3 that the real data isn't there yet.
         */
        relation->rd_rel = (Form_pg_class) palloc0(CLASS_TUPLE_SIZE);
 
@@ -1437,11 +1393,8 @@ formrdesc(const char *relationName, Oid relationReltype,
        if (isshared)
                relation->rd_rel->reltablespace = GLOBALTABLESPACE_OID;
 
-       /*
-        * Likewise, we must know if a relation is temp ... but formrdesc is not
-        * used for any temp relations.
-        */
-       relation->rd_rel->relistemp = false;
+       /* formrdesc is used only for permanent relations */
+       relation->rd_rel->relpersistence = RELPERSISTENCE_PERMANENT;
 
        relation->rd_rel->relpages = 1;
        relation->rd_rel->reltuples = 1;
@@ -1453,8 +1406,8 @@ formrdesc(const char *relationName, Oid relationReltype,
         * 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.
+        * because it will never be replaced.  The data comes from
+        * src/include/catalog/ headers via genbki.pl.
         */
        relation->rd_att = CreateTemplateTupleDesc(natts, hasoids);
        relation->rd_att->tdrefcount = 1;       /* mark as refcounted */
@@ -1492,7 +1445,18 @@ formrdesc(const char *relationName, Oid relationReltype,
         * initialize relation id from info in att array (my, this is ugly)
         */
        RelationGetRelid(relation) = relation->rd_att->attrs[0]->attrelid;
-       relation->rd_rel->relfilenode = RelationGetRelid(relation);
+
+       /*
+        * All relations made with formrdesc are mapped.  This is necessarily so
+        * because there is no other way to know what filenode they currently
+        * have.  In bootstrap mode, add them to the initial relation mapper data,
+        * specifying that the initial filenode is the same as the OID.
+        */
+       relation->rd_rel->relfilenode = InvalidOid;
+       if (IsBootstrapProcessingMode())
+               RelationMapUpdateMap(RelationGetRelid(relation),
+                                                        RelationGetRelid(relation),
+                                                        isshared, true);
 
        /*
         * initialize the relation lock manager information
@@ -1682,24 +1646,17 @@ RelationReloadIndexInfo(Relation relation)
        /* Should be closed at smgr level */
        Assert(relation->rd_smgr == NULL);
 
-       /*
-        * Must reset targblock, fsm_nblocks and vm_nblocks in case rel was
-        * truncated
-        */
-       relation->rd_targblock = InvalidBlockNumber;
-       relation->rd_fsm_nblocks = InvalidBlockNumber;
-       relation->rd_vm_nblocks = InvalidBlockNumber;
-       /* Must free any AM cached data, too */
+       /* Must free any AM cached data upon relcache flush */
        if (relation->rd_amcache)
                pfree(relation->rd_amcache);
        relation->rd_amcache = NULL;
 
        /*
-        * If it's a shared index, we might be called before backend startup
-        * has finished selecting a database, in which case we have no way to
-        * read pg_class yet.  However, a shared index can never have any
-        * significant schema updates, so it's okay to ignore the invalidation
-        * signal.  Just mark it valid and return without doing anything more.
+        * If it's a shared index, we might be called before backend startup has
+        * finished selecting a database, in which case we have no way to read
+        * pg_class yet.  However, a shared index can never have any significant
+        * schema updates, so it's okay to ignore the invalidation signal.  Just
+        * mark it valid and return without doing anything more.
         */
        if (relation->rd_rel->relisshared && !criticalRelcachesBuilt)
        {
@@ -1742,9 +1699,8 @@ RelationReloadIndexInfo(Relation relation)
                HeapTuple       tuple;
                Form_pg_index index;
 
-               tuple = SearchSysCache(INDEXRELID,
-                                                          ObjectIdGetDatum(RelationGetRelid(relation)),
-                                                          0, 0, 0);
+               tuple = SearchSysCache1(INDEXRELID,
+                                                               ObjectIdGetDatum(RelationGetRelid(relation)));
                if (!HeapTupleIsValid(tuple))
                        elog(ERROR, "cache lookup failed for index %u",
                                 RelationGetRelid(relation));
@@ -1782,8 +1738,8 @@ RelationDestroyRelation(Relation relation)
        RelationCloseSmgr(relation);
 
        /*
-        * Free all the subsidiary data structures of the relcache entry,
-        * then the entry itself.
+        * Free all the subsidiary data structures of the relcache entry, then the
+        * entry itself.
         */
        if (relation->rd_rel)
                pfree(relation->rd_rel);
@@ -1812,17 +1768,30 @@ RelationDestroyRelation(Relation relation)
  *
  *      Physically blow away a relation cache entry, or reset it and rebuild
  *      it from scratch (that is, from catalog entries).  The latter path is
- *      usually used when we are notified of a change to an open relation
- *      (one with refcount > 0).  However, this routine just does whichever
- *      it's told to do; callers must determine which they want.
+ *      used when we are notified of a change to an open relation (one with
+ *      refcount > 0).
+ *
+ *      NB: when rebuilding, we'd better hold some lock on the relation,
+ *      else the catalog data we need to read could be changing under us.
+ *      Also, a rel to be rebuilt had better have refcnt > 0.  This is because
+ *      an sinval reset could happen while we're accessing the catalogs, and
+ *      the rel would get blown away underneath us by RelationCacheInvalidate
+ *      if it has zero refcnt.
  *
- *      NB: when rebuilding, we'd better hold some lock on the relation.
- *      In current usages this is presumed true because it has refcnt > 0.
+ *      The "rebuild" parameter is redundant in current usage because it has
+ *      to match the relation's refcnt status, but we keep it as a crosscheck
+ *      that we're doing what the caller expects.
  */
 static void
 RelationClearRelation(Relation relation, bool rebuild)
 {
-       Oid                     old_reltype = relation->rd_rel->reltype;
+       /*
+        * As per notes above, a rel to be rebuilt MUST have refcnt > 0; while of
+        * course it would be a bad idea to blow away one with nonzero refcnt.
+        */
+       Assert(rebuild ?
+                  !RelationHasReferenceCountZero(relation) :
+                  RelationHasReferenceCountZero(relation));
 
        /*
         * Make sure smgr and lower levels close the relation's files, if they
@@ -1835,9 +1804,8 @@ RelationClearRelation(Relation relation, bool rebuild)
 
        /*
         * Never, never ever blow away a nailed-in system relation, because we'd
-        * be unable to recover.  However, we must reset rd_targblock, in case we
-        * got called because of a relation cache flush that was triggered by
-        * VACUUM.  Likewise reset the fsm and vm size info.
+        * be unable to recover.  However, we must redo RelationInitPhysicalAddr
+        * in case it is a mapped relation whose mapping changed.
         *
         * If it's a nailed index, then we need to re-read the pg_class row to see
         * if its relfilenode changed.  We can't necessarily do that here, because
@@ -1848,9 +1816,8 @@ RelationClearRelation(Relation relation, bool rebuild)
         */
        if (relation->rd_isnailed)
        {
-               relation->rd_targblock = InvalidBlockNumber;
-               relation->rd_fsm_nblocks = InvalidBlockNumber;
-               relation->rd_vm_nblocks = InvalidBlockNumber;
+               RelationInitPhysicalAddr(relation);
+
                if (relation->rd_rel->relkind == RELKIND_INDEX)
                {
                        relation->rd_isvalid = false;           /* needs to be revalidated */
@@ -1880,12 +1847,6 @@ RelationClearRelation(Relation relation, bool rebuild)
        relation->rd_isvalid = false;
 
        /*
-        * Clear out catcache's entries for this relation.  This is a bit of
-        * a hack, but it's a convenient place to do it.
-        */
-       CatalogCacheFlushRelation(RelationGetRelid(relation));
-
-       /*
         * If we're really done with the relcache entry, blow it away. But if
         * someone is still using it, reconstruct the whole deal without moving
         * the physical RelationData record (so that the someone's pointer is
@@ -1893,9 +1854,6 @@ RelationClearRelation(Relation relation, bool rebuild)
         */
        if (!rebuild)
        {
-               /* Flush any rowtype cache entry */
-               flush_rowtype_cache(old_reltype);
-
                /* Remove it from the hash table */
                RelationCacheDelete(relation);
 
@@ -1905,29 +1863,29 @@ RelationClearRelation(Relation relation, bool rebuild)
        else
        {
                /*
-                * Our strategy for rebuilding an open relcache entry is to build
-                * a new entry from scratch, swap its contents with the old entry,
-                * and finally delete the new entry (along with any infrastructure
-                * swapped over from the old entry).  This is to avoid trouble in case
-                * an error causes us to lose control partway through.  The old entry
+                * Our strategy for rebuilding an open relcache entry is to build a
+                * new entry from scratch, swap its contents with the old entry, and
+                * finally delete the new entry (along with any infrastructure swapped
+                * over from the old entry).  This is to avoid trouble in case an
+                * error causes us to lose control partway through.  The old entry
                 * will still be marked !rd_isvalid, so we'll try to rebuild it again
-                * on next access.  Meanwhile it's not any less valid than it was
+                * on next access.      Meanwhile it's not any less valid than it was
                 * before, so any code that might expect to continue accessing it
                 * isn't hurt by the rebuild failure.  (Consider for example a
                 * subtransaction that ALTERs a table and then gets cancelled partway
                 * through the cache entry rebuild.  The outer transaction should
                 * still see the not-modified cache entry as valid.)  The worst
-                * consequence of an error is leaking the necessarily-unreferenced
-                * new entry, and this shouldn't happen often enough for that to be
-                * a big problem.
+                * consequence of an error is leaking the necessarily-unreferenced new
+                * entry, and this shouldn't happen often enough for that to be a big
+                * problem.
                 *
-                * When rebuilding an open relcache entry, we must preserve ref count
-                * and rd_createSubid/rd_newRelfilenodeSubid state.  Also attempt to
-                * preserve the pg_class entry (rd_rel), tupledesc, and rewrite-rule
-                * substructures in place, because various places assume that these
-                * structures won't move while they are working with an open relcache
-                * entry.  (Note: the refcount mechanism for tupledescs might someday
-                * allow us to remove this hack for the tupledesc.)
+                * When rebuilding an open relcache entry, we must preserve ref count,
+                * rd_createSubid/rd_newRelfilenodeSubid, and rd_toastoid state.  Also
+                * attempt to preserve the pg_class entry (rd_rel), tupledesc, and
+                * rewrite-rule substructures in place, because various places assume
+                * that these structures won't move while they are working with an
+                * open relcache entry.  (Note: the refcount mechanism for tupledescs
+                * might someday allow us to remove this hack for the tupledesc.)
                 *
                 * Note that this process does not touch CurrentResourceOwner; which
                 * is good because whatever ref counts the entry may have do not
@@ -1943,7 +1901,6 @@ RelationClearRelation(Relation relation, bool rebuild)
                if (newrel == NULL)
                {
                        /* Should only get here if relation was deleted */
-                       flush_rowtype_cache(old_reltype);
                        RelationCacheDelete(relation);
                        RelationDestroyRelation(relation);
                        elog(ERROR, "relation %u deleted while still in use", save_relid);
@@ -1951,18 +1908,16 @@ RelationClearRelation(Relation relation, bool rebuild)
 
                keep_tupdesc = equalTupleDescs(relation->rd_att, newrel->rd_att);
                keep_rules = equalRuleLocks(relation->rd_rules, newrel->rd_rules);
-               if (!keep_tupdesc)
-                       flush_rowtype_cache(old_reltype);
 
                /*
                 * Perform swapping of the relcache entry contents.  Within this
-                * process the old entry is momentarily invalid, so there *must*
-                * be no possibility of CHECK_FOR_INTERRUPTS within this sequence.
-                * Do it in all-in-line code for safety.
+                * process the old entry is momentarily invalid, so there *must* be no
+                * possibility of CHECK_FOR_INTERRUPTS within this sequence. Do it in
+                * all-in-line code for safety.
                 *
-                * Since the vast majority of fields should be swapped, our method
-                * is to swap the whole structures and then re-swap those few fields
-                * we didn't want swapped.
+                * Since the vast majority of fields should be swapped, our method is
+                * to swap the whole structures and then re-swap those few fields we
+                * didn't want swapped.
                 */
 #define SWAPFIELD(fldtype, fldname) \
                do { \
@@ -2001,6 +1956,8 @@ RelationClearRelation(Relation relation, bool rebuild)
                        SWAPFIELD(RuleLock *, rd_rules);
                        SWAPFIELD(MemoryContext, rd_rulescxt);
                }
+               /* toast OID override must be preserved */
+               SWAPFIELD(Oid, rd_toastoid);
                /* pgstat_info must be preserved */
                SWAPFIELD(struct PgStat_TableStatus *, pgstat_info);
 
@@ -2019,8 +1976,6 @@ RelationClearRelation(Relation relation, bool rebuild)
 static void
 RelationFlushRelation(Relation relation)
 {
-       bool            rebuild;
-
        if (relation->rd_createSubid != InvalidSubTransactionId ||
                relation->rd_newRelfilenodeSubid != InvalidSubTransactionId)
        {
@@ -2028,18 +1983,24 @@ RelationFlushRelation(Relation relation)
                 * New relcache entries are always rebuilt, not flushed; else we'd
                 * forget the "new" status of the relation, which is a useful
                 * optimization to have.  Ditto for the new-relfilenode status.
+                *
+                * The rel could have zero refcnt here, so temporarily increment the
+                * refcnt to ensure it's safe to rebuild it.  We can assume that the
+                * current transaction has some lock on the rel already.
                 */
-               rebuild = true;
+               RelationIncrementReferenceCount(relation);
+               RelationClearRelation(relation, true);
+               RelationDecrementReferenceCount(relation);
        }
        else
        {
                /*
                 * Pre-existing rels can be dropped from the relcache if not open.
                 */
-               rebuild = !RelationHasReferenceCountZero(relation);
-       }
+               bool            rebuild = !RelationHasReferenceCountZero(relation);
 
-       RelationClearRelation(relation, rebuild);
+               RelationClearRelation(relation, rebuild);
+       }
 }
 
 /*
@@ -2098,7 +2059,7 @@ RelationCacheInvalidateEntry(Oid relationId)
  * RelationCacheInvalidate
  *      Blow away cached relation descriptors that have zero reference counts,
  *      and rebuild those with positive reference counts.      Also reset the smgr
- *      relation cache.
+ *      relation cache and re-read relation mapping data.
  *
  *      This is currently used only to recover from SI message buffer overflow,
  *      so we do not touch new-in-transaction relations; they cannot be targets
@@ -2184,6 +2145,11 @@ RelationCacheInvalidate(void)
         */
        smgrcloseall();
 
+       /*
+        * Reload relation mapping data before starting to reconstruct cache.
+        */
+       RelationMapInvalidateAll();
+
        /* Phase 2: rebuild the items found to need rebuild in phase 1 */
        foreach(l, rebuildFirstList)
        {
@@ -2200,6 +2166,25 @@ RelationCacheInvalidate(void)
 }
 
 /*
+ * RelationCloseSmgrByOid - close a relcache entry's smgr link
+ *
+ * Needed in some cases where we are changing a relation's physical mapping.
+ * The link will be automatically reopened on next use.
+ */
+void
+RelationCloseSmgrByOid(Oid relationId)
+{
+       Relation        relation;
+
+       RelationIdCacheLookup(relationId, relation);
+
+       if (!PointerIsValid(relation))
+               return;                                 /* not in cache, nothing to do */
+
+       RelationCloseSmgr(relation);
+}
+
+/*
  * AtEOXact_RelationCache
  *
  *     Clean up the relcache at main-transaction commit or abort.
@@ -2344,7 +2329,6 @@ AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
                                relation->rd_createSubid = parentSubid;
                        else
                        {
-                               Assert(RelationHasReferenceCountZero(relation));
                                RelationClearRelation(relation, false);
                                continue;
                        }
@@ -2375,22 +2359,6 @@ AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
        }
 }
 
-/*
- * RelationCacheMarkNewRelfilenode
- *
- *     Mark the rel as having been given a new relfilenode in the current
- *     (sub) transaction.      This is a hint that can be used to optimize
- *     later operations on the rel in the same transaction.
- */
-void
-RelationCacheMarkNewRelfilenode(Relation rel)
-{
-       /* Mark it... */
-       rel->rd_newRelfilenodeSubid = GetCurrentSubTransactionId();
-       /* ... and now we have eoxact cleanup work to do */
-       need_eoxact_work = true;
-}
-
 
 /*
  *             RelationBuildLocalRelation
@@ -2403,7 +2371,9 @@ RelationBuildLocalRelation(const char *relname,
                                                   TupleDesc tupDesc,
                                                   Oid relid,
                                                   Oid reltablespace,
-                                                  bool shared_relation)
+                                                  bool shared_relation,
+                                                  bool mapped_relation,
+                                                  char relpersistence)
 {
        Relation        rel;
        MemoryContext oldcxt;
@@ -2423,6 +2393,8 @@ RelationBuildLocalRelation(const char *relname,
        switch (relid)
        {
                case DatabaseRelationId:
+               case AuthIdRelationId:
+               case AuthMemRelationId:
                case RelationRelationId:
                case AttributeRelationId:
                case ProcedureRelationId:
@@ -2444,6 +2416,9 @@ RelationBuildLocalRelation(const char *relname,
                elog(ERROR, "shared_relation flag for \"%s\" does not match IsSharedRelation(%u)",
                         relname, relid);
 
+       /* Shared relations had better be mapped, too */
+       Assert(mapped_relation || !shared_relation);
+
        /*
         * switch to the cache context to create the relcache entry.
         */
@@ -2457,10 +2432,6 @@ RelationBuildLocalRelation(const char *relname,
         */
        rel = (Relation) palloc0(sizeof(RelationData));
 
-       rel->rd_targblock = InvalidBlockNumber;
-       rel->rd_fsm_nblocks = InvalidBlockNumber;
-       rel->rd_vm_nblocks = InvalidBlockNumber;
-
        /* make sure relation is marked as having no open file yet */
        rel->rd_smgr = NULL;
 
@@ -2476,10 +2447,6 @@ RelationBuildLocalRelation(const char *relname,
        /* must flag that we have rels created in this transaction */
        need_eoxact_work = true;
 
-       /* it is temporary if and only if it is in my temp-table namespace */
-       rel->rd_istemp = isTempOrToastNamespace(relnamespace);
-       rel->rd_islocaltemp = rel->rd_istemp;
-
        /*
         * create a new tuple descriptor from the one passed in.  We do this
         * partly to copy it into the cache context, and partly because the new
@@ -2519,22 +2486,46 @@ RelationBuildLocalRelation(const char *relname,
        /* needed when bootstrapping: */
        rel->rd_rel->relowner = BOOTSTRAP_SUPERUSERID;
 
+       /* set up persistence; rd_backend is a function of persistence type */
+       rel->rd_rel->relpersistence = relpersistence;
+       switch (relpersistence)
+       {
+               case RELPERSISTENCE_PERMANENT:
+                       rel->rd_backend = InvalidBackendId;
+                       break;
+               case RELPERSISTENCE_TEMP:
+                       rel->rd_backend = MyBackendId;
+                       break;
+               default:
+                       elog(ERROR, "invalid relpersistence: %c", relpersistence);
+                       break;
+       }
+
        /*
         * Insert relation physical and logical identifiers (OIDs) into the right
         * places.      Note that the physical ID (relfilenode) is initially the same
-        * as the logical ID (OID).
+        * as the logical ID (OID); except that for a mapped relation, we set
+        * relfilenode to zero and rely on RelationInitPhysicalAddr to consult the
+        * map.
         */
        rel->rd_rel->relisshared = shared_relation;
-       rel->rd_rel->relistemp = rel->rd_istemp;
 
        RelationGetRelid(rel) = relid;
 
        for (i = 0; i < natts; i++)
                rel->rd_att->attrs[i]->attrelid = relid;
 
-       rel->rd_rel->relfilenode = relid;
        rel->rd_rel->reltablespace = reltablespace;
 
+       if (mapped_relation)
+       {
+               rel->rd_rel->relfilenode = InvalidOid;
+               /* Add it to the active mapping information */
+               RelationMapUpdateMap(relid, relid, shared_relation, true);
+       }
+       else
+               rel->rd_rel->relfilenode = relid;
+
        RelationInitLockInfo(rel);      /* see lmgr.c */
 
        RelationInitPhysicalAddr(rel);
@@ -2560,6 +2551,117 @@ RelationBuildLocalRelation(const char *relname,
        return rel;
 }
 
+
+/*
+ * RelationSetNewRelfilenode
+ *
+ * Assign a new relfilenode (physical file name) to the relation.
+ *
+ * This allows a full rewrite of the relation to be done with transactional
+ * safety (since the filenode assignment can be rolled back).  Note however
+ * that there is no simple way to access the relation's old data for the
+ * remainder of the current transaction.  This limits the usefulness to cases
+ * such as TRUNCATE or rebuilding an index from scratch.
+ *
+ * Caller must already hold exclusive lock on the relation.
+ *
+ * The relation is marked with relfrozenxid = freezeXid (InvalidTransactionId
+ * must be passed for indexes and sequences).  This should be a lower bound on
+ * the XIDs that will be put into the new relation contents.
+ */
+void
+RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid)
+{
+       Oid                     newrelfilenode;
+       RelFileNodeBackend newrnode;
+       Relation        pg_class;
+       HeapTuple       tuple;
+       Form_pg_class classform;
+
+       /* Indexes, sequences must have Invalid frozenxid; other rels must not */
+       Assert((relation->rd_rel->relkind == RELKIND_INDEX ||
+                       relation->rd_rel->relkind == RELKIND_SEQUENCE) ?
+                  freezeXid == InvalidTransactionId :
+                  TransactionIdIsNormal(freezeXid));
+
+       /* Allocate a new relfilenode */
+       newrelfilenode = GetNewRelFileNode(relation->rd_rel->reltablespace, NULL,
+                                                                          relation->rd_rel->relpersistence);
+
+       /*
+        * Get a writable copy of the pg_class tuple for the given relation.
+        */
+       pg_class = heap_open(RelationRelationId, RowExclusiveLock);
+
+       tuple = SearchSysCacheCopy1(RELOID,
+                                                               ObjectIdGetDatum(RelationGetRelid(relation)));
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "could not find tuple for relation %u",
+                        RelationGetRelid(relation));
+       classform = (Form_pg_class) GETSTRUCT(tuple);
+
+       /*
+        * Create storage for the main fork of the new relfilenode.
+        *
+        * NOTE: any conflict in relfilenode value will be caught here, if
+        * GetNewRelFileNode messes up for any reason.
+        */
+       newrnode.node = relation->rd_node;
+       newrnode.node.relNode = newrelfilenode;
+       newrnode.backend = relation->rd_backend;
+       RelationCreateStorage(newrnode.node, relation->rd_rel->relpersistence);
+       smgrclosenode(newrnode);
+
+       /*
+        * Schedule unlinking of the old storage at transaction commit.
+        */
+       RelationDropStorage(relation);
+
+       /*
+        * Now update the pg_class row.  However, if we're dealing with a mapped
+        * index, pg_class.relfilenode doesn't change; instead we have to send the
+        * update to the relation mapper.
+        */
+       if (RelationIsMapped(relation))
+               RelationMapUpdateMap(RelationGetRelid(relation),
+                                                        newrelfilenode,
+                                                        relation->rd_rel->relisshared,
+                                                        false);
+       else
+               classform->relfilenode = newrelfilenode;
+
+       /* These changes are safe even for a mapped relation */
+       if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
+       {
+               classform->relpages = 0;        /* it's empty until further notice */
+               classform->reltuples = 0;
+       }
+       classform->relfrozenxid = freezeXid;
+
+       simple_heap_update(pg_class, &tuple->t_self, tuple);
+       CatalogUpdateIndexes(pg_class, tuple);
+
+       heap_freetuple(tuple);
+
+       heap_close(pg_class, RowExclusiveLock);
+
+       /*
+        * Make the pg_class row change visible, as well as the relation map
+        * change if any.  This will cause the relcache entry to get updated, too.
+        */
+       CommandCounterIncrement();
+
+       /*
+        * Mark the rel as having been given a new relfilenode in the current
+        * (sub) transaction.  This is a hint that can be used to optimize later
+        * operations on the rel in the same transaction.
+        */
+       relation->rd_newRelfilenodeSubid = GetCurrentSubTransactionId();
+       /* ... and now we have eoxact cleanup work to do */
+       need_eoxact_work = true;
+}
+
+
 /*
  *             RelationCacheInitialize
  *
@@ -2594,17 +2696,23 @@ RelationCacheInitialize(void)
        ctl.hash = oid_hash;
        RelationIdCache = hash_create("Relcache by OID", INITRELCACHESIZE,
                                                                  &ctl, HASH_ELEM | HASH_FUNCTION);
+
+       /*
+        * relation mapper needs to be initialized too
+        */
+       RelationMapInitialize();
 }
 
 /*
  *             RelationCacheInitializePhase2
  *
- *             This is called to prepare for access to pg_database during startup.
- *             We must at least set up a nailed reldesc for pg_database.  Ideally
- *             we'd like to have reldescs for its indexes, too.  We attempt to
- *             load this information from the shared relcache init file.  If that's
- *             missing or broken, just make a phony entry for pg_database.
- *             RelationCacheInitializePhase3 will clean up as needed.
+ *             This is called to prepare for access to shared catalogs during startup.
+ *             We must at least set up nailed reldescs for pg_database, pg_authid,
+ *             and pg_auth_members.  Ideally we'd like to have reldescs for their
+ *             indexes, too.  We attempt to load this information from the shared
+ *             relcache init file.  If that's missing or broken, just make phony
+ *             entries for the catalogs themselves.  RelationCacheInitializePhase3
+ *             will clean up as needed.
  */
 void
 RelationCacheInitializePhase2(void)
@@ -2612,7 +2720,13 @@ RelationCacheInitializePhase2(void)
        MemoryContext oldcxt;
 
        /*
-        * In bootstrap mode, pg_database isn't there yet anyway, so do nothing.
+        * relation mapper needs initialized too
+        */
+       RelationMapInitializePhase2();
+
+       /*
+        * In bootstrap mode, the shared catalogs aren't there yet anyway, so do
+        * nothing.
         */
        if (IsBootstrapProcessingMode())
                return;
@@ -2623,15 +2737,19 @@ RelationCacheInitializePhase2(void)
        oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
 
        /*
-        * Try to load the shared relcache cache file.  If unsuccessful,
-        * bootstrap the cache with a pre-made descriptor for pg_database.
+        * Try to load the shared relcache cache file.  If unsuccessful, bootstrap
+        * the cache with pre-made descriptors for the critical shared catalogs.
         */
        if (!load_relcache_init_file(true))
        {
                formrdesc("pg_database", DatabaseRelation_Rowtype_Id, true,
                                  true, Natts_pg_database, Desc_pg_database);
+               formrdesc("pg_authid", AuthIdRelation_Rowtype_Id, true,
+                                 true, Natts_pg_authid, Desc_pg_authid);
+               formrdesc("pg_auth_members", AuthMemRelation_Rowtype_Id, true,
+                                 false, Natts_pg_auth_members, Desc_pg_auth_members);
 
-#define NUM_CRITICAL_SHARED_RELS       1       /* fix if you change list above */
+#define NUM_CRITICAL_SHARED_RELS       3       /* fix if you change list above */
        }
 
        MemoryContextSwitchTo(oldcxt);
@@ -2660,14 +2778,19 @@ RelationCacheInitializePhase3(void)
        bool            needNewCacheFile = !criticalSharedRelcachesBuilt;
 
        /*
+        * relation mapper needs initialized too
+        */
+       RelationMapInitializePhase3();
+
+       /*
         * switch to cache memory context
         */
        oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
 
        /*
-        * Try to load the local relcache cache file.  If unsuccessful,
-        * bootstrap the cache with pre-made descriptors for the critical
-        * "nailed-in" system catalogs.
+        * Try to load the local relcache cache file.  If unsuccessful, bootstrap
+        * the cache with pre-made descriptors for the critical "nailed-in" system
+        * catalogs.
         */
        if (IsBootstrapProcessingMode() ||
                !load_relcache_init_file(false))
@@ -2683,7 +2806,7 @@ RelationCacheInitializePhase3(void)
                formrdesc("pg_type", TypeRelation_Rowtype_Id, false,
                                  true, Natts_pg_type, Desc_pg_type);
 
-#define NUM_CRITICAL_LOCAL_RELS        4       /* fix if you change list above */
+#define NUM_CRITICAL_LOCAL_RELS 4              /* fix if you change list above */
        }
 
        MemoryContextSwitchTo(oldcxt);
@@ -2719,17 +2842,22 @@ RelationCacheInitializePhase3(void)
         */
        if (!criticalRelcachesBuilt)
        {
-               load_critical_index(ClassOidIndexId);
-               load_critical_index(AttributeRelidNumIndexId);
-               load_critical_index(IndexRelidIndexId);
-               load_critical_index(OpclassOidIndexId);
-               load_critical_index(AccessMethodStrategyIndexId);
-               load_critical_index(AccessMethodProcedureIndexId);
-               load_critical_index(OperatorOidIndexId);
-               load_critical_index(RewriteRelRulenameIndexId);
-               load_critical_index(TriggerRelidNameIndexId);
-
-#define NUM_CRITICAL_LOCAL_INDEXES     9               /* fix if you change list above */
+               load_critical_index(ClassOidIndexId,
+                                                       RelationRelationId);
+               load_critical_index(AttributeRelidNumIndexId,
+                                                       AttributeRelationId);
+               load_critical_index(IndexRelidIndexId,
+                                                       IndexRelationId);
+               load_critical_index(OpclassOidIndexId,
+                                                       OperatorClassRelationId);
+               load_critical_index(AccessMethodProcedureIndexId,
+                                                       AccessMethodProcedureRelationId);
+               load_critical_index(RewriteRelRulenameIndexId,
+                                                       RewriteRelationId);
+               load_critical_index(TriggerRelidNameIndexId,
+                                                       TriggerRelationId);
+
+#define NUM_CRITICAL_LOCAL_INDEXES     7       /* fix if you change list above */
 
                criticalRelcachesBuilt = true;
        }
@@ -2737,17 +2865,27 @@ RelationCacheInitializePhase3(void)
        /*
         * Process critical shared indexes too.
         *
-        * DatabaseNameIndexId isn't critical for relcache loading, but rather
-        * for initial lookup of MyDatabaseId, without which we'll never find
-        * any non-shared catalogs at all.  Autovacuum calls InitPostgres with
-        * a database OID, so it instead depends on DatabaseOidIndexId.
+        * DatabaseNameIndexId isn't critical for relcache loading, but rather for
+        * initial lookup of MyDatabaseId, without which we'll never find any
+        * non-shared catalogs at all.  Autovacuum calls InitPostgres with a
+        * database OID, so it instead depends on DatabaseOidIndexId.  We also
+        * need to nail up some indexes on pg_authid and pg_auth_members for use
+        * during client authentication.
         */
        if (!criticalSharedRelcachesBuilt)
        {
-               load_critical_index(DatabaseNameIndexId);
-               load_critical_index(DatabaseOidIndexId);
-
-#define NUM_CRITICAL_SHARED_INDEXES    2               /* fix if you change list above */
+               load_critical_index(DatabaseNameIndexId,
+                                                       DatabaseRelationId);
+               load_critical_index(DatabaseOidIndexId,
+                                                       DatabaseRelationId);
+               load_critical_index(AuthIdRolnameIndexId,
+                                                       AuthIdRelationId);
+               load_critical_index(AuthIdOidIndexId,
+                                                       AuthIdRelationId);
+               load_critical_index(AuthMemMemRoleIndexId,
+                                                       AuthMemRelationId);
+
+#define NUM_CRITICAL_SHARED_INDEXES 5  /* fix if you change list above */
 
                criticalSharedRelcachesBuilt = true;
        }
@@ -2760,8 +2898,8 @@ RelationCacheInitializePhase3(void)
         * relcache entries have rules or triggers, load that info the hard way
         * since it isn't recorded in the cache file.
         *
-        * Whenever we access the catalogs to read data, there is a possibility
-        * of a shared-inval cache flush causing relcache entries to be removed.
+        * Whenever we access the catalogs to read data, there is a possibility of
+        * a shared-inval cache flush causing relcache entries to be removed.
         * Since hash_seq_search only guarantees to still work after the *current*
         * entry is removed, it's unsafe to continue the hashtable scan afterward.
         * We handle this by restarting the scan from scratch after each access.
@@ -2788,9 +2926,8 @@ RelationCacheInitializePhase3(void)
                        HeapTuple       htup;
                        Form_pg_class relp;
 
-                       htup = SearchSysCache(RELOID,
-                                                               ObjectIdGetDatum(RelationGetRelid(relation)),
-                                                                 0, 0, 0);
+                       htup = SearchSysCache1(RELOID,
+                                                          ObjectIdGetDatum(RelationGetRelid(relation)));
                        if (!HeapTupleIsValid(htup))
                                elog(FATAL, "cache lookup failed for relation %u",
                                         RelationGetRelid(relation));
@@ -2809,9 +2946,9 @@ RelationCacheInitializePhase3(void)
 
                        /*
                         * Check the values in rd_att were set up correctly.  (We cannot
-                        * just copy them over now: formrdesc must have set up the
-                        * rd_att data correctly to start with, because it may already
-                        * have been copied into one or more catcache entries.)
+                        * just copy them over now: formrdesc must have set up the rd_att
+                        * data correctly to start with, because it may already have been
+                        * copied into one or more catcache entries.)
                         */
                        Assert(relation->rd_att->tdtypeid == relp->reltype);
                        Assert(relation->rd_att->tdtypmod == -1);
@@ -2886,12 +3023,22 @@ RelationCacheInitializePhase3(void)
 
 /*
  * Load one critical system index into the relcache
+ *
+ * indexoid is the OID of the target index, heapoid is the OID of the catalog
+ * it belongs to.
  */
 static void
-load_critical_index(Oid indexoid)
+load_critical_index(Oid indexoid, Oid heapoid)
 {
        Relation        ird;
 
+       /*
+        * We must lock the underlying catalog before locking the index to avoid
+        * deadlock, since RelationBuildDesc might well need to read the catalog,
+        * and if anyone else is exclusive-locking this catalog and index they'll
+        * be doing it in that order.
+        */
+       LockRelationOid(heapoid, AccessShareLock);
        LockRelationOid(indexoid, AccessShareLock);
        ird = RelationBuildDesc(indexoid, true);
        if (ird == NULL)
@@ -2899,6 +3046,7 @@ load_critical_index(Oid indexoid)
        ird->rd_isnailed = true;
        ird->rd_refcnt = 1;
        UnlockRelationOid(indexoid, AccessShareLock);
+       UnlockRelationOid(heapoid, AccessShareLock);
 }
 
 /*
@@ -3537,8 +3685,8 @@ RelationGetExclusionInfo(Relation indexRelation,
        Oid                *funcs;
        uint16     *strats;
        Relation        conrel;
-       SysScanDesc     conscan;
-       ScanKeyData     skey[1];
+       SysScanDesc conscan;
+       ScanKeyData skey[1];
        HeapTuple       htup;
        bool            found;
        MemoryContext oldcxt;
@@ -3559,9 +3707,9 @@ RelationGetExclusionInfo(Relation indexRelation,
        }
 
        /*
-        * Search pg_constraint for the constraint associated with the index.
-        * To make this not too painfully slow, we use the index on conrelid;
-        * that will hold the parent relation's OID not the index's own OID.
+        * Search pg_constraint for the constraint associated with the index. To
+        * make this not too painfully slow, we use the index on conrelid; that
+        * will hold the parent relation's OID not the index's own OID.
         */
        ScanKeyInit(&skey[0],
                                Anum_pg_constraint_conrelid,
@@ -3575,7 +3723,7 @@ RelationGetExclusionInfo(Relation indexRelation,
 
        while (HeapTupleIsValid(htup = systable_getnext(conscan)))
        {
-               Form_pg_constraint       conform = (Form_pg_constraint) GETSTRUCT(htup);
+               Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(htup);
                Datum           val;
                bool            isnull;
                ArrayType  *arr;
@@ -3667,7 +3815,7 @@ RelationGetExclusionInfo(Relation indexRelation,
  *                       relation descriptors using sequential scans and write 'em to
  *                       the initialization file for use by subsequent backends.
  *
- *             As of Postgres 8.5, there is one local initialization file in each
+ *             As of Postgres 9.0, there is one local initialization file in each
  *             database, plus one shared initialization file for shared catalogs.
  *
  *             We could dispense with the initialization files and just build the
@@ -3835,7 +3983,6 @@ load_relcache_init_file(bool shared)
                        MemoryContext indexcxt;
                        Oid                *opfamily;
                        Oid                *opcintype;
-                       Oid                *operator;
                        RegProcedure *support;
                        int                     nsupport;
                        int16      *indoption;
@@ -3896,17 +4043,7 @@ load_relcache_init_file(bool shared)
 
                        rel->rd_opcintype = opcintype;
 
-                       /* next, read the vector of operator OIDs */
-                       if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
-                               goto read_failed;
-
-                       operator = (Oid *) MemoryContextAlloc(indexcxt, len);
-                       if (fread(operator, 1, len, fp) != len)
-                               goto read_failed;
-
-                       rel->rd_operator = operator;
-
-                       /* next, read the vector of support procedures */
+                       /* next, read the vector of support procedure OIDs */
                        if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
                                goto read_failed;
                        support = (RegProcedure *) MemoryContextAlloc(indexcxt, len);
@@ -3945,7 +4082,6 @@ load_relcache_init_file(bool shared)
                        Assert(rel->rd_aminfo == NULL);
                        Assert(rel->rd_opfamily == NULL);
                        Assert(rel->rd_opcintype == NULL);
-                       Assert(rel->rd_operator == NULL);
                        Assert(rel->rd_support == NULL);
                        Assert(rel->rd_supportinfo == NULL);
                        Assert(rel->rd_indoption == NULL);
@@ -3971,9 +4107,6 @@ load_relcache_init_file(bool shared)
                 * Reset transient-state fields in the relcache entry
                 */
                rel->rd_smgr = NULL;
-               rel->rd_targblock = InvalidBlockNumber;
-               rel->rd_fsm_nblocks = InvalidBlockNumber;
-               rel->rd_vm_nblocks = InvalidBlockNumber;
                if (rel->rd_isnailed)
                        rel->rd_refcnt = 1;
                else
@@ -4165,12 +4298,7 @@ write_relcache_init_file(bool shared)
                                           relform->relnatts * sizeof(Oid),
                                           fp);
 
-                       /* next, write the vector of operator OIDs */
-                       write_item(rel->rd_operator,
-                                          relform->relnatts * (am->amstrategies * sizeof(Oid)),
-                                          fp);
-
-                       /* next, write the vector of support procedures */
+                       /* next, write the vector of support procedure OIDs */
                        write_item(rel->rd_support,
                                  relform->relnatts * (am->amsupport * sizeof(RegProcedure)),
                                           fp);
@@ -4322,7 +4450,7 @@ RelationCacheInitFileInvalidate(bool beforeSend)
  *
  * We used to keep the init files across restarts, but that is unsafe in PITR
  * scenarios, and even in simple crash-recovery cases there are windows for
- * the init files to become out-of-sync with the database.  So now we just
+ * the init files to become out-of-sync with the database.     So now we just
  * remove them during startup and expect the first backend launch to rebuild
  * them.  Of course, this has to happen in each database of the cluster.
  */