summaryrefslogtreecommitdiff
path: root/src/backend/utils/cache/inval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/cache/inval.c')
-rw-r--r--src/backend/utils/cache/inval.c612
1 files changed, 612 insertions, 0 deletions
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
new file mode 100644
index 00000000000..7bd214d085b
--- /dev/null
+++ b/src/backend/utils/cache/inval.c
@@ -0,0 +1,612 @@
+/*-------------------------------------------------------------------------
+ *
+ * inval.c--
+ * POSTGRES cache invalidation dispatcher code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $
+ *
+ * Note - this code is real crufty...
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "access/heapam.h" /* XXX to support hacks below */
+#include "access/htup.h"
+#include "catalog/catalog.h"
+#include "storage/bufpage.h"
+#include "storage/buf.h" /* XXX for InvalidBuffer */
+#include "storage/ipc.h"
+#include "storage/sinval.h"
+#include "utils/catcache.h"
+#include "utils/inval.h"
+#include "utils/elog.h"
+#include "utils/rel.h"
+#include "utils/relcache.h"
+#include "catalog/catname.h" /* XXX to support hacks below */
+#include "utils/syscache.h" /* XXX to support the hacks below */
+
+/* ----------------
+ * private invalidation structures
+ * ----------------
+ */
+typedef struct CatalogInvalidationData {
+ Index cacheId;
+ Index hashIndex;
+ ItemPointerData pointerData;
+} CatalogInvalidationData;
+
+typedef struct RelationInvalidationData {
+ Oid relationId;
+ Oid objectId;
+} RelationInvalidationData;
+
+typedef union AnyInvalidation {
+ CatalogInvalidationData catalog;
+ RelationInvalidationData relation;
+} AnyInvalidation;
+
+typedef struct InvalidationMessageData {
+ char kind;
+ AnyInvalidation any;
+} InvalidationMessageData;
+
+typedef InvalidationMessageData *InvalidationMessage;
+
+/* ----------------
+ * variables and macros
+ * ----------------
+ */
+static LocalInvalid Invalid = EmptyLocalInvalid; /* XXX global */
+static bool RefreshWhenInvalidate = false;
+
+Oid MyRelationRelationId = InvalidOid;
+Oid MyAttributeRelationId = InvalidOid;
+Oid MyAMRelationId = InvalidOid;
+Oid MyAMOPRelationId = InvalidOid;
+
+#define ValidateHacks() \
+ if (!OidIsValid(MyRelationRelationId)) getmyrelids()
+
+/* ----------------------------------------------------------------
+ * "local" invalidation support functions
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ * InvalidationEntryAllocate--
+ * Allocates an invalidation entry.
+ * --------------------------------
+ */
+InvalidationEntry
+InvalidationEntryAllocate(uint16 size)
+{
+ InvalidationEntryData *entryDataP;
+ entryDataP = (InvalidationEntryData *)
+ malloc(sizeof (char *) + size); /* XXX alignment */
+ entryDataP->nextP = NULL;
+ return ((Pointer) &entryDataP->userData);
+}
+
+/* --------------------------------
+ * LocalInvalidRegister --
+ * Returns a new local cache invalidation state containing a new entry.
+ * --------------------------------
+ */
+LocalInvalid
+LocalInvalidRegister(LocalInvalid invalid,
+ InvalidationEntry entry)
+{
+ Assert(PointerIsValid(entry));
+
+ ((InvalidationUserData *)entry)->dataP[-1] =
+ (InvalidationUserData *)invalid;
+
+ return (entry);
+}
+
+/* --------------------------------
+ * LocalInvalidInvalidate--
+ * Processes, then frees all entries in a local cache
+ * invalidation state.
+ * --------------------------------
+ */
+void
+LocalInvalidInvalidate(LocalInvalid invalid, void (*function)())
+{
+ InvalidationEntryData *entryDataP;
+
+ while (PointerIsValid(invalid)) {
+ entryDataP = (InvalidationEntryData *)
+ &((InvalidationUserData *)invalid)->dataP[-1];
+
+ if (PointerIsValid(function)) {
+ (*function)((Pointer) &entryDataP->userData);
+ }
+
+ invalid = (Pointer) entryDataP->nextP;
+
+ /* help catch errors */
+ entryDataP->nextP = (InvalidationUserData *) NULL;
+
+ free((Pointer)entryDataP);
+ }
+}
+
+/* ----------------------------------------------------------------
+ * private support functions
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ * CacheIdRegisterLocalInvalid
+ * --------------------------------
+ */
+#ifdef INVALIDDEBUG
+#define CacheIdRegisterLocalInvalid_DEBUG1 \
+elog(DEBUG, "CacheIdRegisterLocalInvalid(%d, %d, [%d, %d])", \
+ cacheId, hashIndex, ItemPointerGetBlockNumber(pointer), \
+ ItemPointerGetOffsetNumber(pointer))
+#else
+#define CacheIdRegisterLocalInvalid_DEBUG1
+#endif /* INVALIDDEBUG */
+
+static void
+CacheIdRegisterLocalInvalid(Index cacheId,
+ Index hashIndex,
+ ItemPointer pointer)
+{
+ InvalidationMessage message;
+
+ /* ----------------
+ * debugging stuff
+ * ----------------
+ */
+ CacheIdRegisterLocalInvalid_DEBUG1;
+
+ /* ----------------
+ * create a message describing the system catalog tuple
+ * we wish to invalidate.
+ * ----------------
+ */
+ message = (InvalidationMessage)
+ InvalidationEntryAllocate(sizeof (InvalidationMessageData));
+
+ message->kind = 'c';
+ message->any.catalog.cacheId = cacheId;
+ message->any.catalog.hashIndex = hashIndex;
+
+ ItemPointerCopy(pointer, &message->any.catalog.pointerData);
+
+ /* ----------------
+ * Note: Invalid is a global variable
+ * ----------------
+ */
+ Invalid = LocalInvalidRegister(Invalid, (InvalidationEntry)message);
+}
+
+/* --------------------------------
+ * RelationIdRegisterLocalInvalid
+ * --------------------------------
+ */
+static void
+RelationIdRegisterLocalInvalid(Oid relationId, Oid objectId)
+{
+ InvalidationMessage message;
+
+ /* ----------------
+ * debugging stuff
+ * ----------------
+ */
+#ifdef INVALIDDEBUG
+ elog(DEBUG, "RelationRegisterLocalInvalid(%d, %d)", relationId,
+ objectId);
+#endif /* defined(INVALIDDEBUG) */
+
+ /* ----------------
+ * create a message describing the relation descriptor
+ * we wish to invalidate.
+ * ----------------
+ */
+ message = (InvalidationMessage)
+ InvalidationEntryAllocate(sizeof (InvalidationMessageData));
+
+ message->kind = 'r';
+ message->any.relation.relationId = relationId;
+ message->any.relation.objectId = objectId;
+
+ /* ----------------
+ * Note: Invalid is a global variable
+ * ----------------
+ */
+ Invalid = LocalInvalidRegister(Invalid, (InvalidationEntry)message);
+}
+
+/* --------------------------------
+ * getmyrelids
+ * --------------------------------
+ */
+void
+getmyrelids()
+{
+ HeapTuple tuple;
+
+ tuple = SearchSysCacheTuple(RELNAME,
+ PointerGetDatum(RelationRelationName),
+ 0,0,0);
+ Assert(HeapTupleIsValid(tuple));
+ MyRelationRelationId = tuple->t_oid;
+
+ tuple = SearchSysCacheTuple(RELNAME,
+ PointerGetDatum(AttributeRelationName),
+ 0,0,0);
+ Assert(HeapTupleIsValid(tuple));
+ MyAttributeRelationId = tuple->t_oid;
+
+ tuple = SearchSysCacheTuple(RELNAME,
+ PointerGetDatum(AccessMethodRelationName),
+ 0,0,0);
+ Assert(HeapTupleIsValid(tuple));
+ MyAMRelationId = tuple->t_oid;
+
+ tuple = SearchSysCacheTuple(RELNAME,
+ PointerGetDatum(AccessMethodOperatorRelationName),
+ 0,0,0);
+ Assert(HeapTupleIsValid(tuple));
+ MyAMOPRelationId = tuple->t_oid;
+}
+
+/* --------------------------------
+ * CacheIdInvalidate
+ *
+ * This routine can invalidate an tuple in a system catalog cache
+ * or a cached relation descriptor. You pay your money and you
+ * take your chances...
+ * --------------------------------
+ */
+#ifdef INVALIDDEBUG
+#define CacheIdInvalidate_DEBUG1 \
+elog(DEBUG, "CacheIdInvalidate(%d, %d, 0x%x[%d])", cacheId, hashIndex,\
+ pointer, ItemPointerIsValid(pointer))
+#else
+#define CacheIdInvalidate_DEBUG1
+#endif /* defined(INVALIDDEBUG) */
+
+static void
+CacheIdInvalidate(Index cacheId,
+ Index hashIndex,
+ ItemPointer pointer)
+{
+ /* ----------------
+ * assume that if the item pointer is valid, then we are
+ * invalidating an item in the specified system catalog cache.
+ * ----------------
+ */
+ if (ItemPointerIsValid(pointer)) {
+ CatalogCacheIdInvalidate(cacheId, hashIndex, pointer);
+ return;
+ }
+
+ CacheIdInvalidate_DEBUG1;
+
+ ValidateHacks(); /* XXX */
+
+ /* ----------------
+ * if the cacheId is the oid of any of the tuples in the
+ * following system relations, then assume we are invalidating
+ * a relation descriptor
+ * ----------------
+ */
+ if (cacheId == MyRelationRelationId) {
+ RelationIdInvalidateRelationCacheByRelationId(hashIndex);
+ return;
+ }
+
+ if (cacheId == MyAttributeRelationId) {
+ RelationIdInvalidateRelationCacheByRelationId(hashIndex);
+ return;
+ }
+
+ if (cacheId == MyAMRelationId) {
+ RelationIdInvalidateRelationCacheByAccessMethodId(hashIndex);
+ return;
+ }
+
+ if (cacheId == MyAMOPRelationId) {
+ RelationIdInvalidateRelationCacheByAccessMethodId(InvalidOid);
+ return;
+ }
+
+ /* ----------------
+ * Yow! the caller asked us to invalidate something else.
+ * ----------------
+ */
+ elog(FATAL, "CacheIdInvalidate: cacheId=%d relation id?", cacheId);
+}
+
+/* --------------------------------
+ * ResetSystemCaches
+ *
+ * this blows away all tuples in the system catalog caches and
+ * all the cached relation descriptors (and closes the files too).
+ * --------------------------------
+ */
+static void
+ResetSystemCaches()
+{
+ ResetSystemCache();
+ RelationCacheInvalidate(false);
+}
+
+/* --------------------------------
+ * InvalidationMessageRegisterSharedInvalid
+ * --------------------------------
+ */
+#ifdef INVALIDDEBUG
+#define InvalidationMessageRegisterSharedInvalid_DEBUG1 \
+elog(DEBUG,\
+ "InvalidationMessageRegisterSharedInvalid(c, %d, %d, [%d, %d])",\
+ message->any.catalog.cacheId,\
+ message->any.catalog.hashIndex,\
+ ItemPointerGetBlockNumber(&message->any.catalog.pointerData),\
+ ItemPointerGetOffsetNumber(&message->any.catalog.pointerData))
+#define InvalidationMessageRegisterSharedInvalid_DEBUG2 \
+ elog(DEBUG, \
+ "InvalidationMessageRegisterSharedInvalid(r, %d, %d)", \
+ message->any.relation.relationId, \
+ message->any.relation.objectId)
+#else
+#define InvalidationMessageRegisterSharedInvalid_DEBUG1
+#define InvalidationMessageRegisterSharedInvalid_DEBUG2
+#endif /* INVALIDDEBUG */
+
+static void
+InvalidationMessageRegisterSharedInvalid(InvalidationMessage message)
+{
+ Assert(PointerIsValid(message));
+
+ switch (message->kind) {
+ case 'c': /* cached system catalog tuple */
+ InvalidationMessageRegisterSharedInvalid_DEBUG1;
+
+ RegisterSharedInvalid(message->any.catalog.cacheId,
+ message->any.catalog.hashIndex,
+ &message->any.catalog.pointerData);
+ break;
+
+ case 'r': /* cached relation descriptor */
+ InvalidationMessageRegisterSharedInvalid_DEBUG2;
+
+ RegisterSharedInvalid(message->any.relation.relationId,
+ message->any.relation.objectId,
+ (ItemPointer) NULL);
+ break;
+
+ default:
+ elog(FATAL,
+ "InvalidationMessageRegisterSharedInvalid: `%c' kind",
+ message->kind);
+ }
+}
+
+/* --------------------------------
+ * InvalidationMessageCacheInvalidate
+ * --------------------------------
+ */
+#ifdef INVALIDDEBUG
+#define InvalidationMessageCacheInvalidate_DEBUG1 \
+elog(DEBUG, "InvalidationMessageCacheInvalidate(c, %d, %d, [%d, %d])",\
+ message->any.catalog.cacheId,\
+ message->any.catalog.hashIndex,\
+ ItemPointerGetBlockNumber(&message->any.catalog.pointerData),\
+ ItemPointerGetOffsetNumber(&message->any.catalog.pointerData))
+#define InvalidationMessageCacheInvalidate_DEBUG2 \
+ elog(DEBUG, "InvalidationMessageCacheInvalidate(r, %d, %d)", \
+ message->any.relation.relationId, \
+ message->any.relation.objectId)
+#else
+#define InvalidationMessageCacheInvalidate_DEBUG1
+#define InvalidationMessageCacheInvalidate_DEBUG2
+#endif /* defined(INVALIDDEBUG) */
+
+static void
+InvalidationMessageCacheInvalidate(InvalidationMessage message)
+{
+ Assert(PointerIsValid(message));
+
+ switch (message->kind) {
+ case 'c': /* cached system catalog tuple */
+ InvalidationMessageCacheInvalidate_DEBUG1;
+
+ CatalogCacheIdInvalidate(message->any.catalog.cacheId,
+ message->any.catalog.hashIndex,
+ &message->any.catalog.pointerData);
+ break;
+
+ case 'r': /* cached relation descriptor */
+ InvalidationMessageCacheInvalidate_DEBUG2;
+
+ /* XXX ignore this--is this correct ??? */
+ break;
+
+ default:
+ elog(FATAL, "InvalidationMessageCacheInvalidate: `%c' kind",
+ message->kind);
+ }
+}
+
+/* --------------------------------
+ * RelationInvalidateRelationCache
+ * --------------------------------
+ */
+static void
+RelationInvalidateRelationCache(Relation relation,
+ HeapTuple tuple,
+ void (*function)())
+{
+ Oid relationId;
+ Oid objectId;
+
+ /* ----------------
+ * get the relation object id
+ * ----------------
+ */
+ ValidateHacks(); /* XXX */
+ relationId = RelationGetRelationId(relation);
+
+ /* ----------------
+ *
+ * ----------------
+ */
+ if (relationId == MyRelationRelationId) {
+ objectId = tuple->t_oid;
+ } else if (relationId == MyAttributeRelationId) {
+ objectId = ((AttributeTupleForm)GETSTRUCT(tuple))->attrelid;
+ } else if (relationId == MyAMRelationId) {
+ objectId = tuple->t_oid;
+ } else if (relationId == MyAMOPRelationId) {
+ ; /* objectId is unused */
+ } else
+ return;
+
+ /* ----------------
+ * can't handle immediate relation descriptor invalidation
+ * ----------------
+ */
+ Assert(PointerIsValid(function));
+
+ (*function)(relationId, objectId);
+}
+
+/*
+ * DiscardInvalid --
+ * Causes the invalidated cache state to be discarded.
+ *
+ * Note:
+ * This should be called as the first step in processing a transaction.
+ * This should be called while waiting for a query from the front end
+ * when other backends are active.
+ */
+void
+DiscardInvalid()
+{
+ /* ----------------
+ * debugging stuff
+ * ----------------
+ */
+#ifdef INVALIDDEBUG
+ elog(DEBUG, "DiscardInvalid called");
+#endif /* defined(INVALIDDEBUG) */
+
+ InvalidateSharedInvalid(CacheIdInvalidate, ResetSystemCaches);
+}
+
+/*
+ * RegisterInvalid --
+ * Causes registration of invalidated state with other backends iff true.
+ *
+ * Note:
+ * This should be called as the last step in processing a transaction.
+ */
+void
+RegisterInvalid(bool send)
+{
+ /* ----------------
+ * debugging stuff
+ * ----------------
+ */
+#ifdef INVALIDDEBUG
+ elog(DEBUG, "RegisterInvalid(%d) called", send);
+#endif /* defined(INVALIDDEBUG) */
+
+ /* ----------------
+ * Note: Invalid is a global variable
+ * ----------------
+ */
+ if (send)
+ LocalInvalidInvalidate(Invalid,
+ InvalidationMessageRegisterSharedInvalid);
+ else
+ LocalInvalidInvalidate(Invalid,
+ InvalidationMessageCacheInvalidate);
+
+ Invalid = EmptyLocalInvalid;
+}
+
+/*
+ * SetRefreshWhenInvalidate --
+ * Causes the local caches to be immediately refreshed iff true.
+ */
+void
+SetRefreshWhenInvalidate(bool on)
+{
+#ifdef INVALIDDEBUG
+ elog(DEBUG, "RefreshWhenInvalidate(%d) called", on);
+#endif /* defined(INVALIDDEBUG) */
+
+ RefreshWhenInvalidate = on;
+}
+
+/*
+ * RelationIdInvalidateHeapTuple --
+ * Causes the given tuple in a relation to be invalidated.
+ *
+ * Note:
+ * Assumes object id is valid.
+ * Assumes tuple is valid.
+ */
+#ifdef INVALIDDEBUG
+#define RelationInvalidateHeapTuple_DEBUG1 \
+elog(DEBUG, "RelationInvalidateHeapTuple(%.16s, [%d,%d])", \
+ RelationGetRelationName(relation), \
+ ItemPointerGetBlockNumber(&tuple->t_ctid), \
+ ItemPointerGetOffsetNumber(&tuple->t_ctid))
+#else
+#define RelationInvalidateHeapTuple_DEBUG1
+#endif /* defined(INVALIDDEBUG) */
+
+void
+RelationInvalidateHeapTuple(Relation relation, HeapTuple tuple)
+{
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ Assert(RelationIsValid(relation));
+ Assert(HeapTupleIsValid(tuple));
+
+ if (IsBootstrapProcessingMode())
+ return;
+ /* ----------------
+ * this only works for system relations now
+ * ----------------
+ */
+ if (! IsSystemRelationName(RelationGetRelationTupleForm(relation)->relname.data))
+ return;
+
+ /* ----------------
+ * debugging stuff
+ * ----------------
+ */
+ RelationInvalidateHeapTuple_DEBUG1;
+
+ /* ----------------
+ *
+ * ----------------
+ */
+ RelationInvalidateCatalogCacheTuple(relation,
+ tuple,
+ CacheIdRegisterLocalInvalid);
+
+ RelationInvalidateRelationCache(relation,
+ tuple,
+ RelationIdRegisterLocalInvalid);
+
+ if (RefreshWhenInvalidate)
+ RelationInvalidateCatalogCacheTuple(relation,
+ tuple,
+ (void (*)()) NULL);
+}
+