diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 3e1c428a53..d8e31f3967 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -1047,17 +1047,36 @@ ReleaseCatCache(HeapTuple tuple) } /* -------------------------------- - * 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; @@ -1069,13 +1088,13 @@ RelationInvalidateCatalogCacheTuple(Relation relation, 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. * ---------------- */ diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c index 8f4fd626f8..37f2c8416c 100644 --- a/src/backend/utils/cache/inval.c +++ b/src/backend/utils/cache/inval.c @@ -3,14 +3,41 @@ * 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) * *------------------------------------------------------------------------- */ @@ -82,7 +109,8 @@ typedef InvalidationMessageData *InvalidationMessage; * ---------------- * 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) * ---------------- */ @@ -107,7 +135,9 @@ static LocalInvalid RollbackStack = EmptyLocalInvalid; 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); @@ -161,7 +191,7 @@ LocalInvalidRegister(LocalInvalid invalid, */ static void LocalInvalidInvalidate(LocalInvalid invalid, - void (*function) (), + void (*function) (InvalidationMessage), bool freemember) { InvalidationEntryData *entryDataP; @@ -172,7 +202,7 @@ LocalInvalidInvalidate(LocalInvalid invalid, &((InvalidationUserData *) invalid)->dataP[-1]; if (PointerIsValid(function)) - (*function) ((Pointer) &entryDataP->userData); + (*function) ((InvalidationMessage) &entryDataP->userData); invalid = (Pointer) entryDataP->nextP; @@ -193,7 +223,9 @@ DiscardInvalidStack(LocalInvalid *invalid) locinv = *invalid; *invalid = EmptyLocalInvalid; if (locinv) - LocalInvalidInvalidate(locinv, (void (*) ()) NULL, true); + LocalInvalidInvalidate(locinv, + (void (*) (InvalidationMessage)) NULL, + true); } /* ---------------------------------------------------------------- @@ -269,7 +301,7 @@ CacheIdRegisterSpecifiedLocalInvalid(LocalInvalid invalid, * -------------------------------- */ static void -CacheIdRegisterLocalInvalid(Index cacheId, +CacheIdRegisterLocalInvalid(int cacheId, Index hashIndex, ItemPointer pointer) { @@ -298,7 +330,8 @@ CacheIdRegisterLocalInvalid(Index cacheId, * -------------------------------- */ static void -CacheIdRegisterLocalRollback(Index cacheId, Index hashIndex, +CacheIdRegisterLocalRollback(int cacheId, + Index hashIndex, ItemPointer pointer) { @@ -477,7 +510,7 @@ CacheIdInvalidate(Index cacheId, * -------------------------------- */ static void -ResetSystemCaches() +ResetSystemCaches(void) { ResetSystemCache(); RelationCacheInvalidate(); @@ -585,13 +618,13 @@ InvalidationMessageCacheInvalidate(InvalidationMessage message) } /* -------------------------------- - * RelationInvalidateRelationCache + * PrepareToInvalidateRelationCache * -------------------------------- */ static void -RelationInvalidateRelationCache(Relation relation, - HeapTuple tuple, - void (*function) ()) +PrepareToInvalidateRelationCache(Relation relation, + HeapTuple tuple, + void (*function) (Oid, Oid)) { Oid relationId; Oid objectId; @@ -614,7 +647,7 @@ RelationInvalidateRelationCache(Relation relation, return; /* ---------------- - * can't handle immediate relation descriptor invalidation + * register the relcache-invalidation action in the appropriate list * ---------------- */ Assert(PointerIsValid(function)); @@ -629,11 +662,9 @@ RelationInvalidateRelationCache(Relation relation, * * 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 @@ -694,7 +725,8 @@ RegisterInvalid(bool send) * 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) @@ -735,7 +767,7 @@ ImmediateLocalInvalidation(bool send) } /* - * InvokeHeapTupleInvalidation + * PrepareForTupleInvalidation * Invoke functions for the tuple which register invalidation * of catalog/relation cache. * Note: @@ -743,20 +775,21 @@ ImmediateLocalInvalidation(bool send) * 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) { /* ---------------- @@ -768,8 +801,11 @@ InvokeHeapTupleInvalidation(Relation relation, HeapTuple tuple, 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))) @@ -779,23 +815,24 @@ InvokeHeapTupleInvalidation(Relation relation, HeapTuple tuple, * 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"); @@ -803,13 +840,13 @@ RelationInvalidateHeapTuple(Relation relation, HeapTuple tuple) /* * 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"); diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h index 70c988ca18..a24307bd90 100644 --- a/src/include/utils/catcache.h +++ b/src/include/utils/catcache.h @@ -13,7 +13,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catcache.h,v 1.29 2000/11/24 04:16:11 tgl Exp $ + * $Id: catcache.h,v 1.30 2001/01/05 22:54:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -95,7 +95,7 @@ extern void ResetSystemCache(void); extern void SystemCacheRelationFlushed(Oid relId); extern void CatalogCacheIdInvalidate(int cacheId, Index hashIndex, ItemPointer pointer); -extern void RelationInvalidateCatalogCacheTuple(Relation relation, +extern void PrepareToInvalidateCacheTuple(Relation relation, HeapTuple tuple, void (*function) (int, Index, ItemPointer));