Age Owner TLA 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 wal_level=logical, write invalidations into WAL at each command end to
98 : * support the decoding of the in-progress transactions. See
99 : * CommandEndInvalidationMessages.
100 : *
101 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
102 : * Portions Copyright (c) 1994, Regents of the University of California
103 : *
104 : * IDENTIFICATION
105 : * src/backend/utils/cache/inval.c
106 : *
107 : *-------------------------------------------------------------------------
108 : */
109 : #include "postgres.h"
110 :
111 : #include <limits.h>
112 :
113 : #include "access/htup_details.h"
114 : #include "access/xact.h"
115 : #include "access/xloginsert.h"
116 : #include "catalog/catalog.h"
117 : #include "catalog/pg_constraint.h"
118 : #include "miscadmin.h"
119 : #include "storage/sinval.h"
120 : #include "storage/smgr.h"
121 : #include "utils/catcache.h"
122 : #include "utils/guc.h"
123 : #include "utils/inval.h"
124 : #include "utils/memdebug.h"
125 : #include "utils/memutils.h"
126 : #include "utils/rel.h"
127 : #include "utils/relmapper.h"
128 : #include "utils/snapmgr.h"
129 : #include "utils/syscache.h"
130 :
131 :
132 : /*
133 : * Pending requests are stored as ready-to-send SharedInvalidationMessages.
134 : * We keep the messages themselves in arrays in TopTransactionContext
135 : * (there are separate arrays for catcache and relcache messages). Control
136 : * information is kept in a chain of TransInvalidationInfo structs, also
137 : * allocated in TopTransactionContext. (We could keep a subtransaction's
138 : * TransInvalidationInfo in its CurTransactionContext; but that's more
139 : * wasteful not less so, since in very many scenarios it'd be the only
140 : * allocation in the subtransaction's CurTransactionContext.)
141 : *
142 : * We can store the message arrays densely, and yet avoid moving data around
143 : * within an array, because within any one subtransaction we need only
144 : * distinguish between messages emitted by prior commands and those emitted
145 : * by the current command. Once a command completes and we've done local
146 : * processing on its messages, we can fold those into the prior-commands
147 : * messages just by changing array indexes in the TransInvalidationInfo
148 : * struct. Similarly, we need distinguish messages of prior subtransactions
149 : * from those of the current subtransaction only until the subtransaction
150 : * completes, after which we adjust the array indexes in the parent's
151 : * TransInvalidationInfo to include the subtransaction's messages.
152 : *
153 : * The ordering of the individual messages within a command's or
154 : * subtransaction's output is not considered significant, although this
155 : * implementation happens to preserve the order in which they were queued.
156 : * (Previous versions of this code did not preserve it.)
157 : *
158 : * For notational convenience, control information is kept in two-element
159 : * arrays, the first for catcache messages and the second for relcache
160 : * messages.
161 : */
162 : #define CatCacheMsgs 0
163 : #define RelCacheMsgs 1
164 :
165 : /* Pointers to main arrays in TopTransactionContext */
166 : typedef struct InvalMessageArray
167 : {
168 : SharedInvalidationMessage *msgs; /* palloc'd array (can be expanded) */
169 : int maxmsgs; /* current allocated size of array */
170 : } InvalMessageArray;
171 :
172 : static InvalMessageArray InvalMessageArrays[2];
173 :
174 : /* Control information for one logical group of messages */
175 : typedef struct InvalidationMsgsGroup
176 : {
177 : int firstmsg[2]; /* first index in relevant array */
178 : int nextmsg[2]; /* last+1 index */
179 : } InvalidationMsgsGroup;
180 :
181 : /* Macros to help preserve InvalidationMsgsGroup abstraction */
182 : #define SetSubGroupToFollow(targetgroup, priorgroup, subgroup) \
183 : do { \
184 : (targetgroup)->firstmsg[subgroup] = \
185 : (targetgroup)->nextmsg[subgroup] = \
186 : (priorgroup)->nextmsg[subgroup]; \
187 : } while (0)
188 :
189 : #define SetGroupToFollow(targetgroup, priorgroup) \
190 : do { \
191 : SetSubGroupToFollow(targetgroup, priorgroup, CatCacheMsgs); \
192 : SetSubGroupToFollow(targetgroup, priorgroup, RelCacheMsgs); \
193 : } while (0)
194 :
195 : #define NumMessagesInSubGroup(group, subgroup) \
196 : ((group)->nextmsg[subgroup] - (group)->firstmsg[subgroup])
197 :
198 : #define NumMessagesInGroup(group) \
199 : (NumMessagesInSubGroup(group, CatCacheMsgs) + \
200 : NumMessagesInSubGroup(group, RelCacheMsgs))
201 :
202 :
203 : /*----------------
204 : * Invalidation messages are divided into two groups:
205 : * 1) events so far in current command, not yet reflected to caches.
206 : * 2) events in previous commands of current transaction; these have
207 : * been reflected to local caches, and must be either broadcast to
208 : * other backends or rolled back from local cache when we commit
209 : * or abort the transaction.
210 : * Actually, we need such groups for each level of nested transaction,
211 : * so that we can discard events from an aborted subtransaction. When
212 : * a subtransaction commits, we append its events to the parent's groups.
213 : *
214 : * The relcache-file-invalidated flag can just be a simple boolean,
215 : * since we only act on it at transaction commit; we don't care which
216 : * command of the transaction set it.
217 : *----------------
218 : */
219 :
220 : typedef struct TransInvalidationInfo
221 : {
222 : /* Back link to parent transaction's info */
223 : struct TransInvalidationInfo *parent;
224 :
225 : /* Subtransaction nesting depth */
226 : int my_level;
227 :
228 : /* Events emitted by current command */
229 : InvalidationMsgsGroup CurrentCmdInvalidMsgs;
230 :
231 : /* Events emitted by previous commands of this (sub)transaction */
232 : InvalidationMsgsGroup PriorCmdInvalidMsgs;
233 :
234 : /* init file must be invalidated? */
235 : bool RelcacheInitFileInval;
236 : } TransInvalidationInfo;
237 :
238 : static TransInvalidationInfo *transInvalInfo = NULL;
239 :
240 : /* GUC storage */
241 : int debug_discard_caches = 0;
242 :
243 : /*
244 : * Dynamically-registered callback functions. Current implementation
245 : * assumes there won't be enough of these to justify a dynamically resizable
246 : * array; it'd be easy to improve that if needed.
247 : *
248 : * To avoid searching in CallSyscacheCallbacks, all callbacks for a given
249 : * syscache are linked into a list pointed to by syscache_callback_links[id].
250 : * The link values are syscache_callback_list[] index plus 1, or 0 for none.
251 : */
252 :
253 : #define MAX_SYSCACHE_CALLBACKS 64
254 : #define MAX_RELCACHE_CALLBACKS 10
255 :
256 : static struct SYSCACHECALLBACK
257 : {
258 : int16 id; /* cache number */
259 : int16 link; /* next callback index+1 for same cache */
260 : SyscacheCallbackFunction function;
261 : Datum arg;
262 : } syscache_callback_list[MAX_SYSCACHE_CALLBACKS];
263 :
264 : static int16 syscache_callback_links[SysCacheSize];
265 :
266 : static int syscache_callback_count = 0;
267 :
268 : static struct RELCACHECALLBACK
269 : {
270 : RelcacheCallbackFunction function;
271 : Datum arg;
272 : } relcache_callback_list[MAX_RELCACHE_CALLBACKS];
273 :
274 : static int relcache_callback_count = 0;
275 :
276 : /* ----------------------------------------------------------------
277 : * Invalidation subgroup support functions
278 : * ----------------------------------------------------------------
279 : */
280 :
281 : /*
282 : * AddInvalidationMessage
283 : * Add an invalidation message to a (sub)group.
284 : *
285 : * The group must be the last active one, since we assume we can add to the
286 : * end of the relevant InvalMessageArray.
287 : *
288 : * subgroup must be CatCacheMsgs or RelCacheMsgs.
289 : */
290 : static void
601 tgl 291 CBC 5881807 : AddInvalidationMessage(InvalidationMsgsGroup *group, int subgroup,
292 : const SharedInvalidationMessage *msg)
293 : {
294 5881807 : InvalMessageArray *ima = &InvalMessageArrays[subgroup];
295 5881807 : int nextindex = group->nextmsg[subgroup];
296 :
297 5881807 : if (nextindex >= ima->maxmsgs)
298 : {
299 564847 : if (ima->msgs == NULL)
300 : {
301 : /* Create new storage array in TopTransactionContext */
302 523048 : int reqsize = 32; /* arbitrary */
303 :
304 523048 : ima->msgs = (SharedInvalidationMessage *)
305 523048 : MemoryContextAlloc(TopTransactionContext,
306 : reqsize * sizeof(SharedInvalidationMessage));
307 523048 : ima->maxmsgs = reqsize;
308 523048 : Assert(nextindex == 0);
309 : }
310 : else
311 : {
312 : /* Enlarge storage array */
313 41799 : int reqsize = 2 * ima->maxmsgs;
314 :
315 41799 : ima->msgs = (SharedInvalidationMessage *)
316 41799 : repalloc(ima->msgs,
317 : reqsize * sizeof(SharedInvalidationMessage));
318 41799 : ima->maxmsgs = reqsize;
319 : }
320 : }
321 : /* Okay, add message to current group */
322 5881807 : ima->msgs[nextindex] = *msg;
323 5881807 : group->nextmsg[subgroup]++;
9770 scrappy 324 5881807 : }
325 :
326 : /*
327 : * Append one subgroup of invalidation messages to another, resetting
328 : * the source subgroup to empty.
329 : */
330 : static void
601 tgl 331 2665488 : AppendInvalidationMessageSubGroup(InvalidationMsgsGroup *dest,
332 : InvalidationMsgsGroup *src,
333 : int subgroup)
334 : {
335 : /* Messages must be adjacent in main array */
336 2665488 : Assert(dest->nextmsg[subgroup] == src->firstmsg[subgroup]);
337 :
338 : /* ... which makes this easy: */
339 2665488 : dest->nextmsg[subgroup] = src->nextmsg[subgroup];
340 :
341 : /*
342 : * This is handy for some callers and irrelevant for others. But we do it
343 : * always, reasoning that it's bad to leave different groups pointing at
344 : * the same fragment of the message array.
345 : */
346 2665488 : SetSubGroupToFollow(src, dest, subgroup);
7707 347 2665488 : }
348 :
349 : /*
350 : * Process a subgroup of invalidation messages.
351 : *
352 : * This is a macro that executes the given code fragment for each message in
353 : * a message subgroup. The fragment should refer to the message as *msg.
354 : */
355 : #define ProcessMessageSubGroup(group, subgroup, codeFragment) \
356 : do { \
357 : int _msgindex = (group)->firstmsg[subgroup]; \
358 : int _endmsg = (group)->nextmsg[subgroup]; \
359 : for (; _msgindex < _endmsg; _msgindex++) \
360 : { \
361 : SharedInvalidationMessage *msg = \
362 : &InvalMessageArrays[subgroup].msgs[_msgindex]; \
363 : codeFragment; \
364 : } \
365 : } while (0)
366 :
367 : /*
368 : * Process a subgroup of invalidation messages as an array.
369 : *
370 : * As above, but the code fragment can handle an array of messages.
371 : * The fragment should refer to the messages as msgs[], with n entries.
372 : */
373 : #define ProcessMessageSubGroupMulti(group, subgroup, codeFragment) \
374 : do { \
375 : int n = NumMessagesInSubGroup(group, subgroup); \
376 : if (n > 0) { \
377 : SharedInvalidationMessage *msgs = \
378 : &InvalMessageArrays[subgroup].msgs[(group)->firstmsg[subgroup]]; \
379 : codeFragment; \
380 : } \
381 : } while (0)
382 :
383 :
384 : /* ----------------------------------------------------------------
385 : * Invalidation group support functions
386 : *
387 : * These routines understand about the division of a logical invalidation
388 : * group into separate physical arrays for catcache and relcache entries.
389 : * ----------------------------------------------------------------
390 : */
391 :
392 : /*
393 : * Add a catcache inval entry
394 : */
395 : static void
601 396 4803966 : AddCatcacheInvalidationMessage(InvalidationMsgsGroup *group,
397 : int id, uint32 hashValue, Oid dbId)
398 : {
399 : SharedInvalidationMessage msg;
400 :
4622 rhaas 401 4803966 : Assert(id < CHAR_MAX);
402 4803966 : msg.cc.id = (int8) id;
7707 tgl 403 4803966 : msg.cc.dbId = dbId;
404 4803966 : msg.cc.hashValue = hashValue;
405 :
406 : /*
407 : * Define padding bytes in SharedInvalidationMessage structs to be
408 : * defined. Otherwise the sinvaladt.c ringbuffer, which is accessed by
409 : * multiple processes, will cause spurious valgrind warnings about
410 : * undefined memory being used. That's because valgrind remembers the
411 : * undefined bytes from the last local process's store, not realizing that
412 : * another process has written since, filling the previously uninitialized
413 : * bytes
414 : */
415 : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
416 :
601 417 4803966 : AddInvalidationMessage(group, CatCacheMsgs, &msg);
8490 inoue 418 4803966 : }
419 :
420 : /*
421 : * Add a whole-catalog inval entry
422 : */
423 : static void
601 tgl 424 100 : AddCatalogInvalidationMessage(InvalidationMsgsGroup *group,
425 : Oid dbId, Oid catId)
426 : {
427 : SharedInvalidationMessage msg;
428 :
4809 429 100 : msg.cat.id = SHAREDINVALCATALOG_ID;
430 100 : msg.cat.dbId = dbId;
431 100 : msg.cat.catId = catId;
432 : /* check AddCatcacheInvalidationMessage() for an explanation */
433 : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
434 :
601 435 100 : AddInvalidationMessage(group, CatCacheMsgs, &msg);
4809 436 100 : }
437 :
438 : /*
439 : * Add a relcache inval entry
440 : */
441 : static void
601 442 1429054 : AddRelcacheInvalidationMessage(InvalidationMsgsGroup *group,
443 : Oid dbId, Oid relId)
444 : {
445 : SharedInvalidationMessage msg;
446 :
447 : /*
448 : * Don't add a duplicate item. We assume dbId need not be checked because
449 : * it will never change. InvalidOid for relId means all relations so we
450 : * don't need to add individual ones when it is present.
451 : */
452 5644200 : ProcessMessageSubGroup(group, RelCacheMsgs,
453 : if (msg->rc.id == SHAREDINVALRELCACHE_ID &&
454 : (msg->rc.relId == relId ||
455 : msg->rc.relId == InvalidOid))
456 : return);
457 :
458 : /* OK, add the item */
7964 459 526744 : msg.rc.id = SHAREDINVALRELCACHE_ID;
460 526744 : msg.rc.dbId = dbId;
461 526744 : msg.rc.relId = relId;
462 : /* check AddCatcacheInvalidationMessage() for an explanation */
463 : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
464 :
601 465 526744 : AddInvalidationMessage(group, RelCacheMsgs, &msg);
466 : }
467 :
468 : /*
469 : * Add a snapshot inval entry
470 : *
471 : * We put these into the relcache subgroup for simplicity.
472 : */
473 : static void
474 1322643 : AddSnapshotInvalidationMessage(InvalidationMsgsGroup *group,
475 : Oid dbId, Oid relId)
476 : {
477 : SharedInvalidationMessage msg;
478 :
479 : /* Don't add a duplicate item */
480 : /* We assume dbId need not be checked because it will never change */
481 1525871 : ProcessMessageSubGroup(group, RelCacheMsgs,
482 : if (msg->sn.id == SHAREDINVALSNAPSHOT_ID &&
483 : msg->sn.relId == relId)
484 : return);
485 :
486 : /* OK, add the item */
3568 rhaas 487 550997 : msg.sn.id = SHAREDINVALSNAPSHOT_ID;
488 550997 : msg.sn.dbId = dbId;
489 550997 : msg.sn.relId = relId;
490 : /* check AddCatcacheInvalidationMessage() for an explanation */
491 : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
492 :
601 tgl 493 550997 : AddInvalidationMessage(group, RelCacheMsgs, &msg);
494 : }
495 :
496 : /*
497 : * Append one group of invalidation messages to another, resetting
498 : * the source group to empty.
499 : */
500 : static void
501 1332744 : AppendInvalidationMessages(InvalidationMsgsGroup *dest,
502 : InvalidationMsgsGroup *src)
503 : {
504 1332744 : AppendInvalidationMessageSubGroup(dest, src, CatCacheMsgs);
505 1332744 : AppendInvalidationMessageSubGroup(dest, src, RelCacheMsgs);
7707 506 1332744 : }
507 :
508 : /*
509 : * Execute the given function for all the messages in an invalidation group.
510 : * The group is not altered.
511 : *
512 : * catcache entries are processed first, for reasons mentioned above.
513 : */
514 : static void
601 515 1019022 : ProcessInvalidationMessages(InvalidationMsgsGroup *group,
516 : void (*func) (SharedInvalidationMessage *msg))
517 : {
518 5520489 : ProcessMessageSubGroup(group, CatCacheMsgs, func(msg));
519 1999340 : ProcessMessageSubGroup(group, RelCacheMsgs, func(msg));
7964 520 1019019 : }
521 :
522 : /*
523 : * As above, but the function is able to process an array of messages
524 : * rather than just one at a time.
525 : */
526 : static void
601 527 315493 : ProcessInvalidationMessagesMulti(InvalidationMsgsGroup *group,
528 : void (*func) (const SharedInvalidationMessage *msgs, int n))
529 : {
530 315493 : ProcessMessageSubGroupMulti(group, CatCacheMsgs, func(msgs, n));
531 315493 : ProcessMessageSubGroupMulti(group, RelCacheMsgs, func(msgs, n));
5407 532 315493 : }
533 :
534 : /* ----------------------------------------------------------------
535 : * private support functions
536 : * ----------------------------------------------------------------
537 : */
538 :
539 : /*
540 : * RegisterCatcacheInvalidation
541 : *
542 : * Register an invalidation event for a catcache tuple entry.
543 : */
544 : static void
7964 545 4803966 : RegisterCatcacheInvalidation(int cacheId,
546 : uint32 hashValue,
547 : Oid dbId)
548 : {
6856 549 4803966 : AddCatcacheInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
550 : cacheId, hashValue, dbId);
9770 scrappy 551 4803966 : }
552 :
553 : /*
554 : * RegisterCatalogInvalidation
555 : *
556 : * Register an invalidation event for all catcache entries from a catalog.
557 : */
558 : static void
4809 tgl 559 100 : RegisterCatalogInvalidation(Oid dbId, Oid catId)
560 : {
561 100 : AddCatalogInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
562 : dbId, catId);
563 100 : }
564 :
565 : /*
566 : * RegisterRelcacheInvalidation
567 : *
568 : * As above, but register a relcache invalidation event.
569 : */
570 : static void
6663 571 1429054 : RegisterRelcacheInvalidation(Oid dbId, Oid relId)
572 : {
6856 573 1429054 : AddRelcacheInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
574 : dbId, relId);
575 :
576 : /*
577 : * Most of the time, relcache invalidation is associated with system
578 : * catalog updates, but there are a few cases where it isn't. Quick hack
579 : * to ensure that the next CommandCounterIncrement() will think that we
580 : * need to do CommandEndInvalidationMessages().
581 : */
5609 582 1429054 : (void) GetCurrentCommandId(true);
583 :
584 : /*
585 : * If the relation being invalidated is one of those cached in a relcache
586 : * init file, mark that we need to zap that file at commit. For simplicity
587 : * invalidations for a specific database always invalidate the shared file
588 : * as well. Also zap when we are invalidating whole relcache.
589 : */
1762 andres 590 1429054 : if (relId == InvalidOid || RelationIdIsInInitFile(relId))
6856 tgl 591 132321 : transInvalInfo->RelcacheInitFileInval = true;
8490 inoue 592 1429054 : }
593 :
594 : /*
595 : * RegisterSnapshotInvalidation
596 : *
597 : * Register an invalidation event for MVCC scans against a given catalog.
598 : * Only needed for catalogs that don't have catcaches.
599 : */
600 : static void
3568 rhaas 601 1322643 : RegisterSnapshotInvalidation(Oid dbId, Oid relId)
602 : {
603 1322643 : AddSnapshotInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
604 : dbId, relId);
605 1322643 : }
606 :
607 : /*
608 : * LocalExecuteInvalidationMessage
609 : *
610 : * Process a single invalidation message (which could be of any type).
611 : * Only the local caches are flushed; this does not transmit the message
612 : * to other backends.
613 : */
614 : void
7964 tgl 615 21485982 : LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
616 : {
617 21485982 : if (msg->id >= 0)
618 : {
4809 619 17579763 : if (msg->cc.dbId == MyDatabaseId || msg->cc.dbId == InvalidOid)
620 : {
3568 rhaas 621 14189209 : InvalidateCatalogSnapshot();
622 :
2158 tgl 623 14189209 : SysCacheInvalidate(msg->cc.id, msg->cc.hashValue);
624 :
4254 625 14189209 : CallSyscacheCallbacks(msg->cc.id, msg->cc.hashValue);
626 : }
627 : }
4809 628 3906219 : else if (msg->id == SHAREDINVALCATALOG_ID)
629 : {
630 387 : if (msg->cat.dbId == MyDatabaseId || msg->cat.dbId == InvalidOid)
631 : {
3568 rhaas 632 333 : InvalidateCatalogSnapshot();
633 :
4809 tgl 634 333 : CatalogCacheFlushCatalog(msg->cat.catId);
635 :
636 : /* CatalogCacheFlushCatalog calls CallSyscacheCallbacks as needed */
637 : }
638 : }
7964 639 3905832 : else if (msg->id == SHAREDINVALRELCACHE_ID)
640 : {
6998 641 1824513 : if (msg->rc.dbId == MyDatabaseId || msg->rc.dbId == InvalidOid)
642 : {
643 : int i;
644 :
2271 peter_e 645 1462649 : if (msg->rc.relId == InvalidOid)
533 noah 646 110 : RelationCacheInvalidate(false);
647 : else
2271 peter_e 648 1462539 : RelationCacheInvalidateEntry(msg->rc.relId);
649 :
5325 tgl 650 3997278 : for (i = 0; i < relcache_callback_count; i++)
651 : {
652 2534632 : struct RELCACHECALLBACK *ccitem = relcache_callback_list + i;
653 :
2040 peter_e 654 2534632 : ccitem->function(ccitem->arg, msg->rc.relId);
655 : }
656 : }
657 : }
6663 tgl 658 2081319 : else if (msg->id == SHAREDINVALSMGR_ID)
659 : {
660 : /*
661 : * We could have smgr entries for relations of other databases, so no
662 : * short-circuit test is possible here.
663 : */
664 : RelFileLocatorBackend rlocator;
665 :
193 rhaas 666 GNC 203565 : rlocator.locator = msg->sm.rlocator;
277 667 203565 : rlocator.backend = (msg->sm.backend_hi << 16) | (int) msg->sm.backend_lo;
668 203565 : smgrcloserellocator(rlocator);
669 : }
4809 tgl 670 CBC 1877754 : else if (msg->id == SHAREDINVALRELMAP_ID)
671 : {
672 : /* We only care about our own database and shared catalogs */
673 164 : if (msg->rm.dbId == InvalidOid)
674 81 : RelationMapInvalidate(true);
675 83 : else if (msg->rm.dbId == MyDatabaseId)
676 50 : RelationMapInvalidate(false);
677 : }
3568 rhaas 678 1877590 : else if (msg->id == SHAREDINVALSNAPSHOT_ID)
679 : {
680 : /* We only care about our own database and shared catalogs */
832 michael 681 1877590 : if (msg->sn.dbId == InvalidOid)
3568 rhaas 682 42527 : InvalidateCatalogSnapshot();
832 michael 683 1835063 : else if (msg->sn.dbId == MyDatabaseId)
3568 rhaas 684 1553114 : InvalidateCatalogSnapshot();
685 : }
686 : else
4312 peter_e 687 UBC 0 : elog(FATAL, "unrecognized SI message ID: %d", msg->id);
9770 scrappy 688 CBC 21485979 : }
689 :
690 : /*
691 : * InvalidateSystemCaches
692 : *
693 : * This blows away all tuples in the system catalog caches and
694 : * all the cached relation descriptors and smgr cache entries.
695 : * Relation descriptors that have positive refcounts are then rebuilt.
696 : *
697 : * We call this when we see a shared-inval-queue overflow signal,
698 : * since that tells us we've lost some shared-inval messages and hence
699 : * don't know what needs to be invalidated.
700 : */
701 : void
7965 tgl 702 1893 : InvalidateSystemCaches(void)
703 : {
533 noah 704 1893 : InvalidateSystemCachesExtended(false);
705 1893 : }
706 :
707 : void
708 1893 : InvalidateSystemCachesExtended(bool debug_discard)
709 : {
710 : int i;
711 :
3568 rhaas 712 1893 : InvalidateCatalogSnapshot();
7965 tgl 713 1893 : ResetCatalogCaches();
533 noah 714 1893 : RelationCacheInvalidate(debug_discard); /* gets smgr and relmap too */
715 :
5325 tgl 716 26600 : for (i = 0; i < syscache_callback_count; i++)
717 : {
718 24707 : struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i;
719 :
2040 peter_e 720 24707 : ccitem->function(ccitem->arg, ccitem->id, 0);
721 : }
722 :
5325 tgl 723 4307 : for (i = 0; i < relcache_callback_count; i++)
724 : {
725 2414 : struct RELCACHECALLBACK *ccitem = relcache_callback_list + i;
726 :
2040 peter_e 727 2414 : ccitem->function(ccitem->arg, InvalidOid);
728 : }
9770 scrappy 729 1893 : }
730 :
731 :
732 : /* ----------------------------------------------------------------
733 : * public functions
734 : * ----------------------------------------------------------------
735 : */
736 :
737 : /*
738 : * AcceptInvalidationMessages
739 : * Read and process invalidation messages from the shared invalidation
740 : * message queue.
741 : *
742 : * Note:
743 : * This should be called as the first step in processing a transaction.
744 : */
745 : void
7964 tgl 746 26448143 : AcceptInvalidationMessages(void)
747 : {
748 26448143 : ReceiveSharedInvalidMessages(LocalExecuteInvalidationMessage,
749 : InvalidateSystemCaches);
750 :
751 : /*----------
752 : * Test code to force cache flushes anytime a flush could happen.
753 : *
754 : * This helps detect intermittent faults caused by code that reads a cache
755 : * entry and then performs an action that could invalidate the entry, but
756 : * rarely actually does so. This can spot issues that would otherwise
757 : * only arise with badly timed concurrent DDL, for example.
758 : *
759 : * The default debug_discard_caches = 0 does no forced cache flushes.
760 : *
761 : * If used with CLOBBER_FREED_MEMORY,
762 : * debug_discard_caches = 1 (formerly known as CLOBBER_CACHE_ALWAYS)
763 : * provides a fairly thorough test that the system contains no cache-flush
764 : * hazards. However, it also makes the system unbelievably slow --- the
765 : * regression tests take about 100 times longer than normal.
766 : *
767 : * If you're a glutton for punishment, try
768 : * debug_discard_caches = 3 (formerly known as CLOBBER_CACHE_RECURSIVELY).
769 : * This slows things by at least a factor of 10000, so I wouldn't suggest
770 : * trying to run the entire regression tests that way. It's useful to try
771 : * a few simple tests, to make sure that cache reload isn't subject to
772 : * internal cache-flush hazards, but after you've done a few thousand
773 : * recursive reloads it's unlikely you'll learn more.
774 : *----------
775 : */
776 : #ifdef DISCARD_CACHES_ENABLED
777 : {
778 : static int recursion_depth = 0;
779 :
635 780 26448143 : if (recursion_depth < debug_discard_caches)
781 : {
1675 tgl 782 UBC 0 : recursion_depth++;
533 noah 783 0 : InvalidateSystemCachesExtended(true);
1675 tgl 784 0 : recursion_depth--;
785 : }
786 : }
787 : #endif
9770 scrappy 788 CBC 26448143 : }
789 :
790 : /*
791 : * PrepareInvalidationState
792 : * Initialize inval data for the current (sub)transaction.
793 : */
794 : static void
3084 rhaas 795 4226292 : PrepareInvalidationState(void)
796 : {
797 : TransInvalidationInfo *myInfo;
798 :
799 8135366 : if (transInvalInfo != NULL &&
800 3909074 : transInvalInfo->my_level == GetCurrentTransactionNestLevel())
801 3909007 : return;
802 :
803 : myInfo = (TransInvalidationInfo *)
6856 tgl 804 317285 : MemoryContextAllocZero(TopTransactionContext,
805 : sizeof(TransInvalidationInfo));
3084 rhaas 806 317285 : myInfo->parent = transInvalInfo;
807 317285 : myInfo->my_level = GetCurrentTransactionNestLevel();
808 :
809 : /* Now, do we have a previous stack entry? */
601 tgl 810 317285 : if (transInvalInfo != NULL)
811 : {
812 : /* Yes; this one should be for a deeper nesting level. */
813 67 : Assert(myInfo->my_level > transInvalInfo->my_level);
814 :
815 : /*
816 : * The parent (sub)transaction must not have any current (i.e.,
817 : * not-yet-locally-processed) messages. If it did, we'd have a
818 : * semantic problem: the new subtransaction presumably ought not be
819 : * able to see those events yet, but since the CommandCounter is
820 : * linear, that can't work once the subtransaction advances the
821 : * counter. This is a convenient place to check for that, as well as
822 : * being important to keep management of the message arrays simple.
823 : */
824 67 : if (NumMessagesInGroup(&transInvalInfo->CurrentCmdInvalidMsgs) != 0)
601 tgl 825 UBC 0 : elog(ERROR, "cannot start a subtransaction when there are unprocessed inval messages");
826 :
827 : /*
828 : * MemoryContextAllocZero set firstmsg = nextmsg = 0 in each group,
829 : * which is fine for the first (sub)transaction, but otherwise we need
830 : * to update them to follow whatever is already in the arrays.
831 : */
601 tgl 832 CBC 67 : SetGroupToFollow(&myInfo->PriorCmdInvalidMsgs,
833 : &transInvalInfo->CurrentCmdInvalidMsgs);
834 67 : SetGroupToFollow(&myInfo->CurrentCmdInvalidMsgs,
835 : &myInfo->PriorCmdInvalidMsgs);
836 : }
837 : else
838 : {
839 : /*
840 : * Here, we need only clear any array pointers left over from a prior
841 : * transaction.
842 : */
843 317218 : InvalMessageArrays[CatCacheMsgs].msgs = NULL;
844 317218 : InvalMessageArrays[CatCacheMsgs].maxmsgs = 0;
845 317218 : InvalMessageArrays[RelCacheMsgs].msgs = NULL;
846 317218 : InvalMessageArrays[RelCacheMsgs].maxmsgs = 0;
847 : }
848 :
3084 rhaas 849 317285 : transInvalInfo = myInfo;
850 : }
851 :
852 : /*
853 : * PostPrepare_Inval
854 : * Clean up after successful PREPARE.
855 : *
856 : * Here, we want to act as though the transaction aborted, so that we will
857 : * undo any syscache changes it made, thereby bringing us into sync with the
858 : * outside world, which doesn't believe the transaction committed yet.
859 : *
860 : * If the prepared transaction is later aborted, there is nothing more to
861 : * do; if it commits, we will receive the consequent inval messages just
862 : * like everyone else.
863 : */
864 : void
6505 tgl 865 364 : PostPrepare_Inval(void)
866 : {
867 364 : AtEOXact_Inval(false);
868 364 : }
869 :
870 : /*
871 : * xactGetCommittedInvalidationMessages() is called by
872 : * RecordTransactionCommit() to collect invalidation messages to add to the
873 : * commit record. This applies only to commit message types, never to
874 : * abort records. Must always run before AtEOXact_Inval(), since that
875 : * removes the data we need to see.
876 : *
877 : * Remember that this runs before we have officially committed, so we
878 : * must not do anything here to change what might occur *if* we should
879 : * fail between here and the actual commit.
880 : *
881 : * see also xact_redo_commit() and xact_desc_commit()
882 : */
883 : int
4859 simon 884 396811 : xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs,
885 : bool *RelcacheInitFileInval)
886 : {
887 : SharedInvalidationMessage *msgarray;
888 : int nummsgs;
889 : int nmsgs;
890 :
891 : /* Quick exit if we haven't done anything with invalidation messages. */
3084 rhaas 892 396811 : if (transInvalInfo == NULL)
893 : {
894 100172 : *RelcacheInitFileInval = false;
895 100172 : *msgs = NULL;
896 100172 : return 0;
897 : }
898 :
899 : /* Must be at top of stack */
900 296639 : Assert(transInvalInfo->my_level == 1 && transInvalInfo->parent == NULL);
901 :
902 : /*
903 : * Relcache init file invalidation requires processing both before and
904 : * after we send the SI messages. However, we need not do anything unless
905 : * we committed.
906 : */
4859 simon 907 296639 : *RelcacheInitFileInval = transInvalInfo->RelcacheInitFileInval;
908 :
909 : /*
910 : * Collect all the pending messages into a single contiguous array of
911 : * invalidation messages, to simplify what needs to happen while building
912 : * the commit WAL message. Maintain the order that they would be
913 : * processed in by AtEOXact_Inval(), to ensure emulated behaviour in redo
914 : * is as similar as possible to original. We want the same bugs, if any,
915 : * not new ones.
916 : */
601 tgl 917 296639 : nummsgs = NumMessagesInGroup(&transInvalInfo->PriorCmdInvalidMsgs) +
918 296639 : NumMessagesInGroup(&transInvalInfo->CurrentCmdInvalidMsgs);
919 :
920 296639 : *msgs = msgarray = (SharedInvalidationMessage *)
921 296639 : MemoryContextAlloc(CurTransactionContext,
922 : nummsgs * sizeof(SharedInvalidationMessage));
923 :
924 296639 : nmsgs = 0;
925 296639 : ProcessMessageSubGroupMulti(&transInvalInfo->PriorCmdInvalidMsgs,
926 : CatCacheMsgs,
927 : (memcpy(msgarray + nmsgs,
928 : msgs,
929 : n * sizeof(SharedInvalidationMessage)),
930 : nmsgs += n));
931 296639 : ProcessMessageSubGroupMulti(&transInvalInfo->CurrentCmdInvalidMsgs,
932 : CatCacheMsgs,
933 : (memcpy(msgarray + nmsgs,
934 : msgs,
935 : n * sizeof(SharedInvalidationMessage)),
936 : nmsgs += n));
937 296639 : ProcessMessageSubGroupMulti(&transInvalInfo->PriorCmdInvalidMsgs,
938 : RelCacheMsgs,
939 : (memcpy(msgarray + nmsgs,
940 : msgs,
941 : n * sizeof(SharedInvalidationMessage)),
942 : nmsgs += n));
943 296639 : ProcessMessageSubGroupMulti(&transInvalInfo->CurrentCmdInvalidMsgs,
944 : RelCacheMsgs,
945 : (memcpy(msgarray + nmsgs,
946 : msgs,
947 : n * sizeof(SharedInvalidationMessage)),
948 : nmsgs += n));
949 296639 : Assert(nmsgs == nummsgs);
950 :
951 296639 : return nmsgs;
952 : }
953 :
954 : /*
955 : * ProcessCommittedInvalidationMessages is executed by xact_redo_commit() or
956 : * standby_redo() to process invalidation messages. Currently that happens
957 : * only at end-of-xact.
958 : *
959 : * Relcache init file invalidation requires processing both
960 : * before and after we send the SI messages. See AtEOXact_Inval()
961 : */
962 : void
4838 simon 963 17285 : ProcessCommittedInvalidationMessages(SharedInvalidationMessage *msgs,
964 : int nmsgs, bool RelcacheInitFileInval,
965 : Oid dbid, Oid tsid)
966 : {
4803 967 17285 : if (nmsgs <= 0)
968 4415 : return;
969 :
970 12870 : elog(trace_recovery(DEBUG4), "replaying commit with %d messages%s", nmsgs,
971 : (RelcacheInitFileInval ? " and relcache file invalidation" : ""));
972 :
4838 973 12870 : if (RelcacheInitFileInval)
974 : {
1762 andres 975 124 : elog(trace_recovery(DEBUG4), "removing relcache init files for database %u",
976 : dbid);
977 :
978 : /*
979 : * RelationCacheInitFilePreInvalidate, when the invalidation message
980 : * is for a specific database, requires DatabasePath to be set, but we
981 : * should not use SetDatabasePath during recovery, since it is
982 : * intended to be used only once by normal backends. Hence, a quick
983 : * hack: set DatabasePath directly then unset after use.
984 : */
985 124 : if (OidIsValid(dbid))
986 124 : DatabasePath = GetDatabasePath(dbid, tsid);
987 :
4254 tgl 988 124 : RelationCacheInitFilePreInvalidate();
989 :
1762 andres 990 124 : if (OidIsValid(dbid))
991 : {
992 124 : pfree(DatabasePath);
993 124 : DatabasePath = NULL;
994 : }
995 : }
996 :
4838 simon 997 12870 : SendSharedInvalidMessages(msgs, nmsgs);
998 :
999 12870 : if (RelcacheInitFileInval)
4254 tgl 1000 124 : RelationCacheInitFilePostInvalidate();
1001 : }
1002 :
1003 : /*
1004 : * AtEOXact_Inval
1005 : * Process queued-up invalidation messages at end of main transaction.
1006 : *
1007 : * If isCommit, we must send out the messages in our PriorCmdInvalidMsgs list
1008 : * to the shared invalidation message queue. Note that these will be read
1009 : * not only by other backends, but also by our own backend at the next
1010 : * transaction start (via AcceptInvalidationMessages). This means that
1011 : * we can skip immediate local processing of anything that's still in
1012 : * CurrentCmdInvalidMsgs, and just send that list out too.
1013 : *
1014 : * If not isCommit, we are aborting, and must locally process the messages
1015 : * in PriorCmdInvalidMsgs. No messages need be sent to other backends,
1016 : * since they'll not have seen our changed tuples anyway. We can forget
1017 : * about CurrentCmdInvalidMsgs too, since those changes haven't touched
1018 : * the caches yet.
1019 : *
1020 : * In any case, reset our state to empty. We need not physically
1021 : * free memory here, since TopTransactionContext is about to be emptied
1022 : * anyway.
1023 : *
1024 : * Note:
1025 : * This should be called as the last step in processing a transaction.
1026 : */
1027 : void
6856 1028 486331 : AtEOXact_Inval(bool isCommit)
1029 : {
1030 : /* Quick exit if no messages */
3084 rhaas 1031 486331 : if (transInvalInfo == NULL)
1032 169145 : return;
1033 :
1034 : /* Must be at top of stack */
1035 317186 : Assert(transInvalInfo->my_level == 1 && transInvalInfo->parent == NULL);
1036 :
7964 tgl 1037 317186 : if (isCommit)
1038 : {
1039 : /*
1040 : * Relcache init file invalidation requires processing both before and
1041 : * after we send the SI messages. However, we need not do anything
1042 : * unless we committed.
1043 : */
6856 1044 315493 : if (transInvalInfo->RelcacheInitFileInval)
4254 1045 44554 : RelationCacheInitFilePreInvalidate();
1046 :
6856 1047 315493 : AppendInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
6385 bruce 1048 315493 : &transInvalInfo->CurrentCmdInvalidMsgs);
1049 :
5407 tgl 1050 315493 : ProcessInvalidationMessagesMulti(&transInvalInfo->PriorCmdInvalidMsgs,
1051 : SendSharedInvalidMessages);
1052 :
6856 1053 315493 : if (transInvalInfo->RelcacheInitFileInval)
4254 1054 44554 : RelationCacheInitFilePostInvalidate();
1055 : }
1056 : else
1057 : {
6856 1058 1693 : ProcessInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
1059 : LocalExecuteInvalidationMessage);
1060 : }
1061 :
1062 : /* Need not free anything explicitly */
1063 317186 : transInvalInfo = NULL;
1064 : }
1065 :
1066 : /*
1067 : * AtEOSubXact_Inval
1068 : * Process queued-up invalidation messages at end of subtransaction.
1069 : *
1070 : * If isCommit, process CurrentCmdInvalidMsgs if any (there probably aren't),
1071 : * and then attach both CurrentCmdInvalidMsgs and PriorCmdInvalidMsgs to the
1072 : * parent's PriorCmdInvalidMsgs list.
1073 : *
1074 : * If not isCommit, we are aborting, and must locally process the messages
1075 : * in PriorCmdInvalidMsgs. No messages need be sent to other backends.
1076 : * We can forget about CurrentCmdInvalidMsgs too, since those changes haven't
1077 : * touched the caches yet.
1078 : *
1079 : * In any case, pop the transaction stack. We need not physically free memory
1080 : * here, since CurTransactionContext is about to be emptied anyway
1081 : * (if aborting). Beware of the possibility of aborting the same nesting
1082 : * level twice, though.
1083 : */
1084 : void
6829 1085 8815 : AtEOSubXact_Inval(bool isCommit)
1086 : {
1087 : int my_level;
6856 1088 8815 : TransInvalidationInfo *myInfo = transInvalInfo;
1089 :
1090 : /* Quick exit if no messages. */
3084 rhaas 1091 8815 : if (myInfo == NULL)
1092 8058 : return;
1093 :
1094 : /* Also bail out quickly if messages are not for this level. */
1095 757 : my_level = GetCurrentTransactionNestLevel();
1096 757 : if (myInfo->my_level != my_level)
1097 : {
1098 624 : Assert(myInfo->my_level < my_level);
1099 624 : return;
1100 : }
1101 :
1102 133 : if (isCommit)
1103 : {
1104 : /* If CurrentCmdInvalidMsgs still has anything, fix it */
6856 tgl 1105 46 : CommandEndInvalidationMessages();
1106 :
1107 : /*
1108 : * We create invalidation stack entries lazily, so the parent might
1109 : * not have one. Instead of creating one, moving all the data over,
1110 : * and then freeing our own, we can just adjust the level of our own
1111 : * entry.
1112 : */
3084 rhaas 1113 46 : if (myInfo->parent == NULL || myInfo->parent->my_level < my_level - 1)
1114 : {
1115 34 : myInfo->my_level--;
1116 34 : return;
1117 : }
1118 :
1119 : /*
1120 : * Pass up my inval messages to parent. Notice that we stick them in
1121 : * PriorCmdInvalidMsgs, not CurrentCmdInvalidMsgs, since they've
1122 : * already been locally processed. (This would trigger the Assert in
1123 : * AppendInvalidationMessageSubGroup if the parent's
1124 : * CurrentCmdInvalidMsgs isn't empty; but we already checked that in
1125 : * PrepareInvalidationState.)
1126 : */
6856 tgl 1127 12 : AppendInvalidationMessages(&myInfo->parent->PriorCmdInvalidMsgs,
1128 : &myInfo->PriorCmdInvalidMsgs);
1129 :
1130 : /* Must readjust parent's CurrentCmdInvalidMsgs indexes now */
601 1131 12 : SetGroupToFollow(&myInfo->parent->CurrentCmdInvalidMsgs,
1132 : &myInfo->parent->PriorCmdInvalidMsgs);
1133 :
1134 : /* Pending relcache inval becomes parent's problem too */
6856 1135 12 : if (myInfo->RelcacheInitFileInval)
6856 tgl 1136 UBC 0 : myInfo->parent->RelcacheInitFileInval = true;
1137 :
1138 : /* Pop the transaction state stack */
6789 tgl 1139 CBC 12 : transInvalInfo = myInfo->parent;
1140 :
1141 : /* Need not free anything else explicitly */
1142 12 : pfree(myInfo);
1143 : }
1144 : else
1145 : {
6856 1146 87 : ProcessInvalidationMessages(&myInfo->PriorCmdInvalidMsgs,
1147 : LocalExecuteInvalidationMessage);
1148 :
1149 : /* Pop the transaction state stack */
6789 1150 87 : transInvalInfo = myInfo->parent;
1151 :
1152 : /* Need not free anything else explicitly */
1153 87 : pfree(myInfo);
1154 : }
1155 : }
1156 :
1157 : /*
1158 : * CommandEndInvalidationMessages
1159 : * Process queued-up invalidation messages at end of one command
1160 : * in a transaction.
1161 : *
1162 : * Here, we send no messages to the shared queue, since we don't know yet if
1163 : * we will commit. We do need to locally process the CurrentCmdInvalidMsgs
1164 : * list, so as to flush our caches of any entries we have outdated in the
1165 : * current command. We then move the current-cmd list over to become part
1166 : * of the prior-cmds list.
1167 : *
1168 : * Note:
1169 : * This should be called during CommandCounterIncrement(),
1170 : * after we have advanced the command ID.
1171 : */
1172 : void
6856 1173 1206640 : CommandEndInvalidationMessages(void)
1174 : {
1175 : /*
1176 : * You might think this shouldn't be called outside any transaction, but
1177 : * bootstrap does it, and also ABORT issued when not in a transaction. So
1178 : * just quietly return if no state to work on.
1179 : */
1180 1206640 : if (transInvalInfo == NULL)
1181 189398 : return;
1182 :
1183 1017242 : ProcessInvalidationMessages(&transInvalInfo->CurrentCmdInvalidMsgs,
1184 : LocalExecuteInvalidationMessage);
1185 :
1186 : /* WAL Log per-command invalidation messages for wal_level=logical */
990 akapila 1187 1017239 : if (XLogLogicalInfoActive())
1188 5311 : LogLogicalInvalidations();
1189 :
6856 tgl 1190 1017239 : AppendInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
1191 1017239 : &transInvalInfo->CurrentCmdInvalidMsgs);
1192 : }
1193 :
1194 :
1195 : /*
1196 : * CacheInvalidateHeapTuple
1197 : * Register the given tuple for invalidation at end of command
1198 : * (ie, current command is creating or outdating this tuple).
1199 : * Also, detect whether a relcache invalidation is implied.
1200 : *
1201 : * For an insert or delete, tuple is the target tuple and newtuple is NULL.
1202 : * For an update, we are called just once, with tuple being the old tuple
1203 : * version and newtuple the new version. This allows avoidance of duplicate
1204 : * effort during an update.
1205 : */
1206 : void
4254 1207 15987220 : CacheInvalidateHeapTuple(Relation relation,
1208 : HeapTuple tuple,
1209 : HeapTuple newtuple)
1210 : {
1211 : Oid tupleRelId;
1212 : Oid databaseId;
1213 : Oid relationId;
1214 :
1215 : /* Do nothing during bootstrap */
1216 15987220 : if (IsBootstrapProcessingMode())
1217 3817380 : return;
1218 :
1219 : /*
1220 : * We only need to worry about invalidation for tuples that are in system
1221 : * catalogs; user-relation tuples are never in catcaches and can't affect
1222 : * the relcache either.
1223 : */
3419 rhaas 1224 12169840 : if (!IsCatalogRelation(relation))
4254 tgl 1225 8018555 : return;
1226 :
1227 : /*
1228 : * IsCatalogRelation() will return true for TOAST tables of system
1229 : * catalogs, but we don't care about those, either.
1230 : */
1231 4151285 : if (IsToastRelation(relation))
1232 93617 : return;
1233 :
1234 : /*
1235 : * If we're not prepared to queue invalidation messages for this
1236 : * subtransaction level, get ready now.
1237 : */
3084 rhaas 1238 4057668 : PrepareInvalidationState();
1239 :
1240 : /*
1241 : * First let the catcache do its thing
1242 : */
3568 1243 4057668 : tupleRelId = RelationGetRelid(relation);
1244 4057668 : if (RelationInvalidatesSnapshotsOnly(tupleRelId))
1245 : {
1246 1322643 : databaseId = IsSharedRelation(tupleRelId) ? InvalidOid : MyDatabaseId;
1247 1322643 : RegisterSnapshotInvalidation(databaseId, tupleRelId);
1248 : }
1249 : else
1250 2735025 : PrepareToInvalidateCacheTuple(relation, tuple, newtuple,
1251 : RegisterCatcacheInvalidation);
1252 :
1253 : /*
1254 : * Now, is this tuple one of the primary definers of a relcache entry? See
1255 : * comments in file header for deeper explanation.
1256 : *
1257 : * Note we ignore newtuple here; we assume an update cannot move a tuple
1258 : * from being part of one relcache entry to being part of another.
1259 : */
4254 tgl 1260 4057668 : if (tupleRelId == RelationRelationId)
1261 : {
1262 346899 : Form_pg_class classtup = (Form_pg_class) GETSTRUCT(tuple);
1263 :
1601 andres 1264 346899 : relationId = classtup->oid;
4254 tgl 1265 346899 : if (classtup->relisshared)
1266 13981 : databaseId = InvalidOid;
1267 : else
1268 332918 : databaseId = MyDatabaseId;
1269 : }
1270 3710769 : else if (tupleRelId == AttributeRelationId)
1271 : {
1272 878222 : Form_pg_attribute atttup = (Form_pg_attribute) GETSTRUCT(tuple);
1273 :
1274 878222 : relationId = atttup->attrelid;
1275 :
1276 : /*
1277 : * KLUGE ALERT: we always send the relcache event with MyDatabaseId,
1278 : * even if the rel in question is shared (which we can't easily tell).
1279 : * This essentially means that only backends in this same database
1280 : * will react to the relcache flush request. This is in fact
1281 : * appropriate, since only those backends could see our pg_attribute
1282 : * change anyway. It looks a bit ugly though. (In practice, shared
1283 : * relations can't have schema changes after bootstrap, so we should
1284 : * never come here for a shared rel anyway.)
1285 : */
1286 878222 : databaseId = MyDatabaseId;
1287 : }
1288 2832547 : else if (tupleRelId == IndexRelationId)
1289 : {
1290 45197 : Form_pg_index indextup = (Form_pg_index) GETSTRUCT(tuple);
1291 :
1292 : /*
1293 : * When a pg_index row is updated, we should send out a relcache inval
1294 : * for the index relation. As above, we don't know the shared status
1295 : * of the index, but in practice it doesn't matter since indexes of
1296 : * shared catalogs can't have such updates.
1297 : */
1298 45197 : relationId = indextup->indexrelid;
1299 45197 : databaseId = MyDatabaseId;
1300 : }
1539 alvherre 1301 2787350 : else if (tupleRelId == ConstraintRelationId)
1302 : {
1303 54369 : Form_pg_constraint constrtup = (Form_pg_constraint) GETSTRUCT(tuple);
1304 :
1305 : /*
1306 : * Foreign keys are part of relcache entries, too, so send out an
1307 : * inval for the table that the FK applies to.
1308 : */
1309 54369 : if (constrtup->contype == CONSTRAINT_FOREIGN &&
1310 3110 : OidIsValid(constrtup->conrelid))
1311 : {
1312 3110 : relationId = constrtup->conrelid;
1313 3110 : databaseId = MyDatabaseId;
1314 : }
1315 : else
1316 51259 : return;
1317 : }
1318 : else
4254 tgl 1319 2732981 : return;
1320 :
1321 : /*
1322 : * Yes. We need to register a relcache invalidation event.
1323 : */
1324 1273428 : RegisterRelcacheInvalidation(databaseId, relationId);
1325 : }
1326 :
1327 : /*
1328 : * CacheInvalidateCatalog
1329 : * Register invalidation of the whole content of a system catalog.
1330 : *
1331 : * This is normally used in VACUUM FULL/CLUSTER, where we haven't so much
1332 : * changed any tuples as moved them around. Some uses of catcache entries
1333 : * expect their TIDs to be correct, so we have to blow away the entries.
1334 : *
1335 : * Note: we expect caller to verify that the rel actually is a system
1336 : * catalog. If it isn't, no great harm is done, just a wasted sinval message.
1337 : */
1338 : void
4809 1339 100 : CacheInvalidateCatalog(Oid catalogId)
1340 : {
1341 : Oid databaseId;
1342 :
3084 rhaas 1343 100 : PrepareInvalidationState();
1344 :
4809 tgl 1345 100 : if (IsSharedRelation(catalogId))
1346 17 : databaseId = InvalidOid;
1347 : else
1348 83 : databaseId = MyDatabaseId;
1349 :
1350 100 : RegisterCatalogInvalidation(databaseId, catalogId);
1351 100 : }
1352 :
1353 : /*
1354 : * CacheInvalidateRelcache
1355 : * Register invalidation of the specified relation's relcache entry
1356 : * at end of command.
1357 : *
1358 : * This is used in places that need to force relcache rebuild but aren't
1359 : * changing any of the tuples recognized as contributors to the relcache
1360 : * entry by CacheInvalidateHeapTuple. (An example is dropping an index.)
1361 : */
1362 : void
6998 1363 110023 : CacheInvalidateRelcache(Relation relation)
1364 : {
1365 : Oid databaseId;
1366 : Oid relationId;
1367 :
3084 rhaas 1368 110023 : PrepareInvalidationState();
1369 :
6998 tgl 1370 110023 : relationId = RelationGetRelid(relation);
1371 110023 : if (relation->rd_rel->relisshared)
1372 12304 : databaseId = InvalidOid;
1373 : else
1374 97719 : databaseId = MyDatabaseId;
1375 :
6663 1376 110023 : RegisterRelcacheInvalidation(databaseId, relationId);
6998 1377 110023 : }
1378 :
1379 : /*
1380 : * CacheInvalidateRelcacheAll
1381 : * Register invalidation of the whole relcache at the end of command.
1382 : *
1383 : * This is used by alter publication as changes in publications may affect
1384 : * large number of tables.
1385 : */
1386 : void
2271 peter_e 1387 38 : CacheInvalidateRelcacheAll(void)
1388 : {
1389 38 : PrepareInvalidationState();
1390 :
1391 38 : RegisterRelcacheInvalidation(InvalidOid, InvalidOid);
1392 38 : }
1393 :
1394 : /*
1395 : * CacheInvalidateRelcacheByTuple
1396 : * As above, but relation is identified by passing its pg_class tuple.
1397 : */
1398 : void
6998 tgl 1399 45565 : CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
1400 : {
1401 45565 : Form_pg_class classtup = (Form_pg_class) GETSTRUCT(classTuple);
1402 : Oid databaseId;
1403 : Oid relationId;
1404 :
3084 rhaas 1405 45565 : PrepareInvalidationState();
1406 :
1601 andres 1407 45565 : relationId = classtup->oid;
6998 tgl 1408 45565 : if (classtup->relisshared)
1409 3267 : databaseId = InvalidOid;
1410 : else
1411 42298 : databaseId = MyDatabaseId;
6663 1412 45565 : RegisterRelcacheInvalidation(databaseId, relationId);
8490 inoue 1413 45565 : }
1414 :
1415 : /*
1416 : * CacheInvalidateRelcacheByRelid
1417 : * As above, but relation is identified by passing its OID.
1418 : * This is the least efficient of the three options; use one of
1419 : * the above routines if you have a Relation or pg_class tuple.
1420 : */
1421 : void
6912 tgl 1422 12898 : CacheInvalidateRelcacheByRelid(Oid relid)
1423 : {
1424 : HeapTuple tup;
1425 :
3084 rhaas 1426 12898 : PrepareInvalidationState();
1427 :
4802 1428 12898 : tup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
6912 tgl 1429 12898 : if (!HeapTupleIsValid(tup))
6912 tgl 1430 UBC 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
6912 tgl 1431 CBC 12898 : CacheInvalidateRelcacheByTuple(tup);
1432 12898 : ReleaseSysCache(tup);
1433 12898 : }
1434 :
1435 :
1436 : /*
1437 : * CacheInvalidateSmgr
1438 : * Register invalidation of smgr references to a physical relation.
1439 : *
1440 : * Sending this type of invalidation msg forces other backends to close open
1441 : * smgr entries for the rel. This should be done to flush dangling open-file
1442 : * references when the physical rel is being dropped or truncated. Because
1443 : * these are nontransactional (i.e., not-rollback-able) operations, we just
1444 : * send the inval message immediately without any queuing.
1445 : *
1446 : * Note: in most cases there will have been a relcache flush issued against
1447 : * the rel at the logical level. We need a separate smgr-level flush because
1448 : * it is possible for backends to have open smgr entries for rels they don't
1449 : * have a relcache entry for, e.g. because the only thing they ever did with
1450 : * the rel is write out dirty shared buffers.
1451 : *
1452 : * Note: because these messages are nontransactional, they won't be captured
1453 : * in commit/abort WAL entries. Instead, calls to CacheInvalidateSmgr()
1454 : * should happen in low-level smgr.c routines, which are executed while
1455 : * replaying WAL as well as when creating it.
1456 : *
1457 : * Note: In order to avoid bloating SharedInvalidationMessage, we store only
1458 : * three bytes of the backend ID using what would otherwise be padding space.
1459 : * Thus, the maximum possible backend ID is 2^23-1.
1460 : */
1461 : void
277 rhaas 1462 GNC 51360 : CacheInvalidateSmgr(RelFileLocatorBackend rlocator)
1463 : {
1464 : SharedInvalidationMessage msg;
1465 :
4813 tgl 1466 CBC 51360 : msg.sm.id = SHAREDINVALSMGR_ID;
277 rhaas 1467 GNC 51360 : msg.sm.backend_hi = rlocator.backend >> 16;
1468 51360 : msg.sm.backend_lo = rlocator.backend & 0xffff;
193 1469 51360 : msg.sm.rlocator = rlocator.locator;
1470 : /* check AddCatcacheInvalidationMessage() for an explanation */
1471 : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
1472 :
4813 tgl 1473 CBC 51360 : SendSharedInvalidMessages(&msg, 1);
1474 51360 : }
1475 :
1476 : /*
1477 : * CacheInvalidateRelmap
1478 : * Register invalidation of the relation mapping for a database,
1479 : * or for the shared catalogs if databaseId is zero.
1480 : *
1481 : * Sending this type of invalidation msg forces other backends to re-read
1482 : * the indicated relation mapping file. It is also necessary to send a
1483 : * relcache inval for the specific relations whose mapping has been altered,
1484 : * else the relcache won't get updated with the new filenode data.
1485 : *
1486 : * Note: because these messages are nontransactional, they won't be captured
1487 : * in commit/abort WAL entries. Instead, calls to CacheInvalidateRelmap()
1488 : * should happen in low-level relmapper.c routines, which are executed while
1489 : * replaying WAL as well as when creating it.
1490 : */
1491 : void
4809 1492 105 : CacheInvalidateRelmap(Oid databaseId)
1493 : {
1494 : SharedInvalidationMessage msg;
1495 :
1496 105 : msg.rm.id = SHAREDINVALRELMAP_ID;
1497 105 : msg.rm.dbId = databaseId;
1498 : /* check AddCatcacheInvalidationMessage() for an explanation */
1499 : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
1500 :
1501 105 : SendSharedInvalidMessages(&msg, 1);
1502 105 : }
1503 :
1504 :
1505 : /*
1506 : * CacheRegisterSyscacheCallback
1507 : * Register the specified function to be called for all future
1508 : * invalidation events in the specified cache. The cache ID and the
1509 : * hash value of the tuple being invalidated will be passed to the
1510 : * function.
1511 : *
1512 : * NOTE: Hash value zero will be passed if a cache reset request is received.
1513 : * In this case the called routines should flush all cached state.
1514 : * Yes, there's a possibility of a false match to zero, but it doesn't seem
1515 : * worth troubling over, especially since most of the current callees just
1516 : * flush all cached state anyway.
1517 : */
1518 : void
7650 1519 158567 : CacheRegisterSyscacheCallback(int cacheid,
1520 : SyscacheCallbackFunction func,
1521 : Datum arg)
1522 : {
2158 1523 158567 : if (cacheid < 0 || cacheid >= SysCacheSize)
2158 tgl 1524 UBC 0 : elog(FATAL, "invalid cache ID: %d", cacheid);
5325 tgl 1525 CBC 158567 : if (syscache_callback_count >= MAX_SYSCACHE_CALLBACKS)
5325 tgl 1526 UBC 0 : elog(FATAL, "out of syscache_callback_list slots");
1527 :
2158 tgl 1528 CBC 158567 : if (syscache_callback_links[cacheid] == 0)
1529 : {
1530 : /* first callback for this cache */
1531 134641 : syscache_callback_links[cacheid] = syscache_callback_count + 1;
1532 : }
1533 : else
1534 : {
1535 : /* add to end of chain, so that older callbacks are called first */
1536 23926 : int i = syscache_callback_links[cacheid] - 1;
1537 :
1538 24211 : while (syscache_callback_list[i].link > 0)
1539 285 : i = syscache_callback_list[i].link - 1;
1540 23926 : syscache_callback_list[i].link = syscache_callback_count + 1;
1541 : }
1542 :
5325 1543 158567 : syscache_callback_list[syscache_callback_count].id = cacheid;
2158 1544 158567 : syscache_callback_list[syscache_callback_count].link = 0;
5325 1545 158567 : syscache_callback_list[syscache_callback_count].function = func;
1546 158567 : syscache_callback_list[syscache_callback_count].arg = arg;
1547 :
1548 158567 : ++syscache_callback_count;
7650 1549 158567 : }
1550 :
1551 : /*
1552 : * CacheRegisterRelcacheCallback
1553 : * Register the specified function to be called for all future
1554 : * relcache invalidation events. The OID of the relation being
1555 : * invalidated will be passed to the function.
1556 : *
1557 : * NOTE: InvalidOid will be passed if a cache reset request is received.
1558 : * In this case the called routines should flush all cached state.
1559 : */
1560 : void
5325 1561 15321 : CacheRegisterRelcacheCallback(RelcacheCallbackFunction func,
1562 : Datum arg)
1563 : {
1564 15321 : if (relcache_callback_count >= MAX_RELCACHE_CALLBACKS)
5325 tgl 1565 UBC 0 : elog(FATAL, "out of relcache_callback_list slots");
1566 :
5325 tgl 1567 CBC 15321 : relcache_callback_list[relcache_callback_count].function = func;
1568 15321 : relcache_callback_list[relcache_callback_count].arg = arg;
1569 :
1570 15321 : ++relcache_callback_count;
7650 1571 15321 : }
1572 :
1573 : /*
1574 : * CallSyscacheCallbacks
1575 : *
1576 : * This is exported so that CatalogCacheFlushCatalog can call it, saving
1577 : * this module from knowing which catcache IDs correspond to which catalogs.
1578 : */
1579 : void
4254 1580 14189632 : CallSyscacheCallbacks(int cacheid, uint32 hashvalue)
1581 : {
1582 : int i;
1583 :
2158 1584 14189632 : if (cacheid < 0 || cacheid >= SysCacheSize)
2158 tgl 1585 UBC 0 : elog(ERROR, "invalid cache ID: %d", cacheid);
1586 :
2158 tgl 1587 CBC 14189632 : i = syscache_callback_links[cacheid] - 1;
1588 15845160 : while (i >= 0)
1589 : {
4809 1590 1655528 : struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i;
1591 :
2158 1592 1655528 : Assert(ccitem->id == cacheid);
2040 peter_e 1593 1655528 : ccitem->function(ccitem->arg, cacheid, hashvalue);
2158 tgl 1594 1655528 : i = ccitem->link - 1;
1595 : }
4809 1596 14189632 : }
1597 :
1598 : /*
1599 : * LogLogicalInvalidations
1600 : *
1601 : * Emit WAL for invalidations caused by the current command.
1602 : *
1603 : * This is currently only used for logging invalidations at the command end
1604 : * or at commit time if any invalidations are pending.
1605 : */
1606 : void
601 1607 17863 : LogLogicalInvalidations(void)
1608 : {
1609 : xl_xact_invals xlrec;
1610 : InvalidationMsgsGroup *group;
1611 : int nmsgs;
1612 :
1613 : /* Quick exit if we haven't done anything with invalidation messages. */
990 akapila 1614 17863 : if (transInvalInfo == NULL)
1615 10562 : return;
1616 :
601 tgl 1617 7301 : group = &transInvalInfo->CurrentCmdInvalidMsgs;
1618 7301 : nmsgs = NumMessagesInGroup(group);
1619 :
990 akapila 1620 7301 : if (nmsgs > 0)
1621 : {
1622 : /* prepare record */
1623 5690 : memset(&xlrec, 0, MinSizeOfXactInvals);
1624 5690 : xlrec.nmsgs = nmsgs;
1625 :
1626 : /* perform insertion */
1627 5690 : XLogBeginInsert();
1628 5690 : XLogRegisterData((char *) (&xlrec), MinSizeOfXactInvals);
601 tgl 1629 5690 : ProcessMessageSubGroupMulti(group, CatCacheMsgs,
1630 : XLogRegisterData((char *) msgs,
1631 : n * sizeof(SharedInvalidationMessage)));
1632 5690 : ProcessMessageSubGroupMulti(group, RelCacheMsgs,
1633 : XLogRegisterData((char *) msgs,
1634 : n * sizeof(SharedInvalidationMessage)));
990 akapila 1635 5690 : XLogInsert(RM_XACT_ID, XLOG_XACT_INVALIDATIONS);
1636 : }
1637 : }
|