aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/cache/inval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/cache/inval.c')
-rw-r--r--src/backend/utils/cache/inval.c238
1 files changed, 122 insertions, 116 deletions
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
index 0e383b32c84..6d397a7ba98 100644
--- a/src/backend/utils/cache/inval.c
+++ b/src/backend/utils/cache/inval.c
@@ -5,26 +5,38 @@
*
* 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
+ * When a tuple is updated or deleted, our standard 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, and note that system catalogs are generally
+ * scanned under SnapshotNow rules by the system, or plain user snapshots
+ * for user queries.) 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. This list
- * definitely can't be processed until after we commit, otherwise the other
- * backends won't see our updated tuples as good.
+ * flushes at the next command boundary. We must also keep track of
+ * inserted tuples so that we can flush "negative" cache entries that match
+ * the new tuples; again, that mustn't happen until end of command.
+ *
+ * Once we have finished the command, we still need to remember inserted
+ * tuples (including new versions of updated tuples), so that we can flush
+ * them from the caches if we abort the transaction. Similarly, we'd better
+ * be able to flush "negative" cache entries that may have been loaded in
+ * place of deleted tuples, so we still need the deleted ones too.
+ *
+ * If we successfully complete the transaction, we have to broadcast all
+ * these invalidation events to other backends (via the SI message queue)
+ * so that they can flush obsolete entries from their caches. Note we have
+ * to record the transaction commit before sending SI messages, otherwise
+ * the other backends won't see our updated tuples as good.
+ *
+ * In short, we need to remember until xact end every insert or delete
+ * of a tuple that might be in the system caches. Updates are treated as
+ * two events, delete + insert, for simplicity. (There are cases where
+ * it'd be possible to record just one event, but we don't currently try.)
*
* We do not need to register EVERY tuple operation in this way, just those
* on tuples in relations that have associated catcaches. We do, however,
@@ -62,7 +74,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.48 2002/02/19 20:11:17 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.49 2002/03/03 17:47:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -99,33 +111,26 @@ typedef struct InvalidationListHeader
/*
* ----------------
- * Invalidation info is divided into three parts.
- * 1) shared invalidation to be sent to all backends at commit
- * 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)
+ * Invalidation info is divided into two lists:
+ * 1) events so far in current command, not yet reflected to caches.
+ * 2) events in previous commands of current transaction; these have
+ * been reflected to local caches, and must be either broadcast to
+ * other backends or rolled back from local cache when we commit
+ * or abort the transaction.
+ *
+ * The relcache-file-invalidated flag can just be a simple boolean,
+ * since we only act on it at transaction commit; we don't care which
+ * command of the transaction set it.
* ----------------
*/
-/*
- * head of invalidation message list for all backends
- * eaten by AtCommit_Cache() in CommitTransaction()
- */
-static InvalidationListHeader GlobalInvalidMsgs;
+/* head of current-command event list */
+static InvalidationListHeader CurrentCmdInvalidMsgs;
-static bool RelcacheInitFileInval; /* init file must be invalidated? */
+/* head of previous-commands event list */
+static InvalidationListHeader PriorCmdInvalidMsgs;
-/*
- * head of invalidation message list for the current command
- * eaten by AtCommit_LocalCache() in CommandCounterIncrement()
- */
-static InvalidationListHeader LocalInvalidMsgs;
-
-/*
- * head of rollback message list for abort-time processing
- * eaten by AtAbort_Cache() in AbortTransaction()
- */
-static InvalidationListHeader RollbackMsgs;
+static bool RelcacheInitFileInval; /* init file must be invalidated? */
/* ----------------------------------------------------------------
@@ -205,6 +210,29 @@ FreeInvalidationMessageList(InvalidationChunk **listHdr)
}
/*
+ * Append one list of invalidation message chunks to another, resetting
+ * the source chunk-list pointer to NULL.
+ */
+static void
+AppendInvalidationMessageList(InvalidationChunk **destHdr,
+ InvalidationChunk **srcHdr)
+{
+ InvalidationChunk *chunk = *srcHdr;
+
+ if (chunk == NULL)
+ return; /* nothing to do */
+
+ while (chunk->next != NULL)
+ chunk = chunk->next;
+
+ chunk->next = *destHdr;
+
+ *destHdr = *srcHdr;
+
+ *srcHdr = NULL;
+}
+
+/*
* Process a list of invalidation messages.
*
* This is a macro that executes the given code fragment for each message in
@@ -238,15 +266,15 @@ FreeInvalidationMessageList(InvalidationChunk **listHdr)
*/
static void
AddCatcacheInvalidationMessage(InvalidationListHeader *hdr,
- int id, Index hashIndex,
+ int id, uint32 hashValue,
ItemPointer tuplePtr, Oid dbId)
{
SharedInvalidationMessage msg;
msg.cc.id = (int16) id;
- msg.cc.hashIndex = (uint16) hashIndex;
- msg.cc.dbId = dbId;
msg.cc.tuplePtr = *tuplePtr;
+ msg.cc.dbId = dbId;
+ msg.cc.hashValue = hashValue;
AddInvalidationMessage(&hdr->cclist, &msg);
}
@@ -272,6 +300,18 @@ AddRelcacheInvalidationMessage(InvalidationListHeader *hdr,
}
/*
+ * Append one list of invalidation messages to another, resetting
+ * the source list to empty.
+ */
+static void
+AppendInvalidationMessages(InvalidationListHeader *dest,
+ InvalidationListHeader *src)
+{
+ AppendInvalidationMessageList(&dest->cclist, &src->cclist);
+ AppendInvalidationMessageList(&dest->rclist, &src->rclist);
+}
+
+/*
* Reset an invalidation list to empty
*
* physicalFree may be set false if caller knows transaction is ending
@@ -318,21 +358,16 @@ ProcessInvalidationMessages(InvalidationListHeader *hdr,
/*
* RegisterCatcacheInvalidation
*
- * Register an invalidation event for an updated/deleted catcache entry.
- * We insert the event into both GlobalInvalidMsgs (for transmission
- * to other backends at transaction commit) and LocalInvalidMsgs (for
- * my local invalidation at end of command within xact).
+ * Register an invalidation event for a catcache tuple entry.
*/
static void
RegisterCatcacheInvalidation(int cacheId,
- Index hashIndex,
+ uint32 hashValue,
ItemPointer tuplePtr,
Oid dbId)
{
- AddCatcacheInvalidationMessage(&GlobalInvalidMsgs,
- cacheId, hashIndex, tuplePtr, dbId);
- AddCatcacheInvalidationMessage(&LocalInvalidMsgs,
- cacheId, hashIndex, tuplePtr, dbId);
+ AddCatcacheInvalidationMessage(&CurrentCmdInvalidMsgs,
+ cacheId, hashValue, tuplePtr, dbId);
}
/*
@@ -343,11 +378,8 @@ RegisterCatcacheInvalidation(int cacheId,
static void
RegisterRelcacheInvalidation(Oid dbId, Oid relId)
{
- AddRelcacheInvalidationMessage(&GlobalInvalidMsgs,
+ AddRelcacheInvalidationMessage(&CurrentCmdInvalidMsgs,
dbId, relId);
- AddRelcacheInvalidationMessage(&LocalInvalidMsgs,
- dbId, relId);
-
/*
* If the relation being invalidated is one of those cached in the
* relcache init file, mark that we need to zap that file at commit.
@@ -357,34 +389,6 @@ RegisterRelcacheInvalidation(Oid dbId, Oid relId)
}
/*
- * RegisterCatcacheRollback
- *
- * Register an invalidation event for an inserted catcache entry.
- * This only needs to be flushed out of my local catcache, if I abort.
- */
-static void
-RegisterCatcacheRollback(int cacheId,
- Index hashIndex,
- ItemPointer tuplePtr,
- Oid dbId)
-{
- AddCatcacheInvalidationMessage(&RollbackMsgs,
- cacheId, hashIndex, tuplePtr, dbId);
-}
-
-/*
- * RegisterRelcacheRollback
- *
- * As above, but register a relcache invalidation event.
- */
-static void
-RegisterRelcacheRollback(Oid dbId, Oid relId)
-{
- AddRelcacheInvalidationMessage(&RollbackMsgs,
- dbId, relId);
-}
-
-/*
* LocalExecuteInvalidationMessage
*
* Process a single invalidation message (which could be either type).
@@ -398,7 +402,7 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
{
if (msg->cc.dbId == MyDatabaseId || msg->cc.dbId == 0)
CatalogCacheIdInvalidate(msg->cc.id,
- msg->cc.hashIndex,
+ msg->cc.hashValue,
&msg->cc.tuplePtr);
}
else if (msg->id == SHAREDINVALRELCACHE_ID)
@@ -438,7 +442,7 @@ InvalidateSystemCaches(void)
*/
static void
PrepareForTupleInvalidation(Relation relation, HeapTuple tuple,
- void (*CacheIdRegisterFunc) (int, Index,
+ void (*CacheIdRegisterFunc) (int, uint32,
ItemPointer, Oid),
void (*RelationIdRegisterFunc) (Oid, Oid))
{
@@ -517,16 +521,18 @@ AcceptInvalidationMessages(void)
* AtEOXactInvalidationMessages
* Process queued-up invalidation messages at end of transaction.
*
- * If isCommit, we must send out the messages in our GlobalInvalidMsgs list
+ * If isCommit, we must send out the messages in our PriorCmdInvalidMsgs list
* to the shared invalidation message queue. Note that these will be read
* not only by other backends, but also by our own backend at the next
- * transaction start (via AcceptInvalidationMessages). Therefore, it's okay
- * to discard any pending LocalInvalidMsgs, since these will be redundant
- * with the global list.
+ * transaction start (via AcceptInvalidationMessages). This means that
+ * we can skip immediate local processing of anything that's still in
+ * CurrentCmdInvalidMsgs, and just send that list out too.
*
* If not isCommit, we are aborting, and must locally process the messages
- * in our RollbackMsgs list. No messages need be sent to other backends,
- * since they'll not have seen our changed tuples anyway.
+ * in PriorCmdInvalidMsgs. No messages need be sent to other backends,
+ * since they'll not have seen our changed tuples anyway. We can forget
+ * about CurrentCmdInvalidMsgs too, since those changes haven't touched
+ * the caches yet.
*
* In any case, reset the various lists to empty. We need not physically
* free memory here, since TopTransactionContext is about to be emptied
@@ -548,7 +554,10 @@ AtEOXactInvalidationMessages(bool isCommit)
if (RelcacheInitFileInval)
RelationCacheInitFileInvalidate(true);
- ProcessInvalidationMessages(&GlobalInvalidMsgs,
+ AppendInvalidationMessages(&PriorCmdInvalidMsgs,
+ &CurrentCmdInvalidMsgs);
+
+ ProcessInvalidationMessages(&PriorCmdInvalidMsgs,
SendSharedInvalidMessage);
if (RelcacheInitFileInval)
@@ -556,15 +565,14 @@ AtEOXactInvalidationMessages(bool isCommit)
}
else
{
- ProcessInvalidationMessages(&RollbackMsgs,
+ ProcessInvalidationMessages(&PriorCmdInvalidMsgs,
LocalExecuteInvalidationMessage);
}
RelcacheInitFileInval = false;
- DiscardInvalidationMessages(&GlobalInvalidMsgs, false);
- DiscardInvalidationMessages(&LocalInvalidMsgs, false);
- DiscardInvalidationMessages(&RollbackMsgs, false);
+ DiscardInvalidationMessages(&PriorCmdInvalidMsgs, false);
+ DiscardInvalidationMessages(&CurrentCmdInvalidMsgs, false);
}
/*
@@ -573,13 +581,13 @@ AtEOXactInvalidationMessages(bool isCommit)
* in a transaction.
*
* Here, we send no messages to the shared queue, since we don't know yet if
- * we will commit. But we do need to locally process the LocalInvalidMsgs
- * list, so as to flush our caches of any tuples we have outdated in the
- * current command.
+ * we will commit. We do need to locally process the CurrentCmdInvalidMsgs
+ * list, so as to flush our caches of any entries we have outdated in the
+ * current command. We then move the current-cmd list over to become part
+ * of the prior-cmds list.
*
* The isCommit = false case is not currently used, but may someday be
* needed to support rollback to a savepoint within a transaction.
- * (I suspect it needs more work first --- tgl.)
*
* Note:
* This should be called during CommandCounterIncrement(),
@@ -590,29 +598,24 @@ CommandEndInvalidationMessages(bool isCommit)
{
if (isCommit)
{
- ProcessInvalidationMessages(&LocalInvalidMsgs,
+ ProcessInvalidationMessages(&CurrentCmdInvalidMsgs,
LocalExecuteInvalidationMessage);
+ AppendInvalidationMessages(&PriorCmdInvalidMsgs,
+ &CurrentCmdInvalidMsgs);
}
else
{
- ProcessInvalidationMessages(&RollbackMsgs,
- LocalExecuteInvalidationMessage);
+ /* XXX what needs to be done here? */
}
-
- /*
- * LocalInvalidMsgs list is not interesting anymore, so flush it (for
- * real). Do *not* clear GlobalInvalidMsgs or RollbackMsgs.
- */
- DiscardInvalidationMessages(&LocalInvalidMsgs, true);
}
/*
- * RelationInvalidateHeapTuple
+ * CacheInvalidateHeapTuple
* Register the given tuple for invalidation at end of command
* (ie, current command is outdating this tuple).
*/
void
-RelationInvalidateHeapTuple(Relation relation, HeapTuple tuple)
+CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple)
{
PrepareForTupleInvalidation(relation, tuple,
RegisterCatcacheInvalidation,
@@ -620,14 +623,17 @@ RelationInvalidateHeapTuple(Relation relation, HeapTuple tuple)
}
/*
- * RelationMark4RollbackHeapTuple
- * Register the given tuple for invalidation in case of abort
- * (ie, current command is creating this tuple).
+ * CacheInvalidateRelcache
+ * Register invalidation of the specified relation's relcache entry
+ * at end of command.
+ *
+ * This is used in places that need to force relcache rebuild but aren't
+ * changing any of the tuples recognized as contributors to the relcache
+ * entry by PrepareForTupleInvalidation. (An example is dropping an index.)
*/
void
-RelationMark4RollbackHeapTuple(Relation relation, HeapTuple tuple)
+CacheInvalidateRelcache(Oid relationId)
{
- PrepareForTupleInvalidation(relation, tuple,
- RegisterCatcacheRollback,
- RegisterRelcacheRollback);
+ /* See KLUGE ALERT in PrepareForTupleInvalidation */
+ RegisterRelcacheInvalidation(MyDatabaseId, relationId);
}