]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/cache/relcache.c
Fix an oversight I made in a cleanup patch over a year ago:
[postgresql] / src / backend / utils / cache / relcache.c
index da1f9fd924052e2694977dd90e9c8b23483e4433..9a32fd5845e77556076ee63aa39545622fb351bb 100644 (file)
@@ -3,12 +3,12 @@
  * relcache.c
  *       POSTGRES relation descriptor cache code
  *
- * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.255 2007/01/25 02:17:26 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.270 2008/04/01 00:48:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,6 +34,7 @@
 #include "access/reloptions.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"
@@ -51,6 +52,7 @@
 #include "optimizer/clauses.h"
 #include "optimizer/planmain.h"
 #include "optimizer/prep.h"
+#include "optimizer/var.h"
 #include "rewrite/rewriteDefine.h"
 #include "storage/fd.h"
 #include "storage/smgr.h"
@@ -61,6 +63,7 @@
 #include "utils/relcache.h"
 #include "utils/resowner.h"
 #include "utils/syscache.h"
+#include "utils/tqual.h"
 #include "utils/typcache.h"
 
 
@@ -181,7 +184,7 @@ static HTAB *OpClassCache = NULL;
 
 static void RelationClearRelation(Relation relation, bool rebuild);
 
-static void RelationReloadClassinfo(Relation relation);
+static void RelationReloadIndexInfo(Relation relation);
 static void RelationFlushRelation(Relation relation);
 static bool load_relcache_init_file(void);
 static void write_relcache_init_file(void);
@@ -640,7 +643,6 @@ RelationBuildRuleLock(Relation relation)
                Form_pg_rewrite rewrite_form = (Form_pg_rewrite) GETSTRUCT(rewrite_tuple);
                bool            isnull;
                Datum           rule_datum;
-               text       *rule_text;
                char       *rule_str;
                RewriteRule *rule;
 
@@ -651,6 +653,7 @@ RelationBuildRuleLock(Relation relation)
 
                rule->event = rewrite_form->ev_type - '0';
                rule->attrno = rewrite_form->ev_attr;
+               rule->enabled = rewrite_form->ev_enabled;
                rule->isInstead = rewrite_form->is_instead;
 
                /*
@@ -664,30 +667,22 @@ RelationBuildRuleLock(Relation relation)
                                                                  rewrite_tupdesc,
                                                                  &isnull);
                Assert(!isnull);
-               rule_text = DatumGetTextP(rule_datum);
-               rule_str = DatumGetCString(DirectFunctionCall1(textout,
-                                                                                               PointerGetDatum(rule_text)));
+               rule_str = TextDatumGetCString(rule_datum);
                oldcxt = MemoryContextSwitchTo(rulescxt);
                rule->actions = (List *) stringToNode(rule_str);
                MemoryContextSwitchTo(oldcxt);
                pfree(rule_str);
-               if ((Pointer) rule_text != DatumGetPointer(rule_datum))
-                       pfree(rule_text);
 
                rule_datum = heap_getattr(rewrite_tuple,
                                                                  Anum_pg_rewrite_ev_qual,
                                                                  rewrite_tupdesc,
                                                                  &isnull);
                Assert(!isnull);
-               rule_text = DatumGetTextP(rule_datum);
-               rule_str = DatumGetCString(DirectFunctionCall1(textout,
-                                                                                               PointerGetDatum(rule_text)));
+               rule_str = TextDatumGetCString(rule_datum);
                oldcxt = MemoryContextSwitchTo(rulescxt);
                rule->qual = (Node *) stringToNode(rule_str);
                MemoryContextSwitchTo(oldcxt);
                pfree(rule_str);
-               if ((Pointer) rule_text != DatumGetPointer(rule_datum))
-                       pfree(rule_text);
 
                /*
                 * We want the rule's table references to be checked as though by the
@@ -837,7 +832,7 @@ RelationBuildDesc(Oid targetRelId, Relation oldrelation)
        relation->rd_isnailed = false;
        relation->rd_createSubid = InvalidSubTransactionId;
        relation->rd_newRelfilenodeSubid = InvalidSubTransactionId;
-       relation->rd_istemp = isTempNamespace(relation->rd_rel->relnamespace);
+       relation->rd_istemp = isTempOrToastNamespace(relation->rd_rel->relnamespace);
 
        /*
         * initialize the tuple descriptor (relation->rd_att).
@@ -929,7 +924,7 @@ RelationInitIndexAccessInfo(Relation relation)
        Datum           indoptionDatum;
        bool            isnull;
        oidvector  *indclass;
-       int2vector  *indoption;
+       int2vector *indoption;
        MemoryContext indexcxt;
        MemoryContext oldcontext;
        int                     natts;
@@ -1027,8 +1022,8 @@ RelationInitIndexAccessInfo(Relation relation)
 
        /*
         * indclass cannot be referenced directly through the C struct, because it
-        * comes after the variable-width indkey field.  Must extract the
-        * datum the hard way...
+        * comes after the variable-width indkey field.  Must extract the datum
+        * the hard way...
         */
        indclassDatum = fastgetattr(relation->rd_indextuple,
                                                                Anum_pg_index_indclass,
@@ -1038,9 +1033,9 @@ 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 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)
         */
        IndexSupportInitialize(indclass,
                                                   relation->rd_operator, relation->rd_support,
@@ -1130,9 +1125,13 @@ IndexSupportInitialize(oidvector *indclass,
  * 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 :-(
+ * Note there is no provision for flushing the cache.  This is OK at the
+ * moment because there is no way to ALTER any interesting properties of an
+ * existing opclass --- all you can do is drop it, which will result in
+ * a useless but harmless dead entry in the cache.  To support altering
+ * opclass membership (not the same as opfamily membership!), we'd need to
+ * be able to flush this cache as well as the contents of relcache entries
+ * for indexes.
  */
 static OpClassCacheEnt *
 LookupOpclassInfo(Oid operatorClassOid,
@@ -1167,34 +1166,50 @@ LookupOpclassInfo(Oid operatorClassOid,
                                                                                           (void *) &operatorClassOid,
                                                                                           HASH_ENTER, &found);
 
-       if (found && opcentry->valid)
+       if (!found)
+       {
+               /* 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,
+                                                                          numSupport * sizeof(RegProcedure));
+               else
+                       opcentry->supportProcs = NULL;
+       }
+       else
        {
-               /* 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 *)
-                       MemoryContextAllocZero(CacheMemoryContext,
-                                                                  numStrats * sizeof(Oid));
-       else
-               opcentry->operatorOids = NULL;
+       /*
+        * When testing for cache-flush hazards, we intentionally disable the
+        * operator class cache and force reloading of the info on each call.
+        * This is helpful because we want to test the case where a cache flush
+        * occurs while we are loading the info, and it's very hard to provoke
+        * that if this happens only once per opclass per backend.
+        */
+#if defined(CLOBBER_CACHE_ALWAYS)
+       opcentry->valid = false;
+#endif
 
-       if (numSupport > 0)
-               opcentry->supportProcs = (RegProcedure *)
-                       MemoryContextAllocZero(CacheMemoryContext,
-                                                                  numSupport * sizeof(RegProcedure));
-       else
-               opcentry->supportProcs = NULL;
+       if (opcentry->valid)
+               return opcentry;
 
        /*
+        * Need to fill in new entry.
+        *
         * To avoid infinite recursion during startup, force heap scans if we're
         * looking up info for the opclasses used by the indexes we would like to
         * reference here.
@@ -1503,7 +1518,7 @@ RelationIdGetRelation(Oid relationId)
                RelationIncrementReferenceCount(rd);
                /* revalidate nailed index if necessary */
                if (!rd->rd_isvalid)
-                       RelationReloadClassinfo(rd);
+                       RelationReloadIndexInfo(rd);
                return rd;
        }
 
@@ -1571,30 +1586,31 @@ RelationClose(Relation relation)
 
 #ifdef RELCACHE_FORCE_RELEASE
        if (RelationHasReferenceCountZero(relation) &&
-               relation->rd_createSubid == InvalidSubTransactionId)
+               relation->rd_createSubid == InvalidSubTransactionId &&
+               relation->rd_newRelfilenodeSubid == InvalidSubTransactionId)
                RelationClearRelation(relation, false);
 #endif
 }
 
 /*
- * RelationReloadClassinfo - reload the pg_class row (only)
+ * RelationReloadIndexInfo - reload minimal information for an open index
  *
- *     This function is used only for indexes.  We currently allow only the
- *     pg_class row of an existing index to change (to support changes of
- *     owner, tablespace, or relfilenode), not its pg_index row or other
- *     subsidiary index schema information.  Therefore it's sufficient to do
- *     this when we get an SI invalidation.  Furthermore, there are cases
- *     where it's necessary not to throw away the index information, especially
- *     for "nailed" indexes which we are unable to rebuild on-the-fly.
+ *     This function is used only for indexes.  A relcache inval on an index
+ *     can mean that its pg_class or pg_index row changed.  There are only
+ *     very limited changes that are allowed to an existing index's schema,
+ *     so we can update the relcache entry without a complete rebuild; which
+ *     is fortunate because we can't rebuild an index entry that is "nailed"
+ *     and/or in active use.  We support full replacement of the pg_class row,
+ *     as well as updates of a few simple fields of the pg_index row.
  *
- *     We can't necessarily reread the pg_class row right away; we might be
+ *     We can't necessarily reread the catalog rows right away; we might be
  *     in a failed transaction when we receive the SI notification.  If so,
  *     RelationClearRelation just marks the entry as invalid by setting
  *     rd_isvalid to false.  This routine is called to fix the entry when it
  *     is next needed.
  */
 static void
-RelationReloadClassinfo(Relation relation)
+RelationReloadIndexInfo(Relation relation)
 {
        bool            indexOK;
        HeapTuple       pg_class_tuple;
@@ -1633,6 +1649,37 @@ RelationReloadClassinfo(Relation relation)
        if (relation->rd_amcache)
                pfree(relation->rd_amcache);
        relation->rd_amcache = NULL;
+
+       /*
+        * For a non-system index, there are fields of the pg_index row that are
+        * allowed to change, so re-read that row and update the relcache entry.
+        * Most of the info derived from pg_index (such as support function lookup
+        * info) cannot change, and indeed the whole point of this routine is to
+        * update the relcache entry without clobbering that data; so wholesale
+        * replacement is not appropriate.
+        */
+       if (!IsSystemRelation(relation))
+       {
+               HeapTuple       tuple;
+               Form_pg_index index;
+
+               tuple = SearchSysCache(INDEXRELID,
+                                                          ObjectIdGetDatum(RelationGetRelid(relation)),
+                                                          0, 0, 0);
+               if (!HeapTupleIsValid(tuple))
+                       elog(ERROR, "cache lookup failed for index %u",
+                                RelationGetRelid(relation));
+               index = (Form_pg_index) GETSTRUCT(tuple);
+
+               relation->rd_index->indisvalid = index->indisvalid;
+               relation->rd_index->indcheckxmin = index->indcheckxmin;
+               relation->rd_index->indisready = index->indisready;
+               HeapTupleHeaderSetXmin(relation->rd_indextuple->t_data,
+                                                          HeapTupleHeaderGetXmin(tuple->t_data));
+
+               ReleaseSysCache(tuple);
+       }
+
        /* Okay, now it's valid again */
        relation->rd_isvalid = true;
 }
@@ -1681,7 +1728,7 @@ RelationClearRelation(Relation relation, bool rebuild)
                {
                        relation->rd_isvalid = false;           /* needs to be revalidated */
                        if (relation->rd_refcnt > 1)
-                               RelationReloadClassinfo(relation);
+                               RelationReloadIndexInfo(relation);
                }
                return;
        }
@@ -1691,14 +1738,14 @@ RelationClearRelation(Relation relation, bool rebuild)
         * have valid index support information.  This avoids problems with active
         * use of the index support information.  As with nailed indexes, we
         * re-read the pg_class row to handle possible physical relocation of the
-        * index.
+        * index, and we check for pg_index updates too.
         */
        if (relation->rd_rel->relkind == RELKIND_INDEX &&
                relation->rd_refcnt > 0 &&
                relation->rd_indexcxt != NULL)
        {
                relation->rd_isvalid = false;   /* needs to be revalidated */
-               RelationReloadClassinfo(relation);
+               RelationReloadIndexInfo(relation);
                return;
        }
 
@@ -1733,6 +1780,7 @@ RelationClearRelation(Relation relation, bool rebuild)
        if (relation->rd_options)
                pfree(relation->rd_options);
        list_free(relation->rd_indexlist);
+       bms_free(relation->rd_indexattr);
        if (relation->rd_indexcxt)
                MemoryContextDelete(relation->rd_indexcxt);
 
@@ -1758,11 +1806,12 @@ RelationClearRelation(Relation relation, bool rebuild)
        {
                /*
                 * When rebuilding an open relcache entry, must preserve ref count and
-                * rd_createSubid state.  Also attempt to preserve the tupledesc and
-                * rewrite-rule substructures in place.  (Note: the refcount mechanism
-                * for tupledescs may eventually ensure that we don't really need to
-                * preserve the tupledesc in-place, but for now there are still a lot
-                * of places that assume an open rel's tupledesc won't move.)
+                * rd_createSubid/rd_newRelfilenodeSubid state.  Also attempt to
+                * preserve the tupledesc and rewrite-rule substructures in place.
+                * (Note: the refcount mechanism for tupledescs may eventually ensure
+                * that we don't really need to preserve the tupledesc in-place, but
+                * for now there are still a lot of places that assume an open rel's
+                * tupledesc won't move.)
                 *
                 * Note that this process does not touch CurrentResourceOwner; which
                 * is good because whatever ref counts the entry may have do not
@@ -1772,6 +1821,7 @@ RelationClearRelation(Relation relation, bool rebuild)
                int                     old_refcnt = relation->rd_refcnt;
                SubTransactionId old_createSubid = relation->rd_createSubid;
                SubTransactionId old_newRelfilenodeSubid = relation->rd_newRelfilenodeSubid;
+               struct PgStat_TableStatus *old_pgstat_info = relation->pgstat_info;
                TupleDesc       old_att = relation->rd_att;
                RuleLock   *old_rules = relation->rd_rules;
                MemoryContext old_rulescxt = relation->rd_rulescxt;
@@ -1791,6 +1841,7 @@ RelationClearRelation(Relation relation, bool rebuild)
                relation->rd_refcnt = old_refcnt;
                relation->rd_createSubid = old_createSubid;
                relation->rd_newRelfilenodeSubid = old_newRelfilenodeSubid;
+               relation->pgstat_info = old_pgstat_info;
 
                if (equalTupleDescs(old_att, relation->rd_att))
                {
@@ -1838,7 +1889,7 @@ 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.
+                * optimization to have.  Ditto for the new-relfilenode status.
                 */
                rebuild = true;
        }
@@ -1915,9 +1966,8 @@ RelationCacheInvalidateEntry(Oid relationId)
  *      so we do not touch new-in-transaction relations; they cannot be targets
  *      of cross-backend SI updates (and our own updates now go through a
  *      separate linked list that isn't limited by the SI message buffer size).
- *      We don't do anything special for newRelfilenode-in-transaction relations, 
- *      though since we have a lock on the relation nobody else should be 
- *      generating cache invalidation messages for it anyhow.
+ *      Likewise, we need not discard new-relfilenode-in-transaction hints,
+ *      since any invalidation of those would be a local event.
  *
  *      We do this in two phases: the first pass deletes deletable items, and
  *      the second one rebuilds the rebuildable items.  This is essential for
@@ -2039,9 +2089,10 @@ AtEOXact_RelationCache(bool isCommit)
         * the debug-only Assert checks, most transactions don't create any work
         * for us to do here, so we keep a static flag that gets set if there is
         * anything to do.      (Currently, this means either a relation is created in
-        * the current xact, or an index list is forced.)  For simplicity, the
-        * flag remains set till end of top-level transaction, even though we
-        * could clear it at subtransaction end in some cases.
+        * the current xact, or one is given a new relfilenode, or an index list
+        * is forced.)  For simplicity, the flag remains set till end of top-level
+        * transaction, even though we could clear it at subtransaction end in
+        * some cases.
         */
        if (!need_eoxact_work
 #ifdef USE_ASSERT_CHECKING
@@ -2094,6 +2145,10 @@ AtEOXact_RelationCache(bool isCommit)
                                continue;
                        }
                }
+
+               /*
+                * Likewise, reset the hint about the relfilenode being new.
+                */
                relation->rd_newRelfilenodeSubid = InvalidSubTransactionId;
 
                /*
@@ -2156,12 +2211,17 @@ AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
                                continue;
                        }
                }
+
+               /*
+                * Likewise, update or drop any new-relfilenode-in-subtransaction
+                * hint.
+                */
                if (relation->rd_newRelfilenodeSubid == mySubid)
                {
                        if (isCommit)
                                relation->rd_newRelfilenodeSubid = parentSubid;
                        else
-                        relation->rd_newRelfilenodeSubid = InvalidSubTransactionId;
+                               relation->rd_newRelfilenodeSubid = InvalidSubTransactionId;
                }
 
                /*
@@ -2177,6 +2237,23 @@ 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
  *                     Build a relcache entry for an about-to-be-created relation,
@@ -2258,7 +2335,7 @@ RelationBuildLocalRelation(const char *relname,
        need_eoxact_work = true;
 
        /* is it a temporary relation? */
-       rel->rd_istemp = isTempNamespace(relnamespace);
+       rel->rd_istemp = isTempOrToastNamespace(relnamespace);
 
        /*
         * create a new tuple descriptor from the one passed in.  We do this
@@ -2465,7 +2542,10 @@ RelationCacheInitializePhase2(void)
 
 #define LOAD_CRIT_INDEX(indexoid) \
                do { \
-                       ird = RelationBuildDesc((indexoid), NULL); \
+                       ird = RelationBuildDesc(indexoid, NULL); \
+                       if (ird == NULL) \
+                               elog(PANIC, "could not open critical system index %u", \
+                                        indexoid); \
                        ird->rd_isnailed = true; \
                        ird->rd_refcnt = 1; \
                } while (0)
@@ -2682,8 +2762,7 @@ AttrDefaultFetch(Relation relation)
                                         RelationGetRelationName(relation));
                        else
                                attrdef[i].adbin = MemoryContextStrdup(CacheMemoryContext,
-                                                                DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                                                        val)));
+                                                                                                       TextDatumGetCString(val));
                        break;
                }
 
@@ -2746,8 +2825,7 @@ CheckConstraintFetch(Relation relation)
                                 RelationGetRelationName(relation));
 
                check[found].ccbin = MemoryContextStrdup(CacheMemoryContext,
-                                                                DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                                                        val)));
+                                                                                                TextDatumGetCString(val));
                found++;
        }
 
@@ -2912,6 +2990,7 @@ RelationSetIndexList(Relation relation, List *indexIds, Oid oidIndex)
        relation->rd_indexvalid = 2;    /* mark list as forced */
        /* must flag that we have a forced index list */
        need_eoxact_work = true;
+       /* we deliberately do not change rd_indexattr */
 }
 
 /*
@@ -2979,7 +3058,7 @@ RelationGetIndexExpressions(Relation relation)
                                                          GetPgIndexDescriptor(),
                                                          &isnull);
        Assert(!isnull);
-       exprsString = DatumGetCString(DirectFunctionCall1(textout, exprsDatum));
+       exprsString = TextDatumGetCString(exprsDatum);
        result = (List *) stringToNode(exprsString);
        pfree(exprsString);
 
@@ -2989,7 +3068,7 @@ RelationGetIndexExpressions(Relation relation)
         * them to similarly-processed qual clauses, and may fail to detect valid
         * matches without this.  We don't bother with canonicalize_qual, however.
         */
-       result = (List *) eval_const_expressions((Node *) result);
+       result = (List *) eval_const_expressions(NULL, (Node *) result);
 
        /*
         * Also mark any coercion format fields as "don't care", so that the
@@ -3046,7 +3125,7 @@ RelationGetIndexPredicate(Relation relation)
                                                         GetPgIndexDescriptor(),
                                                         &isnull);
        Assert(!isnull);
-       predString = DatumGetCString(DirectFunctionCall1(textout, predDatum));
+       predString = TextDatumGetCString(predDatum);
        result = (List *) stringToNode(predString);
        pfree(predString);
 
@@ -3059,7 +3138,7 @@ RelationGetIndexPredicate(Relation relation)
         * stuff involving subqueries, however, since we don't allow any in index
         * predicates.)
         */
-       result = (List *) eval_const_expressions((Node *) result);
+       result = (List *) eval_const_expressions(NULL, (Node *) result);
 
        result = (List *) canonicalize_qual((Expr *) result);
 
@@ -3083,6 +3162,91 @@ RelationGetIndexPredicate(Relation relation)
        return result;
 }
 
+/*
+ * RelationGetIndexAttrBitmap -- get a bitmap of index attribute numbers
+ *
+ * The result has a bit set for each attribute used anywhere in the index
+ * definitions of all the indexes on this relation.  (This includes not only
+ * simple index keys, but attributes used in expressions and partial-index
+ * predicates.)
+ *
+ * Attribute numbers are offset by FirstLowInvalidHeapAttributeNumber so that
+ * we can include system attributes (e.g., OID) in the bitmap representation.
+ *
+ * The returned result is palloc'd in the caller's memory context and should
+ * be bms_free'd when not needed anymore.
+ */
+Bitmapset *
+RelationGetIndexAttrBitmap(Relation relation)
+{
+       Bitmapset  *indexattrs;
+       List       *indexoidlist;
+       ListCell   *l;
+       MemoryContext oldcxt;
+
+       /* Quick exit if we already computed the result. */
+       if (relation->rd_indexattr != NULL)
+               return bms_copy(relation->rd_indexattr);
+
+       /* Fast path if definitely no indexes */
+       if (!RelationGetForm(relation)->relhasindex)
+               return NULL;
+
+       /*
+        * Get cached list of index OIDs
+        */
+       indexoidlist = RelationGetIndexList(relation);
+
+       /* Fall out if no indexes (but relhasindex was set) */
+       if (indexoidlist == NIL)
+               return NULL;
+
+       /*
+        * For each index, add referenced attributes to indexattrs.
+        */
+       indexattrs = NULL;
+       foreach(l, indexoidlist)
+       {
+               Oid                     indexOid = lfirst_oid(l);
+               Relation        indexDesc;
+               IndexInfo  *indexInfo;
+               int                     i;
+
+               indexDesc = index_open(indexOid, AccessShareLock);
+
+               /* Extract index key information from the index's pg_index row */
+               indexInfo = BuildIndexInfo(indexDesc);
+
+               /* Collect simple attribute references */
+               for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
+               {
+                       int                     attrnum = indexInfo->ii_KeyAttrNumbers[i];
+
+                       if (attrnum != 0)
+                               indexattrs = bms_add_member(indexattrs,
+                                                          attrnum - FirstLowInvalidHeapAttributeNumber);
+               }
+
+               /* Collect all attributes used in expressions, too */
+               pull_varattnos((Node *) indexInfo->ii_Expressions, &indexattrs);
+
+               /* Collect all attributes in the index predicate, too */
+               pull_varattnos((Node *) indexInfo->ii_Predicate, &indexattrs);
+
+               index_close(indexDesc, AccessShareLock);
+       }
+
+       list_free(indexoidlist);
+
+       /* Now save a copy of the bitmap in the relcache entry. */
+       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+       relation->rd_indexattr = bms_copy(indexattrs);
+       MemoryContextSwitchTo(oldcxt);
+
+       /* We return our original working copy for caller to play with */
+       return indexattrs;
+}
+
 
 /*
  *     load_relcache_init_file, write_relcache_init_file
@@ -3244,7 +3408,7 @@ load_relcache_init_file(void)
                        rel->rd_options = palloc(len);
                        if ((nread = fread(rel->rd_options, 1, len, fp)) != len)
                                goto read_failed;
-                       if (len != VARATT_SIZE(rel->rd_options))
+                       if (len != VARSIZE(rel->rd_options))
                                goto read_failed;               /* sanity check */
                }
                else
@@ -3408,6 +3572,7 @@ load_relcache_init_file(void)
                        rel->rd_refcnt = 0;
                rel->rd_indexvalid = 0;
                rel->rd_indexlist = NIL;
+               rel->rd_indexattr = NULL;
                rel->rd_oidindex = InvalidOid;
                rel->rd_createSubid = InvalidSubTransactionId;
                rel->rd_newRelfilenodeSubid = InvalidSubTransactionId;
@@ -3540,7 +3705,7 @@ write_relcache_init_file(void)
 
                /* next, do the access method specific field */
                write_item(rel->rd_options,
-                                  (rel->rd_options ? VARATT_SIZE(rel->rd_options) : 0),
+                                  (rel->rd_options ? VARSIZE(rel->rd_options) : 0),
                                   fp);
 
                /* If it's an index, there's more to do */