diff options
Diffstat (limited to 'src/backend/utils/cache/inval.c')
-rw-r--r-- | src/backend/utils/cache/inval.c | 612 |
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); +} + |