*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.73 2000/11/24 04:16:12 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.74 2001/01/05 22:54:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
/* --------------------------------
- * RelationInvalidateCatalogCacheTuple()
+ * PrepareToInvalidateCacheTuple()
*
- * Invalidate a tuple from a specific relation. This call determines the
- * cache in question and calls CatalogCacheIdInvalidate(). It is -ok-
- * if the relation cannot be found, it simply means this backend has yet
- * to open it.
+ * This is part of a rather subtle chain of events, so pay attention:
+ *
+ * When a tuple is updated or deleted, it cannot be flushed from the
+ * catcaches immediately, for reasons explained at the top of inval.c.
+ * Instead we have to add entry(s) for the tuple to a list of pending tuple
+ * invalidations that will be done at the end of the command or transaction.
+ *
+ * The lists of tuples that need to be flushed are kept by inval.c. This
+ * routine is a helper routine for inval.c. Given a tuple belonging to
+ * the specified relation, find all catcaches it could be in, compute the
+ * correct hashindex for each such catcache, and call the specified function
+ * to record the cache id, hashindex, and tuple ItemPointer in inval.c's
+ * lists. CatalogCacheIdInvalidate will be called later, if appropriate,
+ * using the recorded information.
+ *
+ * Note that it is irrelevant whether the given tuple is actually loaded
+ * into the catcache at the moment. Even if it's not there now, it might
+ * be by the end of the command, so we have to be prepared to flush it.
+ *
+ * Also note that it's not an error if there are no catcaches for the
+ * specified relation. inval.c doesn't know exactly which rels have
+ * catcaches --- it will call this routine for any tuple that's in a
+ * system relation.
* --------------------------------
*/
void
-RelationInvalidateCatalogCacheTuple(Relation relation,
- HeapTuple tuple,
+PrepareToInvalidateCacheTuple(Relation relation,
+ HeapTuple tuple,
void (*function) (int, Index, ItemPointer))
{
CatCache *ccp;
Assert(RelationIsValid(relation));
Assert(HeapTupleIsValid(tuple));
Assert(PointerIsValid(function));
- CACHE1_elog(DEBUG, "RelationInvalidateCatalogCacheTuple: called");
+ CACHE1_elog(DEBUG, "PrepareToInvalidateCacheTuple: called");
/* ----------------
* for each cache
* if the cache contains tuples from the specified relation
- * call the invalidation function on the tuples
- * in the proper hash bucket
+ * compute the tuple's hash index in this cache,
+ * and call the passed function to register the information.
* ----------------
*/
* inval.c
* POSTGRES cache invalidation dispatcher code.
*
+ * This is subtle stuff, so pay attention:
+ *
+ * When a tuple is updated or deleted, our time qualification rules consider
+ * that it is *still valid* so long as we are in the same command, ie,
+ * until the next CommandCounterIncrement() or transaction commit.
+ * (See utils/time/tqual.c.) At the command boundary, the old tuple stops
+ * being valid and the new version, if any, becomes valid. Therefore,
+ * we cannot simply flush a tuple from the system caches during heap_update()
+ * or heap_delete(). The tuple is still good at that point; what's more,
+ * even if we did flush it, it might be reloaded into the caches by a later
+ * request in the same command. So the correct behavior is to keep a list
+ * of outdated (updated/deleted) tuples and then do the required cache
+ * flushes at the next command boundary. Similarly, we need a list of
+ * inserted tuples (including new versions of updated tuples), which we will
+ * use to flush those tuples out of the caches if we abort the transaction.
+ * Notice that the first list lives only till command boundary, whereas the
+ * second lives till end of transaction. Finally, we need a third list of
+ * all tuples outdated in the current transaction; if we commit, we send
+ * those invalidation events to all other backends (via the SI message queue)
+ * so that they can flush obsolete entries from their caches.
+ *
+ * We do not need to register EVERY tuple operation in this way, just those
+ * on tuples in relations that have associated catcaches. Also, whenever
+ * we see an operation on a pg_class or pg_attribute tuple, we register
+ * a relcache flush operation for the relation described by that tuple.
+ *
+ *
* 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.38 2000/11/08 22:10:01 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.39 2001/01/05 22:54:37 tgl Exp $
*
- * Note - this code is real crufty...
+ * Note - this code is real crufty... badly needs a rewrite to improve
+ * readability and portability. (Shouldn't assume Oid == Index, for example)
*
*-------------------------------------------------------------------------
*/
* ----------------
* Invalidation info is divided into three parts.
* 1) shared invalidation to be registered for all backends
- * 2) local invalidation for the transaction itself
+ * 2) local invalidation for the transaction itself (actually, just
+ * for the current command within the transaction)
* 3) rollback information for the transaction itself (in case we abort)
* ----------------
*/
static InvalidationEntry InvalidationEntryAllocate(uint16 size);
-static void LocalInvalidInvalidate(LocalInvalid invalid, void (*function) (), bool freemember);
+static void LocalInvalidInvalidate(LocalInvalid invalid,
+ void (*function) (InvalidationMessage),
+ bool freemember);
static LocalInvalid LocalInvalidRegister(LocalInvalid invalid,
InvalidationEntry entry);
static void DiscardInvalidStack(LocalInvalid *invalid);
*/
static void
LocalInvalidInvalidate(LocalInvalid invalid,
- void (*function) (),
+ void (*function) (InvalidationMessage),
bool freemember)
{
InvalidationEntryData *entryDataP;
&((InvalidationUserData *) invalid)->dataP[-1];
if (PointerIsValid(function))
- (*function) ((Pointer) &entryDataP->userData);
+ (*function) ((InvalidationMessage) &entryDataP->userData);
invalid = (Pointer) entryDataP->nextP;
locinv = *invalid;
*invalid = EmptyLocalInvalid;
if (locinv)
- LocalInvalidInvalidate(locinv, (void (*) ()) NULL, true);
+ LocalInvalidInvalidate(locinv,
+ (void (*) (InvalidationMessage)) NULL,
+ true);
}
/* ----------------------------------------------------------------
* --------------------------------
*/
static void
-CacheIdRegisterLocalInvalid(Index cacheId,
+CacheIdRegisterLocalInvalid(int cacheId,
Index hashIndex,
ItemPointer pointer)
{
* --------------------------------
*/
static void
-CacheIdRegisterLocalRollback(Index cacheId, Index hashIndex,
+CacheIdRegisterLocalRollback(int cacheId,
+ Index hashIndex,
ItemPointer pointer)
{
* --------------------------------
*/
static void
-ResetSystemCaches()
+ResetSystemCaches(void)
{
ResetSystemCache();
RelationCacheInvalidate();
}
/* --------------------------------
- * RelationInvalidateRelationCache
+ * PrepareToInvalidateRelationCache
* --------------------------------
*/
static void
-RelationInvalidateRelationCache(Relation relation,
- HeapTuple tuple,
- void (*function) ())
+PrepareToInvalidateRelationCache(Relation relation,
+ HeapTuple tuple,
+ void (*function) (Oid, Oid))
{
Oid relationId;
Oid objectId;
return;
/* ----------------
- * can't handle immediate relation descriptor invalidation
+ * register the relcache-invalidation action in the appropriate list
* ----------------
*/
Assert(PointerIsValid(function));
*
* 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()
+DiscardInvalid(void)
{
/* ----------------
* debugging stuff
* Causes invalidation immediately for the next command of the transaction.
*
* Note:
- * This should be called in time of CommandCounterIncrement().
+ * This should be called during CommandCounterIncrement(),
+ * after we have advanced the command ID.
*/
void
ImmediateLocalInvalidation(bool send)
}
/*
- * InvokeHeapTupleInvalidation
+ * PrepareForTupleInvalidation
* Invoke functions for the tuple which register invalidation
* of catalog/relation cache.
* Note:
* Assumes tuple is valid.
*/
#ifdef INVALIDDEBUG
-#define InvokeHeapTupleInvalidation_DEBUG1 \
+#define PrepareForTupleInvalidation_DEBUG1 \
elog(DEBUG, "%s(%s, [%d,%d])", \
funcname,\
RelationGetPhysicalRelationName(relation), \
ItemPointerGetBlockNumber(&tuple->t_self), \
ItemPointerGetOffsetNumber(&tuple->t_self))
#else
-#define InvokeHeapTupleInvalidation_DEBUG1
+#define PrepareForTupleInvalidation_DEBUG1
#endif /* defined(INVALIDDEBUG) */
static void
-InvokeHeapTupleInvalidation(Relation relation, HeapTuple tuple,
- void (*CacheIdRegisterFunc) (),
- void (*RelationIdRegisterFunc) (),
+PrepareForTupleInvalidation(Relation relation, HeapTuple tuple,
+ void (*CacheIdRegisterFunc) (int, Index,
+ ItemPointer),
+ void (*RelationIdRegisterFunc) (Oid, Oid),
const char *funcname)
{
/* ----------------
if (IsBootstrapProcessingMode())
return;
+
/* ----------------
- * this only works for system relations now
+ * We only need to worry about invalidation for tuples that are in
+ * system relations; user-relation tuples are never in catcaches
+ * and can't affect the relcache either.
* ----------------
*/
if (!IsSystemRelationName(NameStr(RelationGetForm(relation)->relname)))
* debugging stuff
* ----------------
*/
- InvokeHeapTupleInvalidation_DEBUG1;
+ PrepareForTupleInvalidation_DEBUG1;
- RelationInvalidateCatalogCacheTuple(relation, tuple,
- CacheIdRegisterFunc);
+ PrepareToInvalidateCacheTuple(relation, tuple,
+ CacheIdRegisterFunc);
- RelationInvalidateRelationCache(relation, tuple,
- RelationIdRegisterFunc);
+ PrepareToInvalidateRelationCache(relation, tuple,
+ RelationIdRegisterFunc);
}
/*
* RelationInvalidateHeapTuple
- * Causes the given tuple in a relation to be invalidated.
+ * Register the given tuple for invalidation at end of command
+ * (ie, current command is outdating this tuple).
*/
void
RelationInvalidateHeapTuple(Relation relation, HeapTuple tuple)
{
- InvokeHeapTupleInvalidation(relation, tuple,
+ PrepareForTupleInvalidation(relation, tuple,
CacheIdRegisterLocalInvalid,
RelationIdRegisterLocalInvalid,
"RelationInvalidateHeapTuple");
/*
* RelationMark4RollbackHeapTuple
- * keep the given tuple in a relation to be invalidated
- * in case of abort.
+ * Register the given tuple for invalidation in case of abort
+ * (ie, current command is creating this tuple).
*/
void
RelationMark4RollbackHeapTuple(Relation relation, HeapTuple tuple)
{
- InvokeHeapTupleInvalidation(relation, tuple,
+ PrepareForTupleInvalidation(relation, tuple,
CacheIdRegisterLocalRollback,
RelationIdRegisterLocalRollback,
"RelationMark4RollbackHeapTuple");