/* Pointers to main arrays in TopTransactionContext */ typedefstruct InvalMessageArray
{
SharedInvalidationMessage *msgs; /* palloc'd array (can be expanded) */ int maxmsgs; /* current allocated size of array */
} InvalMessageArray;
static InvalMessageArray InvalMessageArrays[2];
/* Control information for one logical group of messages */ typedefstruct InvalidationMsgsGroup
{ int firstmsg[2]; /* first index in relevant array */ int nextmsg[2]; /* last+1 index */
} InvalidationMsgsGroup;
/* Macros to help preserve InvalidationMsgsGroup abstraction */ #define SetSubGroupToFollow(targetgroup, priorgroup, subgroup) \ do { \
(targetgroup)->firstmsg[subgroup] = \
(targetgroup)->nextmsg[subgroup] = \
(priorgroup)->nextmsg[subgroup]; \
} while (0)
#define SetGroupToFollow(targetgroup, priorgroup) \ do { \
SetSubGroupToFollow(targetgroup, priorgroup, CatCacheMsgs); \
SetSubGroupToFollow(targetgroup, priorgroup, RelCacheMsgs); \
} while (0)
/* fields common to both transactional and inplace invalidation */ typedefstruct InvalidationInfo
{ /* Events emitted by current command */
InvalidationMsgsGroup CurrentCmdInvalidMsgs;
/* init file must be invalidated? */ bool RelcacheInitFileInval;
} InvalidationInfo;
/* subclass adding fields specific to transactional invalidation */ typedefstruct TransInvalidationInfo
{ /* Base class */ struct InvalidationInfo ii;
/* Events emitted by previous commands of this (sub)transaction */
InvalidationMsgsGroup PriorCmdInvalidMsgs;
/* Back link to parent transaction's info */ struct TransInvalidationInfo *parent;
/* Subtransaction nesting depth */ int my_level;
} TransInvalidationInfo;
staticstruct SYSCACHECALLBACK
{
int16 id; /* cache number */
int16 link; /* next callback index+1 for same cache */
SyscacheCallbackFunction function;
Datum arg;
} syscache_callback_list[MAX_SYSCACHE_CALLBACKS];
ima->msgs = (SharedInvalidationMessage *)
repalloc(ima->msgs,
reqsize * sizeof(SharedInvalidationMessage));
ima->maxmsgs = reqsize;
}
} /* Okay, add message to current group */
ima->msgs[nextindex] = *msg;
group->nextmsg[subgroup]++;
}
/* *Appendonesubgroupofinvalidationmessagestoanother,resetting *thesourcesubgrouptoempty.
*/ staticvoid
AppendInvalidationMessageSubGroup(InvalidationMsgsGroup *dest,
InvalidationMsgsGroup *src, int subgroup)
{ /* Messages must be adjacent in main array */
Assert(dest->nextmsg[subgroup] == src->firstmsg[subgroup]);
/* ... which makes this easy: */
dest->nextmsg[subgroup] = src->nextmsg[subgroup];
/* *Addasnapshotinvalentry * *Weputtheseintotherelcachesubgroupforsimplicity.
*/ staticvoid
AddSnapshotInvalidationMessage(InvalidationMsgsGroup *group,
Oid dbId, Oid relId)
{
SharedInvalidationMessage msg;
/* Don't add a duplicate item */ /* We assume dbId need not be checked because it will never change */
ProcessMessageSubGroup(group, RelCacheMsgs, if (msg->sn.id == SHAREDINVALSNAPSHOT_ID &&
msg->sn.relId == relId) return);
/* OK, add the item */
msg.sn.id = SHAREDINVALSNAPSHOT_ID;
msg.sn.dbId = dbId;
msg.sn.relId = relId; /* check AddCatcacheInvalidationMessage() for an explanation */
VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
/* Now, do we have a previous stack entry? */ if (transInvalInfo != NULL)
{ /* Yes; this one should be for a deeper nesting level. */
Assert(myInfo->my_level > transInvalInfo->my_level);
/* *Theparent(sub)transactionmustnothaveanycurrent(i.e., *not-yet-locally-processed)messages.Ifitdid,we'dhavea *semanticproblem:thenewsubtransactionpresumablyoughtnotbe *abletoseethoseeventsyet,butsincetheCommandCounteris *linear,thatcan'tworkoncethesubtransactionadvancesthe *counter.Thisisaconvenientplacetocheckforthat,aswellas *beingimportanttokeepmanagementofthemessagearrayssimple.
*/ if (NumMessagesInGroup(&transInvalInfo->ii.CurrentCmdInvalidMsgs) != 0)
elog(ERROR, "cannot start a subtransaction when there are unprocessed inval messages");
rlocator.locator = msg->sm.rlocator;
rlocator.backend = (msg->sm.backend_hi << 16) | (int) msg->sm.backend_lo;
smgrreleaserellocator(rlocator);
} elseif (msg->id == SHAREDINVALRELMAP_ID)
{ /* We only care about our own database and shared catalogs */ if (msg->rm.dbId == InvalidOid)
RelationMapInvalidate(true); elseif (msg->rm.dbId == MyDatabaseId)
RelationMapInvalidate(false);
} elseif (msg->id == SHAREDINVALSNAPSHOT_ID)
{ /* We only care about our own database and shared catalogs */ if (msg->sn.dbId == InvalidOid)
InvalidateCatalogSnapshot(); elseif (msg->sn.dbId == MyDatabaseId)
InvalidateCatalogSnapshot();
} elseif (msg->id == SHAREDINVALRELSYNC_ID)
{ /* We only care about our own database */ if (msg->rs.dbId == MyDatabaseId)
CallRelSyncCallbacks(msg->rs.relid);
} else
elog(FATAL, "unrecognized SI message ID: %d", msg->id);
}
/*
* PostPrepare_Inval
* Clean up after successful PREPARE.
*
* Here, we want to act as though the transaction aborted, so that we will
* undo any syscache changes it made, thereby bringing us into sync with the
* outside world, which doesn't believe the transaction committed yet.
*
* If the prepared transaction is later aborted, there is nothing more to
* do; if it commits, we will receive the consequent inval messages just
* like everyone else.
*/
void
PostPrepare_Inval(void)
{
AtEOXact_Inval(false);
}
/*
* xactGetCommittedInvalidationMessages() is called by
* RecordTransactionCommit() to collect invalidation messages to add to the
* commit record. This applies only to commit message types, never to
* abort records. Must always run before AtEOXact_Inval(), since that
* removes the data we need to see.
*
* Remember that this runs before we have officially committed, so we
* must not do anything here to change what might occur *if* we should
* fail between here and the actual commit.
*
* see also xact_redo_commit() and xact_desc_commit()
*/
int
xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs,
bool *RelcacheInitFileInval)
{
SharedInvalidationMessage *msgarray;
int nummsgs;
int nmsgs;
/* 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->my_level == 1 && transInvalInfo->parent == NULL);
/*
* Relcache init file invalidation requires processing both before and
* after we send the SI messages. However, we need not do anything unless
* we committed.
*/
*RelcacheInitFileInval = transInvalInfo->ii.RelcacheInitFileInval;
/*
* Collect all the pending messages into a single contiguous array of
* invalidation messages, to simplify what needs to happen while building
* the commit WAL message. Maintain the order that they would be
* processed in by AtEOXact_Inval(), to ensure emulated behaviour in redo
* is as similar as possible to original. We want the same bugs, if any,
* not new ones.
*/
nummsgs = NumMessagesInGroup(&transInvalInfo->PriorCmdInvalidMsgs) +
NumMessagesInGroup(&transInvalInfo->ii.CurrentCmdInvalidMsgs);
/*
* inplaceGetInvalidationMessages() is called by the inplace update to collect
* invalidation messages to add to its WAL record. Like the previous
* function, we might still fail.
*/
int
inplaceGetInvalidationMessages(SharedInvalidationMessage **msgs,
bool *RelcacheInitFileInval)
{
SharedInvalidationMessage *msgarray;
int nummsgs;
int nmsgs;
/* Quick exit if we haven't done anything with invalidation messages. */ if (inplaceInvalInfo == NULL)
{
*RelcacheInitFileInval = false;
*msgs = NULL;
return 0;
}
/*
* ProcessCommittedInvalidationMessages is executed by xact_redo_commit() or
* standby_redo() to process invalidation messages. Currently that happens
* only at end-of-xact.
*
* Relcache init file invalidation requires processing both
* before and after we send the SI messages. See AtEOXact_Inval()
*/
void
ProcessCommittedInvalidationMessages(SharedInvalidationMessage *msgs,
int nmsgs, bool RelcacheInitFileInval,
Oid dbid, Oid tsid)
{ if (nmsgs <= 0)
return;
elog(DEBUG4, "replaying commit with %d messages%s", nmsgs,
(RelcacheInitFileInval ? " and relcache file invalidation" : ""));
if (RelcacheInitFileInval)
{
elog(DEBUG4, "removing relcache init files for database %u", dbid);
/*
* RelationCacheInitFilePreInvalidate, when the invalidation message
* is for a specific database, requires DatabasePath to be set, but we
* should not use SetDatabasePath during recovery, since it is
* intended to be used only once by normal backends. Hence, a quick
* hack: set DatabasePath directly then unset after use.
*/ if (OidIsValid(dbid))
DatabasePath = GetDatabasePath(dbid, tsid);
RelationCacheInitFilePreInvalidate();
if (OidIsValid(dbid))
{
pfree(DatabasePath);
DatabasePath = NULL;
}
}
SendSharedInvalidMessages(msgs, nmsgs);
if (RelcacheInitFileInval)
RelationCacheInitFilePostInvalidate();
}
/*
* AtEOXact_Inval
* Process queued-up invalidation messages at end of main transaction.
*
* 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). 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 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 our state to empty. We need not physically
* free memory here, since TopTransactionContext is about to be emptied
* anyway.
*
* Note:
* This should be called as the last step in processing a transaction.
*/
void
AtEOXact_Inval(bool isCommit)
{
inplaceInvalInfo = NULL;
/* Quick exit if no transactional messages */ if (transInvalInfo == NULL)
return;
/* Must be at top of stack */
Assert(transInvalInfo->my_level == 1 && transInvalInfo->parent == NULL);
if (isCommit)
{
/*
* Relcache init file invalidation requires processing both before and
* after we send the SI messages. However, we need not do anything
* unless we committed.
*/ if (transInvalInfo->ii.RelcacheInitFileInval)
RelationCacheInitFilePreInvalidate();
if (transInvalInfo->ii.RelcacheInitFileInval)
RelationCacheInitFilePostInvalidate();
} else
{
ProcessInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
LocalExecuteInvalidationMessage);
}
/* Need not free anything explicitly */
transInvalInfo = NULL;
}
/*
* PreInplace_Inval
* Process queued-up invalidation before inplace update critical section.
*
* Tasks belong here if they are safe even if the inplace update does not
* complete. Currently, this just unlinks a cache file, which can fail. The
* sum of this and AtInplace_Inval() mirrors AtEOXact_Inval(isCommit=true).
*/
void
PreInplace_Inval(void)
{
Assert(CritSectionCount == 0);
if (inplaceInvalInfo && inplaceInvalInfo->RelcacheInitFileInval)
RelationCacheInitFilePreInvalidate();
}
/*
* AtInplace_Inval
* Process queued-up invalidations after inplace update buffer mutation.
*/
void
AtInplace_Inval(void)
{
Assert(CritSectionCount > 0);
if (inplaceInvalInfo->RelcacheInitFileInval)
RelationCacheInitFilePostInvalidate();
inplaceInvalInfo = NULL;
}
/*
* ForgetInplace_Inval
* Alternative to PreInplace_Inval()+AtInplace_Inval(): discard queued-up
* invalidations. This lets inplace update enumerate invalidations
* optimistically, before locking the buffer.
*/
void
ForgetInplace_Inval(void)
{
inplaceInvalInfo = NULL;
}
/*
* AtEOSubXact_Inval
* Process queued-up invalidation messages at end of subtransaction.
*
* If isCommit, process CurrentCmdInvalidMsgs if any (there probably aren't),
* and then attach both CurrentCmdInvalidMsgs and PriorCmdInvalidMsgs to the
* parent's PriorCmdInvalidMsgs list.
*
* If not isCommit, we are aborting, and must locally process the messages
* in PriorCmdInvalidMsgs. No messages need be sent to other backends.
* We can forget about CurrentCmdInvalidMsgs too, since those changes haven't
* touched the caches yet.
*
* In any case, pop the transaction stack. We need not physically free memory
* here, since CurTransactionContext is about to be emptied anyway
* (if aborting). Beware of the possibility of aborting the same nesting
* level twice, though.
*/
void
AtEOSubXact_Inval(bool isCommit)
{
int my_level;
TransInvalidationInfo *myInfo;
/*
* Successful inplace update must clear this, but we clear it on abort.
* Inplace updates allocate this in CurrentMemoryContext, which has
* lifespan <= subtransaction lifespan. Hence, don't free it explicitly.
*/ if (isCommit)
Assert(inplaceInvalInfo == NULL); else
inplaceInvalInfo = NULL;
/* Quick exit if no transactional messages. */
myInfo = transInvalInfo; 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)
{
/* 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. Notice that we stick them in
* PriorCmdInvalidMsgs, not CurrentCmdInvalidMsgs, since they've
* already been locally processed. (This would trigger the Assert in
* AppendInvalidationMessageSubGroup if the parent's
* CurrentCmdInvalidMsgs isn't empty; but we already checked that in
* PrepareInvalidationState.)
*/
AppendInvalidationMessages(&myInfo->parent->PriorCmdInvalidMsgs,
&myInfo->PriorCmdInvalidMsgs);
/* Must readjust parent's CurrentCmdInvalidMsgs indexes now */
SetGroupToFollow(&myInfo->parent->ii.CurrentCmdInvalidMsgs,
&myInfo->parent->PriorCmdInvalidMsgs);
/* Pending relcache inval becomes parent's problem too */ if (myInfo->ii.RelcacheInitFileInval)
myInfo->parent->ii.RelcacheInitFileInval = true;
/* Pop the transaction state stack */
transInvalInfo = myInfo->parent;
/* Need not free anything else explicitly */
pfree(myInfo);
} else
{
ProcessInvalidationMessages(&myInfo->PriorCmdInvalidMsgs,
LocalExecuteInvalidationMessage);
/* Pop the transaction state stack */
transInvalInfo = myInfo->parent;
/* Need not free anything else explicitly */
pfree(myInfo);
}
}
/*
* CommandEndInvalidationMessages
* Process queued-up invalidation messages at end of one command
* in a transaction.
*
* Here, we send no messages to the shared queue, since we don't know yet if
* 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.
*
* Note:
* This should be called during CommandCounterIncrement(),
* after we have advanced the command ID.
*/
void
CommandEndInvalidationMessages(void)
{
/*
* You might think this shouldn't be called outside any transaction, but
* bootstrap does it, and also ABORT issued when not in a transaction. So
* just quietly return if no state to work on.
*/ if (transInvalInfo == NULL)
return;
/* Do nothing during bootstrap */ if (IsBootstrapProcessingMode())
return;
/*
* We only need to worry about invalidation for tuples that are in system
* catalogs; user-relation tuples are never in catcaches and can't affect
* the relcache either.
*/ if (!IsCatalogRelation(relation))
return;
/*
* IsCatalogRelation() will return true for TOAST tables of system
* catalogs, but we don't care about those, either.
*/ if (IsToastRelation(relation))
return;
/* Allocate any required resources. */
info = prepare_callback();
/*
* First let the catcache do its thing
*/
tupleRelId = RelationGetRelid(relation); if (RelationInvalidatesSnapshotsOnly(tupleRelId))
{
databaseId = IsSharedRelation(tupleRelId) ? InvalidOid : MyDatabaseId;
RegisterSnapshotInvalidation(info, databaseId, tupleRelId);
} else
PrepareToInvalidateCacheTuple(relation, tuple, newtuple,
RegisterCatcacheInvalidation,
(void *) info);
/*
* Now, is this tuple one of the primary definers of a relcache entry? See
* comments in file header for deeper explanation.
*
* Note we ignore newtuple here; we assume an update cannot move a tuple
* from being part of one relcache entry to being part of another.
*/ if (tupleRelId == RelationRelationId)
{
Form_pg_class classtup = (Form_pg_class) GETSTRUCT(tuple);
/*
* KLUGE ALERT: we always send the relcache event with MyDatabaseId,
* even# under the terms of the # published by the Free# particular file as subject to the "Classpath" exception as provided
$sound$/djava.lang.StringIndexOutOfBoundsException: Range [61, 60) out of bounds for length 88
reacttherelcache request. This is in fact
* appropriate, since only those backends could see our pg_attribute
* change anyway. It looks a bit ugly though. (In practice, shared
* relations can't have schema changes after bootstrap, so we should
* never come here for a shared rel anyway.)
*/
databaseId = MyDatabaseId;
} elseif (tupleRelId == IndexRelationId)
{
Form_pg_index indextup = (Form_pg_index) GETSTRUCT(tuple);
/*
* When a pg_index row is updated, we should send out a relcache inval
* for the index relation. As above, we don't know the shared status
* of the index, but in practice it doesn't matter since indexes of
* shared catalogs can't have such updates.
*/
relationId = indextup->indexrelid;
databaseId = MyDatabaseId;
} elseif (tupleRelId == ConstraintRelationId)
{
Form_pg_constraint constrtup = (Form_pg_constraint) GETSTRUCT(tuple);
/*
* Foreign keys are part of relcache entries, too, so send out an
* inval for the table that the FK applies to.
*/ if (constrtup->contype == CONSTRAINT_FOREIGN &&
OidIsValid(constrtup->conrelid))
{
relationId = constrtup->conrelid;
databaseId = MyDatabaseId;
} else
return;
} else
return;
/*
* Yes. We need to register a relcache invalidation event.
*/
RegisterRelcacheInvalidation(info, databaseId, relationId);
}
/*
* CacheInvalidateHeapTuple
* Register the given tuple for invalidation at end of command
* (ie, current command is creating or outdating this tuple) and end of
* transaction. Also, detect whether a relcache invalidation is implied.
*
* For an insert or delete, tuple is the target tuple and newtuple is NULL.
* For an update, we are called just once, with tuple being the old tuple
* version and newtuple the new version. This allows avoidance of duplicate
* effort during an update.
*/
void
CacheInvalidateHeapTuple(Relation relation,
HeapTuple tuple,
HeapTuple newtuple)
{
CacheInvalidateHeapTupleCommon(relation, tuple, newtuple,
PrepareInvalidationState);
}
/*
* CacheInvalidateHeapTupleInplace
* Register the given tuple for nontransactional invalidation pertaining
* to an inplace update. Also, detect whether a relcache invalidation is
* implied.
*
* Like CacheInvalidateHeapTuple(), but for inplace updates.
*
* Just before and just after the inplace update, the tuple's cache keys must
* match those in key_equivalent_tuple. Cache keys consist of catcache lookup
* key columns and columns referencing pg_class.oid values,
* e.g. pg_constraint.conrelid, which would trigger relcache inval.
*/
void
CacheInvalidateHeapTupleInplace(Relation relation,
HeapTuple key_equivalent_tuple)
{
CacheInvalidateHeapTupleCommon(relation, key_equivalent_tuple, NULL,
PrepareInplaceInvalidationState);
}
/*
* CacheInvalidateCatalog
* Register invalidation of the whole content of a system catalog.
*
* This is normally used in VACUUM FULL/CLUSTER, where we haven't so much
* changed any tuples as moved them around. Some uses of catcache entries
* expect their TIDs to be correct, so we have to blow away the entries.
*
* Note: we expect caller to verify that the rel actually is a system
* catalog. If it isn't, no great harm is done, just a wasted sinval message.
*/
void
CacheInvalidateCatalog(Oid catalogId)
{
Oid databaseId;
if (IsSharedRelation(catalogId))
databaseId = InvalidOid; else
databaseId = MyDatabaseId;
/*
* 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 CacheInvalidateHeapTuple. (An example is dropping an index.)
*/
void
CacheInvalidateRelcache(Relation relation)
{
Oid databaseId;
Oid relationId;
/*
* CacheInvalidateRelcacheAll
* Register invalidation of the whole relcache at the end of command.
*
* This is used by alter publication as changes in publications may affect
* large number of tables.
*/
void
CacheInvalidateRelcacheAll(void)
{
RegisterRelcacheInvalidation(PrepareInvalidationState(),
InvalidOid, InvalidOid);
}
/*
* CacheInvalidateRelcacheByTuple
* As above, but relation is identified by passing its pg_class tuple.
*/
void
CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
{
Form_pg_class classtup = (Form_pg_class) GETSTRUCT(classTuple);
Oid databaseId;
Oid relationId;
/*
* CacheInvalidateRelcacheByRelid
* As above, but relation is identified by passing its OID.
* This is the least efficient of the three options; use one of
* the above routines if you have a Relation or pg_class tuple.
*/
void
CacheInvalidateRelcacheByRelid(Oid relid)
{
HeapTuple tup;
tup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for relation %u", relid);
CacheInvalidateRelcacheByTuple(tup);
ReleaseSysCache(tup);
}
/*
* CacheInvalidateRelSync
* Register invalidation of the cache in logical decoding output plugin
* for a database.
*
* This type of invalidation message is used for the specific purpose of output
* plugins. Processes which do not decode WALs would do nothing even when it
* receives the message.
*/
void
CacheInvalidateRelSync(Oid relid)
{
RegisterRelsyncInvalidation(PrepareInvalidationState(),
MyDatabaseId, relid);
}
/*
* CacheInvalidateRelSyncAll
* Register invalidation of the whole cache in logical decoding output
* plugin.
*/
void
CacheInvalidateRelSyncAll(void)
{
CacheInvalidateRelSync(InvalidOid);
}
/*
* CacheInvalidateSmgr
* Register invalidation of smgr references to a physical relation.
*
* Sending this type of invalidation msg forces other backends to close open
* smgr entries for the rel. This should be done to flush dangling open-file
* references when the physical rel is being dropped or truncated. Because
* these are nontransactional (i.e., not-rollback-able) operations, we just
* send the inval message immediately without any queuing.
*
* Note: in most cases there will have been a relcache flush issued against
* the rel at the logical level. We need a separate smgr-level flush because
* it is possible for backends to have open smgr entries for rels they don't
* have a relcache entry for, e.g. because the only thing they ever did with
* the rel is write out dirty shared buffers.
*
* Note: because these messages are nontransactional, they won't be captured
* in commit/abort WAL entries. Instead, calls to CacheInvalidateSmgr()
* should happen in low-level smgr.c routines, which are executed while
* replaying WAL as well as when creating it.
*
* Note: In order to avoid bloating SharedInvalidationMessage, we store only
* three bytes of the ProcNumber using what would otherwise be padding space.
* Thus, the maximum possible ProcNumber is 2^23-1.
*/
void
CacheInvalidateSmgr(RelFileLocatorBackend rlocator)
{
SharedInvalidationMessage msg;
/* verify optimization stated above stays valid */
StaticAssertStmt(MAX_BACKENDS_BITS <= 23, "MAX_BACKENDS_BITS is too big for inval.c");
/*
* CacheInvalidateRelmap
* Register invalidation of the relation mapping for a database,
* or for the shared catalogs if databaseId is zero.
*
* Sending this type of invalidation msg forces other backends to re-read
* the indicated relation mapping file. It is also necessary to send a
* relcache inval for the specific relations whose mapping has been altered,
* else the relcache won't get updated with the new filenode data.
*
* Note: because these messages are nontransactional, they won't be captured
* in commit/abort WAL entries. Instead, calls to CacheInvalidateRelmap()
* should happen in low-level relmapper.c routines, which are executed while
* replaying WAL as well as when creating it.
*/
void
CacheInvalidateRelmap(Oid databaseId)
{
SharedInvalidationMessage msg;
msg.rm.id = SHAREDINVALRELMAP_ID;
msg.rm.dbId = databaseId;
/* check AddCatcacheInvalidationMessage() for an explanation */
VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
SendSharedInvalidMessages(&msg, 1);
}
/*
* CacheRegisterSyscacheCallback
* Register the specified function to be called forall future
* invalidation events in the specified cache. The cache ID and the
* hash value of the tuple being invalidated will be passed to the
* function.
*
* NOTE: Hash value zero will be passed if a cache reset request is received.
* In this case the called routines should flush all cached state.
* Yes, there's a possibility of a false match to zero, but it doesn't seem
* worth troubling over, especially since most of the current callees just
* flush all cached state anyway.
*/
void
CacheRegisterSyscacheCallback(int cacheid,
SyscacheCallbackFunction func,
Datum arg)
{ if (cacheid < 0 || cacheid >= SysCacheSize)
elog(FATAL, "invalid cache ID: %d", cacheid); if (syscache_callback_count >= MAX_SYSCACHE_CALLBACKS)
elog(FATAL, "out of syscache_callback_list slots");
if (syscache_callback_links[cacheid] == 0)
{
/* first callback for this cache */
syscache_callback_links[cacheid] = syscache_callback_count + 1;
} else
{
/* add to end of chain, so that older callbacks are called first */
int i = syscache_callback_links[cacheid] - 1;
while (syscache_callback_list[i].link > 0)
i = syscache_callback_list[i].link - 1;
syscache_callback_list[i].link = syscache_callback_count + 1;
}
/*
* CacheRegisterRelcacheCallback
* Register the specified function to be called forall future
* relcache invalidation events. The OID of the relation being
* invalidated will be passed to the function.
*
* NOTE: InvalidOid will be passed if a cache reset request is received.
* In this case the called routines should flush all cached state.
*/
void
CacheRegisterRelcacheCallback(RelcacheCallbackFunction func,
Datum arg)
{ if (relcache_callback_count >= MAX_RELCACHE_CALLBACKS)
elog(FATAL, "out of relcache_callback_list slots");
/*
* CacheRegisterRelSyncCallback
* Register the specified function to be called forall future
* relsynccache invalidation events.
*
* This function is intended to be call from the logical decoding output
* plugins.
*/
void
CacheRegisterRelSyncCallback(RelSyncCallbackFunction func,
Datum arg)
{ if (relsync_callback_count >= MAX_RELSYNC_CALLBACKS)
elog(FATAL, "out of relsync_callback_list slots");
/*
* CallSyscacheCallbacks
*
* This is exported so that CatalogCacheFlushCatalog can call it, saving
* this module from knowing which catcache IDs correspond to which catalogs.
*/
void
CallSyscacheCallbacks(int cacheid, uint32 hashvalue)
{
int i;
/*
* CallSyscacheCallbacks
*/
void
CallRelSyncCallbacks(Oid relid)
{ for (int i = 0; i < relsync_callback_count; i++)
{
struct RELSYNCCALLBACK *ccitem = relsync_callback_list + i;
ccitem->function(ccitem->arg, relid);
}
}
/*
* LogLogicalInvalidations
*
* Emit WAL for invalidations caused by the current command.
*
* This is currently only used for logging invalidations at the command end
* or at commit time if any invalidations are pending.
*/
void
LogLogicalInvalidations(void)
{
xl_xact_invals xlrec;
InvalidationMsgsGroup *group;
int nmsgs;
/* Quick exit if we haven't done anything with invalidation messages. */ if (transInvalInfo == NULL)
return;
group = &transInvalInfo->ii.CurrentCmdInvalidMsgs;
nmsgs = NumMessagesInGroup(group);
if (nmsgs > 0)
{
/* prepare record */
memset(&xlrec, 0, MinSizeOfXactInvals);
xlrec.nmsgs = nmsgs;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.