diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 5b5d31b33d..651a5c40f4 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -1838,7 +1838,6 @@ StartTransaction(void) * initialize other subsystems for new transaction */ AtStart_GUC(); - AtStart_Inval(); AtStart_Cache(); AfterTriggerBeginXact(); @@ -4151,7 +4150,6 @@ StartSubTransaction(void) */ AtSubStart_Memory(); AtSubStart_ResourceOwner(); - AtSubStart_Inval(); AtSubStart_Notify(); AfterTriggerBeginSubXact(); diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c index a7a768efa6..6b6c88eaf0 100644 --- a/src/backend/utils/cache/inval.c +++ b/src/backend/utils/cache/inval.c @@ -693,19 +693,32 @@ AcceptInvalidationMessages(void) } /* - * AtStart_Inval - * Initialize inval lists at start of a main transaction. + * PrepareInvalidationState + * Initialize inval lists for the current (sub)transaction. */ -void -AtStart_Inval(void) +static void +PrepareInvalidationState(void) { - Assert(transInvalInfo == NULL); - transInvalInfo = (TransInvalidationInfo *) + TransInvalidationInfo *myInfo; + + if (transInvalInfo != NULL && + transInvalInfo->my_level == GetCurrentTransactionNestLevel()) + return; + + myInfo = (TransInvalidationInfo *) MemoryContextAllocZero(TopTransactionContext, sizeof(TransInvalidationInfo)); - transInvalInfo->my_level = GetCurrentTransactionNestLevel(); - SharedInvalidMessagesArray = NULL; - numSharedInvalidMessagesArray = 0; + myInfo->parent = transInvalInfo; + myInfo->my_level = GetCurrentTransactionNestLevel(); + + /* + * If there's any previous entry, this one should be for a deeper + * nesting level. + */ + Assert(transInvalInfo == NULL || + myInfo->my_level > transInvalInfo->my_level); + + transInvalInfo = myInfo; } /* @@ -726,24 +739,6 @@ PostPrepare_Inval(void) AtEOXact_Inval(false); } -/* - * AtSubStart_Inval - * Initialize inval lists at start of a subtransaction. - */ -void -AtSubStart_Inval(void) -{ - TransInvalidationInfo *myInfo; - - Assert(transInvalInfo != NULL); - myInfo = (TransInvalidationInfo *) - MemoryContextAllocZero(TopTransactionContext, - sizeof(TransInvalidationInfo)); - myInfo->parent = transInvalInfo; - myInfo->my_level = GetCurrentTransactionNestLevel(); - transInvalInfo = myInfo; -} - /* * Collect invalidation messages into SharedInvalidMessagesArray array. */ @@ -803,8 +798,16 @@ xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs, { MemoryContext oldcontext; + /* Quick exit if we haven't done anything with invalidation messages. */ + if (transInvalInfo == NULL) + { + *RelcacheInitFileInval = false; + *msgs = NULL; + return 0; + } + /* Must be at top of stack */ - Assert(transInvalInfo != NULL && transInvalInfo->parent == NULL); + Assert(transInvalInfo->my_level == 1 && transInvalInfo->parent == NULL); /* * Relcache init file invalidation requires processing both before and @@ -904,11 +907,15 @@ ProcessCommittedInvalidationMessages(SharedInvalidationMessage *msgs, void AtEOXact_Inval(bool isCommit) { + /* Quick exit if no messages */ + if (transInvalInfo == NULL) + return; + + /* Must be at top of stack */ + Assert(transInvalInfo->my_level == 1 && transInvalInfo->parent == NULL); + if (isCommit) { - /* Must be at top of stack */ - Assert(transInvalInfo != NULL && transInvalInfo->parent == NULL); - /* * Relcache init file invalidation requires processing both before and * after we send the SI messages. However, we need not do anything @@ -926,17 +933,16 @@ AtEOXact_Inval(bool isCommit) if (transInvalInfo->RelcacheInitFileInval) RelationCacheInitFilePostInvalidate(); } - else if (transInvalInfo != NULL) + else { - /* Must be at top of stack */ - Assert(transInvalInfo->parent == NULL); - ProcessInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs, LocalExecuteInvalidationMessage); } /* Need not free anything explicitly */ transInvalInfo = NULL; + SharedInvalidMessagesArray = NULL; + numSharedInvalidMessagesArray = 0; } /* @@ -960,18 +966,38 @@ AtEOXact_Inval(bool isCommit) void AtEOSubXact_Inval(bool isCommit) { - int my_level = GetCurrentTransactionNestLevel(); + int my_level; TransInvalidationInfo *myInfo = transInvalInfo; + /* Quick exit if no messages. */ + if (myInfo == NULL) + return; + + /* Also bail out quickly if messages are not for this level. */ + my_level = GetCurrentTransactionNestLevel(); + if (myInfo->my_level != my_level) + { + Assert(myInfo->my_level < my_level); + return; + } + if (isCommit) { - /* Must be at non-top of stack */ - Assert(myInfo != NULL && myInfo->parent != NULL); - Assert(myInfo->my_level == my_level); - /* If CurrentCmdInvalidMsgs still has anything, fix it */ CommandEndInvalidationMessages(); + /* + * We create invalidation stack entries lazily, so the parent might + * not have one. Instead of creating one, moving all the data over, + * and then freeing our own, we can just adjust the level of our own + * entry. + */ + if (myInfo->parent == NULL || myInfo->parent->my_level < my_level - 1) + { + myInfo->my_level--; + return; + } + /* Pass up my inval messages to parent */ AppendInvalidationMessages(&myInfo->parent->PriorCmdInvalidMsgs, &myInfo->PriorCmdInvalidMsgs); @@ -986,11 +1012,8 @@ AtEOSubXact_Inval(bool isCommit) /* Need not free anything else explicitly */ pfree(myInfo); } - else if (myInfo != NULL && myInfo->my_level == my_level) + else { - /* Must be at non-top of stack */ - Assert(myInfo->parent != NULL); - ProcessInvalidationMessages(&myInfo->PriorCmdInvalidMsgs, LocalExecuteInvalidationMessage); @@ -1074,6 +1097,12 @@ CacheInvalidateHeapTuple(Relation relation, if (IsToastRelation(relation)) return; + /* + * If we're not prepared to queue invalidation messages for this + * subtransaction level, get ready now. + */ + PrepareInvalidationState(); + /* * First let the catcache do its thing */ @@ -1159,6 +1188,8 @@ CacheInvalidateCatalog(Oid catalogId) { Oid databaseId; + PrepareInvalidationState(); + if (IsSharedRelation(catalogId)) databaseId = InvalidOid; else @@ -1182,6 +1213,8 @@ CacheInvalidateRelcache(Relation relation) Oid databaseId; Oid relationId; + PrepareInvalidationState(); + relationId = RelationGetRelid(relation); if (relation->rd_rel->relisshared) databaseId = InvalidOid; @@ -1202,6 +1235,8 @@ CacheInvalidateRelcacheByTuple(HeapTuple classTuple) Oid databaseId; Oid relationId; + PrepareInvalidationState(); + relationId = HeapTupleGetOid(classTuple); if (classtup->relisshared) databaseId = InvalidOid; @@ -1221,6 +1256,8 @@ CacheInvalidateRelcacheByRelid(Oid relid) { HeapTuple tup; + PrepareInvalidationState(); + tup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for relation %u", relid); diff --git a/src/include/utils/inval.h b/src/include/utils/inval.h index 6156e0219d..9842b698d6 100644 --- a/src/include/utils/inval.h +++ b/src/include/utils/inval.h @@ -25,10 +25,6 @@ typedef void (*RelcacheCallbackFunction) (Datum arg, Oid relid); extern void AcceptInvalidationMessages(void); -extern void AtStart_Inval(void); - -extern void AtSubStart_Inval(void); - extern void AtEOXact_Inval(bool isCommit); extern void AtEOSubXact_Inval(bool isCommit);