Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * inval.c
4 : * POSTGRES cache invalidation dispatcher code.
5 : *
6 : * This is subtle stuff, so pay attention:
7 : *
8 : * When a tuple is updated or deleted, our standard visibility rules
9 : * consider that it is *still valid* so long as we are in the same command,
10 : * ie, until the next CommandCounterIncrement() or transaction commit.
11 : * (See access/heap/heapam_visibility.c, and note that system catalogs are
12 : * generally scanned under the most current snapshot available, rather than
13 : * the transaction snapshot.) At the command boundary, the old tuple stops
14 : * being valid and the new version, if any, becomes valid. Therefore,
15 : * we cannot simply flush a tuple from the system caches during heap_update()
16 : * or heap_delete(). The tuple is still good at that point; what's more,
17 : * even if we did flush it, it might be reloaded into the caches by a later
18 : * request in the same command. So the correct behavior is to keep a list
19 : * of outdated (updated/deleted) tuples and then do the required cache
20 : * flushes at the next command boundary. We must also keep track of
21 : * inserted tuples so that we can flush "negative" cache entries that match
22 : * the new tuples; again, that mustn't happen until end of command.
23 : *
24 : * Once we have finished the command, we still need to remember inserted
25 : * tuples (including new versions of updated tuples), so that we can flush
26 : * them from the caches if we abort the transaction. Similarly, we'd better
27 : * be able to flush "negative" cache entries that may have been loaded in
28 : * place of deleted tuples, so we still need the deleted ones too.
29 : *
30 : * If we successfully complete the transaction, we have to broadcast all
31 : * these invalidation events to other backends (via the SI message queue)
32 : * so that they can flush obsolete entries from their caches. Note we have
33 : * to record the transaction commit before sending SI messages, otherwise
34 : * the other backends won't see our updated tuples as good.
35 : *
36 : * When a subtransaction aborts, we can process and discard any events
37 : * it has queued. When a subtransaction commits, we just add its events
38 : * to the pending lists of the parent transaction.
39 : *
40 : * In short, we need to remember until xact end every insert or delete
41 : * of a tuple that might be in the system caches. Updates are treated as
42 : * two events, delete + insert, for simplicity. (If the update doesn't
43 : * change the tuple hash value, catcache.c optimizes this into one event.)
44 : *
45 : * We do not need to register EVERY tuple operation in this way, just those
46 : * on tuples in relations that have associated catcaches. We do, however,
47 : * have to register every operation on every tuple that *could* be in a
48 : * catcache, whether or not it currently is in our cache. Also, if the
49 : * tuple is in a relation that has multiple catcaches, we need to register
50 : * an invalidation message for each such catcache. catcache.c's
51 : * PrepareToInvalidateCacheTuple() routine provides the knowledge of which
52 : * catcaches may need invalidation for a given tuple.
53 : *
54 : * Also, whenever we see an operation on a pg_class, pg_attribute, or
55 : * pg_index tuple, we register a relcache flush operation for the relation
56 : * described by that tuple (as specified in CacheInvalidateHeapTuple()).
57 : * Likewise for pg_constraint tuples for foreign keys on relations.
58 : *
59 : * We keep the relcache flush requests in lists separate from the catcache
60 : * tuple flush requests. This allows us to issue all the pending catcache
61 : * flushes before we issue relcache flushes, which saves us from loading
62 : * a catcache tuple during relcache load only to flush it again right away.
63 : * Also, we avoid queuing multiple relcache flush requests for the same
64 : * relation, since a relcache flush is relatively expensive to do.
65 : * (XXX is it worth testing likewise for duplicate catcache flush entries?
66 : * Probably not.)
67 : *
68 : * Many subsystems own higher-level caches that depend on relcache and/or
69 : * catcache, and they register callbacks here to invalidate their caches.
70 : * While building a higher-level cache entry, a backend may receive a
71 : * callback for the being-built entry or one of its dependencies. This
72 : * implies the new higher-level entry would be born stale, and it might
73 : * remain stale for the life of the backend. Many caches do not prevent
74 : * that. They rely on DDL for can't-miss catalog changes taking
75 : * AccessExclusiveLock on suitable objects. (For a change made with less
76 : * locking, backends might never read the change.) The relation cache,
77 : * however, needs to reflect changes from CREATE INDEX CONCURRENTLY no later
78 : * than the beginning of the next transaction. Hence, when a relevant
79 : * invalidation callback arrives during a build, relcache.c reattempts that
80 : * build. Caches with similar needs could do likewise.
81 : *
82 : * If a relcache flush is issued for a system relation that we preload
83 : * from the relcache init file, we must also delete the init file so that
84 : * it will be rebuilt during the next backend restart. The actual work of
85 : * manipulating the init file is in relcache.c, but we keep track of the
86 : * need for it here.
87 : *
88 : * Currently, inval messages are sent without regard for the possibility
89 : * that the object described by the catalog tuple might be a session-local
90 : * object such as a temporary table. This is because (1) this code has
91 : * no practical way to tell the difference, and (2) it is not certain that
92 : * other backends don't have catalog cache or even relcache entries for
93 : * such tables, anyway; there is nothing that prevents that. It might be
94 : * worth trying to avoid sending such inval traffic in the future, if those
95 : * problems can be overcome cheaply.
96 : *
97 : * When making a nontransactional change to a cacheable object, we must
98 : * likewise send the invalidation immediately, before ending the change's
99 : * critical section. This includes inplace heap updates, relmap, and smgr.
100 : *
101 : * When effective_wal_level is 'logical', write invalidations into WAL at
102 : * each command end to support the decoding of the in-progress transactions.
103 : * See CommandEndInvalidationMessages.
104 : *
105 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
106 : * Portions Copyright (c) 1994, Regents of the University of California
107 : *
108 : * IDENTIFICATION
109 : * src/backend/utils/cache/inval.c
110 : *
111 : *-------------------------------------------------------------------------
112 : */
113 : #include "postgres.h"
114 :
115 : #include <limits.h>
116 :
117 : #include "access/htup_details.h"
118 : #include "access/xact.h"
119 : #include "access/xloginsert.h"
120 : #include "catalog/catalog.h"
121 : #include "catalog/pg_constraint.h"
122 : #include "miscadmin.h"
123 : #include "storage/procnumber.h"
124 : #include "storage/sinval.h"
125 : #include "storage/smgr.h"
126 : #include "utils/catcache.h"
127 : #include "utils/injection_point.h"
128 : #include "utils/inval.h"
129 : #include "utils/memdebug.h"
130 : #include "utils/memutils.h"
131 : #include "utils/rel.h"
132 : #include "utils/relmapper.h"
133 : #include "utils/snapmgr.h"
134 : #include "utils/syscache.h"
135 :
136 :
137 : /*
138 : * Pending requests are stored as ready-to-send SharedInvalidationMessages.
139 : * We keep the messages themselves in arrays in TopTransactionContext (there
140 : * are separate arrays for catcache and relcache messages). For transactional
141 : * messages, control information is kept in a chain of TransInvalidationInfo
142 : * structs, also allocated in TopTransactionContext. (We could keep a
143 : * subtransaction's TransInvalidationInfo in its CurTransactionContext; but
144 : * that's more wasteful not less so, since in very many scenarios it'd be the
145 : * only allocation in the subtransaction's CurTransactionContext.) For
146 : * inplace update messages, control information appears in an
147 : * InvalidationInfo, allocated in CurrentMemoryContext.
148 : *
149 : * We can store the message arrays densely, and yet avoid moving data around
150 : * within an array, because within any one subtransaction we need only
151 : * distinguish between messages emitted by prior commands and those emitted
152 : * by the current command. Once a command completes and we've done local
153 : * processing on its messages, we can fold those into the prior-commands
154 : * messages just by changing array indexes in the TransInvalidationInfo
155 : * struct. Similarly, we need distinguish messages of prior subtransactions
156 : * from those of the current subtransaction only until the subtransaction
157 : * completes, after which we adjust the array indexes in the parent's
158 : * TransInvalidationInfo to include the subtransaction's messages. Inplace
159 : * invalidations don't need a concept of command or subtransaction boundaries,
160 : * since we send them during the WAL insertion critical section.
161 : *
162 : * The ordering of the individual messages within a command's or
163 : * subtransaction's output is not considered significant, although this
164 : * implementation happens to preserve the order in which they were queued.
165 : * (Previous versions of this code did not preserve it.)
166 : *
167 : * For notational convenience, control information is kept in two-element
168 : * arrays, the first for catcache messages and the second for relcache
169 : * messages.
170 : */
171 : #define CatCacheMsgs 0
172 : #define RelCacheMsgs 1
173 :
174 : /* Pointers to main arrays in TopTransactionContext */
175 : typedef struct InvalMessageArray
176 : {
177 : SharedInvalidationMessage *msgs; /* palloc'd array (can be expanded) */
178 : int maxmsgs; /* current allocated size of array */
179 : } InvalMessageArray;
180 :
181 : static InvalMessageArray InvalMessageArrays[2];
182 :
183 : /* Control information for one logical group of messages */
184 : typedef struct InvalidationMsgsGroup
185 : {
186 : int firstmsg[2]; /* first index in relevant array */
187 : int nextmsg[2]; /* last+1 index */
188 : } InvalidationMsgsGroup;
189 :
190 : /* Macros to help preserve InvalidationMsgsGroup abstraction */
191 : #define SetSubGroupToFollow(targetgroup, priorgroup, subgroup) \
192 : do { \
193 : (targetgroup)->firstmsg[subgroup] = \
194 : (targetgroup)->nextmsg[subgroup] = \
195 : (priorgroup)->nextmsg[subgroup]; \
196 : } while (0)
197 :
198 : #define SetGroupToFollow(targetgroup, priorgroup) \
199 : do { \
200 : SetSubGroupToFollow(targetgroup, priorgroup, CatCacheMsgs); \
201 : SetSubGroupToFollow(targetgroup, priorgroup, RelCacheMsgs); \
202 : } while (0)
203 :
204 : #define NumMessagesInSubGroup(group, subgroup) \
205 : ((group)->nextmsg[subgroup] - (group)->firstmsg[subgroup])
206 :
207 : #define NumMessagesInGroup(group) \
208 : (NumMessagesInSubGroup(group, CatCacheMsgs) + \
209 : NumMessagesInSubGroup(group, RelCacheMsgs))
210 :
211 :
212 : /*----------------
213 : * Transactional invalidation messages are divided into two groups:
214 : * 1) events so far in current command, not yet reflected to caches.
215 : * 2) events in previous commands of current transaction; these have
216 : * been reflected to local caches, and must be either broadcast to
217 : * other backends or rolled back from local cache when we commit
218 : * or abort the transaction.
219 : * Actually, we need such groups for each level of nested transaction,
220 : * so that we can discard events from an aborted subtransaction. When
221 : * a subtransaction commits, we append its events to the parent's groups.
222 : *
223 : * The relcache-file-invalidated flag can just be a simple boolean,
224 : * since we only act on it at transaction commit; we don't care which
225 : * command of the transaction set it.
226 : *----------------
227 : */
228 :
229 : /* fields common to both transactional and inplace invalidation */
230 : typedef struct InvalidationInfo
231 : {
232 : /* Events emitted by current command */
233 : InvalidationMsgsGroup CurrentCmdInvalidMsgs;
234 :
235 : /* init file must be invalidated? */
236 : bool RelcacheInitFileInval;
237 : } InvalidationInfo;
238 :
239 : /* subclass adding fields specific to transactional invalidation */
240 : typedef struct TransInvalidationInfo
241 : {
242 : /* Base class */
243 : struct InvalidationInfo ii;
244 :
245 : /* Events emitted by previous commands of this (sub)transaction */
246 : InvalidationMsgsGroup PriorCmdInvalidMsgs;
247 :
248 : /* Back link to parent transaction's info */
249 : struct TransInvalidationInfo *parent;
250 :
251 : /* Subtransaction nesting depth */
252 : int my_level;
253 : } TransInvalidationInfo;
254 :
255 : static TransInvalidationInfo *transInvalInfo = NULL;
256 :
257 : static InvalidationInfo *inplaceInvalInfo = NULL;
258 :
259 : /* GUC storage */
260 : int debug_discard_caches = 0;
261 :
262 : /*
263 : * Dynamically-registered callback functions. Current implementation
264 : * assumes there won't be enough of these to justify a dynamically resizable
265 : * array; it'd be easy to improve that if needed.
266 : *
267 : * To avoid searching in CallSyscacheCallbacks, all callbacks for a given
268 : * syscache are linked into a list pointed to by syscache_callback_links[id].
269 : * The link values are syscache_callback_list[] index plus 1, or 0 for none.
270 : */
271 :
272 : #define MAX_SYSCACHE_CALLBACKS 64
273 : #define MAX_RELCACHE_CALLBACKS 10
274 : #define MAX_RELSYNC_CALLBACKS 10
275 :
276 : static struct SYSCACHECALLBACK
277 : {
278 : int16 id; /* cache number */
279 : int16 link; /* next callback index+1 for same cache */
280 : SyscacheCallbackFunction function;
281 : Datum arg;
282 : } syscache_callback_list[MAX_SYSCACHE_CALLBACKS];
283 :
284 : static int16 syscache_callback_links[SysCacheSize];
285 :
286 : static int syscache_callback_count = 0;
287 :
288 : static struct RELCACHECALLBACK
289 : {
290 : RelcacheCallbackFunction function;
291 : Datum arg;
292 : } relcache_callback_list[MAX_RELCACHE_CALLBACKS];
293 :
294 : static int relcache_callback_count = 0;
295 :
296 : static struct RELSYNCCALLBACK
297 : {
298 : RelSyncCallbackFunction function;
299 : Datum arg;
300 : } relsync_callback_list[MAX_RELSYNC_CALLBACKS];
301 :
302 : static int relsync_callback_count = 0;
303 :
304 :
305 : /* ----------------------------------------------------------------
306 : * Invalidation subgroup support functions
307 : * ----------------------------------------------------------------
308 : */
309 :
310 : /*
311 : * AddInvalidationMessage
312 : * Add an invalidation message to a (sub)group.
313 : *
314 : * The group must be the last active one, since we assume we can add to the
315 : * end of the relevant InvalMessageArray.
316 : *
317 : * subgroup must be CatCacheMsgs or RelCacheMsgs.
318 : */
319 : static void
320 4779886 : AddInvalidationMessage(InvalidationMsgsGroup *group, int subgroup,
321 : const SharedInvalidationMessage *msg)
322 : {
323 4779886 : InvalMessageArray *ima = &InvalMessageArrays[subgroup];
324 4779886 : int nextindex = group->nextmsg[subgroup];
325 :
326 4779886 : if (nextindex >= ima->maxmsgs)
327 : {
328 601778 : if (ima->msgs == NULL)
329 : {
330 : /* Create new storage array in TopTransactionContext */
331 562509 : int reqsize = 32; /* arbitrary */
332 :
333 562509 : ima->msgs = (SharedInvalidationMessage *)
334 562509 : MemoryContextAlloc(TopTransactionContext,
335 : reqsize * sizeof(SharedInvalidationMessage));
336 562509 : ima->maxmsgs = reqsize;
337 : Assert(nextindex == 0);
338 : }
339 : else
340 : {
341 : /* Enlarge storage array */
342 39269 : int reqsize = 2 * ima->maxmsgs;
343 :
344 39269 : ima->msgs = (SharedInvalidationMessage *)
345 39269 : repalloc(ima->msgs,
346 : reqsize * sizeof(SharedInvalidationMessage));
347 39269 : ima->maxmsgs = reqsize;
348 : }
349 : }
350 : /* Okay, add message to current group */
351 4779886 : ima->msgs[nextindex] = *msg;
352 4779886 : group->nextmsg[subgroup]++;
353 4779886 : }
354 :
355 : /*
356 : * Append one subgroup of invalidation messages to another, resetting
357 : * the source subgroup to empty.
358 : */
359 : static void
360 1332062 : AppendInvalidationMessageSubGroup(InvalidationMsgsGroup *dest,
361 : InvalidationMsgsGroup *src,
362 : int subgroup)
363 : {
364 : /* Messages must be adjacent in main array */
365 : Assert(dest->nextmsg[subgroup] == src->firstmsg[subgroup]);
366 :
367 : /* ... which makes this easy: */
368 1332062 : dest->nextmsg[subgroup] = src->nextmsg[subgroup];
369 :
370 : /*
371 : * This is handy for some callers and irrelevant for others. But we do it
372 : * always, reasoning that it's bad to leave different groups pointing at
373 : * the same fragment of the message array.
374 : */
375 1332062 : SetSubGroupToFollow(src, dest, subgroup);
376 1332062 : }
377 :
378 : /*
379 : * Process a subgroup of invalidation messages.
380 : *
381 : * This is a macro that executes the given code fragment for each message in
382 : * a message subgroup. The fragment should refer to the message as *msg.
383 : */
384 : #define ProcessMessageSubGroup(group, subgroup, codeFragment) \
385 : do { \
386 : int _msgindex = (group)->firstmsg[subgroup]; \
387 : int _endmsg = (group)->nextmsg[subgroup]; \
388 : for (; _msgindex < _endmsg; _msgindex++) \
389 : { \
390 : SharedInvalidationMessage *msg = \
391 : &InvalMessageArrays[subgroup].msgs[_msgindex]; \
392 : codeFragment; \
393 : } \
394 : } while (0)
395 :
396 : /*
397 : * Process a subgroup of invalidation messages as an array.
398 : *
399 : * As above, but the code fragment can handle an array of messages.
400 : * The fragment should refer to the messages as msgs[], with n entries.
401 : */
402 : #define ProcessMessageSubGroupMulti(group, subgroup, codeFragment) \
403 : do { \
404 : int n = NumMessagesInSubGroup(group, subgroup); \
405 : if (n > 0) { \
406 : SharedInvalidationMessage *msgs = \
407 : &InvalMessageArrays[subgroup].msgs[(group)->firstmsg[subgroup]]; \
408 : codeFragment; \
409 : } \
410 : } while (0)
411 :
412 :
413 : /* ----------------------------------------------------------------
414 : * Invalidation group support functions
415 : *
416 : * These routines understand about the division of a logical invalidation
417 : * group into separate physical arrays for catcache and relcache entries.
418 : * ----------------------------------------------------------------
419 : */
420 :
421 : /*
422 : * Add a catcache inval entry
423 : */
424 : static void
425 3756642 : AddCatcacheInvalidationMessage(InvalidationMsgsGroup *group,
426 : int id, uint32 hashValue, Oid dbId)
427 : {
428 : SharedInvalidationMessage msg;
429 :
430 : Assert(id < CHAR_MAX);
431 3756642 : msg.cc.id = (int8) id;
432 3756642 : msg.cc.dbId = dbId;
433 3756642 : msg.cc.hashValue = hashValue;
434 :
435 : /*
436 : * Define padding bytes in SharedInvalidationMessage structs to be
437 : * defined. Otherwise the sinvaladt.c ringbuffer, which is accessed by
438 : * multiple processes, will cause spurious valgrind warnings about
439 : * undefined memory being used. That's because valgrind remembers the
440 : * undefined bytes from the last local process's store, not realizing that
441 : * another process has written since, filling the previously uninitialized
442 : * bytes
443 : */
444 : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
445 :
446 3756642 : AddInvalidationMessage(group, CatCacheMsgs, &msg);
447 3756642 : }
448 :
449 : /*
450 : * Add a whole-catalog inval entry
451 : */
452 : static void
453 121 : AddCatalogInvalidationMessage(InvalidationMsgsGroup *group,
454 : Oid dbId, Oid catId)
455 : {
456 : SharedInvalidationMessage msg;
457 :
458 121 : msg.cat.id = SHAREDINVALCATALOG_ID;
459 121 : msg.cat.dbId = dbId;
460 121 : msg.cat.catId = catId;
461 : /* check AddCatcacheInvalidationMessage() for an explanation */
462 : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
463 :
464 121 : AddInvalidationMessage(group, CatCacheMsgs, &msg);
465 121 : }
466 :
467 : /*
468 : * Add a relcache inval entry
469 : */
470 : static void
471 1480911 : AddRelcacheInvalidationMessage(InvalidationMsgsGroup *group,
472 : Oid dbId, Oid relId)
473 : {
474 : SharedInvalidationMessage msg;
475 :
476 : /*
477 : * Don't add a duplicate item. We assume dbId need not be checked because
478 : * it will never change. InvalidOid for relId means all relations so we
479 : * don't need to add individual ones when it is present.
480 : */
481 4463068 : ProcessMessageSubGroup(group, RelCacheMsgs,
482 : if (msg->rc.id == SHAREDINVALRELCACHE_ID &&
483 : (msg->rc.relId == relId ||
484 : msg->rc.relId == InvalidOid))
485 : return);
486 :
487 : /* OK, add the item */
488 658537 : msg.rc.id = SHAREDINVALRELCACHE_ID;
489 658537 : msg.rc.dbId = dbId;
490 658537 : msg.rc.relId = relId;
491 : /* check AddCatcacheInvalidationMessage() for an explanation */
492 : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
493 :
494 658537 : AddInvalidationMessage(group, RelCacheMsgs, &msg);
495 : }
496 :
497 : /*
498 : * Add a relsync inval entry
499 : *
500 : * We put these into the relcache subgroup for simplicity. This message is the
501 : * same as AddRelcacheInvalidationMessage() except that it is for
502 : * RelationSyncCache maintained by decoding plugin pgoutput.
503 : */
504 : static void
505 6 : AddRelsyncInvalidationMessage(InvalidationMsgsGroup *group,
506 : Oid dbId, Oid relId)
507 : {
508 : SharedInvalidationMessage msg;
509 :
510 : /* Don't add a duplicate item. */
511 6 : ProcessMessageSubGroup(group, RelCacheMsgs,
512 : if (msg->rc.id == SHAREDINVALRELSYNC_ID &&
513 : (msg->rc.relId == relId ||
514 : msg->rc.relId == InvalidOid))
515 : return);
516 :
517 : /* OK, add the item */
518 6 : msg.rc.id = SHAREDINVALRELSYNC_ID;
519 6 : msg.rc.dbId = dbId;
520 6 : msg.rc.relId = relId;
521 : /* check AddCatcacheInvalidationMessage() for an explanation */
522 : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
523 :
524 6 : AddInvalidationMessage(group, RelCacheMsgs, &msg);
525 : }
526 :
527 : /*
528 : * Add a snapshot inval entry
529 : *
530 : * We put these into the relcache subgroup for simplicity.
531 : */
532 : static void
533 726339 : AddSnapshotInvalidationMessage(InvalidationMsgsGroup *group,
534 : Oid dbId, Oid relId)
535 : {
536 : SharedInvalidationMessage msg;
537 :
538 : /* Don't add a duplicate item */
539 : /* We assume dbId need not be checked because it will never change */
540 1067758 : ProcessMessageSubGroup(group, RelCacheMsgs,
541 : if (msg->sn.id == SHAREDINVALSNAPSHOT_ID &&
542 : msg->sn.relId == relId)
543 : return);
544 :
545 : /* OK, add the item */
546 364580 : msg.sn.id = SHAREDINVALSNAPSHOT_ID;
547 364580 : msg.sn.dbId = dbId;
548 364580 : msg.sn.relId = relId;
549 : /* check AddCatcacheInvalidationMessage() for an explanation */
550 : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
551 :
552 364580 : AddInvalidationMessage(group, RelCacheMsgs, &msg);
553 : }
554 :
555 : /*
556 : * Append one group of invalidation messages to another, resetting
557 : * the source group to empty.
558 : */
559 : static void
560 666031 : AppendInvalidationMessages(InvalidationMsgsGroup *dest,
561 : InvalidationMsgsGroup *src)
562 : {
563 666031 : AppendInvalidationMessageSubGroup(dest, src, CatCacheMsgs);
564 666031 : AppendInvalidationMessageSubGroup(dest, src, RelCacheMsgs);
565 666031 : }
566 :
567 : /*
568 : * Execute the given function for all the messages in an invalidation group.
569 : * The group is not altered.
570 : *
571 : * catcache entries are processed first, for reasons mentioned above.
572 : */
573 : static void
574 514183 : ProcessInvalidationMessages(InvalidationMsgsGroup *group,
575 : void (*func) (SharedInvalidationMessage *msg))
576 : {
577 3840468 : ProcessMessageSubGroup(group, CatCacheMsgs, func(msg));
578 1303354 : ProcessMessageSubGroup(group, RelCacheMsgs, func(msg));
579 514179 : }
580 :
581 : /*
582 : * As above, but the function is able to process an array of messages
583 : * rather than just one at a time.
584 : */
585 : static void
586 237967 : ProcessInvalidationMessagesMulti(InvalidationMsgsGroup *group,
587 : void (*func) (const SharedInvalidationMessage *msgs, int n))
588 : {
589 237967 : ProcessMessageSubGroupMulti(group, CatCacheMsgs, func(msgs, n));
590 237967 : ProcessMessageSubGroupMulti(group, RelCacheMsgs, func(msgs, n));
591 237967 : }
592 :
593 : /* ----------------------------------------------------------------
594 : * private support functions
595 : * ----------------------------------------------------------------
596 : */
597 :
598 : /*
599 : * RegisterCatcacheInvalidation
600 : *
601 : * Register an invalidation event for a catcache tuple entry.
602 : */
603 : static void
604 3756642 : RegisterCatcacheInvalidation(int cacheId,
605 : uint32 hashValue,
606 : Oid dbId,
607 : void *context)
608 : {
609 3756642 : InvalidationInfo *info = (InvalidationInfo *) context;
610 :
611 3756642 : AddCatcacheInvalidationMessage(&info->CurrentCmdInvalidMsgs,
612 : cacheId, hashValue, dbId);
613 3756642 : }
614 :
615 : /*
616 : * RegisterCatalogInvalidation
617 : *
618 : * Register an invalidation event for all catcache entries from a catalog.
619 : */
620 : static void
621 121 : RegisterCatalogInvalidation(InvalidationInfo *info, Oid dbId, Oid catId)
622 : {
623 121 : AddCatalogInvalidationMessage(&info->CurrentCmdInvalidMsgs, dbId, catId);
624 121 : }
625 :
626 : /*
627 : * RegisterRelcacheInvalidation
628 : *
629 : * As above, but register a relcache invalidation event.
630 : */
631 : static void
632 1480911 : RegisterRelcacheInvalidation(InvalidationInfo *info, Oid dbId, Oid relId)
633 : {
634 1480911 : AddRelcacheInvalidationMessage(&info->CurrentCmdInvalidMsgs, dbId, relId);
635 :
636 : /*
637 : * Most of the time, relcache invalidation is associated with system
638 : * catalog updates, but there are a few cases where it isn't. Quick hack
639 : * to ensure that the next CommandCounterIncrement() will think that we
640 : * need to do CommandEndInvalidationMessages().
641 : */
642 1480911 : (void) GetCurrentCommandId(true);
643 :
644 : /*
645 : * If the relation being invalidated is one of those cached in a relcache
646 : * init file, mark that we need to zap that file at commit. For simplicity
647 : * invalidations for a specific database always invalidate the shared file
648 : * as well. Also zap when we are invalidating whole relcache.
649 : */
650 1480911 : if (relId == InvalidOid || RelationIdIsInInitFile(relId))
651 131709 : info->RelcacheInitFileInval = true;
652 1480911 : }
653 :
654 : /*
655 : * RegisterRelsyncInvalidation
656 : *
657 : * As above, but register a relsynccache invalidation event.
658 : */
659 : static void
660 6 : RegisterRelsyncInvalidation(InvalidationInfo *info, Oid dbId, Oid relId)
661 : {
662 6 : AddRelsyncInvalidationMessage(&info->CurrentCmdInvalidMsgs, dbId, relId);
663 6 : }
664 :
665 : /*
666 : * RegisterSnapshotInvalidation
667 : *
668 : * Register an invalidation event for MVCC scans against a given catalog.
669 : * Only needed for catalogs that don't have catcaches.
670 : */
671 : static void
672 726339 : RegisterSnapshotInvalidation(InvalidationInfo *info, Oid dbId, Oid relId)
673 : {
674 726339 : AddSnapshotInvalidationMessage(&info->CurrentCmdInvalidMsgs, dbId, relId);
675 726339 : }
676 :
677 : /*
678 : * PrepareInvalidationState
679 : * Initialize inval data for the current (sub)transaction.
680 : */
681 : static InvalidationInfo *
682 2743289 : PrepareInvalidationState(void)
683 : {
684 : TransInvalidationInfo *myInfo;
685 :
686 : /* PrepareToInvalidateCacheTuple() needs relcache */
687 2743289 : AssertCouldGetRelation();
688 : /* Can't queue transactional message while collecting inplace messages. */
689 : Assert(inplaceInvalInfo == NULL);
690 :
691 5327708 : if (transInvalInfo != NULL &&
692 2584419 : transInvalInfo->my_level == GetCurrentTransactionNestLevel())
693 2584313 : return (InvalidationInfo *) transInvalInfo;
694 :
695 : myInfo = (TransInvalidationInfo *)
696 158976 : MemoryContextAllocZero(TopTransactionContext,
697 : sizeof(TransInvalidationInfo));
698 158976 : myInfo->parent = transInvalInfo;
699 158976 : myInfo->my_level = GetCurrentTransactionNestLevel();
700 :
701 : /* Now, do we have a previous stack entry? */
702 158976 : if (transInvalInfo != NULL)
703 : {
704 : /* Yes; this one should be for a deeper nesting level. */
705 : Assert(myInfo->my_level > transInvalInfo->my_level);
706 :
707 : /*
708 : * The parent (sub)transaction must not have any current (i.e.,
709 : * not-yet-locally-processed) messages. If it did, we'd have a
710 : * semantic problem: the new subtransaction presumably ought not be
711 : * able to see those events yet, but since the CommandCounter is
712 : * linear, that can't work once the subtransaction advances the
713 : * counter. This is a convenient place to check for that, as well as
714 : * being important to keep management of the message arrays simple.
715 : */
716 106 : if (NumMessagesInGroup(&transInvalInfo->ii.CurrentCmdInvalidMsgs) != 0)
717 0 : elog(ERROR, "cannot start a subtransaction when there are unprocessed inval messages");
718 :
719 : /*
720 : * MemoryContextAllocZero set firstmsg = nextmsg = 0 in each group,
721 : * which is fine for the first (sub)transaction, but otherwise we need
722 : * to update them to follow whatever is already in the arrays.
723 : */
724 106 : SetGroupToFollow(&myInfo->PriorCmdInvalidMsgs,
725 : &transInvalInfo->ii.CurrentCmdInvalidMsgs);
726 106 : SetGroupToFollow(&myInfo->ii.CurrentCmdInvalidMsgs,
727 : &myInfo->PriorCmdInvalidMsgs);
728 : }
729 : else
730 : {
731 : /*
732 : * Here, we need only clear any array pointers left over from a prior
733 : * transaction.
734 : */
735 158870 : InvalMessageArrays[CatCacheMsgs].msgs = NULL;
736 158870 : InvalMessageArrays[CatCacheMsgs].maxmsgs = 0;
737 158870 : InvalMessageArrays[RelCacheMsgs].msgs = NULL;
738 158870 : InvalMessageArrays[RelCacheMsgs].maxmsgs = 0;
739 : }
740 :
741 158976 : transInvalInfo = myInfo;
742 158976 : return (InvalidationInfo *) myInfo;
743 : }
744 :
745 : /*
746 : * PrepareInplaceInvalidationState
747 : * Initialize inval data for an inplace update.
748 : *
749 : * See previous function for more background.
750 : */
751 : static InvalidationInfo *
752 206392 : PrepareInplaceInvalidationState(void)
753 : {
754 : InvalidationInfo *myInfo;
755 :
756 206392 : AssertCouldGetRelation();
757 : /* limit of one inplace update under assembly */
758 : Assert(inplaceInvalInfo == NULL);
759 :
760 : /* gone after WAL insertion CritSection ends, so use current context */
761 206392 : myInfo = palloc0_object(InvalidationInfo);
762 :
763 : /* Stash our messages past end of the transactional messages, if any. */
764 206392 : if (transInvalInfo != NULL)
765 70764 : SetGroupToFollow(&myInfo->CurrentCmdInvalidMsgs,
766 : &transInvalInfo->ii.CurrentCmdInvalidMsgs);
767 : else
768 : {
769 135628 : InvalMessageArrays[CatCacheMsgs].msgs = NULL;
770 135628 : InvalMessageArrays[CatCacheMsgs].maxmsgs = 0;
771 135628 : InvalMessageArrays[RelCacheMsgs].msgs = NULL;
772 135628 : InvalMessageArrays[RelCacheMsgs].maxmsgs = 0;
773 : }
774 :
775 206392 : inplaceInvalInfo = myInfo;
776 206392 : return myInfo;
777 : }
778 :
779 : /* ----------------------------------------------------------------
780 : * public functions
781 : * ----------------------------------------------------------------
782 : */
783 :
784 : void
785 2779 : InvalidateSystemCachesExtended(bool debug_discard)
786 : {
787 : int i;
788 :
789 2779 : InvalidateCatalogSnapshot();
790 2779 : ResetCatalogCachesExt(debug_discard);
791 2779 : RelationCacheInvalidate(debug_discard); /* gets smgr and relmap too */
792 :
793 47365 : for (i = 0; i < syscache_callback_count; i++)
794 : {
795 44586 : struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i;
796 :
797 44586 : ccitem->function(ccitem->arg, ccitem->id, 0);
798 : }
799 :
800 6227 : for (i = 0; i < relcache_callback_count; i++)
801 : {
802 3448 : struct RELCACHECALLBACK *ccitem = relcache_callback_list + i;
803 :
804 3448 : ccitem->function(ccitem->arg, InvalidOid);
805 : }
806 :
807 2805 : for (i = 0; i < relsync_callback_count; i++)
808 : {
809 26 : struct RELSYNCCALLBACK *ccitem = relsync_callback_list + i;
810 :
811 26 : ccitem->function(ccitem->arg, InvalidOid);
812 : }
813 2779 : }
814 :
815 : /*
816 : * LocalExecuteInvalidationMessage
817 : *
818 : * Process a single invalidation message (which could be of any type).
819 : * Only the local caches are flushed; this does not transmit the message
820 : * to other backends.
821 : */
822 : void
823 25448305 : LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
824 : {
825 25448305 : if (msg->id >= 0)
826 : {
827 20184018 : if (msg->cc.dbId == MyDatabaseId || msg->cc.dbId == InvalidOid)
828 : {
829 15642560 : InvalidateCatalogSnapshot();
830 :
831 15642560 : SysCacheInvalidate(msg->cc.id, msg->cc.hashValue);
832 :
833 15642560 : CallSyscacheCallbacks(msg->cc.id, msg->cc.hashValue);
834 : }
835 : }
836 5264287 : else if (msg->id == SHAREDINVALCATALOG_ID)
837 : {
838 542 : if (msg->cat.dbId == MyDatabaseId || msg->cat.dbId == InvalidOid)
839 : {
840 468 : InvalidateCatalogSnapshot();
841 :
842 468 : CatalogCacheFlushCatalog(msg->cat.catId);
843 :
844 : /* CatalogCacheFlushCatalog calls CallSyscacheCallbacks as needed */
845 : }
846 : }
847 5263745 : else if (msg->id == SHAREDINVALRELCACHE_ID)
848 : {
849 2851111 : if (msg->rc.dbId == MyDatabaseId || msg->rc.dbId == InvalidOid)
850 : {
851 : int i;
852 :
853 2205814 : if (msg->rc.relId == InvalidOid)
854 714 : RelationCacheInvalidate(false);
855 : else
856 2205100 : RelationCacheInvalidateEntry(msg->rc.relId);
857 :
858 6096433 : for (i = 0; i < relcache_callback_count; i++)
859 : {
860 3890623 : struct RELCACHECALLBACK *ccitem = relcache_callback_list + i;
861 :
862 3890623 : ccitem->function(ccitem->arg, msg->rc.relId);
863 : }
864 : }
865 : }
866 2412634 : else if (msg->id == SHAREDINVALSMGR_ID)
867 : {
868 : /*
869 : * We could have smgr entries for relations of other databases, so no
870 : * short-circuit test is possible here.
871 : */
872 : RelFileLocatorBackend rlocator;
873 :
874 324520 : rlocator.locator = msg->sm.rlocator;
875 324520 : rlocator.backend = (msg->sm.backend_hi << 16) | (int) msg->sm.backend_lo;
876 324520 : smgrreleaserellocator(rlocator);
877 : }
878 2088114 : else if (msg->id == SHAREDINVALRELMAP_ID)
879 : {
880 : /* We only care about our own database and shared catalogs */
881 405 : if (msg->rm.dbId == InvalidOid)
882 172 : RelationMapInvalidate(true);
883 233 : else if (msg->rm.dbId == MyDatabaseId)
884 166 : RelationMapInvalidate(false);
885 : }
886 2087709 : else if (msg->id == SHAREDINVALSNAPSHOT_ID)
887 : {
888 : /* We only care about our own database and shared catalogs */
889 2087678 : if (msg->sn.dbId == InvalidOid)
890 62274 : InvalidateCatalogSnapshot();
891 2025404 : else if (msg->sn.dbId == MyDatabaseId)
892 1611500 : InvalidateCatalogSnapshot();
893 : }
894 31 : else if (msg->id == SHAREDINVALRELSYNC_ID)
895 : {
896 : /* We only care about our own database */
897 31 : if (msg->rs.dbId == MyDatabaseId)
898 31 : CallRelSyncCallbacks(msg->rs.relid);
899 : }
900 : else
901 0 : elog(FATAL, "unrecognized SI message ID: %d", msg->id);
902 25448301 : }
903 :
904 : /*
905 : * InvalidateSystemCaches
906 : *
907 : * This blows away all tuples in the system catalog caches and
908 : * all the cached relation descriptors and smgr cache entries.
909 : * Relation descriptors that have positive refcounts are then rebuilt.
910 : *
911 : * We call this when we see a shared-inval-queue overflow signal,
912 : * since that tells us we've lost some shared-inval messages and hence
913 : * don't know what needs to be invalidated.
914 : */
915 : void
916 2779 : InvalidateSystemCaches(void)
917 : {
918 2779 : InvalidateSystemCachesExtended(false);
919 2779 : }
920 :
921 : /*
922 : * AcceptInvalidationMessages
923 : * Read and process invalidation messages from the shared invalidation
924 : * message queue.
925 : *
926 : * Note:
927 : * This should be called as the first step in processing a transaction.
928 : */
929 : void
930 24699206 : AcceptInvalidationMessages(void)
931 : {
932 : #ifdef USE_ASSERT_CHECKING
933 : /* message handlers shall access catalogs only during transactions */
934 : if (IsTransactionState())
935 : AssertCouldGetRelation();
936 : #endif
937 :
938 24699206 : ReceiveSharedInvalidMessages(LocalExecuteInvalidationMessage,
939 : InvalidateSystemCaches);
940 :
941 : /*----------
942 : * Test code to force cache flushes anytime a flush could happen.
943 : *
944 : * This helps detect intermittent faults caused by code that reads a cache
945 : * entry and then performs an action that could invalidate the entry, but
946 : * rarely actually does so. This can spot issues that would otherwise
947 : * only arise with badly timed concurrent DDL, for example.
948 : *
949 : * The default debug_discard_caches = 0 does no forced cache flushes.
950 : *
951 : * If used with CLOBBER_FREED_MEMORY,
952 : * debug_discard_caches = 1 (formerly known as CLOBBER_CACHE_ALWAYS)
953 : * provides a fairly thorough test that the system contains no cache-flush
954 : * hazards. However, it also makes the system unbelievably slow --- the
955 : * regression tests take about 100 times longer than normal.
956 : *
957 : * If you're a glutton for punishment, try
958 : * debug_discard_caches = 3 (formerly known as CLOBBER_CACHE_RECURSIVELY).
959 : * This slows things by at least a factor of 10000, so I wouldn't suggest
960 : * trying to run the entire regression tests that way. It's useful to try
961 : * a few simple tests, to make sure that cache reload isn't subject to
962 : * internal cache-flush hazards, but after you've done a few thousand
963 : * recursive reloads it's unlikely you'll learn more.
964 : *----------
965 : */
966 : #ifdef DISCARD_CACHES_ENABLED
967 : {
968 : static int recursion_depth = 0;
969 :
970 : if (recursion_depth < debug_discard_caches)
971 : {
972 : recursion_depth++;
973 : InvalidateSystemCachesExtended(true);
974 : recursion_depth--;
975 : }
976 : }
977 : #endif
978 24699206 : }
979 :
980 : /*
981 : * PostPrepare_Inval
982 : * Clean up after successful PREPARE.
983 : *
984 : * Here, we want to act as though the transaction aborted, so that we will
985 : * undo any syscache changes it made, thereby bringing us into sync with the
986 : * outside world, which doesn't believe the transaction committed yet.
987 : *
988 : * If the prepared transaction is later aborted, there is nothing more to
989 : * do; if it commits, we will receive the consequent inval messages just
990 : * like everyone else.
991 : */
992 : void
993 325 : PostPrepare_Inval(void)
994 : {
995 325 : AtEOXact_Inval(false);
996 325 : }
997 :
998 : /*
999 : * xactGetCommittedInvalidationMessages() is called by
1000 : * RecordTransactionCommit() to collect invalidation messages to add to the
1001 : * commit record. This applies only to commit message types, never to
1002 : * abort records. Must always run before AtEOXact_Inval(), since that
1003 : * removes the data we need to see.
1004 : *
1005 : * Remember that this runs before we have officially committed, so we
1006 : * must not do anything here to change what might occur *if* we should
1007 : * fail between here and the actual commit.
1008 : *
1009 : * see also xact_redo_commit() and xact_desc_commit()
1010 : */
1011 : int
1012 328300 : xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs,
1013 : bool *RelcacheInitFileInval)
1014 : {
1015 : SharedInvalidationMessage *msgarray;
1016 : int nummsgs;
1017 : int nmsgs;
1018 :
1019 : /* Quick exit if we haven't done anything with invalidation messages. */
1020 328300 : if (transInvalInfo == NULL)
1021 : {
1022 204217 : *RelcacheInitFileInval = false;
1023 204217 : *msgs = NULL;
1024 204217 : return 0;
1025 : }
1026 :
1027 : /* Must be at top of stack */
1028 : Assert(transInvalInfo->my_level == 1 && transInvalInfo->parent == NULL);
1029 :
1030 : /*
1031 : * Relcache init file invalidation requires processing both before and
1032 : * after we send the SI messages. However, we need not do anything unless
1033 : * we committed.
1034 : */
1035 124083 : *RelcacheInitFileInval = transInvalInfo->ii.RelcacheInitFileInval;
1036 :
1037 : /*
1038 : * Collect all the pending messages into a single contiguous array of
1039 : * invalidation messages, to simplify what needs to happen while building
1040 : * the commit WAL message. Maintain the order that they would be
1041 : * processed in by AtEOXact_Inval(), to ensure emulated behaviour in redo
1042 : * is as similar as possible to original. We want the same bugs, if any,
1043 : * not new ones.
1044 : */
1045 124083 : nummsgs = NumMessagesInGroup(&transInvalInfo->PriorCmdInvalidMsgs) +
1046 124083 : NumMessagesInGroup(&transInvalInfo->ii.CurrentCmdInvalidMsgs);
1047 :
1048 124083 : *msgs = msgarray = (SharedInvalidationMessage *)
1049 124083 : MemoryContextAlloc(CurTransactionContext,
1050 : nummsgs * sizeof(SharedInvalidationMessage));
1051 :
1052 124083 : nmsgs = 0;
1053 124083 : ProcessMessageSubGroupMulti(&transInvalInfo->PriorCmdInvalidMsgs,
1054 : CatCacheMsgs,
1055 : (memcpy(msgarray + nmsgs,
1056 : msgs,
1057 : n * sizeof(SharedInvalidationMessage)),
1058 : nmsgs += n));
1059 124083 : ProcessMessageSubGroupMulti(&transInvalInfo->ii.CurrentCmdInvalidMsgs,
1060 : CatCacheMsgs,
1061 : (memcpy(msgarray + nmsgs,
1062 : msgs,
1063 : n * sizeof(SharedInvalidationMessage)),
1064 : nmsgs += n));
1065 124083 : ProcessMessageSubGroupMulti(&transInvalInfo->PriorCmdInvalidMsgs,
1066 : RelCacheMsgs,
1067 : (memcpy(msgarray + nmsgs,
1068 : msgs,
1069 : n * sizeof(SharedInvalidationMessage)),
1070 : nmsgs += n));
1071 124083 : ProcessMessageSubGroupMulti(&transInvalInfo->ii.CurrentCmdInvalidMsgs,
1072 : RelCacheMsgs,
1073 : (memcpy(msgarray + nmsgs,
1074 : msgs,
1075 : n * sizeof(SharedInvalidationMessage)),
1076 : nmsgs += n));
1077 : Assert(nmsgs == nummsgs);
1078 :
1079 124083 : return nmsgs;
1080 : }
1081 :
1082 : /*
1083 : * inplaceGetInvalidationMessages() is called by the inplace update to collect
1084 : * invalidation messages to add to its WAL record. Like the previous
1085 : * function, we might still fail.
1086 : */
1087 : int
1088 73219 : inplaceGetInvalidationMessages(SharedInvalidationMessage **msgs,
1089 : bool *RelcacheInitFileInval)
1090 : {
1091 : SharedInvalidationMessage *msgarray;
1092 : int nummsgs;
1093 : int nmsgs;
1094 :
1095 : /* Quick exit if we haven't done anything with invalidation messages. */
1096 73219 : if (inplaceInvalInfo == NULL)
1097 : {
1098 17955 : *RelcacheInitFileInval = false;
1099 17955 : *msgs = NULL;
1100 17955 : return 0;
1101 : }
1102 :
1103 55264 : *RelcacheInitFileInval = inplaceInvalInfo->RelcacheInitFileInval;
1104 55264 : nummsgs = NumMessagesInGroup(&inplaceInvalInfo->CurrentCmdInvalidMsgs);
1105 55264 : *msgs = msgarray = (SharedInvalidationMessage *)
1106 55264 : palloc(nummsgs * sizeof(SharedInvalidationMessage));
1107 :
1108 55264 : nmsgs = 0;
1109 55264 : ProcessMessageSubGroupMulti(&inplaceInvalInfo->CurrentCmdInvalidMsgs,
1110 : CatCacheMsgs,
1111 : (memcpy(msgarray + nmsgs,
1112 : msgs,
1113 : n * sizeof(SharedInvalidationMessage)),
1114 : nmsgs += n));
1115 55264 : ProcessMessageSubGroupMulti(&inplaceInvalInfo->CurrentCmdInvalidMsgs,
1116 : RelCacheMsgs,
1117 : (memcpy(msgarray + nmsgs,
1118 : msgs,
1119 : n * sizeof(SharedInvalidationMessage)),
1120 : nmsgs += n));
1121 : Assert(nmsgs == nummsgs);
1122 :
1123 55264 : return nmsgs;
1124 : }
1125 :
1126 : /*
1127 : * ProcessCommittedInvalidationMessages is executed by xact_redo_commit() or
1128 : * standby_redo() to process invalidation messages. Currently that happens
1129 : * only at end-of-xact.
1130 : *
1131 : * Relcache init file invalidation requires processing both
1132 : * before and after we send the SI messages. See AtEOXact_Inval()
1133 : */
1134 : void
1135 30700 : ProcessCommittedInvalidationMessages(SharedInvalidationMessage *msgs,
1136 : int nmsgs, bool RelcacheInitFileInval,
1137 : Oid dbid, Oid tsid)
1138 : {
1139 30700 : if (nmsgs <= 0)
1140 5553 : return;
1141 :
1142 25147 : elog(DEBUG4, "replaying commit with %d messages%s", nmsgs,
1143 : (RelcacheInitFileInval ? " and relcache file invalidation" : ""));
1144 :
1145 25147 : if (RelcacheInitFileInval)
1146 : {
1147 513 : elog(DEBUG4, "removing relcache init files for database %u", dbid);
1148 :
1149 : /*
1150 : * RelationCacheInitFilePreInvalidate, when the invalidation message
1151 : * is for a specific database, requires DatabasePath to be set, but we
1152 : * should not use SetDatabasePath during recovery, since it is
1153 : * intended to be used only once by normal backends. Hence, a quick
1154 : * hack: set DatabasePath directly then unset after use.
1155 : */
1156 513 : if (OidIsValid(dbid))
1157 513 : DatabasePath = GetDatabasePath(dbid, tsid);
1158 :
1159 513 : RelationCacheInitFilePreInvalidate();
1160 :
1161 513 : if (OidIsValid(dbid))
1162 : {
1163 513 : pfree(DatabasePath);
1164 513 : DatabasePath = NULL;
1165 : }
1166 : }
1167 :
1168 25147 : SendSharedInvalidMessages(msgs, nmsgs);
1169 :
1170 25147 : if (RelcacheInitFileInval)
1171 513 : RelationCacheInitFilePostInvalidate();
1172 : }
1173 :
1174 : /*
1175 : * AtEOXact_Inval
1176 : * Process queued-up invalidation messages at end of main transaction.
1177 : *
1178 : * If isCommit, we must send out the messages in our PriorCmdInvalidMsgs list
1179 : * to the shared invalidation message queue. Note that these will be read
1180 : * not only by other backends, but also by our own backend at the next
1181 : * transaction start (via AcceptInvalidationMessages). This means that
1182 : * we can skip immediate local processing of anything that's still in
1183 : * CurrentCmdInvalidMsgs, and just send that list out too.
1184 : *
1185 : * If not isCommit, we are aborting, and must locally process the messages
1186 : * in PriorCmdInvalidMsgs. No messages need be sent to other backends,
1187 : * since they'll not have seen our changed tuples anyway. We can forget
1188 : * about CurrentCmdInvalidMsgs too, since those changes haven't touched
1189 : * the caches yet.
1190 : *
1191 : * In any case, reset our state to empty. We need not physically
1192 : * free memory here, since TopTransactionContext is about to be emptied
1193 : * anyway.
1194 : */
1195 : void
1196 652822 : AtEOXact_Inval(bool isCommit)
1197 : {
1198 652822 : inplaceInvalInfo = NULL;
1199 :
1200 : /* Quick exit if no transactional messages */
1201 652822 : if (transInvalInfo == NULL)
1202 493993 : return;
1203 :
1204 : /* Must be at top of stack */
1205 : Assert(transInvalInfo->my_level == 1 && transInvalInfo->parent == NULL);
1206 :
1207 158829 : INJECTION_POINT("transaction-end-process-inval", NULL);
1208 :
1209 158829 : if (isCommit)
1210 : {
1211 : /*
1212 : * Relcache init file invalidation requires processing both before and
1213 : * after we send the SI messages. However, we need not do anything
1214 : * unless we committed.
1215 : */
1216 155399 : if (transInvalInfo->ii.RelcacheInitFileInval)
1217 22493 : RelationCacheInitFilePreInvalidate();
1218 :
1219 155399 : AppendInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
1220 155399 : &transInvalInfo->ii.CurrentCmdInvalidMsgs);
1221 :
1222 155399 : ProcessInvalidationMessagesMulti(&transInvalInfo->PriorCmdInvalidMsgs,
1223 : SendSharedInvalidMessages);
1224 :
1225 155399 : if (transInvalInfo->ii.RelcacheInitFileInval)
1226 22493 : RelationCacheInitFilePostInvalidate();
1227 : }
1228 : else
1229 : {
1230 3430 : ProcessInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
1231 : LocalExecuteInvalidationMessage);
1232 : }
1233 :
1234 : /* Need not free anything explicitly */
1235 158829 : transInvalInfo = NULL;
1236 : }
1237 :
1238 : /*
1239 : * PreInplace_Inval
1240 : * Process queued-up invalidation before inplace update critical section.
1241 : *
1242 : * Tasks belong here if they are safe even if the inplace update does not
1243 : * complete. Currently, this just unlinks a cache file, which can fail. The
1244 : * sum of this and AtInplace_Inval() mirrors AtEOXact_Inval(isCommit=true).
1245 : */
1246 : void
1247 100523 : PreInplace_Inval(void)
1248 : {
1249 : Assert(CritSectionCount == 0);
1250 :
1251 100523 : if (inplaceInvalInfo && inplaceInvalInfo->RelcacheInitFileInval)
1252 20580 : RelationCacheInitFilePreInvalidate();
1253 100523 : }
1254 :
1255 : /*
1256 : * AtInplace_Inval
1257 : * Process queued-up invalidations after inplace update buffer mutation.
1258 : */
1259 : void
1260 100523 : AtInplace_Inval(void)
1261 : {
1262 : Assert(CritSectionCount > 0);
1263 :
1264 100523 : if (inplaceInvalInfo == NULL)
1265 17955 : return;
1266 :
1267 82568 : ProcessInvalidationMessagesMulti(&inplaceInvalInfo->CurrentCmdInvalidMsgs,
1268 : SendSharedInvalidMessages);
1269 :
1270 82568 : if (inplaceInvalInfo->RelcacheInitFileInval)
1271 20580 : RelationCacheInitFilePostInvalidate();
1272 :
1273 82568 : inplaceInvalInfo = NULL;
1274 : }
1275 :
1276 : /*
1277 : * ForgetInplace_Inval
1278 : * Alternative to PreInplace_Inval()+AtInplace_Inval(): discard queued-up
1279 : * invalidations. This lets inplace update enumerate invalidations
1280 : * optimistically, before locking the buffer.
1281 : */
1282 : void
1283 127586 : ForgetInplace_Inval(void)
1284 : {
1285 127586 : inplaceInvalInfo = NULL;
1286 127586 : }
1287 :
1288 : /*
1289 : * AtEOSubXact_Inval
1290 : * Process queued-up invalidation messages at end of subtransaction.
1291 : *
1292 : * If isCommit, process CurrentCmdInvalidMsgs if any (there probably aren't),
1293 : * and then attach both CurrentCmdInvalidMsgs and PriorCmdInvalidMsgs to the
1294 : * parent's PriorCmdInvalidMsgs list.
1295 : *
1296 : * If not isCommit, we are aborting, and must locally process the messages
1297 : * in PriorCmdInvalidMsgs. No messages need be sent to other backends.
1298 : * We can forget about CurrentCmdInvalidMsgs too, since those changes haven't
1299 : * touched the caches yet.
1300 : *
1301 : * In any case, pop the transaction stack. We need not physically free memory
1302 : * here, since CurTransactionContext is about to be emptied anyway
1303 : * (if aborting). Beware of the possibility of aborting the same nesting
1304 : * level twice, though.
1305 : */
1306 : void
1307 11048 : AtEOSubXact_Inval(bool isCommit)
1308 : {
1309 : int my_level;
1310 : TransInvalidationInfo *myInfo;
1311 :
1312 : /*
1313 : * Successful inplace update must clear this, but we clear it on abort.
1314 : * Inplace updates allocate this in CurrentMemoryContext, which has
1315 : * lifespan <= subtransaction lifespan. Hence, don't free it explicitly.
1316 : */
1317 11048 : if (isCommit)
1318 : Assert(inplaceInvalInfo == NULL);
1319 : else
1320 5414 : inplaceInvalInfo = NULL;
1321 :
1322 : /* Quick exit if no transactional messages. */
1323 11048 : myInfo = transInvalInfo;
1324 11048 : if (myInfo == NULL)
1325 9946 : return;
1326 :
1327 : /* Also bail out quickly if messages are not for this level. */
1328 1102 : my_level = GetCurrentTransactionNestLevel();
1329 1102 : if (myInfo->my_level != my_level)
1330 : {
1331 : Assert(myInfo->my_level < my_level);
1332 902 : return;
1333 : }
1334 :
1335 200 : if (isCommit)
1336 : {
1337 : /* If CurrentCmdInvalidMsgs still has anything, fix it */
1338 68 : CommandEndInvalidationMessages();
1339 :
1340 : /*
1341 : * We create invalidation stack entries lazily, so the parent might
1342 : * not have one. Instead of creating one, moving all the data over,
1343 : * and then freeing our own, we can just adjust the level of our own
1344 : * entry.
1345 : */
1346 68 : if (myInfo->parent == NULL || myInfo->parent->my_level < my_level - 1)
1347 : {
1348 53 : myInfo->my_level--;
1349 53 : return;
1350 : }
1351 :
1352 : /*
1353 : * Pass up my inval messages to parent. Notice that we stick them in
1354 : * PriorCmdInvalidMsgs, not CurrentCmdInvalidMsgs, since they've
1355 : * already been locally processed. (This would trigger the Assert in
1356 : * AppendInvalidationMessageSubGroup if the parent's
1357 : * CurrentCmdInvalidMsgs isn't empty; but we already checked that in
1358 : * PrepareInvalidationState.)
1359 : */
1360 15 : AppendInvalidationMessages(&myInfo->parent->PriorCmdInvalidMsgs,
1361 : &myInfo->PriorCmdInvalidMsgs);
1362 :
1363 : /* Must readjust parent's CurrentCmdInvalidMsgs indexes now */
1364 15 : SetGroupToFollow(&myInfo->parent->ii.CurrentCmdInvalidMsgs,
1365 : &myInfo->parent->PriorCmdInvalidMsgs);
1366 :
1367 : /* Pending relcache inval becomes parent's problem too */
1368 15 : if (myInfo->ii.RelcacheInitFileInval)
1369 0 : myInfo->parent->ii.RelcacheInitFileInval = true;
1370 :
1371 : /* Pop the transaction state stack */
1372 15 : transInvalInfo = myInfo->parent;
1373 :
1374 : /* Need not free anything else explicitly */
1375 15 : pfree(myInfo);
1376 : }
1377 : else
1378 : {
1379 132 : ProcessInvalidationMessages(&myInfo->PriorCmdInvalidMsgs,
1380 : LocalExecuteInvalidationMessage);
1381 :
1382 : /* Pop the transaction state stack */
1383 132 : transInvalInfo = myInfo->parent;
1384 :
1385 : /* Need not free anything else explicitly */
1386 132 : pfree(myInfo);
1387 : }
1388 : }
1389 :
1390 : /*
1391 : * CommandEndInvalidationMessages
1392 : * Process queued-up invalidation messages at end of one command
1393 : * in a transaction.
1394 : *
1395 : * Here, we send no messages to the shared queue, since we don't know yet if
1396 : * we will commit. We do need to locally process the CurrentCmdInvalidMsgs
1397 : * list, so as to flush our caches of any entries we have outdated in the
1398 : * current command. We then move the current-cmd list over to become part
1399 : * of the prior-cmds list.
1400 : *
1401 : * Note:
1402 : * This should be called during CommandCounterIncrement(),
1403 : * after we have advanced the command ID.
1404 : */
1405 : void
1406 726677 : CommandEndInvalidationMessages(void)
1407 : {
1408 : /*
1409 : * You might think this shouldn't be called outside any transaction, but
1410 : * bootstrap does it, and also ABORT issued when not in a transaction. So
1411 : * just quietly return if no state to work on.
1412 : */
1413 726677 : if (transInvalInfo == NULL)
1414 216056 : return;
1415 :
1416 510621 : ProcessInvalidationMessages(&transInvalInfo->ii.CurrentCmdInvalidMsgs,
1417 : LocalExecuteInvalidationMessage);
1418 :
1419 : /* WAL Log per-command invalidation messages for logical decoding */
1420 510617 : if (XLogLogicalInfoActive())
1421 7752 : LogLogicalInvalidations();
1422 :
1423 510617 : AppendInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
1424 510617 : &transInvalInfo->ii.CurrentCmdInvalidMsgs);
1425 : }
1426 :
1427 :
1428 : /*
1429 : * CacheInvalidateHeapTupleCommon
1430 : * Common logic for end-of-command and inplace variants.
1431 : */
1432 : static void
1433 17378661 : CacheInvalidateHeapTupleCommon(Relation relation,
1434 : HeapTuple tuple,
1435 : HeapTuple newtuple,
1436 : InvalidationInfo *(*prepare_callback) (void))
1437 : {
1438 : InvalidationInfo *info;
1439 : Oid tupleRelId;
1440 : Oid databaseId;
1441 : Oid relationId;
1442 :
1443 : /* PrepareToInvalidateCacheTuple() needs relcache */
1444 17378661 : AssertCouldGetRelation();
1445 :
1446 : /* Do nothing during bootstrap */
1447 17378661 : if (IsBootstrapProcessingMode())
1448 759525 : return;
1449 :
1450 : /*
1451 : * We only need to worry about invalidation for tuples that are in system
1452 : * catalogs; user-relation tuples are never in catcaches and can't affect
1453 : * the relcache either.
1454 : */
1455 16619136 : if (!IsCatalogRelation(relation))
1456 13802662 : return;
1457 :
1458 : /*
1459 : * IsCatalogRelation() will return true for TOAST tables of system
1460 : * catalogs, but we don't care about those, either.
1461 : */
1462 2816474 : if (IsToastRelation(relation))
1463 23072 : return;
1464 :
1465 : /* Allocate any required resources. */
1466 2793402 : info = prepare_callback();
1467 :
1468 : /*
1469 : * First let the catcache do its thing
1470 : */
1471 2793402 : tupleRelId = RelationGetRelid(relation);
1472 2793402 : if (RelationInvalidatesSnapshotsOnly(tupleRelId))
1473 : {
1474 726339 : databaseId = IsSharedRelation(tupleRelId) ? InvalidOid : MyDatabaseId;
1475 726339 : RegisterSnapshotInvalidation(info, databaseId, tupleRelId);
1476 : }
1477 : else
1478 2067063 : PrepareToInvalidateCacheTuple(relation, tuple, newtuple,
1479 : RegisterCatcacheInvalidation,
1480 : info);
1481 :
1482 : /*
1483 : * Now, is this tuple one of the primary definers of a relcache entry? See
1484 : * comments in file header for deeper explanation.
1485 : *
1486 : * Note we ignore newtuple here; we assume an update cannot move a tuple
1487 : * from being part of one relcache entry to being part of another.
1488 : */
1489 2793402 : if (tupleRelId == RelationRelationId)
1490 : {
1491 498612 : Form_pg_class classtup = (Form_pg_class) GETSTRUCT(tuple);
1492 :
1493 498612 : relationId = classtup->oid;
1494 498612 : if (classtup->relisshared)
1495 30544 : databaseId = InvalidOid;
1496 : else
1497 468068 : databaseId = MyDatabaseId;
1498 : }
1499 2294790 : else if (tupleRelId == AttributeRelationId)
1500 : {
1501 776377 : Form_pg_attribute atttup = (Form_pg_attribute) GETSTRUCT(tuple);
1502 :
1503 776377 : relationId = atttup->attrelid;
1504 :
1505 : /*
1506 : * KLUGE ALERT: we always send the relcache event with MyDatabaseId,
1507 : * even if the rel in question is shared (which we can't easily tell).
1508 : * This essentially means that only backends in this same database
1509 : * will react to the relcache flush request. This is in fact
1510 : * appropriate, since only those backends could see our pg_attribute
1511 : * change anyway. It looks a bit ugly though. (In practice, shared
1512 : * relations can't have schema changes after bootstrap, so we should
1513 : * never come here for a shared rel anyway.)
1514 : */
1515 776377 : databaseId = MyDatabaseId;
1516 : }
1517 1518413 : else if (tupleRelId == IndexRelationId)
1518 : {
1519 43798 : Form_pg_index indextup = (Form_pg_index) GETSTRUCT(tuple);
1520 :
1521 : /*
1522 : * When a pg_index row is updated, we should send out a relcache inval
1523 : * for the index relation. As above, we don't know the shared status
1524 : * of the index, but in practice it doesn't matter since indexes of
1525 : * shared catalogs can't have such updates.
1526 : */
1527 43798 : relationId = indextup->indexrelid;
1528 43798 : databaseId = MyDatabaseId;
1529 : }
1530 1474615 : else if (tupleRelId == ConstraintRelationId)
1531 : {
1532 57780 : Form_pg_constraint constrtup = (Form_pg_constraint) GETSTRUCT(tuple);
1533 :
1534 : /*
1535 : * Foreign keys are part of relcache entries, too, so send out an
1536 : * inval for the table that the FK applies to.
1537 : */
1538 57780 : if (constrtup->contype == CONSTRAINT_FOREIGN &&
1539 5972 : OidIsValid(constrtup->conrelid))
1540 : {
1541 5972 : relationId = constrtup->conrelid;
1542 5972 : databaseId = MyDatabaseId;
1543 : }
1544 : else
1545 51808 : return;
1546 : }
1547 : else
1548 1416835 : return;
1549 :
1550 : /*
1551 : * Yes. We need to register a relcache invalidation event.
1552 : */
1553 1324759 : RegisterRelcacheInvalidation(info, databaseId, relationId);
1554 : }
1555 :
1556 : /*
1557 : * CacheInvalidateHeapTuple
1558 : * Register the given tuple for invalidation at end of command
1559 : * (ie, current command is creating or outdating this tuple) and end of
1560 : * transaction. Also, detect whether a relcache invalidation is implied.
1561 : *
1562 : * For an insert or delete, tuple is the target tuple and newtuple is NULL.
1563 : * For an update, we are called just once, with tuple being the old tuple
1564 : * version and newtuple the new version. This allows avoidance of duplicate
1565 : * effort during an update.
1566 : */
1567 : void
1568 17150552 : CacheInvalidateHeapTuple(Relation relation,
1569 : HeapTuple tuple,
1570 : HeapTuple newtuple)
1571 : {
1572 17150552 : CacheInvalidateHeapTupleCommon(relation, tuple, newtuple,
1573 : PrepareInvalidationState);
1574 17150552 : }
1575 :
1576 : /*
1577 : * CacheInvalidateHeapTupleInplace
1578 : * Register the given tuple for nontransactional invalidation pertaining
1579 : * to an inplace update. Also, detect whether a relcache invalidation is
1580 : * implied.
1581 : *
1582 : * Like CacheInvalidateHeapTuple(), but for inplace updates.
1583 : *
1584 : * Just before and just after the inplace update, the tuple's cache keys must
1585 : * match those in key_equivalent_tuple. Cache keys consist of catcache lookup
1586 : * key columns and columns referencing pg_class.oid values,
1587 : * e.g. pg_constraint.conrelid, which would trigger relcache inval.
1588 : */
1589 : void
1590 228109 : CacheInvalidateHeapTupleInplace(Relation relation,
1591 : HeapTuple key_equivalent_tuple)
1592 : {
1593 228109 : CacheInvalidateHeapTupleCommon(relation, key_equivalent_tuple, NULL,
1594 : PrepareInplaceInvalidationState);
1595 228109 : }
1596 :
1597 : /*
1598 : * CacheInvalidateCatalog
1599 : * Register invalidation of the whole content of a system catalog.
1600 : *
1601 : * This is normally used in VACUUM FULL/CLUSTER, where we haven't so much
1602 : * changed any tuples as moved them around. Some uses of catcache entries
1603 : * expect their TIDs to be correct, so we have to blow away the entries.
1604 : *
1605 : * Note: we expect caller to verify that the rel actually is a system
1606 : * catalog. If it isn't, no great harm is done, just a wasted sinval message.
1607 : */
1608 : void
1609 121 : CacheInvalidateCatalog(Oid catalogId)
1610 : {
1611 : Oid databaseId;
1612 :
1613 121 : if (IsSharedRelation(catalogId))
1614 19 : databaseId = InvalidOid;
1615 : else
1616 102 : databaseId = MyDatabaseId;
1617 :
1618 121 : RegisterCatalogInvalidation(PrepareInvalidationState(),
1619 : databaseId, catalogId);
1620 121 : }
1621 :
1622 : /*
1623 : * CacheInvalidateRelcache
1624 : * Register invalidation of the specified relation's relcache entry
1625 : * at end of command.
1626 : *
1627 : * This is used in places that need to force relcache rebuild but aren't
1628 : * changing any of the tuples recognized as contributors to the relcache
1629 : * entry by CacheInvalidateHeapTuple. (An example is dropping an index.)
1630 : */
1631 : void
1632 103818 : CacheInvalidateRelcache(Relation relation)
1633 : {
1634 : Oid databaseId;
1635 : Oid relationId;
1636 :
1637 103818 : relationId = RelationGetRelid(relation);
1638 103818 : if (relation->rd_rel->relisshared)
1639 3976 : databaseId = InvalidOid;
1640 : else
1641 99842 : databaseId = MyDatabaseId;
1642 :
1643 103818 : RegisterRelcacheInvalidation(PrepareInvalidationState(),
1644 : databaseId, relationId);
1645 103818 : }
1646 :
1647 : /*
1648 : * CacheInvalidateRelcacheAll
1649 : * Register invalidation of the whole relcache at the end of command.
1650 : *
1651 : * This is used by alter publication as changes in publications may affect
1652 : * large number of tables.
1653 : */
1654 : void
1655 224 : CacheInvalidateRelcacheAll(void)
1656 : {
1657 224 : RegisterRelcacheInvalidation(PrepareInvalidationState(),
1658 : InvalidOid, InvalidOid);
1659 224 : }
1660 :
1661 : /*
1662 : * CacheInvalidateRelcacheByTuple
1663 : * As above, but relation is identified by passing its pg_class tuple.
1664 : */
1665 : void
1666 52110 : CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
1667 : {
1668 52110 : Form_pg_class classtup = (Form_pg_class) GETSTRUCT(classTuple);
1669 : Oid databaseId;
1670 : Oid relationId;
1671 :
1672 52110 : relationId = classtup->oid;
1673 52110 : if (classtup->relisshared)
1674 1107 : databaseId = InvalidOid;
1675 : else
1676 51003 : databaseId = MyDatabaseId;
1677 52110 : RegisterRelcacheInvalidation(PrepareInvalidationState(),
1678 : databaseId, relationId);
1679 52110 : }
1680 :
1681 : /*
1682 : * CacheInvalidateRelcacheByRelid
1683 : * As above, but relation is identified by passing its OID.
1684 : * This is the least efficient of the three options; use one of
1685 : * the above routines if you have a Relation or pg_class tuple.
1686 : */
1687 : void
1688 21466 : CacheInvalidateRelcacheByRelid(Oid relid)
1689 : {
1690 : HeapTuple tup;
1691 :
1692 21466 : tup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
1693 21466 : if (!HeapTupleIsValid(tup))
1694 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
1695 21466 : CacheInvalidateRelcacheByTuple(tup);
1696 21466 : ReleaseSysCache(tup);
1697 21466 : }
1698 :
1699 : /*
1700 : * CacheInvalidateRelSync
1701 : * Register invalidation of the cache in logical decoding output plugin
1702 : * for a database.
1703 : *
1704 : * This type of invalidation message is used for the specific purpose of output
1705 : * plugins. Processes which do not decode WALs would do nothing even when it
1706 : * receives the message.
1707 : */
1708 : void
1709 6 : CacheInvalidateRelSync(Oid relid)
1710 : {
1711 6 : RegisterRelsyncInvalidation(PrepareInvalidationState(),
1712 : MyDatabaseId, relid);
1713 6 : }
1714 :
1715 : /*
1716 : * CacheInvalidateRelSyncAll
1717 : * Register invalidation of the whole cache in logical decoding output
1718 : * plugin.
1719 : */
1720 : void
1721 3 : CacheInvalidateRelSyncAll(void)
1722 : {
1723 3 : CacheInvalidateRelSync(InvalidOid);
1724 3 : }
1725 :
1726 : /*
1727 : * CacheInvalidateSmgr
1728 : * Register invalidation of smgr references to a physical relation.
1729 : *
1730 : * Sending this type of invalidation msg forces other backends to close open
1731 : * smgr entries for the rel. This should be done to flush dangling open-file
1732 : * references when the physical rel is being dropped or truncated. Because
1733 : * these are nontransactional (i.e., not-rollback-able) operations, we just
1734 : * send the inval message immediately without any queuing.
1735 : *
1736 : * Note: in most cases there will have been a relcache flush issued against
1737 : * the rel at the logical level. We need a separate smgr-level flush because
1738 : * it is possible for backends to have open smgr entries for rels they don't
1739 : * have a relcache entry for, e.g. because the only thing they ever did with
1740 : * the rel is write out dirty shared buffers.
1741 : *
1742 : * Note: because these messages are nontransactional, they won't be captured
1743 : * in commit/abort WAL entries. Instead, calls to CacheInvalidateSmgr()
1744 : * should happen in low-level smgr.c routines, which are executed while
1745 : * replaying WAL as well as when creating it.
1746 : *
1747 : * Note: In order to avoid bloating SharedInvalidationMessage, we store only
1748 : * three bytes of the ProcNumber using what would otherwise be padding space.
1749 : * Thus, the maximum possible ProcNumber is 2^23-1.
1750 : */
1751 : void
1752 66483 : CacheInvalidateSmgr(RelFileLocatorBackend rlocator)
1753 : {
1754 : SharedInvalidationMessage msg;
1755 :
1756 : /* verify optimization stated above stays valid */
1757 : StaticAssertDecl(MAX_BACKENDS_BITS <= 23,
1758 : "MAX_BACKENDS_BITS is too big for inval.c");
1759 :
1760 66483 : msg.sm.id = SHAREDINVALSMGR_ID;
1761 66483 : msg.sm.backend_hi = rlocator.backend >> 16;
1762 66483 : msg.sm.backend_lo = rlocator.backend & 0xffff;
1763 66483 : msg.sm.rlocator = rlocator.locator;
1764 : /* check AddCatcacheInvalidationMessage() for an explanation */
1765 : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
1766 :
1767 66483 : SendSharedInvalidMessages(&msg, 1);
1768 66483 : }
1769 :
1770 : /*
1771 : * CacheInvalidateRelmap
1772 : * Register invalidation of the relation mapping for a database,
1773 : * or for the shared catalogs if databaseId is zero.
1774 : *
1775 : * Sending this type of invalidation msg forces other backends to re-read
1776 : * the indicated relation mapping file. It is also necessary to send a
1777 : * relcache inval for the specific relations whose mapping has been altered,
1778 : * else the relcache won't get updated with the new filenode data.
1779 : *
1780 : * Note: because these messages are nontransactional, they won't be captured
1781 : * in commit/abort WAL entries. Instead, calls to CacheInvalidateRelmap()
1782 : * should happen in low-level relmapper.c routines, which are executed while
1783 : * replaying WAL as well as when creating it.
1784 : */
1785 : void
1786 219 : CacheInvalidateRelmap(Oid databaseId)
1787 : {
1788 : SharedInvalidationMessage msg;
1789 :
1790 219 : msg.rm.id = SHAREDINVALRELMAP_ID;
1791 219 : msg.rm.dbId = databaseId;
1792 : /* check AddCatcacheInvalidationMessage() for an explanation */
1793 : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
1794 :
1795 219 : SendSharedInvalidMessages(&msg, 1);
1796 219 : }
1797 :
1798 :
1799 : /*
1800 : * CacheRegisterSyscacheCallback
1801 : * Register the specified function to be called for all future
1802 : * invalidation events in the specified cache. The cache ID and the
1803 : * hash value of the tuple being invalidated will be passed to the
1804 : * function.
1805 : *
1806 : * NOTE: Hash value zero will be passed if a cache reset request is received.
1807 : * In this case the called routines should flush all cached state.
1808 : * Yes, there's a possibility of a false match to zero, but it doesn't seem
1809 : * worth troubling over, especially since most of the current callees just
1810 : * flush all cached state anyway.
1811 : */
1812 : void
1813 333684 : CacheRegisterSyscacheCallback(SysCacheIdentifier cacheid,
1814 : SyscacheCallbackFunction func,
1815 : Datum arg)
1816 : {
1817 333684 : if (cacheid < 0 || cacheid >= SysCacheSize)
1818 0 : elog(FATAL, "invalid cache ID: %d", cacheid);
1819 333684 : if (syscache_callback_count >= MAX_SYSCACHE_CALLBACKS)
1820 0 : elog(FATAL, "out of syscache_callback_list slots");
1821 :
1822 333684 : if (syscache_callback_links[cacheid] == 0)
1823 : {
1824 : /* first callback for this cache */
1825 234654 : syscache_callback_links[cacheid] = syscache_callback_count + 1;
1826 : }
1827 : else
1828 : {
1829 : /* add to end of chain, so that older callbacks are called first */
1830 99030 : int i = syscache_callback_links[cacheid] - 1;
1831 :
1832 118543 : while (syscache_callback_list[i].link > 0)
1833 19513 : i = syscache_callback_list[i].link - 1;
1834 99030 : syscache_callback_list[i].link = syscache_callback_count + 1;
1835 : }
1836 :
1837 333684 : syscache_callback_list[syscache_callback_count].id = cacheid;
1838 333684 : syscache_callback_list[syscache_callback_count].link = 0;
1839 333684 : syscache_callback_list[syscache_callback_count].function = func;
1840 333684 : syscache_callback_list[syscache_callback_count].arg = arg;
1841 :
1842 333684 : ++syscache_callback_count;
1843 333684 : }
1844 :
1845 : /*
1846 : * CacheRegisterRelcacheCallback
1847 : * Register the specified function to be called for all future
1848 : * relcache invalidation events. The OID of the relation being
1849 : * invalidated will be passed to the function.
1850 : *
1851 : * NOTE: InvalidOid will be passed if a cache reset request is received.
1852 : * In this case the called routines should flush all cached state.
1853 : */
1854 : void
1855 25842 : CacheRegisterRelcacheCallback(RelcacheCallbackFunction func,
1856 : Datum arg)
1857 : {
1858 25842 : if (relcache_callback_count >= MAX_RELCACHE_CALLBACKS)
1859 0 : elog(FATAL, "out of relcache_callback_list slots");
1860 :
1861 25842 : relcache_callback_list[relcache_callback_count].function = func;
1862 25842 : relcache_callback_list[relcache_callback_count].arg = arg;
1863 :
1864 25842 : ++relcache_callback_count;
1865 25842 : }
1866 :
1867 : /*
1868 : * CacheRegisterRelSyncCallback
1869 : * Register the specified function to be called for all future
1870 : * relsynccache invalidation events.
1871 : *
1872 : * This function is intended to be call from the logical decoding output
1873 : * plugins.
1874 : */
1875 : void
1876 464 : CacheRegisterRelSyncCallback(RelSyncCallbackFunction func,
1877 : Datum arg)
1878 : {
1879 464 : if (relsync_callback_count >= MAX_RELSYNC_CALLBACKS)
1880 0 : elog(FATAL, "out of relsync_callback_list slots");
1881 :
1882 464 : relsync_callback_list[relsync_callback_count].function = func;
1883 464 : relsync_callback_list[relsync_callback_count].arg = arg;
1884 :
1885 464 : ++relsync_callback_count;
1886 464 : }
1887 :
1888 : /*
1889 : * CallSyscacheCallbacks
1890 : *
1891 : * This is exported so that CatalogCacheFlushCatalog can call it, saving
1892 : * this module from knowing which catcache IDs correspond to which catalogs.
1893 : */
1894 : void
1895 15643223 : CallSyscacheCallbacks(SysCacheIdentifier cacheid, uint32 hashvalue)
1896 : {
1897 : int i;
1898 :
1899 15643223 : if (cacheid < 0 || cacheid >= SysCacheSize)
1900 0 : elog(ERROR, "invalid cache ID: %d", cacheid);
1901 :
1902 15643223 : i = syscache_callback_links[cacheid] - 1;
1903 18066460 : while (i >= 0)
1904 : {
1905 2423237 : struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i;
1906 :
1907 : Assert(ccitem->id == cacheid);
1908 2423237 : ccitem->function(ccitem->arg, cacheid, hashvalue);
1909 2423237 : i = ccitem->link - 1;
1910 : }
1911 15643223 : }
1912 :
1913 : /*
1914 : * CallSyscacheCallbacks
1915 : */
1916 : void
1917 31 : CallRelSyncCallbacks(Oid relid)
1918 : {
1919 52 : for (int i = 0; i < relsync_callback_count; i++)
1920 : {
1921 21 : struct RELSYNCCALLBACK *ccitem = relsync_callback_list + i;
1922 :
1923 21 : ccitem->function(ccitem->arg, relid);
1924 : }
1925 31 : }
1926 :
1927 : /*
1928 : * LogLogicalInvalidations
1929 : *
1930 : * Emit WAL for invalidations caused by the current command.
1931 : *
1932 : * This is currently only used for logging invalidations at the command end
1933 : * or at commit time if any invalidations are pending.
1934 : */
1935 : void
1936 22331 : LogLogicalInvalidations(void)
1937 : {
1938 : xl_xact_invals xlrec;
1939 : InvalidationMsgsGroup *group;
1940 : int nmsgs;
1941 :
1942 : /* Quick exit if we haven't done anything with invalidation messages. */
1943 22331 : if (transInvalInfo == NULL)
1944 12660 : return;
1945 :
1946 9671 : group = &transInvalInfo->ii.CurrentCmdInvalidMsgs;
1947 9671 : nmsgs = NumMessagesInGroup(group);
1948 :
1949 9671 : if (nmsgs > 0)
1950 : {
1951 : /* prepare record */
1952 7728 : memset(&xlrec, 0, MinSizeOfXactInvals);
1953 7728 : xlrec.nmsgs = nmsgs;
1954 :
1955 : /* perform insertion */
1956 7728 : XLogBeginInsert();
1957 7728 : XLogRegisterData(&xlrec, MinSizeOfXactInvals);
1958 7728 : ProcessMessageSubGroupMulti(group, CatCacheMsgs,
1959 : XLogRegisterData(msgs,
1960 : n * sizeof(SharedInvalidationMessage)));
1961 7728 : ProcessMessageSubGroupMulti(group, RelCacheMsgs,
1962 : XLogRegisterData(msgs,
1963 : n * sizeof(SharedInvalidationMessage)));
1964 7728 : XLogInsert(RM_XACT_ID, XLOG_XACT_INVALIDATIONS);
1965 : }
1966 : }
|