Age Owner Branch data 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 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
1748 tgl@sss.pgh.pa.us 320 :CBC 4461679 : AddInvalidationMessage(InvalidationMsgsGroup *group, int subgroup,
321 : : const SharedInvalidationMessage *msg)
322 : : {
323 : 4461679 : InvalMessageArray *ima = &InvalMessageArrays[subgroup];
324 : 4461679 : int nextindex = group->nextmsg[subgroup];
325 : :
326 [ + + ]: 4461679 : if (nextindex >= ima->maxmsgs)
327 : : {
328 [ + + ]: 343883 : if (ima->msgs == NULL)
329 : : {
330 : : /* Create new storage array in TopTransactionContext */
331 : 304631 : int reqsize = 32; /* arbitrary */
332 : :
333 : 304631 : ima->msgs = (SharedInvalidationMessage *)
334 : 304631 : MemoryContextAlloc(TopTransactionContext,
335 : : reqsize * sizeof(SharedInvalidationMessage));
336 : 304631 : ima->maxmsgs = reqsize;
337 [ - + ]: 304631 : Assert(nextindex == 0);
338 : : }
339 : : else
340 : : {
341 : : /* Enlarge storage array */
342 : 39252 : int reqsize = 2 * ima->maxmsgs;
343 : :
344 : 39252 : ima->msgs = (SharedInvalidationMessage *)
345 : 39252 : repalloc(ima->msgs,
346 : : reqsize * sizeof(SharedInvalidationMessage));
347 : 39252 : ima->maxmsgs = reqsize;
348 : : }
349 : : }
350 : : /* Okay, add message to current group */
351 : 4461679 : ima->msgs[nextindex] = *msg;
352 : 4461679 : group->nextmsg[subgroup]++;
10917 scrappy@hub.org 353 : 4461679 : }
354 : :
355 : : /*
356 : : * Append one subgroup of invalidation messages to another, resetting
357 : : * the source subgroup to empty.
358 : : */
359 : : static void
1748 tgl@sss.pgh.pa.us 360 : 1363408 : AppendInvalidationMessageSubGroup(InvalidationMsgsGroup *dest,
361 : : InvalidationMsgsGroup *src,
362 : : int subgroup)
363 : : {
364 : : /* Messages must be adjacent in main array */
365 [ - + ]: 1363408 : Assert(dest->nextmsg[subgroup] == src->firstmsg[subgroup]);
366 : :
367 : : /* ... which makes this easy: */
368 : 1363408 : 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 : 1363408 : SetSubGroupToFollow(src, dest, subgroup);
8854 376 : 1363408 : }
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
1748 425 : 3568075 : AddCatcacheInvalidationMessage(InvalidationMsgsGroup *group,
426 : : int id, uint32 hashValue, Oid dbId)
427 : : {
428 : : SharedInvalidationMessage msg;
429 : :
5769 rhaas@postgresql.org 430 [ - + ]: 3568075 : Assert(id < CHAR_MAX);
431 : 3568075 : msg.cc.id = (int8) id;
8854 tgl@sss.pgh.pa.us 432 : 3568075 : msg.cc.dbId = dbId;
433 : 3568075 : 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 : :
1748 446 : 3568075 : AddInvalidationMessage(group, CatCacheMsgs, &msg);
9637 inoue@tpf.co.jp 447 : 3568075 : }
448 : :
449 : : /*
450 : : * Add a whole-catalog inval entry
451 : : */
452 : : static void
1748 tgl@sss.pgh.pa.us 453 : 121 : AddCatalogInvalidationMessage(InvalidationMsgsGroup *group,
454 : : Oid dbId, Oid catId)
455 : : {
456 : : SharedInvalidationMessage msg;
457 : :
5956 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 : :
1748 464 : 121 : AddInvalidationMessage(group, CatCacheMsgs, &msg);
5956 465 : 121 : }
466 : :
467 : : /*
468 : : * Add a relcache inval entry
469 : : */
470 : : static void
1748 471 : 1350773 : 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 [ + + + + : 4341403 : 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 */
9111 488 : 529875 : msg.rc.id = SHAREDINVALRELCACHE_ID;
489 : 529875 : msg.rc.dbId = dbId;
490 : 529875 : msg.rc.relId = relId;
491 : : /* check AddCatcacheInvalidationMessage() for an explanation */
492 : : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
493 : :
1748 494 : 529875 : 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
443 akapila@postgresql.o 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
1748 tgl@sss.pgh.pa.us 533 : 725291 : 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 [ + + + + : 1066275 : ProcessMessageSubGroup(group, RelCacheMsgs,
+ + ]
541 : : if (msg->sn.id == SHAREDINVALSNAPSHOT_ID &&
542 : : msg->sn.relId == relId)
543 : : return);
544 : :
545 : : /* OK, add the item */
4715 rhaas@postgresql.org 546 : 363602 : msg.sn.id = SHAREDINVALSNAPSHOT_ID;
547 : 363602 : msg.sn.dbId = dbId;
548 : 363602 : msg.sn.relId = relId;
549 : : /* check AddCatcacheInvalidationMessage() for an explanation */
550 : : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
551 : :
1748 tgl@sss.pgh.pa.us 552 : 363602 : 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 : 681704 : AppendInvalidationMessages(InvalidationMsgsGroup *dest,
561 : : InvalidationMsgsGroup *src)
562 : : {
563 : 681704 : AppendInvalidationMessageSubGroup(dest, src, CatCacheMsgs);
564 : 681704 : AppendInvalidationMessageSubGroup(dest, src, RelCacheMsgs);
8854 565 : 681704 : }
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
1748 574 : 548988 : ProcessInvalidationMessages(InvalidationMsgsGroup *group,
575 : : void (*func) (SharedInvalidationMessage *msg))
576 : : {
577 [ + + ]: 3942663 : ProcessMessageSubGroup(group, CatCacheMsgs, func(msg));
578 [ + + ]: 1336076 : ProcessMessageSubGroup(group, RelCacheMsgs, func(msg));
9111 579 : 548984 : }
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
1748 586 : 199907 : ProcessInvalidationMessagesMulti(InvalidationMsgsGroup *group,
587 : : void (*func) (const SharedInvalidationMessage *msgs, int n))
588 : : {
589 [ + + ]: 199907 : ProcessMessageSubGroupMulti(group, CatCacheMsgs, func(msgs, n));
590 [ + + ]: 199907 : ProcessMessageSubGroupMulti(group, RelCacheMsgs, func(msgs, n));
6554 591 : 199907 : }
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
9111 604 : 3568075 : RegisterCatcacheInvalidation(int cacheId,
605 : : uint32 hashValue,
606 : : Oid dbId,
607 : : void *context)
608 : : {
582 noah@leadboat.com 609 : 3568075 : InvalidationInfo *info = (InvalidationInfo *) context;
610 : :
611 : 3568075 : AddCatcacheInvalidationMessage(&info->CurrentCmdInvalidMsgs,
612 : : cacheId, hashValue, dbId);
10917 scrappy@hub.org 613 : 3568075 : }
614 : :
615 : : /*
616 : : * RegisterCatalogInvalidation
617 : : *
618 : : * Register an invalidation event for all catcache entries from a catalog.
619 : : */
620 : : static void
582 noah@leadboat.com 621 : 121 : RegisterCatalogInvalidation(InvalidationInfo *info, Oid dbId, Oid catId)
622 : : {
623 : 121 : AddCatalogInvalidationMessage(&info->CurrentCmdInvalidMsgs, dbId, catId);
5956 tgl@sss.pgh.pa.us 624 : 121 : }
625 : :
626 : : /*
627 : : * RegisterRelcacheInvalidation
628 : : *
629 : : * As above, but register a relcache invalidation event.
630 : : */
631 : : static void
582 noah@leadboat.com 632 : 1350773 : RegisterRelcacheInvalidation(InvalidationInfo *info, Oid dbId, Oid relId)
633 : : {
634 : 1350773 : 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 : : */
6756 tgl@sss.pgh.pa.us 642 : 1350773 : (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 : : */
2909 andres@anarazel.de 650 [ + + + + ]: 1350773 : if (relId == InvalidOid || RelationIdIsInInitFile(relId))
582 noah@leadboat.com 651 : 69684 : info->RelcacheInitFileInval = true;
9637 inoue@tpf.co.jp 652 : 1350773 : }
653 : :
654 : : /*
655 : : * RegisterRelsyncInvalidation
656 : : *
657 : : * As above, but register a relsynccache invalidation event.
658 : : */
659 : : static void
443 akapila@postgresql.o 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
582 noah@leadboat.com 672 : 725291 : RegisterSnapshotInvalidation(InvalidationInfo *info, Oid dbId, Oid relId)
673 : : {
674 : 725291 : AddSnapshotInvalidationMessage(&info->CurrentCmdInvalidMsgs, dbId, relId);
4715 rhaas@postgresql.org 675 : 725291 : }
676 : :
677 : : /*
678 : : * PrepareInvalidationState
679 : : * Initialize inval data for the current (sub)transaction.
680 : : */
681 : : static InvalidationInfo *
935 michael@paquier.xyz 682 : 2757395 : PrepareInvalidationState(void)
683 : : {
684 : : TransInvalidationInfo *myInfo;
685 : :
686 : : /* PrepareToInvalidateCacheTuple() needs relcache */
408 noah@leadboat.com 687 : 2757395 : AssertCouldGetRelation();
688 : : /* Can't queue transactional message while collecting inplace messages. */
582 689 [ - + ]: 2757395 : Assert(inplaceInvalInfo == NULL);
690 : :
935 michael@paquier.xyz 691 [ + + + + ]: 5375093 : if (transInvalInfo != NULL &&
692 : 2617698 : transInvalInfo->my_level == GetCurrentTransactionNestLevel())
582 noah@leadboat.com 693 : 2617599 : return (InvalidationInfo *) transInvalInfo;
694 : :
695 : : myInfo = (TransInvalidationInfo *)
935 michael@paquier.xyz 696 : 139796 : MemoryContextAllocZero(TopTransactionContext,
697 : : sizeof(TransInvalidationInfo));
698 : 139796 : myInfo->parent = transInvalInfo;
699 : 139796 : myInfo->my_level = GetCurrentTransactionNestLevel();
700 : :
701 : : /* Now, do we have a previous stack entry? */
702 [ + + ]: 139796 : if (transInvalInfo != NULL)
703 : : {
704 : : /* Yes; this one should be for a deeper nesting level. */
705 [ - + ]: 99 : 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 : : */
582 noah@leadboat.com 716 [ - + ]: 99 : if (NumMessagesInGroup(&transInvalInfo->ii.CurrentCmdInvalidMsgs) != 0)
935 michael@paquier.xyz 717 [ # # ]:UBC 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 : : */
935 michael@paquier.xyz 724 :CBC 99 : SetGroupToFollow(&myInfo->PriorCmdInvalidMsgs,
725 : : &transInvalInfo->ii.CurrentCmdInvalidMsgs);
582 noah@leadboat.com 726 : 99 : 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 : : */
935 michael@paquier.xyz 735 : 139697 : InvalMessageArrays[CatCacheMsgs].msgs = NULL;
736 : 139697 : InvalMessageArrays[CatCacheMsgs].maxmsgs = 0;
737 : 139697 : InvalMessageArrays[RelCacheMsgs].msgs = NULL;
738 : 139697 : InvalMessageArrays[RelCacheMsgs].maxmsgs = 0;
739 : : }
740 : :
741 : 139796 : transInvalInfo = myInfo;
582 noah@leadboat.com 742 : 139796 : 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 : 96013 : PrepareInplaceInvalidationState(void)
753 : : {
754 : : InvalidationInfo *myInfo;
755 : :
408 756 : 96013 : AssertCouldGetRelation();
757 : : /* limit of one inplace update under assembly */
582 758 [ - + ]: 96013 : Assert(inplaceInvalInfo == NULL);
759 : :
760 : : /* gone after WAL insertion CritSection ends, so use current context */
171 michael@paquier.xyz 761 :GNC 96013 : myInfo = palloc0_object(InvalidationInfo);
762 : :
763 : : /* Stash our messages past end of the transactional messages, if any. */
582 noah@leadboat.com 764 [ + + ]:CBC 96013 : if (transInvalInfo != NULL)
765 : 70998 : SetGroupToFollow(&myInfo->CurrentCmdInvalidMsgs,
766 : : &transInvalInfo->ii.CurrentCmdInvalidMsgs);
767 : : else
768 : : {
769 : 25015 : InvalMessageArrays[CatCacheMsgs].msgs = NULL;
770 : 25015 : InvalMessageArrays[CatCacheMsgs].maxmsgs = 0;
771 : 25015 : InvalMessageArrays[RelCacheMsgs].msgs = NULL;
772 : 25015 : InvalMessageArrays[RelCacheMsgs].maxmsgs = 0;
773 : : }
774 : :
775 : 96013 : inplaceInvalInfo = myInfo;
776 : 96013 : return myInfo;
777 : : }
778 : :
779 : : /* ----------------------------------------------------------------
780 : : * public functions
781 : : * ----------------------------------------------------------------
782 : : */
783 : :
784 : : void
935 michael@paquier.xyz 785 : 2751 : InvalidateSystemCachesExtended(bool debug_discard)
786 : : {
787 : : int i;
788 : :
789 : 2751 : InvalidateCatalogSnapshot();
501 heikki.linnakangas@i 790 : 2751 : ResetCatalogCachesExt(debug_discard);
935 michael@paquier.xyz 791 : 2751 : RelationCacheInvalidate(debug_discard); /* gets smgr and relmap too */
792 : :
793 [ + + ]: 46894 : for (i = 0; i < syscache_callback_count; i++)
794 : : {
795 : 44143 : struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i;
796 : :
797 : 44143 : ccitem->function(ccitem->arg, ccitem->id, 0);
798 : : }
799 : :
800 [ + + ]: 6166 : for (i = 0; i < relcache_callback_count; i++)
801 : : {
802 : 3415 : struct RELCACHECALLBACK *ccitem = relcache_callback_list + i;
803 : :
804 : 3415 : ccitem->function(ccitem->arg, InvalidOid);
805 : : }
806 : :
443 akapila@postgresql.o 807 [ + + ]: 2777 : 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 : : }
935 michael@paquier.xyz 813 : 2751 : }
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
9111 tgl@sss.pgh.pa.us 823 : 25608095 : LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
824 : : {
825 [ + + ]: 25608095 : if (msg->id >= 0)
826 : : {
5956 827 [ + + + + ]: 20426681 : if (msg->cc.dbId == MyDatabaseId || msg->cc.dbId == InvalidOid)
828 : : {
4715 rhaas@postgresql.org 829 : 16037976 : InvalidateCatalogSnapshot();
830 : :
3305 tgl@sss.pgh.pa.us 831 : 16037976 : SysCacheInvalidate(msg->cc.id, msg->cc.hashValue);
832 : :
5401 833 : 16037976 : CallSyscacheCallbacks(msg->cc.id, msg->cc.hashValue);
834 : : }
835 : : }
5956 836 [ + + ]: 5181414 : else if (msg->id == SHAREDINVALCATALOG_ID)
837 : : {
838 [ + + + + ]: 524 : if (msg->cat.dbId == MyDatabaseId || msg->cat.dbId == InvalidOid)
839 : : {
4715 rhaas@postgresql.org 840 : 450 : InvalidateCatalogSnapshot();
841 : :
5956 tgl@sss.pgh.pa.us 842 : 450 : CatalogCacheFlushCatalog(msg->cat.catId);
843 : :
844 : : /* CatalogCacheFlushCatalog calls CallSyscacheCallbacks as needed */
845 : : }
846 : : }
9111 847 [ + + ]: 5180890 : else if (msg->id == SHAREDINVALRELCACHE_ID)
848 : : {
8145 849 [ + + + + ]: 2750947 : if (msg->rc.dbId == MyDatabaseId || msg->rc.dbId == InvalidOid)
850 : : {
851 : : int i;
852 : :
3418 peter_e@gmx.net 853 [ + + ]: 2172838 : if (msg->rc.relId == InvalidOid)
1680 noah@leadboat.com 854 : 713 : RelationCacheInvalidate(false);
855 : : else
3418 peter_e@gmx.net 856 : 2172125 : RelationCacheInvalidateEntry(msg->rc.relId);
857 : :
6472 tgl@sss.pgh.pa.us 858 [ + + ]: 6049863 : for (i = 0; i < relcache_callback_count; i++)
859 : : {
860 : 3877029 : struct RELCACHECALLBACK *ccitem = relcache_callback_list + i;
861 : :
3187 peter_e@gmx.net 862 : 3877029 : ccitem->function(ccitem->arg, msg->rc.relId);
863 : : }
864 : : }
865 : : }
7810 tgl@sss.pgh.pa.us 866 [ + + ]: 2429943 : 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 : :
1340 rhaas@postgresql.org 874 : 323913 : rlocator.locator = msg->sm.rlocator;
1424 875 : 323913 : rlocator.backend = (msg->sm.backend_hi << 16) | (int) msg->sm.backend_lo;
850 heikki.linnakangas@i 876 : 323913 : smgrreleaserellocator(rlocator);
877 : : }
5956 tgl@sss.pgh.pa.us 878 [ + + ]: 2106030 : else if (msg->id == SHAREDINVALRELMAP_ID)
879 : : {
880 : : /* We only care about our own database and shared catalogs */
881 [ + + ]: 377 : if (msg->rm.dbId == InvalidOid)
882 : 154 : RelationMapInvalidate(true);
883 [ + + ]: 223 : else if (msg->rm.dbId == MyDatabaseId)
884 : 156 : RelationMapInvalidate(false);
885 : : }
4715 rhaas@postgresql.org 886 [ + + ]: 2105653 : else if (msg->id == SHAREDINVALSNAPSHOT_ID)
887 : : {
888 : : /* We only care about our own database and shared catalogs */
1979 michael@paquier.xyz 889 [ + + ]: 2105622 : if (msg->sn.dbId == InvalidOid)
4715 rhaas@postgresql.org 890 : 65297 : InvalidateCatalogSnapshot();
1979 michael@paquier.xyz 891 [ + + ]: 2040325 : else if (msg->sn.dbId == MyDatabaseId)
4715 rhaas@postgresql.org 892 : 1630004 : InvalidateCatalogSnapshot();
893 : : }
443 akapila@postgresql.o 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
5459 peter_e@gmx.net 901 [ # # ]:UBC 0 : elog(FATAL, "unrecognized SI message ID: %d", msg->id);
10917 scrappy@hub.org 902 :CBC 25608091 : }
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
9112 tgl@sss.pgh.pa.us 916 : 2751 : InvalidateSystemCaches(void)
917 : : {
1680 noah@leadboat.com 918 : 2751 : InvalidateSystemCachesExtended(false);
919 : 2751 : }
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
9111 tgl@sss.pgh.pa.us 930 : 23096029 : AcceptInvalidationMessages(void)
931 : : {
932 : : #ifdef USE_ASSERT_CHECKING
933 : : /* message handlers shall access catalogs only during transactions */
408 noah@leadboat.com 934 [ + + ]: 23096029 : if (IsTransactionState())
935 : 22664518 : AssertCouldGetRelation();
936 : : #endif
937 : :
9111 tgl@sss.pgh.pa.us 938 : 23096029 : 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 : :
1782 970 [ - + ]: 23096029 : if (recursion_depth < debug_discard_caches)
971 : : {
2822 tgl@sss.pgh.pa.us 972 :UBC 0 : recursion_depth++;
1680 noah@leadboat.com 973 : 0 : InvalidateSystemCachesExtended(true);
2822 tgl@sss.pgh.pa.us 974 : 0 : recursion_depth--;
975 : : }
976 : : }
977 : : #endif
10917 scrappy@hub.org 978 :CBC 23096029 : }
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
7652 tgl@sss.pgh.pa.us 993 : 337 : PostPrepare_Inval(void)
994 : : {
995 : 337 : AtEOXact_Inval(false);
996 : 337 : }
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
6006 simon@2ndQuadrant.co 1012 : 325855 : 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. */
4231 rhaas@postgresql.org 1020 [ + + ]: 325855 : if (transInvalInfo == NULL)
1021 : : {
1022 : 201381 : *RelcacheInitFileInval = false;
1023 : 201381 : *msgs = NULL;
1024 : 201381 : return 0;
1025 : : }
1026 : :
1027 : : /* Must be at top of stack */
1028 [ + - - + ]: 124474 : 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 : : */
582 noah@leadboat.com 1035 : 124474 : *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 : : */
1748 tgl@sss.pgh.pa.us 1045 : 124474 : nummsgs = NumMessagesInGroup(&transInvalInfo->PriorCmdInvalidMsgs) +
582 noah@leadboat.com 1046 : 124474 : NumMessagesInGroup(&transInvalInfo->ii.CurrentCmdInvalidMsgs);
1047 : :
1748 tgl@sss.pgh.pa.us 1048 : 124474 : *msgs = msgarray = (SharedInvalidationMessage *)
1049 : 124474 : MemoryContextAlloc(CurTransactionContext,
1050 : : nummsgs * sizeof(SharedInvalidationMessage));
1051 : :
1052 : 124474 : nmsgs = 0;
1053 [ + + ]: 124474 : ProcessMessageSubGroupMulti(&transInvalInfo->PriorCmdInvalidMsgs,
1054 : : CatCacheMsgs,
1055 : : (memcpy(msgarray + nmsgs,
1056 : : msgs,
1057 : : n * sizeof(SharedInvalidationMessage)),
1058 : : nmsgs += n));
582 noah@leadboat.com 1059 [ + + ]: 124474 : ProcessMessageSubGroupMulti(&transInvalInfo->ii.CurrentCmdInvalidMsgs,
1060 : : CatCacheMsgs,
1061 : : (memcpy(msgarray + nmsgs,
1062 : : msgs,
1063 : : n * sizeof(SharedInvalidationMessage)),
1064 : : nmsgs += n));
1748 tgl@sss.pgh.pa.us 1065 [ + + ]: 124474 : ProcessMessageSubGroupMulti(&transInvalInfo->PriorCmdInvalidMsgs,
1066 : : RelCacheMsgs,
1067 : : (memcpy(msgarray + nmsgs,
1068 : : msgs,
1069 : : n * sizeof(SharedInvalidationMessage)),
1070 : : nmsgs += n));
582 noah@leadboat.com 1071 [ + + ]: 124474 : ProcessMessageSubGroupMulti(&transInvalInfo->ii.CurrentCmdInvalidMsgs,
1072 : : RelCacheMsgs,
1073 : : (memcpy(msgarray + nmsgs,
1074 : : msgs,
1075 : : n * sizeof(SharedInvalidationMessage)),
1076 : : nmsgs += n));
1077 [ - + ]: 124474 : Assert(nmsgs == nummsgs);
1078 : :
1079 : 124474 : 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 : 73843 : 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 [ + + ]: 73843 : if (inplaceInvalInfo == NULL)
1097 : : {
1098 : 17955 : *RelcacheInitFileInval = false;
1099 : 17955 : *msgs = NULL;
1100 : 17955 : return 0;
1101 : : }
1102 : :
1103 : 55888 : *RelcacheInitFileInval = inplaceInvalInfo->RelcacheInitFileInval;
1104 : 55888 : nummsgs = NumMessagesInGroup(&inplaceInvalInfo->CurrentCmdInvalidMsgs);
1105 : 55888 : *msgs = msgarray = (SharedInvalidationMessage *)
1106 : 55888 : palloc(nummsgs * sizeof(SharedInvalidationMessage));
1107 : :
1108 : 55888 : nmsgs = 0;
1109 [ + - ]: 55888 : ProcessMessageSubGroupMulti(&inplaceInvalInfo->CurrentCmdInvalidMsgs,
1110 : : CatCacheMsgs,
1111 : : (memcpy(msgarray + nmsgs,
1112 : : msgs,
1113 : : n * sizeof(SharedInvalidationMessage)),
1114 : : nmsgs += n));
1115 [ + + ]: 55888 : ProcessMessageSubGroupMulti(&inplaceInvalInfo->CurrentCmdInvalidMsgs,
1116 : : RelCacheMsgs,
1117 : : (memcpy(msgarray + nmsgs,
1118 : : msgs,
1119 : : n * sizeof(SharedInvalidationMessage)),
1120 : : nmsgs += n));
1748 tgl@sss.pgh.pa.us 1121 [ - + ]: 55888 : Assert(nmsgs == nummsgs);
1122 : :
1123 : 55888 : 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
5985 simon@2ndQuadrant.co 1135 : 30823 : ProcessCommittedInvalidationMessages(SharedInvalidationMessage *msgs,
1136 : : int nmsgs, bool RelcacheInitFileInval,
1137 : : Oid dbid, Oid tsid)
1138 : : {
5950 1139 [ + + ]: 30823 : if (nmsgs <= 0)
1140 : 5553 : return;
1141 : :
901 michael@paquier.xyz 1142 [ - + - - ]: 25270 : elog(DEBUG4, "replaying commit with %d messages%s", nmsgs,
1143 : : (RelcacheInitFileInval ? " and relcache file invalidation" : ""));
1144 : :
5985 simon@2ndQuadrant.co 1145 [ + + ]: 25270 : if (RelcacheInitFileInval)
1146 : : {
901 michael@paquier.xyz 1147 [ - + ]: 583 : 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 : : */
2909 andres@anarazel.de 1156 [ + - ]: 583 : if (OidIsValid(dbid))
1157 : 583 : DatabasePath = GetDatabasePath(dbid, tsid);
1158 : :
5401 tgl@sss.pgh.pa.us 1159 : 583 : RelationCacheInitFilePreInvalidate();
1160 : :
2909 andres@anarazel.de 1161 [ + - ]: 583 : if (OidIsValid(dbid))
1162 : : {
1163 : 583 : pfree(DatabasePath);
1164 : 583 : DatabasePath = NULL;
1165 : : }
1166 : : }
1167 : :
5985 simon@2ndQuadrant.co 1168 : 25270 : SendSharedInvalidMessages(msgs, nmsgs);
1169 : :
1170 [ + + ]: 25270 : if (RelcacheInitFileInval)
5401 tgl@sss.pgh.pa.us 1171 : 583 : 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
8003 1196 : 426350 : AtEOXact_Inval(bool isCommit)
1197 : : {
582 noah@leadboat.com 1198 : 426350 : inplaceInvalInfo = NULL;
1199 : :
1200 : : /* Quick exit if no transactional messages */
4231 rhaas@postgresql.org 1201 [ + + ]: 426350 : if (transInvalInfo == NULL)
1202 : 286694 : return;
1203 : :
1204 : : /* Must be at top of stack */
1205 [ + - - + ]: 139656 : Assert(transInvalInfo->my_level == 1 && transInvalInfo->parent == NULL);
1206 : :
385 michael@paquier.xyz 1207 : 139656 : INJECTION_POINT("transaction-end-process-inval", NULL);
1208 : :
9111 tgl@sss.pgh.pa.us 1209 [ + + ]: 139656 : 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 : : */
582 noah@leadboat.com 1216 [ + + ]: 136245 : if (transInvalInfo->ii.RelcacheInitFileInval)
5401 tgl@sss.pgh.pa.us 1217 : 13148 : RelationCacheInitFilePreInvalidate();
1218 : :
8003 1219 : 136245 : AppendInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
582 noah@leadboat.com 1220 : 136245 : &transInvalInfo->ii.CurrentCmdInvalidMsgs);
1221 : :
6554 tgl@sss.pgh.pa.us 1222 : 136245 : ProcessInvalidationMessagesMulti(&transInvalInfo->PriorCmdInvalidMsgs,
1223 : : SendSharedInvalidMessages);
1224 : :
582 noah@leadboat.com 1225 [ + + ]: 136245 : if (transInvalInfo->ii.RelcacheInitFileInval)
5401 tgl@sss.pgh.pa.us 1226 : 13148 : RelationCacheInitFilePostInvalidate();
1227 : : }
1228 : : else
1229 : : {
8003 1230 : 3411 : ProcessInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
1231 : : LocalExecuteInvalidationMessage);
1232 : : }
1233 : :
1234 : : /* Need not free anything explicitly */
1235 : 139656 : 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
582 noah@leadboat.com 1247 : 81617 : PreInplace_Inval(void)
1248 : : {
1249 [ - + ]: 81617 : Assert(CritSectionCount == 0);
1250 : :
1251 [ + + + + ]: 81617 : if (inplaceInvalInfo && inplaceInvalInfo->RelcacheInitFileInval)
1252 : 11412 : RelationCacheInitFilePreInvalidate();
1253 : 81617 : }
1254 : :
1255 : : /*
1256 : : * AtInplace_Inval
1257 : : * Process queued-up invalidations after inplace update buffer mutation.
1258 : : */
1259 : : void
1260 : 81617 : AtInplace_Inval(void)
1261 : : {
1262 [ - + ]: 81617 : Assert(CritSectionCount > 0);
1263 : :
1264 [ + + ]: 81617 : if (inplaceInvalInfo == NULL)
1265 : 17955 : return;
1266 : :
1267 : 63662 : ProcessInvalidationMessagesMulti(&inplaceInvalInfo->CurrentCmdInvalidMsgs,
1268 : : SendSharedInvalidMessages);
1269 : :
1270 [ + + ]: 63662 : if (inplaceInvalInfo->RelcacheInitFileInval)
1271 : 11412 : RelationCacheInitFilePostInvalidate();
1272 : :
1273 : 63662 : 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
574 1283 : 36113 : ForgetInplace_Inval(void)
1284 : : {
1285 : 36113 : inplaceInvalInfo = NULL;
1286 : 36113 : }
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
7976 tgl@sss.pgh.pa.us 1307 : 12677 : 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 : : */
582 noah@leadboat.com 1317 [ + + ]: 12677 : if (isCommit)
1318 [ - + ]: 7280 : Assert(inplaceInvalInfo == NULL);
1319 : : else
1320 : 5397 : inplaceInvalInfo = NULL;
1321 : :
1322 : : /* Quick exit if no transactional messages. */
1323 : 12677 : myInfo = transInvalInfo;
4231 rhaas@postgresql.org 1324 [ + + ]: 12677 : if (myInfo == NULL)
1325 : 11592 : return;
1326 : :
1327 : : /* Also bail out quickly if messages are not for this level. */
1328 : 1085 : my_level = GetCurrentTransactionNestLevel();
1329 [ + + ]: 1085 : if (myInfo->my_level != my_level)
1330 : : {
1331 [ - + ]: 893 : Assert(myInfo->my_level < my_level);
1332 : 893 : return;
1333 : : }
1334 : :
1335 [ + + ]: 192 : if (isCommit)
1336 : : {
1337 : : /* If CurrentCmdInvalidMsgs still has anything, fix it */
8003 tgl@sss.pgh.pa.us 1338 : 65 : 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 : : */
4231 rhaas@postgresql.org 1346 [ + + + + ]: 65 : if (myInfo->parent == NULL || myInfo->parent->my_level < my_level - 1)
1347 : : {
1348 : 52 : myInfo->my_level--;
1349 : 52 : 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 : : */
8003 tgl@sss.pgh.pa.us 1360 : 13 : AppendInvalidationMessages(&myInfo->parent->PriorCmdInvalidMsgs,
1361 : : &myInfo->PriorCmdInvalidMsgs);
1362 : :
1363 : : /* Must readjust parent's CurrentCmdInvalidMsgs indexes now */
582 noah@leadboat.com 1364 : 13 : SetGroupToFollow(&myInfo->parent->ii.CurrentCmdInvalidMsgs,
1365 : : &myInfo->parent->PriorCmdInvalidMsgs);
1366 : :
1367 : : /* Pending relcache inval becomes parent's problem too */
1368 [ - + ]: 13 : if (myInfo->ii.RelcacheInitFileInval)
582 noah@leadboat.com 1369 :UBC 0 : myInfo->parent->ii.RelcacheInitFileInval = true;
1370 : :
1371 : : /* Pop the transaction state stack */
7936 tgl@sss.pgh.pa.us 1372 :CBC 13 : transInvalInfo = myInfo->parent;
1373 : :
1374 : : /* Need not free anything else explicitly */
1375 : 13 : pfree(myInfo);
1376 : : }
1377 : : else
1378 : : {
8003 1379 : 127 : ProcessInvalidationMessages(&myInfo->PriorCmdInvalidMsgs,
1380 : : LocalExecuteInvalidationMessage);
1381 : :
1382 : : /* Pop the transaction state stack */
7936 1383 : 127 : transInvalInfo = myInfo->parent;
1384 : :
1385 : : /* Need not free anything else explicitly */
1386 : 127 : 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
8003 1406 : 758543 : 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 [ + + ]: 758543 : if (transInvalInfo == NULL)
1414 : 213093 : return;
1415 : :
582 noah@leadboat.com 1416 : 545450 : ProcessInvalidationMessages(&transInvalInfo->ii.CurrentCmdInvalidMsgs,
1417 : : LocalExecuteInvalidationMessage);
1418 : :
1419 : : /* WAL Log per-command invalidation messages for logical decoding */
2137 akapila@postgresql.o 1420 [ + + ]: 545446 : if (XLogLogicalInfoActive())
[ + + + + ]
1421 : 4930 : LogLogicalInvalidations();
1422 : :
8003 tgl@sss.pgh.pa.us 1423 : 545446 : AppendInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
582 noah@leadboat.com 1424 : 545446 : &transInvalInfo->ii.CurrentCmdInvalidMsgs);
1425 : : }
1426 : :
1427 : :
1428 : : /*
1429 : : * CacheInvalidateHeapTupleCommon
1430 : : * Common logic for end-of-command and inplace variants.
1431 : : */
1432 : : static void
1433 : 17468809 : 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 */
408 1444 : 17468809 : AssertCouldGetRelation();
1445 : :
1446 : : /* Do nothing during bootstrap */
5401 tgl@sss.pgh.pa.us 1447 [ + + ]: 17468809 : 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 : : */
4566 rhaas@postgresql.org 1455 [ + + ]: 16709284 : if (!IsCatalogRelation(relation))
5401 tgl@sss.pgh.pa.us 1456 : 13988458 : 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 [ + + ]: 2720826 : if (IsToastRelation(relation))
1463 : 23211 : return;
1464 : :
1465 : : /* Allocate any required resources. */
582 noah@leadboat.com 1466 : 2697615 : info = prepare_callback();
1467 : :
1468 : : /*
1469 : : * First let the catcache do its thing
1470 : : */
4715 rhaas@postgresql.org 1471 : 2697615 : tupleRelId = RelationGetRelid(relation);
1472 [ + + ]: 2697615 : if (RelationInvalidatesSnapshotsOnly(tupleRelId))
1473 : : {
1474 [ + + ]: 725291 : databaseId = IsSharedRelation(tupleRelId) ? InvalidOid : MyDatabaseId;
582 noah@leadboat.com 1475 : 725291 : RegisterSnapshotInvalidation(info, databaseId, tupleRelId);
1476 : : }
1477 : : else
4715 rhaas@postgresql.org 1478 : 1972324 : 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 : : */
5401 tgl@sss.pgh.pa.us 1489 [ + + ]: 2697615 : if (tupleRelId == RelationRelationId)
1490 : : {
1491 : 370488 : Form_pg_class classtup = (Form_pg_class) GETSTRUCT(tuple);
1492 : :
2748 andres@anarazel.de 1493 : 370488 : relationId = classtup->oid;
5401 tgl@sss.pgh.pa.us 1494 [ + + ]: 370488 : if (classtup->relisshared)
1495 : 10770 : databaseId = InvalidOid;
1496 : : else
1497 : 359718 : databaseId = MyDatabaseId;
1498 : : }
1499 [ + + ]: 2327127 : else if (tupleRelId == AttributeRelationId)
1500 : : {
1501 : 774950 : Form_pg_attribute atttup = (Form_pg_attribute) GETSTRUCT(tuple);
1502 : :
1503 : 774950 : 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 : 774950 : databaseId = MyDatabaseId;
1516 : : }
1517 [ + + ]: 1552177 : else if (tupleRelId == IndexRelationId)
1518 : : {
1519 : 43701 : 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 : 43701 : relationId = indextup->indexrelid;
1528 : 43701 : databaseId = MyDatabaseId;
1529 : : }
2686 alvherre@alvh.no-ip. 1530 [ + + ]: 1508476 : else if (tupleRelId == ConstraintRelationId)
1531 : : {
1532 : 57654 : 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 [ + + ]: 57654 : if (constrtup->contype == CONSTRAINT_FOREIGN &&
1539 [ + - ]: 5968 : OidIsValid(constrtup->conrelid))
1540 : : {
1541 : 5968 : relationId = constrtup->conrelid;
1542 : 5968 : databaseId = MyDatabaseId;
1543 : : }
1544 : : else
1545 : 51686 : return;
1546 : : }
1547 : : else
5401 tgl@sss.pgh.pa.us 1548 : 1450822 : return;
1549 : :
1550 : : /*
1551 : : * Yes. We need to register a relcache invalidation event.
1552 : : */
582 noah@leadboat.com 1553 : 1195107 : 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 : 17351079 : CacheInvalidateHeapTuple(Relation relation,
1569 : : HeapTuple tuple,
1570 : : HeapTuple newtuple)
1571 : : {
1572 : 17351079 : CacheInvalidateHeapTupleCommon(relation, tuple, newtuple,
1573 : : PrepareInvalidationState);
1574 : 17351079 : }
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 : 117730 : CacheInvalidateHeapTupleInplace(Relation relation,
1591 : : HeapTuple key_equivalent_tuple)
1592 : : {
166 1593 : 117730 : CacheInvalidateHeapTupleCommon(relation, key_equivalent_tuple, NULL,
1594 : : PrepareInplaceInvalidationState);
9637 inoue@tpf.co.jp 1595 : 117730 : }
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
5956 tgl@sss.pgh.pa.us 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 : :
582 noah@leadboat.com 1618 : 121 : RegisterCatalogInvalidation(PrepareInvalidationState(),
1619 : : databaseId, catalogId);
5956 tgl@sss.pgh.pa.us 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
8145 1632 : 103635 : CacheInvalidateRelcache(Relation relation)
1633 : : {
1634 : : Oid databaseId;
1635 : : Oid relationId;
1636 : :
1637 : 103635 : relationId = RelationGetRelid(relation);
1638 [ + + ]: 103635 : if (relation->rd_rel->relisshared)
1639 : 3948 : databaseId = InvalidOid;
1640 : : else
1641 : 99687 : databaseId = MyDatabaseId;
1642 : :
582 noah@leadboat.com 1643 : 103635 : RegisterRelcacheInvalidation(PrepareInvalidationState(),
1644 : : databaseId, relationId);
8145 tgl@sss.pgh.pa.us 1645 : 103635 : }
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
3418 peter_e@gmx.net 1655 : 224 : CacheInvalidateRelcacheAll(void)
1656 : : {
582 noah@leadboat.com 1657 : 224 : RegisterRelcacheInvalidation(PrepareInvalidationState(),
1658 : : InvalidOid, InvalidOid);
3418 peter_e@gmx.net 1659 : 224 : }
1660 : :
1661 : : /*
1662 : : * CacheInvalidateRelcacheByTuple
1663 : : * As above, but relation is identified by passing its pg_class tuple.
1664 : : */
1665 : : void
8145 tgl@sss.pgh.pa.us 1666 : 51807 : CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
1667 : : {
1668 : 51807 : Form_pg_class classtup = (Form_pg_class) GETSTRUCT(classTuple);
1669 : : Oid databaseId;
1670 : : Oid relationId;
1671 : :
2748 andres@anarazel.de 1672 : 51807 : relationId = classtup->oid;
8145 tgl@sss.pgh.pa.us 1673 [ + + ]: 51807 : if (classtup->relisshared)
1674 : 1051 : databaseId = InvalidOid;
1675 : : else
1676 : 50756 : databaseId = MyDatabaseId;
582 noah@leadboat.com 1677 : 51807 : RegisterRelcacheInvalidation(PrepareInvalidationState(),
1678 : : databaseId, relationId);
9637 inoue@tpf.co.jp 1679 : 51807 : }
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
8059 tgl@sss.pgh.pa.us 1688 : 21488 : CacheInvalidateRelcacheByRelid(Oid relid)
1689 : : {
1690 : : HeapTuple tup;
1691 : :
5949 rhaas@postgresql.org 1692 : 21488 : tup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
8059 tgl@sss.pgh.pa.us 1693 [ - + ]: 21488 : if (!HeapTupleIsValid(tup))
8059 tgl@sss.pgh.pa.us 1694 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
8059 tgl@sss.pgh.pa.us 1695 :CBC 21488 : CacheInvalidateRelcacheByTuple(tup);
1696 : 21488 : ReleaseSysCache(tup);
1697 : 21488 : }
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
443 akapila@postgresql.o 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
1424 rhaas@postgresql.org 1752 : 66159 : 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 : :
5960 tgl@sss.pgh.pa.us 1760 : 66159 : msg.sm.id = SHAREDINVALSMGR_ID;
1424 rhaas@postgresql.org 1761 : 66159 : msg.sm.backend_hi = rlocator.backend >> 16;
1762 : 66159 : msg.sm.backend_lo = rlocator.backend & 0xffff;
1340 1763 : 66159 : msg.sm.rlocator = rlocator.locator;
1764 : : /* check AddCatcacheInvalidationMessage() for an explanation */
1765 : : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
1766 : :
5960 tgl@sss.pgh.pa.us 1767 : 66159 : SendSharedInvalidMessages(&msg, 1);
1768 : 66159 : }
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
5956 1786 : 204 : CacheInvalidateRelmap(Oid databaseId)
1787 : : {
1788 : : SharedInvalidationMessage msg;
1789 : :
1790 : 204 : msg.rm.id = SHAREDINVALRELMAP_ID;
1791 : 204 : msg.rm.dbId = databaseId;
1792 : : /* check AddCatcacheInvalidationMessage() for an explanation */
1793 : : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
1794 : :
1795 : 204 : SendSharedInvalidMessages(&msg, 1);
1796 : 204 : }
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
101 michael@paquier.xyz 1813 :GNC 310875 : CacheRegisterSyscacheCallback(SysCacheIdentifier cacheid,
1814 : : SyscacheCallbackFunction func,
1815 : : Datum arg)
1816 : : {
3305 tgl@sss.pgh.pa.us 1817 [ + - - + ]:CBC 310875 : if (cacheid < 0 || cacheid >= SysCacheSize)
3305 tgl@sss.pgh.pa.us 1818 [ # # ]:UBC 0 : elog(FATAL, "invalid cache ID: %d", cacheid);
6472 tgl@sss.pgh.pa.us 1819 [ - + ]:CBC 310875 : if (syscache_callback_count >= MAX_SYSCACHE_CALLBACKS)
6472 tgl@sss.pgh.pa.us 1820 [ # # ]:UBC 0 : elog(FATAL, "out of syscache_callback_list slots");
1821 : :
3305 tgl@sss.pgh.pa.us 1822 [ + + ]:CBC 310875 : if (syscache_callback_links[cacheid] == 0)
1823 : : {
1824 : : /* first callback for this cache */
1825 : 219266 : 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 : 91609 : int i = syscache_callback_links[cacheid] - 1;
1831 : :
1832 [ + + ]: 109933 : while (syscache_callback_list[i].link > 0)
1833 : 18324 : i = syscache_callback_list[i].link - 1;
1834 : 91609 : syscache_callback_list[i].link = syscache_callback_count + 1;
1835 : : }
1836 : :
6472 1837 : 310875 : syscache_callback_list[syscache_callback_count].id = cacheid;
3305 1838 : 310875 : syscache_callback_list[syscache_callback_count].link = 0;
6472 1839 : 310875 : syscache_callback_list[syscache_callback_count].function = func;
1840 : 310875 : syscache_callback_list[syscache_callback_count].arg = arg;
1841 : :
1842 : 310875 : ++syscache_callback_count;
8797 1843 : 310875 : }
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
6472 1855 : 24487 : CacheRegisterRelcacheCallback(RelcacheCallbackFunction func,
1856 : : Datum arg)
1857 : : {
1858 [ - + ]: 24487 : if (relcache_callback_count >= MAX_RELCACHE_CALLBACKS)
6472 tgl@sss.pgh.pa.us 1859 [ # # ]:UBC 0 : elog(FATAL, "out of relcache_callback_list slots");
1860 : :
6472 tgl@sss.pgh.pa.us 1861 :CBC 24487 : relcache_callback_list[relcache_callback_count].function = func;
1862 : 24487 : relcache_callback_list[relcache_callback_count].arg = arg;
1863 : :
1864 : 24487 : ++relcache_callback_count;
8797 1865 : 24487 : }
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
443 akapila@postgresql.o 1876 : 443 : CacheRegisterRelSyncCallback(RelSyncCallbackFunction func,
1877 : : Datum arg)
1878 : : {
1879 [ - + ]: 443 : if (relsync_callback_count >= MAX_RELSYNC_CALLBACKS)
443 akapila@postgresql.o 1880 [ # # ]:UBC 0 : elog(FATAL, "out of relsync_callback_list slots");
1881 : :
443 akapila@postgresql.o 1882 :CBC 443 : relsync_callback_list[relsync_callback_count].function = func;
1883 : 443 : relsync_callback_list[relsync_callback_count].arg = arg;
1884 : :
1885 : 443 : ++relsync_callback_count;
1886 : 443 : }
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
101 michael@paquier.xyz 1895 :GNC 16038608 : CallSyscacheCallbacks(SysCacheIdentifier cacheid, uint32 hashvalue)
1896 : : {
1897 : : int i;
1898 : :
3305 tgl@sss.pgh.pa.us 1899 [ + - - + ]:CBC 16038608 : if (cacheid < 0 || cacheid >= SysCacheSize)
3305 tgl@sss.pgh.pa.us 1900 [ # # ]:UBC 0 : elog(ERROR, "invalid cache ID: %d", cacheid);
1901 : :
3305 tgl@sss.pgh.pa.us 1902 :CBC 16038608 : i = syscache_callback_links[cacheid] - 1;
1903 [ + + ]: 18528367 : while (i >= 0)
1904 : : {
5956 1905 : 2489759 : struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i;
1906 : :
3305 1907 [ - + ]: 2489759 : Assert(ccitem->id == cacheid);
3187 peter_e@gmx.net 1908 : 2489759 : ccitem->function(ccitem->arg, cacheid, hashvalue);
3305 tgl@sss.pgh.pa.us 1909 : 2489759 : i = ccitem->link - 1;
1910 : : }
5956 1911 : 16038608 : }
1912 : :
1913 : : /*
1914 : : * CallSyscacheCallbacks
1915 : : */
1916 : : void
443 akapila@postgresql.o 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
1748 tgl@sss.pgh.pa.us 1936 : 19229 : 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. */
2137 akapila@postgresql.o 1943 [ + + ]: 19229 : if (transInvalInfo == NULL)
1944 : 12415 : return;
1945 : :
582 noah@leadboat.com 1946 : 6814 : group = &transInvalInfo->ii.CurrentCmdInvalidMsgs;
1748 tgl@sss.pgh.pa.us 1947 : 6814 : nmsgs = NumMessagesInGroup(group);
1948 : :
2137 akapila@postgresql.o 1949 [ + + ]: 6814 : if (nmsgs > 0)
1950 : : {
1951 : : /* prepare record */
1952 : 5370 : memset(&xlrec, 0, MinSizeOfXactInvals);
1953 : 5370 : xlrec.nmsgs = nmsgs;
1954 : :
1955 : : /* perform insertion */
1956 : 5370 : XLogBeginInsert();
473 peter@eisentraut.org 1957 : 5370 : XLogRegisterData(&xlrec, MinSizeOfXactInvals);
1748 tgl@sss.pgh.pa.us 1958 [ + + ]: 5370 : ProcessMessageSubGroupMulti(group, CatCacheMsgs,
1959 : : XLogRegisterData(msgs,
1960 : : n * sizeof(SharedInvalidationMessage)));
1961 [ + + ]: 5370 : ProcessMessageSubGroupMulti(group, RelCacheMsgs,
1962 : : XLogRegisterData(msgs,
1963 : : n * sizeof(SharedInvalidationMessage)));
2137 akapila@postgresql.o 1964 : 5370 : XLogInsert(RM_XACT_ID, XLOG_XACT_INVALIDATIONS);
1965 : : }
1966 : : }
|