mirror of
https://github.com/postgres/postgres.git
synced 2025-10-22 14:32:25 +03:00
syscache and relcache flushes). Relcache entry rebuild now preserves original tupledesc, rewrite rules, and triggers if possible, so that pointers to these things remain valid --- if these things change while relcache entry has positive refcount, we elog(ERROR) to avoid later crash. Arrange for xact-local rels to be rebuilt when an SI inval message is seen for them, so that they are updated by CommandCounterIncrement the same as regular rels. (This is useful because of Hiroshi's recent changes to process our own SI messages at CommandCounterIncrement time.) This allows simplification of some routines that previously hacked around the lack of an automatic update. catcache now keeps its own copy of tupledesc for its relation, rather than depending on the relcache's copy; this avoids needing to reinitialize catcache during a cache flush, which saves some cycles and eliminates nasty circularity problems that occur if a cache flush happens while trying to initialize a catcache. Eliminate a number of permanent memory leaks that used to happen during catcache or relcache flush; not least of which was that catcache never freed any cached tuples! (Rule parsetree storage is still leaked, however; will fix that separately.) Nothing done yet about code that uses tuples retrieved by SearchSysCache for longer than is safe.
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.34 2000/01/31 04:35:52 tgl Exp $
|
|
*
|
|
* Note - this code is real crufty...
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "catalog/catalog.h"
|
|
#include "catalog/catname.h"
|
|
#include "catalog/heap.h"
|
|
#include "catalog/pg_class.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));
|
|
}
|