943 lines
24 KiB
C
943 lines
24 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* inval.c
|
|
* POSTGRES cache invalidation dispatcher code.
|
|
*
|
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.36 2000/05/30 00:49:54 momjian Exp $
|
|
*
|
|
* Note - this code is real crufty...
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "catalog/catalog.h"
|
|
#include "miscadmin.h"
|
|
#include "storage/sinval.h"
|
|
#include "utils/catcache.h"
|
|
#include "utils/inval.h"
|
|
#include "utils/relcache.h"
|
|
|
|
|
|
/* ----------------
|
|
* private invalidation structures
|
|
* ----------------
|
|
*/
|
|
|
|
typedef struct InvalidationUserData
|
|
{
|
|
struct InvalidationUserData *dataP[1]; /* VARIABLE LENGTH */
|
|
} InvalidationUserData; /* VARIABLE LENGTH STRUCTURE */
|
|
|
|
typedef struct InvalidationEntryData
|
|
{
|
|
InvalidationUserData *nextP;
|
|
InvalidationUserData userData; /* VARIABLE LENGTH ARRAY */
|
|
} InvalidationEntryData; /* VARIABLE LENGTH STRUCTURE */
|
|
|
|
typedef Pointer InvalidationEntry;
|
|
|
|
typedef InvalidationEntry LocalInvalid;
|
|
|
|
#define EmptyLocalInvalid NULL
|
|
|
|
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
|
|
* ----------------
|
|
*/
|
|
|
|
/*
|
|
* ----------------
|
|
* Invalidation info was devided into three parts.
|
|
* 1) shared invalidation to be registerd for all backends
|
|
* 2) local invalidation for the transaction itself
|
|
* 3) rollback information for the transaction itself
|
|
* ----------------
|
|
*/
|
|
|
|
/*
|
|
* head of invalidation linked list for all backends
|
|
* eaten by AtCommit_Cache() in CommitTransaction()
|
|
*/
|
|
static LocalInvalid InvalidForall = EmptyLocalInvalid;
|
|
|
|
/*
|
|
* head of invalidation linked list for the backend itself
|
|
* eaten by AtCommit_LocalCache() in CommandCounterIncrement()
|
|
*/
|
|
static LocalInvalid InvalidLocal = EmptyLocalInvalid;
|
|
|
|
/*
|
|
* head of rollback linked list for the backend itself
|
|
* eaten by AtAbort_Cache() in AbortTransaction()
|
|
*/
|
|
static LocalInvalid RollbackStack = EmptyLocalInvalid;
|
|
|
|
|
|
static InvalidationEntry InvalidationEntryAllocate(uint16 size);
|
|
static void LocalInvalidInvalidate(LocalInvalid invalid, void (*function) (), bool freemember);
|
|
static LocalInvalid LocalInvalidRegister(LocalInvalid invalid,
|
|
InvalidationEntry entry);
|
|
static void DiscardInvalidStack(LocalInvalid *invalid);
|
|
static void InvalidationMessageRegisterSharedInvalid(InvalidationMessage message);
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
* "local" invalidation support functions
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
/* --------------------------------
|
|
* InvalidationEntryAllocate
|
|
* Allocates an invalidation entry.
|
|
* --------------------------------
|
|
*/
|
|
static InvalidationEntry
|
|
InvalidationEntryAllocate(uint16 size)
|
|
{
|
|
InvalidationEntryData *entryDataP;
|
|
|
|
entryDataP = (InvalidationEntryData *)
|
|
malloc(sizeof(char *) + size); /* XXX alignment */
|
|
entryDataP->nextP = NULL;
|
|
return (Pointer) &entryDataP->userData;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* LocalInvalidRegister
|
|
* Link an invalidation entry into a chain of them. Really ugly
|
|
* coding here.
|
|
* --------------------------------
|
|
*/
|
|
static 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 list unless freemember parameter is false.
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
LocalInvalidInvalidate(LocalInvalid invalid, void (*function) (), bool freemember)
|
|
{
|
|
InvalidationEntryData *entryDataP;
|
|
|
|
while (PointerIsValid(invalid))
|
|
{
|
|
entryDataP = (InvalidationEntryData *)
|
|
&((InvalidationUserData *) invalid)->dataP[-1];
|
|
|
|
if (PointerIsValid(function))
|
|
(*function) ((Pointer) &entryDataP->userData);
|
|
|
|
invalid = (Pointer) entryDataP->nextP;
|
|
|
|
if (!freemember)
|
|
continue;
|
|
/* help catch errors */
|
|
entryDataP->nextP = (InvalidationUserData *) NULL;
|
|
|
|
free((Pointer) entryDataP);
|
|
}
|
|
}
|
|
|
|
static void
|
|
DiscardInvalidStack(LocalInvalid *invalid)
|
|
{
|
|
LocalInvalid locinv;
|
|
|
|
locinv = *invalid;
|
|
*invalid = EmptyLocalInvalid;
|
|
if (locinv)
|
|
LocalInvalidInvalidate(locinv, (void (*) ()) NULL, true);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* private support functions
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
/* --------------------------------
|
|
* CacheIdRegister.......
|
|
* RelationIdRegister....
|
|
* --------------------------------
|
|
*/
|
|
#ifdef INVALIDDEBUG
|
|
#define CacheIdRegisterSpecifiedLocalInvalid_DEBUG1 \
|
|
elog(DEBUG, "CacheIdRegisterSpecifiedLocalInvalid(%d, %d, [%d, %d])", \
|
|
cacheId, hashIndex, ItemPointerGetBlockNumber(pointer), \
|
|
ItemPointerGetOffsetNumber(pointer))
|
|
#define CacheIdRegisterLocalInvalid_DEBUG1 \
|
|
elog(DEBUG, "CacheIdRegisterLocalInvalid(%d, %d, [%d, %d])", \
|
|
cacheId, hashIndex, ItemPointerGetBlockNumber(pointer), \
|
|
ItemPointerGetOffsetNumber(pointer))
|
|
#define CacheIdRegisterLocalRollback_DEBUG1 \
|
|
elog(DEBUG, "CacheIdRegisterLocalRollback(%d, %d, [%d, %d])", \
|
|
cacheId, hashIndex, ItemPointerGetBlockNumber(pointer), \
|
|
ItemPointerGetOffsetNumber(pointer))
|
|
#define CacheIdImmediateRegisterSharedInvalid_DEBUG1 \
|
|
elog(DEBUG, "CacheIdImmediateRegisterSharedInvalid(%d, %d, [%d, %d])", \
|
|
cacheId, hashIndex, ItemPointerGetBlockNumber(pointer), \
|
|
ItemPointerGetOffsetNumber(pointer))
|
|
#else
|
|
#define CacheIdRegisterSpecifiedLocalInvalid_DEBUG1
|
|
#define CacheIdRegisterLocalInvalid_DEBUG1
|
|
#define CacheIdRegisterLocalRollback_DEBUG1
|
|
#define CacheIdImmediateRegisterSharedInvalid_DEBUG1
|
|
#endif /* INVALIDDEBUG */
|
|
|
|
/* --------------------------------
|
|
* CacheIdRegisterSpecifiedLocalInvalid
|
|
* --------------------------------
|
|
*/
|
|
static LocalInvalid
|
|
CacheIdRegisterSpecifiedLocalInvalid(LocalInvalid invalid,
|
|
Index cacheId, Index hashIndex, ItemPointer pointer)
|
|
{
|
|
InvalidationMessage message;
|
|
|
|
/* ----------------
|
|
* debugging stuff
|
|
* ----------------
|
|
*/
|
|
CacheIdRegisterSpecifiedLocalInvalid_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);
|
|
|
|
/* ----------------
|
|
* Add message to linked list of unprocessed messages.
|
|
* ----------------
|
|
*/
|
|
invalid = LocalInvalidRegister(invalid, (InvalidationEntry) message);
|
|
return invalid;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* CacheIdRegisterLocalInvalid
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
CacheIdRegisterLocalInvalid(Index cacheId,
|
|
Index hashIndex,
|
|
ItemPointer pointer)
|
|
{
|
|
/* ----------------
|
|
* debugging stuff
|
|
* ----------------
|
|
*/
|
|
CacheIdRegisterLocalInvalid_DEBUG1;
|
|
|
|
/* ----------------
|
|
* Add message to InvalidForall linked list.
|
|
* ----------------
|
|
*/
|
|
InvalidForall = CacheIdRegisterSpecifiedLocalInvalid(InvalidForall,
|
|
cacheId, hashIndex, pointer);
|
|
/* ----------------
|
|
* Add message to InvalidLocal linked list.
|
|
* ----------------
|
|
*/
|
|
InvalidLocal = CacheIdRegisterSpecifiedLocalInvalid(InvalidLocal,
|
|
cacheId, hashIndex, pointer);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* CacheIdRegisterLocalRollback
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
CacheIdRegisterLocalRollback(Index cacheId, Index hashIndex,
|
|
ItemPointer pointer)
|
|
{
|
|
|
|
/* ----------------
|
|
* debugging stuff
|
|
* ----------------
|
|
*/
|
|
CacheIdRegisterLocalRollback_DEBUG1;
|
|
|
|
/* ----------------
|
|
* Add message to RollbackStack linked list.
|
|
* ----------------
|
|
*/
|
|
RollbackStack = CacheIdRegisterSpecifiedLocalInvalid(
|
|
RollbackStack, cacheId, hashIndex, pointer);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* CacheIdImmediateRegisterSharedInvalid
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
CacheIdImmediateRegisterSharedInvalid(Index cacheId, Index hashIndex,
|
|
ItemPointer pointer)
|
|
{
|
|
InvalidationMessage message;
|
|
|
|
/* ----------------
|
|
* debugging stuff
|
|
* ----------------
|
|
*/
|
|
CacheIdImmediateRegisterSharedInvalid_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);
|
|
/* ----------------
|
|
* Register a shared catalog cache invalidation.
|
|
* ----------------
|
|
*/
|
|
InvalidationMessageRegisterSharedInvalid(message);
|
|
free((Pointer) &((InvalidationUserData *) message)->dataP[-1]);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationIdRegisterSpecifiedLocalInvalid
|
|
* --------------------------------
|
|
*/
|
|
static LocalInvalid
|
|
RelationIdRegisterSpecifiedLocalInvalid(LocalInvalid invalid,
|
|
Oid relationId, Oid objectId)
|
|
{
|
|
InvalidationMessage message;
|
|
|
|
/* ----------------
|
|
* debugging stuff
|
|
* ----------------
|
|
*/
|
|
#ifdef INVALIDDEBUG
|
|
elog(DEBUG, "RelationRegisterSpecifiedLocalInvalid(%u, %u)", 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;
|
|
|
|
/* ----------------
|
|
* Add message to linked list of unprocessed messages.
|
|
* ----------------
|
|
*/
|
|
invalid = LocalInvalidRegister(invalid, (InvalidationEntry) message);
|
|
return invalid;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationIdRegisterLocalInvalid
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
RelationIdRegisterLocalInvalid(Oid relationId, Oid objectId)
|
|
{
|
|
/* ----------------
|
|
* debugging stuff
|
|
* ----------------
|
|
*/
|
|
#ifdef INVALIDDEBUG
|
|
elog(DEBUG, "RelationRegisterLocalInvalid(%u, %u)", relationId,
|
|
objectId);
|
|
#endif /* defined(INVALIDDEBUG) */
|
|
|
|
/* ----------------
|
|
* Add message to InvalidForall linked list.
|
|
* ----------------
|
|
*/
|
|
InvalidForall = RelationIdRegisterSpecifiedLocalInvalid(InvalidForall,
|
|
relationId, objectId);
|
|
/* ----------------
|
|
* Add message to InvalidLocal linked list.
|
|
* ----------------
|
|
*/
|
|
InvalidLocal = RelationIdRegisterSpecifiedLocalInvalid(InvalidLocal,
|
|
relationId, objectId);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationIdRegisterLocalRollback
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
RelationIdRegisterLocalRollback(Oid relationId, Oid objectId)
|
|
{
|
|
|
|
/* ----------------
|
|
* debugging stuff
|
|
* ----------------
|
|
*/
|
|
#ifdef INVALIDDEBUG
|
|
elog(DEBUG, "RelationRegisterLocalRollback(%u, %u)", relationId,
|
|
objectId);
|
|
#endif /* defined(INVALIDDEBUG) */
|
|
|
|
/* ----------------
|
|
* Add message to RollbackStack linked list.
|
|
* ----------------
|
|
*/
|
|
RollbackStack = RelationIdRegisterSpecifiedLocalInvalid(
|
|
RollbackStack, relationId, objectId);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationIdImmediateRegisterSharedInvalid
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
RelationIdImmediateRegisterSharedInvalid(Oid relationId, Oid objectId)
|
|
{
|
|
InvalidationMessage message;
|
|
|
|
/* ----------------
|
|
* debugging stuff
|
|
* ----------------
|
|
*/
|
|
#ifdef INVALIDDEBUG
|
|
elog(DEBUG, "RelationImmediateRegisterSharedInvalid(%u, %u)", 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;
|
|
|
|
/* ----------------
|
|
* Register a shared catalog cache invalidation.
|
|
* ----------------
|
|
*/
|
|
InvalidationMessageRegisterSharedInvalid(message);
|
|
free((Pointer) &((InvalidationUserData *) message)->dataP[-1]);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* CacheIdInvalidate
|
|
*
|
|
* This routine can invalidate a 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;
|
|
|
|
/* ----------------
|
|
* if the cacheId is the oid of any of the following system relations,
|
|
* then assume we are invalidating a relation descriptor
|
|
* ----------------
|
|
*/
|
|
if (cacheId == RelOid_pg_class)
|
|
{
|
|
RelationIdInvalidateRelationCacheByRelationId(hashIndex);
|
|
return;
|
|
}
|
|
|
|
if (cacheId == RelOid_pg_attribute)
|
|
{
|
|
RelationIdInvalidateRelationCacheByRelationId(hashIndex);
|
|
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 their files too).
|
|
* Relation descriptors that have positive refcounts are then rebuilt.
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
ResetSystemCaches()
|
|
{
|
|
ResetSystemCache();
|
|
RelationCacheInvalidate();
|
|
}
|
|
|
|
/* --------------------------------
|
|
* 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, %u, %u)", \
|
|
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, %u, %u)", \
|
|
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;
|
|
|
|
CacheIdInvalidate(message->any.catalog.cacheId,
|
|
message->any.catalog.hashIndex,
|
|
&message->any.catalog.pointerData);
|
|
break;
|
|
|
|
case 'r': /* cached relation descriptor */
|
|
InvalidationMessageCacheInvalidate_DEBUG2;
|
|
|
|
CacheIdInvalidate(message->any.relation.relationId,
|
|
message->any.relation.objectId,
|
|
(ItemPointer) NULL);
|
|
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
|
|
* ----------------
|
|
*/
|
|
relationId = RelationGetRelid(relation);
|
|
|
|
/* ----------------
|
|
* is it one of the ones we need to send an SI message for?
|
|
* ----------------
|
|
*/
|
|
if (relationId == RelOid_pg_class)
|
|
objectId = tuple->t_data->t_oid;
|
|
else if (relationId == RelOid_pg_attribute)
|
|
objectId = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid;
|
|
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)
|
|
{
|
|
LocalInvalid invalid;
|
|
|
|
/* ----------------
|
|
* debugging stuff
|
|
* ----------------
|
|
*/
|
|
#ifdef INVALIDDEBUG
|
|
elog(DEBUG, "RegisterInvalid(%d) called", send);
|
|
#endif /* defined(INVALIDDEBUG) */
|
|
|
|
/* ----------------
|
|
* Process and free the current list of inval messages.
|
|
* ----------------
|
|
*/
|
|
|
|
DiscardInvalidStack(&InvalidLocal);
|
|
if (send)
|
|
{
|
|
DiscardInvalidStack(&RollbackStack);
|
|
invalid = InvalidForall;
|
|
InvalidForall = EmptyLocalInvalid; /* clear InvalidForall */
|
|
LocalInvalidInvalidate(invalid, InvalidationMessageRegisterSharedInvalid, true);
|
|
}
|
|
else
|
|
{
|
|
DiscardInvalidStack(&InvalidForall);
|
|
invalid = RollbackStack;
|
|
RollbackStack = EmptyLocalInvalid; /* clear RollbackStack */
|
|
LocalInvalidInvalidate(invalid, InvalidationMessageCacheInvalidate, true);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* ImmediateLocalInvalidation
|
|
* Causes invalidation immediately for the next command of the transaction.
|
|
*
|
|
* Note:
|
|
* This should be called in time of CommandCounterIncrement().
|
|
*/
|
|
void
|
|
ImmediateLocalInvalidation(bool send)
|
|
{
|
|
LocalInvalid invalid;
|
|
|
|
/* ----------------
|
|
* debugging stuff
|
|
* ----------------
|
|
*/
|
|
#ifdef INVALIDDEBUG
|
|
elog(DEBUG, "ImmediateLocalInvalidation(%d) called", send);
|
|
#endif /* defined(INVALIDDEBUG) */
|
|
|
|
/* ----------------
|
|
* Process and free the local list of inval messages.
|
|
* ----------------
|
|
*/
|
|
|
|
if (send)
|
|
{
|
|
invalid = InvalidLocal;
|
|
InvalidLocal = EmptyLocalInvalid; /* clear InvalidLocal */
|
|
LocalInvalidInvalidate(invalid, InvalidationMessageCacheInvalidate, true);
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* This may be used for rollback to a savepoint. Don't clear
|
|
* InvalidForall and RollbackStack here.
|
|
*/
|
|
DiscardInvalidStack(&InvalidLocal);
|
|
invalid = RollbackStack;
|
|
LocalInvalidInvalidate(invalid, InvalidationMessageCacheInvalidate, false);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* InvokeHeapTupleInvalidation
|
|
* Invoke functions for the tuple which register invalidation
|
|
* of catalog/relation cache.
|
|
* Note:
|
|
* Assumes object id is valid.
|
|
* Assumes tuple is valid.
|
|
*/
|
|
#ifdef INVALIDDEBUG
|
|
#define InvokeHeapTupleInvalidation_DEBUG1 \
|
|
elog(DEBUG, "%s(%s, [%d,%d])", \
|
|
funcname,\
|
|
RelationGetPhysicalRelationName(relation), \
|
|
ItemPointerGetBlockNumber(&tuple->t_self), \
|
|
ItemPointerGetOffsetNumber(&tuple->t_self))
|
|
#else
|
|
#define InvokeHeapTupleInvalidation_DEBUG1
|
|
#endif /* defined(INVALIDDEBUG) */
|
|
|
|
static void
|
|
InvokeHeapTupleInvalidation(Relation relation, HeapTuple tuple,
|
|
void (*CacheIdRegisterFunc) (),
|
|
void (*RelationIdRegisterFunc) (),
|
|
const char *funcname)
|
|
{
|
|
/* ----------------
|
|
* sanity checks
|
|
* ----------------
|
|
*/
|
|
Assert(RelationIsValid(relation));
|
|
Assert(HeapTupleIsValid(tuple));
|
|
|
|
if (IsBootstrapProcessingMode())
|
|
return;
|
|
/* ----------------
|
|
* this only works for system relations now
|
|
* ----------------
|
|
*/
|
|
if (!IsSystemRelationName(NameStr(RelationGetForm(relation)->relname)))
|
|
return;
|
|
|
|
/* ----------------
|
|
* debugging stuff
|
|
* ----------------
|
|
*/
|
|
InvokeHeapTupleInvalidation_DEBUG1;
|
|
|
|
RelationInvalidateCatalogCacheTuple(relation, tuple,
|
|
CacheIdRegisterFunc);
|
|
|
|
RelationInvalidateRelationCache(relation, tuple,
|
|
RelationIdRegisterFunc);
|
|
}
|
|
|
|
/*
|
|
* RelationInvalidateHeapTuple
|
|
* Causes the given tuple in a relation to be invalidated.
|
|
*/
|
|
void
|
|
RelationInvalidateHeapTuple(Relation relation, HeapTuple tuple)
|
|
{
|
|
InvokeHeapTupleInvalidation(relation, tuple,
|
|
CacheIdRegisterLocalInvalid,
|
|
RelationIdRegisterLocalInvalid,
|
|
"RelationInvalidateHeapTuple");
|
|
}
|
|
|
|
/*
|
|
* RelationMark4RollbackHeapTuple
|
|
* keep the given tuple in a relation to be invalidated
|
|
* in case of abort.
|
|
*/
|
|
void
|
|
RelationMark4RollbackHeapTuple(Relation relation, HeapTuple tuple)
|
|
{
|
|
InvokeHeapTupleInvalidation(relation, tuple,
|
|
CacheIdRegisterLocalRollback,
|
|
RelationIdRegisterLocalRollback,
|
|
"RelationMark4RollbackHeapTuple");
|
|
}
|
|
|
|
/*
|
|
* ImmediateInvalidateSharedHeapTuple
|
|
* Different from RelationInvalidateHeapTuple()
|
|
* this function queues shared invalidation info immediately.
|
|
*/
|
|
void
|
|
ImmediateInvalidateSharedHeapTuple(Relation relation, HeapTuple tuple)
|
|
{
|
|
InvokeHeapTupleInvalidation(relation, tuple,
|
|
CacheIdImmediateRegisterSharedInvalid,
|
|
RelationIdImmediateRegisterSharedInvalid,
|
|
"ImmediateInvalidateSharedHeapTuple");
|
|
}
|
|
|
|
/*
|
|
* ImmediateSharedRelationCacheInvalidate
|
|
* Register shared relation cache invalidation immediately
|
|
*
|
|
* This is needed for smgrunlink()/smgrtruncate().
|
|
* Those functions unlink/truncate the base file immediately
|
|
* and couldn't be rollbacked in case of abort/crash.
|
|
* So relation cache invalidation must be registerd immediately.
|
|
* Note:
|
|
* Assumes Relation is valid.
|
|
*/
|
|
void
|
|
ImmediateSharedRelationCacheInvalidate(Relation relation)
|
|
{
|
|
/* ----------------
|
|
* sanity checks
|
|
* ----------------
|
|
*/
|
|
Assert(RelationIsValid(relation));
|
|
|
|
if (IsBootstrapProcessingMode())
|
|
return;
|
|
|
|
/* ----------------
|
|
* debugging stuff
|
|
* ----------------
|
|
*/
|
|
#ifdef INVALIDDEBUG
|
|
elog(DEBUG, "ImmediateSharedRelationCacheInvalidate(%s)", \
|
|
RelationGetPhysicalRelationName(relation));
|
|
#endif /* defined(INVALIDDEBUG) */
|
|
|
|
RelationIdImmediateRegisterSharedInvalid(
|
|
RelOid_pg_class, RelationGetRelid(relation));
|
|
}
|