* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
#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"
#include "utils/relcache.h"
#include "utils/resowner.h"
#include "utils/syscache.h"
+#include "utils/tqual.h"
#include "utils/typcache.h"
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);
Form_pg_rewrite rewrite_form = (Form_pg_rewrite) GETSTRUCT(rewrite_tuple);
bool isnull;
Datum rule_datum;
- text *rule_text;
char *rule_str;
RewriteRule *rule;
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;
/*
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
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).
Datum indoptionDatum;
bool isnull;
oidvector *indclass;
- int2vector *indoption;
+ int2vector *indoption;
MemoryContext indexcxt;
MemoryContext oldcontext;
int natts;
/*
* 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,
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,
* 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,
(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.
RelationIncrementReferenceCount(rd);
/* revalidate nailed index if necessary */
if (!rd->rd_isvalid)
- RelationReloadClassinfo(rd);
+ RelationReloadIndexInfo(rd);
return rd;
}
#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;
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;
}
{
relation->rd_isvalid = false; /* needs to be revalidated */
if (relation->rd_refcnt > 1)
- RelationReloadClassinfo(relation);
+ RelationReloadIndexInfo(relation);
}
return;
}
* 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;
}
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);
{
/*
* 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
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;
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))
{
/*
* 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;
}
* 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
* 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
continue;
}
}
+
+ /*
+ * Likewise, reset the hint about the relfilenode being new.
+ */
relation->rd_newRelfilenodeSubid = InvalidSubTransactionId;
/*
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;
}
/*
}
}
+/*
+ * 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,
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
#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)
RelationGetRelationName(relation));
else
attrdef[i].adbin = MemoryContextStrdup(CacheMemoryContext,
- DatumGetCString(DirectFunctionCall1(textout,
- val)));
+ TextDatumGetCString(val));
break;
}
RelationGetRelationName(relation));
check[found].ccbin = MemoryContextStrdup(CacheMemoryContext,
- DatumGetCString(DirectFunctionCall1(textout,
- val)));
+ TextDatumGetCString(val));
found++;
}
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 */
}
/*
GetPgIndexDescriptor(),
&isnull);
Assert(!isnull);
- exprsString = DatumGetCString(DirectFunctionCall1(textout, exprsDatum));
+ exprsString = TextDatumGetCString(exprsDatum);
result = (List *) stringToNode(exprsString);
pfree(exprsString);
* 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
GetPgIndexDescriptor(),
&isnull);
Assert(!isnull);
- predString = DatumGetCString(DirectFunctionCall1(textout, predDatum));
+ predString = TextDatumGetCString(predDatum);
result = (List *) stringToNode(predString);
pfree(predString);
* 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);
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
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
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;
/* 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 */