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
1837 tgl@sss.pgh.pa.us 320 :CBC 4313270 : AddInvalidationMessage(InvalidationMsgsGroup *group, int subgroup,
321 : : const SharedInvalidationMessage *msg)
322 : : {
323 : 4313270 : InvalMessageArray *ima = &InvalMessageArrays[subgroup];
324 : 4313270 : int nextindex = group->nextmsg[subgroup];
325 : :
326 [ + + ]: 4313270 : if (nextindex >= ima->maxmsgs)
327 : : {
328 [ + + ]: 341043 : if (ima->msgs == NULL)
329 : : {
330 : : /* Create new storage array in TopTransactionContext */
331 : 303048 : int reqsize = 32; /* arbitrary */
332 : :
333 : 303048 : ima->msgs = (SharedInvalidationMessage *)
334 : 303048 : MemoryContextAlloc(TopTransactionContext,
335 : : reqsize * sizeof(SharedInvalidationMessage));
336 : 303048 : ima->maxmsgs = reqsize;
337 [ - + ]: 303048 : Assert(nextindex == 0);
338 : : }
339 : : else
340 : : {
341 : : /* Enlarge storage array */
342 : 37995 : int reqsize = 2 * ima->maxmsgs;
343 : :
10 michael@paquier.xyz 344 :GNC 37995 : ima->msgs = repalloc_array(ima->msgs, SharedInvalidationMessage, reqsize);
1837 tgl@sss.pgh.pa.us 345 :CBC 37995 : ima->maxmsgs = reqsize;
346 : : }
347 : : }
348 : : /* Okay, add message to current group */
349 : 4313270 : ima->msgs[nextindex] = *msg;
350 : 4313270 : group->nextmsg[subgroup]++;
11006 scrappy@hub.org 351 : 4313270 : }
352 : :
353 : : /*
354 : : * Append one subgroup of invalidation messages to another, resetting
355 : : * the source subgroup to empty.
356 : : */
357 : : static void
1837 tgl@sss.pgh.pa.us 358 : 1270972 : AppendInvalidationMessageSubGroup(InvalidationMsgsGroup *dest,
359 : : InvalidationMsgsGroup *src,
360 : : int subgroup)
361 : : {
362 : : /* Messages must be adjacent in main array */
363 [ - + ]: 1270972 : Assert(dest->nextmsg[subgroup] == src->firstmsg[subgroup]);
364 : :
365 : : /* ... which makes this easy: */
366 : 1270972 : dest->nextmsg[subgroup] = src->nextmsg[subgroup];
367 : :
368 : : /*
369 : : * This is handy for some callers and irrelevant for others. But we do it
370 : : * always, reasoning that it's bad to leave different groups pointing at
371 : : * the same fragment of the message array.
372 : : */
373 : 1270972 : SetSubGroupToFollow(src, dest, subgroup);
8943 374 : 1270972 : }
375 : :
376 : : /*
377 : : * Process a subgroup of invalidation messages.
378 : : *
379 : : * This is a macro that executes the given code fragment for each message in
380 : : * a message subgroup. The fragment should refer to the message as *msg.
381 : : */
382 : : #define ProcessMessageSubGroup(group, subgroup, codeFragment) \
383 : : do { \
384 : : int _msgindex = (group)->firstmsg[subgroup]; \
385 : : int _endmsg = (group)->nextmsg[subgroup]; \
386 : : for (; _msgindex < _endmsg; _msgindex++) \
387 : : { \
388 : : SharedInvalidationMessage *msg = \
389 : : &InvalMessageArrays[subgroup].msgs[_msgindex]; \
390 : : codeFragment; \
391 : : } \
392 : : } while (0)
393 : :
394 : : /*
395 : : * Process a subgroup of invalidation messages as an array.
396 : : *
397 : : * As above, but the code fragment can handle an array of messages.
398 : : * The fragment should refer to the messages as msgs[], with n entries.
399 : : */
400 : : #define ProcessMessageSubGroupMulti(group, subgroup, codeFragment) \
401 : : do { \
402 : : int n = NumMessagesInSubGroup(group, subgroup); \
403 : : if (n > 0) { \
404 : : SharedInvalidationMessage *msgs = \
405 : : &InvalMessageArrays[subgroup].msgs[(group)->firstmsg[subgroup]]; \
406 : : codeFragment; \
407 : : } \
408 : : } while (0)
409 : :
410 : :
411 : : /* ----------------------------------------------------------------
412 : : * Invalidation group support functions
413 : : *
414 : : * These routines understand about the division of a logical invalidation
415 : : * group into separate physical arrays for catcache and relcache entries.
416 : : * ----------------------------------------------------------------
417 : : */
418 : :
419 : : /*
420 : : * Add a catcache inval entry
421 : : */
422 : : static void
1837 423 : 3436864 : AddCatcacheInvalidationMessage(InvalidationMsgsGroup *group,
424 : : int id, uint32 hashValue, Oid dbId)
425 : : {
426 : : SharedInvalidationMessage msg;
427 : :
5858 rhaas@postgresql.org 428 [ - + ]: 3436864 : Assert(id < CHAR_MAX);
429 : 3436864 : msg.cc.id = (int8) id;
8943 tgl@sss.pgh.pa.us 430 : 3436864 : msg.cc.dbId = dbId;
431 : 3436864 : msg.cc.hashValue = hashValue;
432 : :
433 : : /*
434 : : * Define padding bytes in SharedInvalidationMessage structs to be
435 : : * defined. Otherwise the sinvaladt.c ringbuffer, which is accessed by
436 : : * multiple processes, will cause spurious valgrind warnings about
437 : : * undefined memory being used. That's because valgrind remembers the
438 : : * undefined bytes from the last local process's store, not realizing that
439 : : * another process has written since, filling the previously uninitialized
440 : : * bytes
441 : : */
442 : : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
443 : :
1837 444 : 3436864 : AddInvalidationMessage(group, CatCacheMsgs, &msg);
9726 inoue@tpf.co.jp 445 : 3436864 : }
446 : :
447 : : /*
448 : : * Add a whole-catalog inval entry
449 : : */
450 : : static void
1837 tgl@sss.pgh.pa.us 451 : 121 : AddCatalogInvalidationMessage(InvalidationMsgsGroup *group,
452 : : Oid dbId, Oid catId)
453 : : {
454 : : SharedInvalidationMessage msg;
455 : :
6045 456 : 121 : msg.cat.id = SHAREDINVALCATALOG_ID;
457 : 121 : msg.cat.dbId = dbId;
458 : 121 : msg.cat.catId = catId;
459 : : /* check AddCatcacheInvalidationMessage() for an explanation */
460 : : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
461 : :
1837 462 : 121 : AddInvalidationMessage(group, CatCacheMsgs, &msg);
6045 463 : 121 : }
464 : :
465 : : /*
466 : : * Add a relcache inval entry
467 : : */
468 : : static void
1837 469 : 1318868 : AddRelcacheInvalidationMessage(InvalidationMsgsGroup *group,
470 : : Oid dbId, Oid relId)
471 : : {
472 : : SharedInvalidationMessage msg;
473 : :
474 : : /*
475 : : * Don't add a duplicate item. We assume dbId need not be checked because
476 : : * it will never change. InvalidOid for relId means all relations so we
477 : : * don't need to add individual ones when it is present.
478 : : */
479 [ + + + + : 4269463 : ProcessMessageSubGroup(group, RelCacheMsgs,
- + + + ]
480 : : if (msg->rc.id == SHAREDINVALRELCACHE_ID &&
481 : : (msg->rc.relId == relId ||
482 : : msg->rc.relId == InvalidOid))
483 : : return);
484 : :
485 : : /* OK, add the item */
9200 486 : 519784 : msg.rc.id = SHAREDINVALRELCACHE_ID;
487 : 519784 : msg.rc.dbId = dbId;
488 : 519784 : msg.rc.relId = relId;
489 : : /* check AddCatcacheInvalidationMessage() for an explanation */
490 : : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
491 : :
1837 492 : 519784 : AddInvalidationMessage(group, RelCacheMsgs, &msg);
493 : : }
494 : :
495 : : /*
496 : : * Add a relsync inval entry
497 : : *
498 : : * We put these into the relcache subgroup for simplicity. This message is the
499 : : * same as AddRelcacheInvalidationMessage() except that it is for
500 : : * RelationSyncCache maintained by decoding plugin pgoutput.
501 : : */
502 : : static void
532 akapila@postgresql.o 503 : 6 : AddRelsyncInvalidationMessage(InvalidationMsgsGroup *group,
504 : : Oid dbId, Oid relId)
505 : : {
506 : : SharedInvalidationMessage msg;
507 : :
508 : : /* Don't add a duplicate item. */
509 [ - - - - : 6 : ProcessMessageSubGroup(group, RelCacheMsgs,
- - - + ]
510 : : if (msg->rs.id == SHAREDINVALRELSYNC_ID &&
511 : : (msg->rs.relid == relId ||
512 : : msg->rs.relid == InvalidOid))
513 : : return);
514 : :
515 : : /* OK, add the item */
77 rhaas@postgresql.org 516 : 6 : msg.rs.id = SHAREDINVALRELSYNC_ID;
517 : 6 : msg.rs.dbId = dbId;
518 : 6 : msg.rs.relid = relId;
519 : : /* check AddCatcacheInvalidationMessage() for an explanation */
520 : : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
521 : :
532 akapila@postgresql.o 522 : 6 : AddInvalidationMessage(group, RelCacheMsgs, &msg);
523 : : }
524 : :
525 : : /*
526 : : * Add a snapshot inval entry
527 : : *
528 : : * We put these into the relcache subgroup for simplicity.
529 : : */
530 : : static void
1837 tgl@sss.pgh.pa.us 531 : 709054 : AddSnapshotInvalidationMessage(InvalidationMsgsGroup *group,
532 : : Oid dbId, Oid relId)
533 : : {
534 : : SharedInvalidationMessage msg;
535 : :
536 : : /* Don't add a duplicate item */
537 : : /* We assume dbId need not be checked because it will never change */
538 [ + + + + : 1035737 : ProcessMessageSubGroup(group, RelCacheMsgs,
+ + ]
539 : : if (msg->sn.id == SHAREDINVALSNAPSHOT_ID &&
540 : : msg->sn.relId == relId)
541 : : return);
542 : :
543 : : /* OK, add the item */
4804 rhaas@postgresql.org 544 : 356495 : msg.sn.id = SHAREDINVALSNAPSHOT_ID;
545 : 356495 : msg.sn.dbId = dbId;
546 : 356495 : msg.sn.relId = relId;
547 : : /* check AddCatcacheInvalidationMessage() for an explanation */
548 : : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
549 : :
1837 tgl@sss.pgh.pa.us 550 : 356495 : AddInvalidationMessage(group, RelCacheMsgs, &msg);
551 : : }
552 : :
553 : : /*
554 : : * Append one group of invalidation messages to another, resetting
555 : : * the source group to empty.
556 : : */
557 : : static void
558 : 635486 : AppendInvalidationMessages(InvalidationMsgsGroup *dest,
559 : : InvalidationMsgsGroup *src)
560 : : {
561 : 635486 : AppendInvalidationMessageSubGroup(dest, src, CatCacheMsgs);
562 : 635486 : AppendInvalidationMessageSubGroup(dest, src, RelCacheMsgs);
8943 563 : 635486 : }
564 : :
565 : : /*
566 : : * Execute the given function for all the messages in an invalidation group.
567 : : * The group is not altered.
568 : : *
569 : : * catcache entries are processed first, for reasons mentioned above.
570 : : */
571 : : static void
1837 572 : 504118 : ProcessInvalidationMessages(InvalidationMsgsGroup *group,
573 : : void (*func) (SharedInvalidationMessage *msg))
574 : : {
575 [ + + ]: 3765308 : ProcessMessageSubGroup(group, CatCacheMsgs, func(msg));
576 [ + + ]: 1273432 : ProcessMessageSubGroup(group, RelCacheMsgs, func(msg));
9200 577 : 504114 : }
578 : :
579 : : /*
580 : : * As above, but the function is able to process an array of messages
581 : : * rather than just one at a time.
582 : : */
583 : : static void
1837 584 : 198565 : ProcessInvalidationMessagesMulti(InvalidationMsgsGroup *group,
585 : : void (*func) (const SharedInvalidationMessage *msgs, int n))
586 : : {
587 [ + + ]: 198565 : ProcessMessageSubGroupMulti(group, CatCacheMsgs, func(msgs, n));
588 [ + + ]: 198565 : ProcessMessageSubGroupMulti(group, RelCacheMsgs, func(msgs, n));
6643 589 : 198565 : }
590 : :
591 : : /* ----------------------------------------------------------------
592 : : * private support functions
593 : : * ----------------------------------------------------------------
594 : : */
595 : :
596 : : /*
597 : : * RegisterCatcacheInvalidation
598 : : *
599 : : * Register an invalidation event for a catcache tuple entry.
600 : : */
601 : : static void
9200 602 : 3436864 : RegisterCatcacheInvalidation(int cacheId,
603 : : uint32 hashValue,
604 : : Oid dbId,
605 : : void *context)
606 : : {
671 noah@leadboat.com 607 : 3436864 : InvalidationInfo *info = (InvalidationInfo *) context;
608 : :
609 : 3436864 : AddCatcacheInvalidationMessage(&info->CurrentCmdInvalidMsgs,
610 : : cacheId, hashValue, dbId);
11006 scrappy@hub.org 611 : 3436864 : }
612 : :
613 : : /*
614 : : * RegisterCatalogInvalidation
615 : : *
616 : : * Register an invalidation event for all catcache entries from a catalog.
617 : : */
618 : : static void
671 noah@leadboat.com 619 : 121 : RegisterCatalogInvalidation(InvalidationInfo *info, Oid dbId, Oid catId)
620 : : {
621 : 121 : AddCatalogInvalidationMessage(&info->CurrentCmdInvalidMsgs, dbId, catId);
6045 tgl@sss.pgh.pa.us 622 : 121 : }
623 : :
624 : : /*
625 : : * RegisterRelcacheInvalidation
626 : : *
627 : : * As above, but register a relcache invalidation event.
628 : : */
629 : : static void
671 noah@leadboat.com 630 : 1318868 : RegisterRelcacheInvalidation(InvalidationInfo *info, Oid dbId, Oid relId)
631 : : {
632 : 1318868 : AddRelcacheInvalidationMessage(&info->CurrentCmdInvalidMsgs, dbId, relId);
633 : :
634 : : /*
635 : : * Most of the time, relcache invalidation is associated with system
636 : : * catalog updates, but there are a few cases where it isn't. Quick hack
637 : : * to ensure that the next CommandCounterIncrement() will think that we
638 : : * need to do CommandEndInvalidationMessages().
639 : : */
6845 tgl@sss.pgh.pa.us 640 : 1318868 : (void) GetCurrentCommandId(true);
641 : :
642 : : /*
643 : : * If the relation being invalidated is one of those cached in a relcache
644 : : * init file, mark that we need to zap that file at commit. For simplicity
645 : : * invalidations for a specific database always invalidate the shared file
646 : : * as well. Also zap when we are invalidating whole relcache.
647 : : */
2998 andres@anarazel.de 648 [ + + + + ]: 1318868 : if (relId == InvalidOid || RelationIdIsInInitFile(relId))
671 noah@leadboat.com 649 : 69663 : info->RelcacheInitFileInval = true;
9726 inoue@tpf.co.jp 650 : 1318868 : }
651 : :
652 : : /*
653 : : * RegisterRelsyncInvalidation
654 : : *
655 : : * As above, but register a relsynccache invalidation event.
656 : : */
657 : : static void
532 akapila@postgresql.o 658 : 6 : RegisterRelsyncInvalidation(InvalidationInfo *info, Oid dbId, Oid relId)
659 : : {
660 : 6 : AddRelsyncInvalidationMessage(&info->CurrentCmdInvalidMsgs, dbId, relId);
661 : 6 : }
662 : :
663 : : /*
664 : : * RegisterSnapshotInvalidation
665 : : *
666 : : * Register an invalidation event for MVCC scans against a given catalog.
667 : : * Only needed for catalogs that don't have catcaches.
668 : : */
669 : : static void
671 noah@leadboat.com 670 : 709054 : RegisterSnapshotInvalidation(InvalidationInfo *info, Oid dbId, Oid relId)
671 : : {
672 : 709054 : AddSnapshotInvalidationMessage(&info->CurrentCmdInvalidMsgs, dbId, relId);
4804 rhaas@postgresql.org 673 : 709054 : }
674 : :
675 : : /*
676 : : * PrepareInvalidationState
677 : : * Initialize inval data for the current (sub)transaction.
678 : : */
679 : : static InvalidationInfo *
1024 michael@paquier.xyz 680 : 2665515 : PrepareInvalidationState(void)
681 : : {
682 : : TransInvalidationInfo *myInfo;
683 : :
684 : : /* PrepareToInvalidateCacheTuple() needs relcache */
497 noah@leadboat.com 685 : 2665515 : AssertCouldGetRelation();
686 : : /* Can't queue transactional message while collecting inplace messages. */
671 687 [ - + ]: 2665515 : Assert(inplaceInvalInfo == NULL);
688 : :
1024 michael@paquier.xyz 689 [ + + + + ]: 5192556 : if (transInvalInfo != NULL &&
690 : 2527041 : transInvalInfo->my_level == GetCurrentTransactionNestLevel())
671 noah@leadboat.com 691 : 2526953 : return (InvalidationInfo *) transInvalInfo;
692 : :
693 : : myInfo = (TransInvalidationInfo *)
1024 michael@paquier.xyz 694 : 138562 : MemoryContextAllocZero(TopTransactionContext,
695 : : sizeof(TransInvalidationInfo));
696 : 138562 : myInfo->parent = transInvalInfo;
697 : 138562 : myInfo->my_level = GetCurrentTransactionNestLevel();
698 : :
699 : : /* Now, do we have a previous stack entry? */
700 [ + + ]: 138562 : if (transInvalInfo != NULL)
701 : : {
702 : : /* Yes; this one should be for a deeper nesting level. */
703 [ - + ]: 88 : Assert(myInfo->my_level > transInvalInfo->my_level);
704 : :
705 : : /*
706 : : * The parent (sub)transaction must not have any current (i.e.,
707 : : * not-yet-locally-processed) messages. If it did, we'd have a
708 : : * semantic problem: the new subtransaction presumably ought not be
709 : : * able to see those events yet, but since the CommandCounter is
710 : : * linear, that can't work once the subtransaction advances the
711 : : * counter. This is a convenient place to check for that, as well as
712 : : * being important to keep management of the message arrays simple.
713 : : */
671 noah@leadboat.com 714 [ - + ]: 88 : if (NumMessagesInGroup(&transInvalInfo->ii.CurrentCmdInvalidMsgs) != 0)
1024 michael@paquier.xyz 715 [ # # ]:UBC 0 : elog(ERROR, "cannot start a subtransaction when there are unprocessed inval messages");
716 : :
717 : : /*
718 : : * MemoryContextAllocZero set firstmsg = nextmsg = 0 in each group,
719 : : * which is fine for the first (sub)transaction, but otherwise we need
720 : : * to update them to follow whatever is already in the arrays.
721 : : */
1024 michael@paquier.xyz 722 :CBC 88 : SetGroupToFollow(&myInfo->PriorCmdInvalidMsgs,
723 : : &transInvalInfo->ii.CurrentCmdInvalidMsgs);
671 noah@leadboat.com 724 : 88 : SetGroupToFollow(&myInfo->ii.CurrentCmdInvalidMsgs,
725 : : &myInfo->PriorCmdInvalidMsgs);
726 : : }
727 : : else
728 : : {
729 : : /*
730 : : * Here, we need only clear any array pointers left over from a prior
731 : : * transaction.
732 : : */
1024 michael@paquier.xyz 733 : 138474 : InvalMessageArrays[CatCacheMsgs].msgs = NULL;
734 : 138474 : InvalMessageArrays[CatCacheMsgs].maxmsgs = 0;
735 : 138474 : InvalMessageArrays[RelCacheMsgs].msgs = NULL;
736 : 138474 : InvalMessageArrays[RelCacheMsgs].maxmsgs = 0;
737 : : }
738 : :
739 : 138562 : transInvalInfo = myInfo;
671 noah@leadboat.com 740 : 138562 : return (InvalidationInfo *) myInfo;
741 : : }
742 : :
743 : : /*
744 : : * PrepareInplaceInvalidationState
745 : : * Initialize inval data for an inplace update.
746 : : *
747 : : * See previous function for more background.
748 : : */
749 : : static InvalidationInfo *
750 : 96887 : PrepareInplaceInvalidationState(void)
751 : : {
752 : : InvalidationInfo *myInfo;
753 : :
497 754 : 96887 : AssertCouldGetRelation();
755 : : /* limit of one inplace update under assembly */
671 756 [ - + ]: 96887 : Assert(inplaceInvalInfo == NULL);
757 : :
758 : : /* gone after WAL insertion CritSection ends, so use current context */
260 michael@paquier.xyz 759 : 96887 : myInfo = palloc0_object(InvalidationInfo);
760 : :
761 : : /* Stash our messages past end of the transactional messages, if any. */
671 noah@leadboat.com 762 [ + + ]: 96887 : if (transInvalInfo != NULL)
763 : 71534 : SetGroupToFollow(&myInfo->CurrentCmdInvalidMsgs,
764 : : &transInvalInfo->ii.CurrentCmdInvalidMsgs);
765 : : else
766 : : {
767 : 25353 : InvalMessageArrays[CatCacheMsgs].msgs = NULL;
768 : 25353 : InvalMessageArrays[CatCacheMsgs].maxmsgs = 0;
769 : 25353 : InvalMessageArrays[RelCacheMsgs].msgs = NULL;
770 : 25353 : InvalMessageArrays[RelCacheMsgs].maxmsgs = 0;
771 : : }
772 : :
773 : 96887 : inplaceInvalInfo = myInfo;
774 : 96887 : return myInfo;
775 : : }
776 : :
777 : : /* ----------------------------------------------------------------
778 : : * public functions
779 : : * ----------------------------------------------------------------
780 : : */
781 : :
782 : : void
1024 michael@paquier.xyz 783 : 2752 : InvalidateSystemCachesExtended(bool debug_discard)
784 : : {
785 : : int i;
786 : :
787 : 2752 : InvalidateCatalogSnapshot();
590 heikki.linnakangas@i 788 : 2752 : ResetCatalogCachesExt(debug_discard);
1024 michael@paquier.xyz 789 : 2752 : RelationCacheInvalidate(debug_discard); /* gets smgr and relmap too */
790 : :
791 [ + + ]: 55199 : for (i = 0; i < syscache_callback_count; i++)
792 : : {
793 : 52447 : struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i;
794 : :
795 : 52447 : ccitem->function(ccitem->arg, ccitem->id, 0);
796 : : }
797 : :
798 [ + + ]: 6178 : for (i = 0; i < relcache_callback_count; i++)
799 : : {
800 : 3426 : struct RELCACHECALLBACK *ccitem = relcache_callback_list + i;
801 : :
802 : 3426 : ccitem->function(ccitem->arg, InvalidOid);
803 : : }
804 : :
532 akapila@postgresql.o 805 [ + + ]: 2780 : for (i = 0; i < relsync_callback_count; i++)
806 : : {
807 : 28 : struct RELSYNCCALLBACK *ccitem = relsync_callback_list + i;
808 : :
809 : 28 : ccitem->function(ccitem->arg, InvalidOid);
810 : : }
1024 michael@paquier.xyz 811 : 2752 : }
812 : :
813 : : /*
814 : : * LocalExecuteInvalidationMessage
815 : : *
816 : : * Process a single invalidation message (which could be of any type).
817 : : * Only the local caches are flushed; this does not transmit the message
818 : : * to other backends.
819 : : */
820 : : void
9200 tgl@sss.pgh.pa.us 821 : 24391016 : LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
822 : : {
823 [ + + ]: 24391016 : if (msg->id >= 0)
824 : : {
6045 825 [ + + + + ]: 19436711 : if (msg->cc.dbId == MyDatabaseId || msg->cc.dbId == InvalidOid)
826 : : {
4804 rhaas@postgresql.org 827 : 15106250 : InvalidateCatalogSnapshot();
828 : :
3394 tgl@sss.pgh.pa.us 829 : 15106250 : SysCacheInvalidate(msg->cc.id, msg->cc.hashValue);
830 : :
5490 831 : 15106250 : CallSyscacheCallbacks(msg->cc.id, msg->cc.hashValue);
832 : : }
833 : : }
6045 834 [ + + ]: 4954305 : else if (msg->id == SHAREDINVALCATALOG_ID)
835 : : {
836 [ + + + + ]: 523 : if (msg->cat.dbId == MyDatabaseId || msg->cat.dbId == InvalidOid)
837 : : {
4804 rhaas@postgresql.org 838 : 449 : InvalidateCatalogSnapshot();
839 : :
6045 tgl@sss.pgh.pa.us 840 : 449 : CatalogCacheFlushCatalog(msg->cat.catId);
841 : :
842 : : /* CatalogCacheFlushCatalog calls CallSyscacheCallbacks as needed */
843 : : }
844 : : }
9200 845 [ + + ]: 4953782 : else if (msg->id == SHAREDINVALRELCACHE_ID)
846 : : {
8234 847 [ + + + + ]: 2622036 : if (msg->rc.dbId == MyDatabaseId || msg->rc.dbId == InvalidOid)
848 : : {
849 : : int i;
850 : :
3507 peter_e@gmx.net 851 [ + + ]: 2057254 : if (msg->rc.relId == InvalidOid)
1769 noah@leadboat.com 852 : 725 : RelationCacheInvalidate(false);
853 : : else
3507 peter_e@gmx.net 854 : 2056529 : RelationCacheInvalidateEntry(msg->rc.relId);
855 : :
6561 tgl@sss.pgh.pa.us 856 [ + + ]: 5713658 : for (i = 0; i < relcache_callback_count; i++)
857 : : {
858 : 3656408 : struct RELCACHECALLBACK *ccitem = relcache_callback_list + i;
859 : :
3276 peter_e@gmx.net 860 : 3656408 : ccitem->function(ccitem->arg, msg->rc.relId);
861 : : }
862 : : }
863 : : }
7899 tgl@sss.pgh.pa.us 864 [ + + ]: 2331746 : else if (msg->id == SHAREDINVALSMGR_ID)
865 : : {
866 : : /*
867 : : * We could have smgr entries for relations of other databases, so no
868 : : * short-circuit test is possible here.
869 : : */
870 : : RelFileLocatorBackend rlocator;
871 : :
1429 rhaas@postgresql.org 872 : 315263 : rlocator.locator = msg->sm.rlocator;
1513 873 : 315263 : rlocator.backend = (msg->sm.backend_hi << 16) | (int) msg->sm.backend_lo;
939 heikki.linnakangas@i 874 : 315263 : smgrreleaserellocator(rlocator);
875 : : }
6045 tgl@sss.pgh.pa.us 876 [ + + ]: 2016483 : else if (msg->id == SHAREDINVALRELMAP_ID)
877 : : {
878 : : /* We only care about our own database and shared catalogs */
879 [ + + ]: 373 : if (msg->rm.dbId == InvalidOid)
880 : 152 : RelationMapInvalidate(true);
881 [ + + ]: 221 : else if (msg->rm.dbId == MyDatabaseId)
882 : 154 : RelationMapInvalidate(false);
883 : : }
4804 rhaas@postgresql.org 884 [ + + ]: 2016110 : else if (msg->id == SHAREDINVALSNAPSHOT_ID)
885 : : {
886 : : /* We only care about our own database and shared catalogs */
2068 michael@paquier.xyz 887 [ + + ]: 2016079 : if (msg->sn.dbId == InvalidOid)
4804 rhaas@postgresql.org 888 : 65307 : InvalidateCatalogSnapshot();
2068 michael@paquier.xyz 889 [ + + ]: 1950772 : else if (msg->sn.dbId == MyDatabaseId)
4804 rhaas@postgresql.org 890 : 1547319 : InvalidateCatalogSnapshot();
891 : : }
532 akapila@postgresql.o 892 [ + - ]: 31 : else if (msg->id == SHAREDINVALRELSYNC_ID)
893 : : {
894 : : /* We only care about our own database */
895 [ + - ]: 31 : if (msg->rs.dbId == MyDatabaseId)
896 : 31 : CallRelSyncCallbacks(msg->rs.relid);
897 : : }
898 : : else
5548 peter_e@gmx.net 899 [ # # ]:UBC 0 : elog(FATAL, "unrecognized SI message ID: %d", msg->id);
11006 scrappy@hub.org 900 :CBC 24391012 : }
901 : :
902 : : /*
903 : : * InvalidateSystemCaches
904 : : *
905 : : * This blows away all tuples in the system catalog caches and
906 : : * all the cached relation descriptors and smgr cache entries.
907 : : * Relation descriptors that have positive refcounts are then rebuilt.
908 : : *
909 : : * We call this when we see a shared-inval-queue overflow signal,
910 : : * since that tells us we've lost some shared-inval messages and hence
911 : : * don't know what needs to be invalidated.
912 : : */
913 : : void
9201 tgl@sss.pgh.pa.us 914 : 2752 : InvalidateSystemCaches(void)
915 : : {
1769 noah@leadboat.com 916 : 2752 : InvalidateSystemCachesExtended(false);
917 : 2752 : }
918 : :
919 : : /*
920 : : * AcceptInvalidationMessages
921 : : * Read and process invalidation messages from the shared invalidation
922 : : * message queue.
923 : : *
924 : : * Note:
925 : : * This should be called as the first step in processing a transaction.
926 : : */
927 : : void
9200 tgl@sss.pgh.pa.us 928 : 22500582 : AcceptInvalidationMessages(void)
929 : : {
930 : : #ifdef USE_ASSERT_CHECKING
931 : : /* message handlers shall access catalogs only during transactions */
497 noah@leadboat.com 932 [ + + ]: 22500582 : if (IsTransactionState())
933 : 22063390 : AssertCouldGetRelation();
934 : : #endif
935 : :
9200 tgl@sss.pgh.pa.us 936 : 22500582 : ReceiveSharedInvalidMessages(LocalExecuteInvalidationMessage,
937 : : InvalidateSystemCaches);
938 : :
939 : : /*----------
940 : : * Test code to force cache flushes anytime a flush could happen.
941 : : *
942 : : * This helps detect intermittent faults caused by code that reads a cache
943 : : * entry and then performs an action that could invalidate the entry, but
944 : : * rarely actually does so. This can spot issues that would otherwise
945 : : * only arise with badly timed concurrent DDL, for example.
946 : : *
947 : : * The default debug_discard_caches = 0 does no forced cache flushes.
948 : : *
949 : : * If used with CLOBBER_FREED_MEMORY,
950 : : * debug_discard_caches = 1 (formerly known as CLOBBER_CACHE_ALWAYS)
951 : : * provides a fairly thorough test that the system contains no cache-flush
952 : : * hazards. However, it also makes the system unbelievably slow --- the
953 : : * regression tests take about 100 times longer than normal.
954 : : *
955 : : * If you're a glutton for punishment, try
956 : : * debug_discard_caches = 3 (formerly known as CLOBBER_CACHE_RECURSIVELY).
957 : : * This slows things by at least a factor of 10000, so I wouldn't suggest
958 : : * trying to run the entire regression tests that way. It's useful to try
959 : : * a few simple tests, to make sure that cache reload isn't subject to
960 : : * internal cache-flush hazards, but after you've done a few thousand
961 : : * recursive reloads it's unlikely you'll learn more.
962 : : *----------
963 : : */
964 : : #ifdef DISCARD_CACHES_ENABLED
965 : : {
966 : : static int recursion_depth = 0;
967 : :
1871 968 [ - + ]: 22500582 : if (recursion_depth < debug_discard_caches)
969 : : {
2911 tgl@sss.pgh.pa.us 970 :UBC 0 : recursion_depth++;
1769 noah@leadboat.com 971 : 0 : InvalidateSystemCachesExtended(true);
2911 tgl@sss.pgh.pa.us 972 : 0 : recursion_depth--;
973 : : }
974 : : }
975 : : #endif
11006 scrappy@hub.org 976 :CBC 22500582 : }
977 : :
978 : : /*
979 : : * PostPrepare_Inval
980 : : * Clean up after successful PREPARE.
981 : : *
982 : : * Here, we want to act as though the transaction aborted, so that we will
983 : : * undo any syscache changes it made, thereby bringing us into sync with the
984 : : * outside world, which doesn't believe the transaction committed yet.
985 : : *
986 : : * If the prepared transaction is later aborted, there is nothing more to
987 : : * do; if it commits, we will receive the consequent inval messages just
988 : : * like everyone else.
989 : : */
990 : : void
7741 tgl@sss.pgh.pa.us 991 : 336 : PostPrepare_Inval(void)
992 : : {
993 : 336 : AtEOXact_Inval(false);
994 : 336 : }
995 : :
996 : : /*
997 : : * xactGetCommittedInvalidationMessages() is called by
998 : : * RecordTransactionCommit() to collect invalidation messages to add to the
999 : : * commit record. This applies only to commit message types, never to
1000 : : * abort records. Must always run before AtEOXact_Inval(), since that
1001 : : * removes the data we need to see.
1002 : : *
1003 : : * Remember that this runs before we have officially committed, so we
1004 : : * must not do anything here to change what might occur *if* we should
1005 : : * fail between here and the actual commit.
1006 : : *
1007 : : * see also xact_redo_commit() and xact_desc_commit()
1008 : : */
1009 : : int
6095 simon@2ndQuadrant.co 1010 : 328647 : xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs,
1011 : : bool *RelcacheInitFileInval)
1012 : : {
1013 : : SharedInvalidationMessage *msgarray;
1014 : : int nummsgs;
1015 : : int nmsgs;
1016 : :
1017 : : /* Quick exit if we haven't done anything with invalidation messages. */
4320 rhaas@postgresql.org 1018 [ + + ]: 328647 : if (transInvalInfo == NULL)
1019 : : {
1020 : 205327 : *RelcacheInitFileInval = false;
1021 : 205327 : *msgs = NULL;
1022 : 205327 : return 0;
1023 : : }
1024 : :
1025 : : /* Must be at top of stack */
1026 [ + - - + ]: 123320 : Assert(transInvalInfo->my_level == 1 && transInvalInfo->parent == NULL);
1027 : :
1028 : : /*
1029 : : * Relcache init file invalidation requires processing both before and
1030 : : * after we send the SI messages. However, we need not do anything unless
1031 : : * we committed.
1032 : : */
671 noah@leadboat.com 1033 : 123320 : *RelcacheInitFileInval = transInvalInfo->ii.RelcacheInitFileInval;
1034 : :
1035 : : /*
1036 : : * Collect all the pending messages into a single contiguous array of
1037 : : * invalidation messages, to simplify what needs to happen while building
1038 : : * the commit WAL message. Maintain the order that they would be
1039 : : * processed in by AtEOXact_Inval(), to ensure emulated behaviour in redo
1040 : : * is as similar as possible to original. We want the same bugs, if any,
1041 : : * not new ones.
1042 : : */
1837 tgl@sss.pgh.pa.us 1043 : 123320 : nummsgs = NumMessagesInGroup(&transInvalInfo->PriorCmdInvalidMsgs) +
671 noah@leadboat.com 1044 : 123320 : NumMessagesInGroup(&transInvalInfo->ii.CurrentCmdInvalidMsgs);
1045 : :
1837 tgl@sss.pgh.pa.us 1046 : 123320 : *msgs = msgarray = (SharedInvalidationMessage *)
1047 : 123320 : MemoryContextAlloc(CurTransactionContext,
1048 : : nummsgs * sizeof(SharedInvalidationMessage));
1049 : :
1050 : 123320 : nmsgs = 0;
1051 [ + + ]: 123320 : ProcessMessageSubGroupMulti(&transInvalInfo->PriorCmdInvalidMsgs,
1052 : : CatCacheMsgs,
1053 : : (memcpy(msgarray + nmsgs,
1054 : : msgs,
1055 : : n * sizeof(SharedInvalidationMessage)),
1056 : : nmsgs += n));
671 noah@leadboat.com 1057 [ + + ]: 123320 : ProcessMessageSubGroupMulti(&transInvalInfo->ii.CurrentCmdInvalidMsgs,
1058 : : CatCacheMsgs,
1059 : : (memcpy(msgarray + nmsgs,
1060 : : msgs,
1061 : : n * sizeof(SharedInvalidationMessage)),
1062 : : nmsgs += n));
1837 tgl@sss.pgh.pa.us 1063 [ + + ]: 123320 : ProcessMessageSubGroupMulti(&transInvalInfo->PriorCmdInvalidMsgs,
1064 : : RelCacheMsgs,
1065 : : (memcpy(msgarray + nmsgs,
1066 : : msgs,
1067 : : n * sizeof(SharedInvalidationMessage)),
1068 : : nmsgs += n));
671 noah@leadboat.com 1069 [ + + ]: 123320 : ProcessMessageSubGroupMulti(&transInvalInfo->ii.CurrentCmdInvalidMsgs,
1070 : : RelCacheMsgs,
1071 : : (memcpy(msgarray + nmsgs,
1072 : : msgs,
1073 : : n * sizeof(SharedInvalidationMessage)),
1074 : : nmsgs += n));
1075 [ - + ]: 123320 : Assert(nmsgs == nummsgs);
1076 : :
1077 : 123320 : return nmsgs;
1078 : : }
1079 : :
1080 : : /*
1081 : : * inplaceGetInvalidationMessages() is called by the inplace update to collect
1082 : : * invalidation messages to add to its WAL record. Like the previous
1083 : : * function, we might still fail.
1084 : : */
1085 : : int
1086 : 73230 : inplaceGetInvalidationMessages(SharedInvalidationMessage **msgs,
1087 : : bool *RelcacheInitFileInval)
1088 : : {
1089 : : SharedInvalidationMessage *msgarray;
1090 : : int nummsgs;
1091 : : int nmsgs;
1092 : :
1093 : : /* Quick exit if we haven't done anything with invalidation messages. */
1094 [ + + ]: 73230 : if (inplaceInvalInfo == NULL)
1095 : : {
1096 : 17325 : *RelcacheInitFileInval = false;
1097 : 17325 : *msgs = NULL;
1098 : 17325 : return 0;
1099 : : }
1100 : :
1101 : 55905 : *RelcacheInitFileInval = inplaceInvalInfo->RelcacheInitFileInval;
1102 : 55905 : nummsgs = NumMessagesInGroup(&inplaceInvalInfo->CurrentCmdInvalidMsgs);
10 michael@paquier.xyz 1103 :GNC 55905 : *msgs = msgarray = palloc_array(SharedInvalidationMessage, nummsgs);
1104 : :
671 noah@leadboat.com 1105 :CBC 55905 : nmsgs = 0;
1106 [ + - ]: 55905 : ProcessMessageSubGroupMulti(&inplaceInvalInfo->CurrentCmdInvalidMsgs,
1107 : : CatCacheMsgs,
1108 : : (memcpy(msgarray + nmsgs,
1109 : : msgs,
1110 : : n * sizeof(SharedInvalidationMessage)),
1111 : : nmsgs += n));
1112 [ + + ]: 55905 : ProcessMessageSubGroupMulti(&inplaceInvalInfo->CurrentCmdInvalidMsgs,
1113 : : RelCacheMsgs,
1114 : : (memcpy(msgarray + nmsgs,
1115 : : msgs,
1116 : : n * sizeof(SharedInvalidationMessage)),
1117 : : nmsgs += n));
1837 tgl@sss.pgh.pa.us 1118 [ - + ]: 55905 : Assert(nmsgs == nummsgs);
1119 : :
1120 : 55905 : return nmsgs;
1121 : : }
1122 : :
1123 : : /*
1124 : : * ProcessCommittedInvalidationMessages is executed by xact_redo_commit() or
1125 : : * standby_redo() to process invalidation messages. Currently that happens
1126 : : * only at end-of-xact.
1127 : : *
1128 : : * Relcache init file invalidation requires processing both
1129 : : * before and after we send the SI messages. See AtEOXact_Inval()
1130 : : */
1131 : : void
6074 simon@2ndQuadrant.co 1132 : 30859 : ProcessCommittedInvalidationMessages(SharedInvalidationMessage *msgs,
1133 : : int nmsgs, bool RelcacheInitFileInval,
1134 : : Oid dbid, Oid tsid)
1135 : : {
6039 1136 [ + + ]: 30859 : if (nmsgs <= 0)
1137 : 5593 : return;
1138 : :
990 michael@paquier.xyz 1139 [ - + - - ]: 25266 : elog(DEBUG4, "replaying commit with %d messages%s", nmsgs,
1140 : : (RelcacheInitFileInval ? " and relcache file invalidation" : ""));
1141 : :
6074 simon@2ndQuadrant.co 1142 [ + + ]: 25266 : if (RelcacheInitFileInval)
1143 : : {
990 michael@paquier.xyz 1144 [ - + ]: 610 : elog(DEBUG4, "removing relcache init files for database %u", dbid);
1145 : :
1146 : : /*
1147 : : * RelationCacheInitFilePreInvalidate, when the invalidation message
1148 : : * is for a specific database, requires DatabasePath to be set, but we
1149 : : * should not use SetDatabasePath during recovery, since it is
1150 : : * intended to be used only once by normal backends. Hence, a quick
1151 : : * hack: set DatabasePath directly then unset after use.
1152 : : */
2998 andres@anarazel.de 1153 [ + - ]: 610 : if (OidIsValid(dbid))
1154 : 610 : DatabasePath = GetDatabasePath(dbid, tsid);
1155 : :
5490 tgl@sss.pgh.pa.us 1156 : 610 : RelationCacheInitFilePreInvalidate();
1157 : :
2998 andres@anarazel.de 1158 [ + - ]: 610 : if (OidIsValid(dbid))
1159 : : {
1160 : 610 : pfree(DatabasePath);
1161 : 610 : DatabasePath = NULL;
1162 : : }
1163 : : }
1164 : :
6074 simon@2ndQuadrant.co 1165 : 25266 : SendSharedInvalidMessages(msgs, nmsgs);
1166 : :
1167 [ + + ]: 25266 : if (RelcacheInitFileInval)
5490 tgl@sss.pgh.pa.us 1168 : 610 : RelationCacheInitFilePostInvalidate();
1169 : : }
1170 : :
1171 : : /*
1172 : : * AtEOXact_Inval
1173 : : * Process queued-up invalidation messages at end of main transaction.
1174 : : *
1175 : : * If isCommit, we must send out the messages in our PriorCmdInvalidMsgs list
1176 : : * to the shared invalidation message queue. Note that these will be read
1177 : : * not only by other backends, but also by our own backend at the next
1178 : : * transaction start (via AcceptInvalidationMessages). This means that
1179 : : * we can skip immediate local processing of anything that's still in
1180 : : * CurrentCmdInvalidMsgs, and just send that list out too.
1181 : : *
1182 : : * If not isCommit, we are aborting, and must locally process the messages
1183 : : * in PriorCmdInvalidMsgs. No messages need be sent to other backends,
1184 : : * since they'll not have seen our changed tuples anyway. We can forget
1185 : : * about CurrentCmdInvalidMsgs too, since those changes haven't touched
1186 : : * the caches yet.
1187 : : *
1188 : : * In any case, reset our state to empty. We need not physically
1189 : : * free memory here, since TopTransactionContext is about to be emptied
1190 : : * anyway.
1191 : : */
1192 : : void
8092 1193 : 431359 : AtEOXact_Inval(bool isCommit)
1194 : : {
671 noah@leadboat.com 1195 : 431359 : inplaceInvalInfo = NULL;
1196 : :
1197 : : /* Quick exit if no transactional messages */
4320 rhaas@postgresql.org 1198 [ + + ]: 431359 : if (transInvalInfo == NULL)
1199 : 292926 : return;
1200 : :
1201 : : /* Must be at top of stack */
1202 [ + - - + ]: 138433 : Assert(transInvalInfo->my_level == 1 && transInvalInfo->parent == NULL);
1203 : :
474 michael@paquier.xyz 1204 : 138433 : INJECTION_POINT("transaction-end-process-inval", NULL);
1205 : :
9200 tgl@sss.pgh.pa.us 1206 [ + + ]: 138433 : if (isCommit)
1207 : : {
1208 : : /*
1209 : : * Relcache init file invalidation requires processing both before and
1210 : : * after we send the SI messages. However, we need not do anything
1211 : : * unless we committed.
1212 : : */
671 noah@leadboat.com 1213 [ + + ]: 134954 : if (transInvalInfo->ii.RelcacheInitFileInval)
5490 tgl@sss.pgh.pa.us 1214 : 13012 : RelationCacheInitFilePreInvalidate();
1215 : :
8092 1216 : 134954 : AppendInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
671 noah@leadboat.com 1217 : 134954 : &transInvalInfo->ii.CurrentCmdInvalidMsgs);
1218 : :
6643 tgl@sss.pgh.pa.us 1219 : 134954 : ProcessInvalidationMessagesMulti(&transInvalInfo->PriorCmdInvalidMsgs,
1220 : : SendSharedInvalidMessages);
1221 : :
671 noah@leadboat.com 1222 [ + + ]: 134954 : if (transInvalInfo->ii.RelcacheInitFileInval)
5490 tgl@sss.pgh.pa.us 1223 : 13012 : RelationCacheInitFilePostInvalidate();
1224 : : }
1225 : : else
1226 : : {
8092 1227 : 3479 : ProcessInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
1228 : : LocalExecuteInvalidationMessage);
1229 : : }
1230 : :
1231 : : /* Need not free anything explicitly */
1232 : 138433 : transInvalInfo = NULL;
1233 : : }
1234 : :
1235 : : /*
1236 : : * PreInplace_Inval
1237 : : * Process queued-up invalidation before inplace update critical section.
1238 : : *
1239 : : * Tasks belong here if they are safe even if the inplace update does not
1240 : : * complete. Currently, this just unlinks a cache file, which can fail. The
1241 : : * sum of this and AtInplace_Inval() mirrors AtEOXact_Inval(isCommit=true).
1242 : : */
1243 : : void
671 noah@leadboat.com 1244 : 80936 : PreInplace_Inval(void)
1245 : : {
1246 [ - + ]: 80936 : Assert(CritSectionCount == 0);
1247 : :
1248 [ + + + + ]: 80936 : if (inplaceInvalInfo && inplaceInvalInfo->RelcacheInitFileInval)
1249 : 11610 : RelationCacheInitFilePreInvalidate();
1250 : 80936 : }
1251 : :
1252 : : /*
1253 : : * AtInplace_Inval
1254 : : * Process queued-up invalidations after inplace update buffer mutation.
1255 : : */
1256 : : void
1257 : 80936 : AtInplace_Inval(void)
1258 : : {
1259 [ - + ]: 80936 : Assert(CritSectionCount > 0);
1260 : :
1261 [ + + ]: 80936 : if (inplaceInvalInfo == NULL)
1262 : 17325 : return;
1263 : :
1264 : 63611 : ProcessInvalidationMessagesMulti(&inplaceInvalInfo->CurrentCmdInvalidMsgs,
1265 : : SendSharedInvalidMessages);
1266 : :
1267 [ + + ]: 63611 : if (inplaceInvalInfo->RelcacheInitFileInval)
1268 : 11610 : RelationCacheInitFilePostInvalidate();
1269 : :
1270 : 63611 : inplaceInvalInfo = NULL;
1271 : : }
1272 : :
1273 : : /*
1274 : : * ForgetInplace_Inval
1275 : : * Alternative to PreInplace_Inval()+AtInplace_Inval(): discard queued-up
1276 : : * invalidations. This lets inplace update enumerate invalidations
1277 : : * optimistically, before locking the buffer.
1278 : : */
1279 : : void
663 1280 : 36906 : ForgetInplace_Inval(void)
1281 : : {
1282 : 36906 : inplaceInvalInfo = NULL;
1283 : 36906 : }
1284 : :
1285 : : /*
1286 : : * AtEOSubXact_Inval
1287 : : * Process queued-up invalidation messages at end of subtransaction.
1288 : : *
1289 : : * If isCommit, process CurrentCmdInvalidMsgs if any (there probably aren't),
1290 : : * and then attach both CurrentCmdInvalidMsgs and PriorCmdInvalidMsgs to the
1291 : : * parent's PriorCmdInvalidMsgs list.
1292 : : *
1293 : : * If not isCommit, we are aborting, and must locally process the messages
1294 : : * in PriorCmdInvalidMsgs. No messages need be sent to other backends.
1295 : : * We can forget about CurrentCmdInvalidMsgs too, since those changes haven't
1296 : : * touched the caches yet.
1297 : : *
1298 : : * In any case, pop the transaction stack. We need not physically free memory
1299 : : * here, since CurTransactionContext is about to be emptied anyway
1300 : : * (if aborting). Beware of the possibility of aborting the same nesting
1301 : : * level twice, though.
1302 : : */
1303 : : void
8065 tgl@sss.pgh.pa.us 1304 : 22874 : AtEOSubXact_Inval(bool isCommit)
1305 : : {
1306 : : int my_level;
1307 : : TransInvalidationInfo *myInfo;
1308 : :
1309 : : /*
1310 : : * Successful inplace update must clear this, but we clear it on abort.
1311 : : * Inplace updates allocate this in CurrentMemoryContext, which has
1312 : : * lifespan <= subtransaction lifespan. Hence, don't free it explicitly.
1313 : : */
671 noah@leadboat.com 1314 [ + + ]: 22874 : if (isCommit)
1315 [ - + ]: 17386 : Assert(inplaceInvalInfo == NULL);
1316 : : else
1317 : 5488 : inplaceInvalInfo = NULL;
1318 : :
1319 : : /* Quick exit if no transactional messages. */
1320 : 22874 : myInfo = transInvalInfo;
4320 rhaas@postgresql.org 1321 [ + + ]: 22874 : if (myInfo == NULL)
1322 : 21800 : return;
1323 : :
1324 : : /* Also bail out quickly if messages are not for this level. */
1325 : 1074 : my_level = GetCurrentTransactionNestLevel();
1326 [ + + ]: 1074 : if (myInfo->my_level != my_level)
1327 : : {
1328 [ - + ]: 893 : Assert(myInfo->my_level < my_level);
1329 : 893 : return;
1330 : : }
1331 : :
1332 [ + + ]: 181 : if (isCommit)
1333 : : {
1334 : : /* If CurrentCmdInvalidMsgs still has anything, fix it */
8092 tgl@sss.pgh.pa.us 1335 : 65 : CommandEndInvalidationMessages();
1336 : :
1337 : : /*
1338 : : * We create invalidation stack entries lazily, so the parent might
1339 : : * not have one. Instead of creating one, moving all the data over,
1340 : : * and then freeing our own, we can just adjust the level of our own
1341 : : * entry.
1342 : : */
4320 rhaas@postgresql.org 1343 [ + + - + ]: 65 : if (myInfo->parent == NULL || myInfo->parent->my_level < my_level - 1)
1344 : : {
1345 : 52 : myInfo->my_level--;
1346 : 52 : return;
1347 : : }
1348 : :
1349 : : /*
1350 : : * Pass up my inval messages to parent. Notice that we stick them in
1351 : : * PriorCmdInvalidMsgs, not CurrentCmdInvalidMsgs, since they've
1352 : : * already been locally processed. (This would trigger the Assert in
1353 : : * AppendInvalidationMessageSubGroup if the parent's
1354 : : * CurrentCmdInvalidMsgs isn't empty; but we already checked that in
1355 : : * PrepareInvalidationState.)
1356 : : */
8092 tgl@sss.pgh.pa.us 1357 : 13 : AppendInvalidationMessages(&myInfo->parent->PriorCmdInvalidMsgs,
1358 : : &myInfo->PriorCmdInvalidMsgs);
1359 : :
1360 : : /* Must readjust parent's CurrentCmdInvalidMsgs indexes now */
671 noah@leadboat.com 1361 : 13 : SetGroupToFollow(&myInfo->parent->ii.CurrentCmdInvalidMsgs,
1362 : : &myInfo->parent->PriorCmdInvalidMsgs);
1363 : :
1364 : : /* Pending relcache inval becomes parent's problem too */
1365 [ - + ]: 13 : if (myInfo->ii.RelcacheInitFileInval)
671 noah@leadboat.com 1366 :UBC 0 : myInfo->parent->ii.RelcacheInitFileInval = true;
1367 : :
1368 : : /* Pop the transaction state stack */
8025 tgl@sss.pgh.pa.us 1369 :CBC 13 : transInvalInfo = myInfo->parent;
1370 : :
1371 : : /* Need not free anything else explicitly */
1372 : 13 : pfree(myInfo);
1373 : : }
1374 : : else
1375 : : {
8092 1376 : 116 : ProcessInvalidationMessages(&myInfo->PriorCmdInvalidMsgs,
1377 : : LocalExecuteInvalidationMessage);
1378 : :
1379 : : /* Pop the transaction state stack */
8025 1380 : 116 : transInvalInfo = myInfo->parent;
1381 : :
1382 : : /* Need not free anything else explicitly */
1383 : 116 : pfree(myInfo);
1384 : : }
1385 : : }
1386 : :
1387 : : /*
1388 : : * CommandEndInvalidationMessages
1389 : : * Process queued-up invalidation messages at end of one command
1390 : : * in a transaction.
1391 : : *
1392 : : * Here, we send no messages to the shared queue, since we don't know yet if
1393 : : * we will commit. We do need to locally process the CurrentCmdInvalidMsgs
1394 : : * list, so as to flush our caches of any entries we have outdated in the
1395 : : * current command. We then move the current-cmd list over to become part
1396 : : * of the prior-cmds list.
1397 : : *
1398 : : * Note:
1399 : : * This should be called during CommandCounterIncrement(),
1400 : : * after we have advanced the command ID.
1401 : : */
1402 : : void
8092 1403 : 724222 : CommandEndInvalidationMessages(void)
1404 : : {
1405 : : /*
1406 : : * You might think this shouldn't be called outside any transaction, but
1407 : : * bootstrap does it, and also ABORT issued when not in a transaction. So
1408 : : * just quietly return if no state to work on.
1409 : : */
1410 [ + + ]: 724222 : if (transInvalInfo == NULL)
1411 : 223699 : return;
1412 : :
671 noah@leadboat.com 1413 : 500523 : ProcessInvalidationMessages(&transInvalInfo->ii.CurrentCmdInvalidMsgs,
1414 : : LocalExecuteInvalidationMessage);
1415 : :
1416 : : /* WAL Log per-command invalidation messages for logical decoding */
2226 akapila@postgresql.o 1417 [ + + + + ]: 500519 : if (XLogLogicalInfoActive())
1418 : 5037 : LogLogicalInvalidations();
1419 : :
8092 tgl@sss.pgh.pa.us 1420 : 500519 : AppendInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
671 noah@leadboat.com 1421 : 500519 : &transInvalInfo->ii.CurrentCmdInvalidMsgs);
1422 : : }
1423 : :
1424 : :
1425 : : /*
1426 : : * CacheInvalidateHeapTupleCommon
1427 : : * Common logic for end-of-command and inplace variants.
1428 : : */
1429 : : static void
1430 : 17385654 : CacheInvalidateHeapTupleCommon(Relation relation,
1431 : : HeapTuple tuple,
1432 : : HeapTuple newtuple,
1433 : : InvalidationInfo *(*prepare_callback) (void))
1434 : : {
1435 : : InvalidationInfo *info;
1436 : : Oid tupleRelId;
1437 : : Oid databaseId;
1438 : : Oid relationId;
1439 : :
1440 : : /* PrepareToInvalidateCacheTuple() needs relcache */
497 1441 : 17385654 : AssertCouldGetRelation();
1442 : :
1443 : : /* Do nothing during bootstrap */
5490 tgl@sss.pgh.pa.us 1444 [ + + ]: 17385654 : if (IsBootstrapProcessingMode())
1445 : 735185 : return;
1446 : :
1447 : : /*
1448 : : * We only need to worry about invalidation for tuples that are in system
1449 : : * catalogs; user-relation tuples are never in catcaches and can't affect
1450 : : * the relcache either.
1451 : : */
4655 rhaas@postgresql.org 1452 [ + + ]: 16650469 : if (!IsCatalogRelation(relation))
5490 tgl@sss.pgh.pa.us 1453 : 14015287 : return;
1454 : :
1455 : : /*
1456 : : * IsCatalogRelation() will return true for TOAST tables of system
1457 : : * catalogs, but we don't care about those, either.
1458 : : */
1459 [ + + ]: 2635182 : if (IsToastRelation(relation))
1460 : 22835 : return;
1461 : :
1462 : : /* Allocate any required resources. */
671 noah@leadboat.com 1463 : 2612347 : info = prepare_callback();
1464 : :
1465 : : /*
1466 : : * First let the catcache do its thing
1467 : : */
4804 rhaas@postgresql.org 1468 : 2612347 : tupleRelId = RelationGetRelid(relation);
1469 [ + + ]: 2612347 : if (RelationInvalidatesSnapshotsOnly(tupleRelId))
1470 : : {
1471 [ + + ]: 709054 : databaseId = IsSharedRelation(tupleRelId) ? InvalidOid : MyDatabaseId;
671 noah@leadboat.com 1472 : 709054 : RegisterSnapshotInvalidation(info, databaseId, tupleRelId);
1473 : : }
1474 : : else
4804 rhaas@postgresql.org 1475 : 1903293 : PrepareToInvalidateCacheTuple(relation, tuple, newtuple,
1476 : : RegisterCatcacheInvalidation,
1477 : : info);
1478 : :
1479 : : /*
1480 : : * Now, is this tuple one of the primary definers of a relcache entry? See
1481 : : * comments in file header for deeper explanation.
1482 : : *
1483 : : * Note we ignore newtuple here; we assume an update cannot move a tuple
1484 : : * from being part of one relcache entry to being part of another.
1485 : : */
5490 tgl@sss.pgh.pa.us 1486 [ + + ]: 2612347 : if (tupleRelId == RelationRelationId)
1487 : : {
1488 : 366089 : Form_pg_class classtup = (Form_pg_class) GETSTRUCT(tuple);
1489 : :
2837 andres@anarazel.de 1490 : 366089 : relationId = classtup->oid;
5490 tgl@sss.pgh.pa.us 1491 [ + + ]: 366089 : if (classtup->relisshared)
1492 : 10877 : databaseId = InvalidOid;
1493 : : else
1494 : 355212 : databaseId = MyDatabaseId;
1495 : : }
1496 [ + + ]: 2246258 : else if (tupleRelId == AttributeRelationId)
1497 : : {
1498 : 754242 : Form_pg_attribute atttup = (Form_pg_attribute) GETSTRUCT(tuple);
1499 : :
1500 : 754242 : relationId = atttup->attrelid;
1501 : :
1502 : : /*
1503 : : * KLUGE ALERT: we always send the relcache event with MyDatabaseId,
1504 : : * even if the rel in question is shared (which we can't easily tell).
1505 : : * This essentially means that only backends in this same database
1506 : : * will react to the relcache flush request. This is in fact
1507 : : * appropriate, since only those backends could see our pg_attribute
1508 : : * change anyway. It looks a bit ugly though. (In practice, shared
1509 : : * relations can't have schema changes after bootstrap, so we should
1510 : : * never come here for a shared rel anyway.)
1511 : : */
1512 : 754242 : databaseId = MyDatabaseId;
1513 : : }
1514 [ + + ]: 1492016 : else if (tupleRelId == IndexRelationId)
1515 : : {
1516 : 42683 : Form_pg_index indextup = (Form_pg_index) GETSTRUCT(tuple);
1517 : :
1518 : : /*
1519 : : * When a pg_index row is updated, we should send out a relcache inval
1520 : : * for the index relation. As above, we don't know the shared status
1521 : : * of the index, but in practice it doesn't matter since indexes of
1522 : : * shared catalogs can't have such updates.
1523 : : */
1524 : 42683 : relationId = indextup->indexrelid;
1525 : 42683 : databaseId = MyDatabaseId;
1526 : : }
2775 alvherre@alvh.no-ip. 1527 [ + + ]: 1449333 : else if (tupleRelId == ConstraintRelationId)
1528 : : {
1529 : 55838 : Form_pg_constraint constrtup = (Form_pg_constraint) GETSTRUCT(tuple);
1530 : :
1531 : : /*
1532 : : * Foreign keys are part of relcache entries, too, so send out an
1533 : : * inval for the table that the FK applies to.
1534 : : */
1535 [ + + ]: 55838 : if (constrtup->contype == CONSTRAINT_FOREIGN &&
1536 [ + - ]: 5926 : OidIsValid(constrtup->conrelid))
1537 : : {
1538 : 5926 : relationId = constrtup->conrelid;
1539 : 5926 : databaseId = MyDatabaseId;
1540 : : }
1541 : : else
1542 : 49912 : return;
1543 : : }
1544 : : else
5490 tgl@sss.pgh.pa.us 1545 : 1393495 : return;
1546 : :
1547 : : /*
1548 : : * Yes. We need to register a relcache invalidation event.
1549 : : */
671 noah@leadboat.com 1550 : 1168940 : RegisterRelcacheInvalidation(info, databaseId, relationId);
1551 : : }
1552 : :
1553 : : /*
1554 : : * CacheInvalidateHeapTuple
1555 : : * Register the given tuple for invalidation at end of command
1556 : : * (ie, current command is creating or outdating this tuple) and end of
1557 : : * transaction. Also, detect whether a relcache invalidation is implied.
1558 : : *
1559 : : * For an insert or delete, tuple is the target tuple and newtuple is NULL.
1560 : : * For an update, we are called just once, with tuple being the old tuple
1561 : : * version and newtuple the new version. This allows avoidance of duplicate
1562 : : * effort during an update.
1563 : : */
1564 : : void
1565 : 17267812 : CacheInvalidateHeapTuple(Relation relation,
1566 : : HeapTuple tuple,
1567 : : HeapTuple newtuple)
1568 : : {
1569 : 17267812 : CacheInvalidateHeapTupleCommon(relation, tuple, newtuple,
1570 : : PrepareInvalidationState);
1571 : 17267812 : }
1572 : :
1573 : : /*
1574 : : * CacheInvalidateHeapTupleInplace
1575 : : * Register the given tuple for nontransactional invalidation pertaining
1576 : : * to an inplace update. Also, detect whether a relcache invalidation is
1577 : : * implied.
1578 : : *
1579 : : * Like CacheInvalidateHeapTuple(), but for inplace updates.
1580 : : *
1581 : : * Just before and just after the inplace update, the tuple's cache keys must
1582 : : * match those in key_equivalent_tuple. Cache keys consist of catcache lookup
1583 : : * key columns and columns referencing pg_class.oid values,
1584 : : * e.g. pg_constraint.conrelid, which would trigger relcache inval.
1585 : : */
1586 : : void
1587 : 117842 : CacheInvalidateHeapTupleInplace(Relation relation,
1588 : : HeapTuple key_equivalent_tuple)
1589 : : {
255 1590 : 117842 : CacheInvalidateHeapTupleCommon(relation, key_equivalent_tuple, NULL,
1591 : : PrepareInplaceInvalidationState);
9726 inoue@tpf.co.jp 1592 : 117842 : }
1593 : :
1594 : : /*
1595 : : * CacheInvalidateCatalog
1596 : : * Register invalidation of the whole content of a system catalog.
1597 : : *
1598 : : * This is normally used in VACUUM FULL/CLUSTER, where we haven't so much
1599 : : * changed any tuples as moved them around. Some uses of catcache entries
1600 : : * expect their TIDs to be correct, so we have to blow away the entries.
1601 : : *
1602 : : * Note: we expect caller to verify that the rel actually is a system
1603 : : * catalog. If it isn't, no great harm is done, just a wasted sinval message.
1604 : : */
1605 : : void
6045 tgl@sss.pgh.pa.us 1606 : 121 : CacheInvalidateCatalog(Oid catalogId)
1607 : : {
1608 : : Oid databaseId;
1609 : :
1610 [ + + ]: 121 : if (IsSharedRelation(catalogId))
1611 : 19 : databaseId = InvalidOid;
1612 : : else
1613 : 102 : databaseId = MyDatabaseId;
1614 : :
671 noah@leadboat.com 1615 : 121 : RegisterCatalogInvalidation(PrepareInvalidationState(),
1616 : : databaseId, catalogId);
6045 tgl@sss.pgh.pa.us 1617 : 121 : }
1618 : :
1619 : : /*
1620 : : * CacheInvalidateRelcache
1621 : : * Register invalidation of the specified relation's relcache entry
1622 : : * at end of command.
1623 : : *
1624 : : * This is used in places that need to force relcache rebuild but aren't
1625 : : * changing any of the tuples recognized as contributors to the relcache
1626 : : * entry by CacheInvalidateHeapTuple. (An example is dropping an index.)
1627 : : */
1628 : : void
8234 1629 : 100227 : CacheInvalidateRelcache(Relation relation)
1630 : : {
1631 : : Oid databaseId;
1632 : : Oid relationId;
1633 : :
1634 : 100227 : relationId = RelationGetRelid(relation);
1635 [ + + ]: 100227 : if (relation->rd_rel->relisshared)
1636 : 3816 : databaseId = InvalidOid;
1637 : : else
1638 : 96411 : databaseId = MyDatabaseId;
1639 : :
671 noah@leadboat.com 1640 : 100227 : RegisterRelcacheInvalidation(PrepareInvalidationState(),
1641 : : databaseId, relationId);
8234 tgl@sss.pgh.pa.us 1642 : 100227 : }
1643 : :
1644 : : /*
1645 : : * CacheInvalidateRelcacheAll
1646 : : * Register invalidation of the whole relcache at the end of command.
1647 : : *
1648 : : * This is used by alter publication as changes in publications may affect
1649 : : * large number of tables.
1650 : : */
1651 : : void
3507 peter_e@gmx.net 1652 : 224 : CacheInvalidateRelcacheAll(void)
1653 : : {
671 noah@leadboat.com 1654 : 224 : RegisterRelcacheInvalidation(PrepareInvalidationState(),
1655 : : InvalidOid, InvalidOid);
3507 peter_e@gmx.net 1656 : 224 : }
1657 : :
1658 : : /*
1659 : : * CacheInvalidateRelcacheByTuple
1660 : : * As above, but relation is identified by passing its pg_class tuple.
1661 : : */
1662 : : void
8234 tgl@sss.pgh.pa.us 1663 : 49477 : CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
1664 : : {
1665 : 49477 : Form_pg_class classtup = (Form_pg_class) GETSTRUCT(classTuple);
1666 : : Oid databaseId;
1667 : : Oid relationId;
1668 : :
2837 andres@anarazel.de 1669 : 49477 : relationId = classtup->oid;
8234 tgl@sss.pgh.pa.us 1670 [ + + ]: 49477 : if (classtup->relisshared)
1671 : 1031 : databaseId = InvalidOid;
1672 : : else
1673 : 48446 : databaseId = MyDatabaseId;
671 noah@leadboat.com 1674 : 49477 : RegisterRelcacheInvalidation(PrepareInvalidationState(),
1675 : : databaseId, relationId);
9726 inoue@tpf.co.jp 1676 : 49477 : }
1677 : :
1678 : : /*
1679 : : * CacheInvalidateRelcacheByRelid
1680 : : * As above, but relation is identified by passing its OID.
1681 : : * This is the least efficient of the three options; use one of
1682 : : * the above routines if you have a Relation or pg_class tuple.
1683 : : */
1684 : : void
8148 tgl@sss.pgh.pa.us 1685 : 20389 : CacheInvalidateRelcacheByRelid(Oid relid)
1686 : : {
1687 : : HeapTuple tup;
1688 : :
6038 rhaas@postgresql.org 1689 : 20389 : tup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
8148 tgl@sss.pgh.pa.us 1690 [ - + ]: 20389 : if (!HeapTupleIsValid(tup))
8148 tgl@sss.pgh.pa.us 1691 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for relation %u", relid);
8148 tgl@sss.pgh.pa.us 1692 :CBC 20389 : CacheInvalidateRelcacheByTuple(tup);
1693 : 20389 : ReleaseSysCache(tup);
1694 : 20389 : }
1695 : :
1696 : : /*
1697 : : * CacheInvalidateRelSync
1698 : : * Register invalidation of the cache in logical decoding output plugin
1699 : : * for a database.
1700 : : *
1701 : : * This type of invalidation message is used for the specific purpose of output
1702 : : * plugins. Processes which do not decode WALs would do nothing even when it
1703 : : * receives the message.
1704 : : */
1705 : : void
532 akapila@postgresql.o 1706 : 6 : CacheInvalidateRelSync(Oid relid)
1707 : : {
1708 : 6 : RegisterRelsyncInvalidation(PrepareInvalidationState(),
1709 : : MyDatabaseId, relid);
1710 : 6 : }
1711 : :
1712 : : /*
1713 : : * CacheInvalidateRelSyncAll
1714 : : * Register invalidation of the whole cache in logical decoding output
1715 : : * plugin.
1716 : : */
1717 : : void
1718 : 3 : CacheInvalidateRelSyncAll(void)
1719 : : {
1720 : 3 : CacheInvalidateRelSync(InvalidOid);
1721 : 3 : }
1722 : :
1723 : : /*
1724 : : * CacheInvalidateSmgr
1725 : : * Register invalidation of smgr references to a physical relation.
1726 : : *
1727 : : * Sending this type of invalidation msg forces other backends to close open
1728 : : * smgr entries for the rel. This should be done to flush dangling open-file
1729 : : * references when the physical rel is being dropped or truncated. Because
1730 : : * these are nontransactional (i.e., not-rollback-able) operations, we just
1731 : : * send the inval message immediately without any queuing.
1732 : : *
1733 : : * Note: in most cases there will have been a relcache flush issued against
1734 : : * the rel at the logical level. We need a separate smgr-level flush because
1735 : : * it is possible for backends to have open smgr entries for rels they don't
1736 : : * have a relcache entry for, e.g. because the only thing they ever did with
1737 : : * the rel is write out dirty shared buffers.
1738 : : *
1739 : : * Note: because these messages are nontransactional, they won't be captured
1740 : : * in commit/abort WAL entries. Instead, calls to CacheInvalidateSmgr()
1741 : : * should happen in low-level smgr.c routines, which are executed while
1742 : : * replaying WAL as well as when creating it.
1743 : : *
1744 : : * Note: In order to avoid bloating SharedInvalidationMessage, we store only
1745 : : * three bytes of the ProcNumber using what would otherwise be padding space.
1746 : : * Thus, the maximum possible ProcNumber is 2^23-1.
1747 : : */
1748 : : void
1513 rhaas@postgresql.org 1749 : 65303 : CacheInvalidateSmgr(RelFileLocatorBackend rlocator)
1750 : : {
1751 : : SharedInvalidationMessage msg;
1752 : :
1753 : : /* verify optimization stated above stays valid */
1754 : : StaticAssertDecl(MAX_BACKENDS_BITS <= 23,
1755 : : "MAX_BACKENDS_BITS is too big for inval.c");
1756 : :
6049 tgl@sss.pgh.pa.us 1757 : 65303 : msg.sm.id = SHAREDINVALSMGR_ID;
1513 rhaas@postgresql.org 1758 : 65303 : msg.sm.backend_hi = rlocator.backend >> 16;
1759 : 65303 : msg.sm.backend_lo = rlocator.backend & 0xffff;
1429 1760 : 65303 : msg.sm.rlocator = rlocator.locator;
1761 : : /* check AddCatcacheInvalidationMessage() for an explanation */
1762 : : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
1763 : :
6049 tgl@sss.pgh.pa.us 1764 : 65303 : SendSharedInvalidMessages(&msg, 1);
1765 : 65303 : }
1766 : :
1767 : : /*
1768 : : * CacheInvalidateRelmap
1769 : : * Register invalidation of the relation mapping for a database,
1770 : : * or for the shared catalogs if databaseId is zero.
1771 : : *
1772 : : * Sending this type of invalidation msg forces other backends to re-read
1773 : : * the indicated relation mapping file. It is also necessary to send a
1774 : : * relcache inval for the specific relations whose mapping has been altered,
1775 : : * else the relcache won't get updated with the new filenode data.
1776 : : *
1777 : : * Note: because these messages are nontransactional, they won't be captured
1778 : : * in commit/abort WAL entries. Instead, calls to CacheInvalidateRelmap()
1779 : : * should happen in low-level relmapper.c routines, which are executed while
1780 : : * replaying WAL as well as when creating it.
1781 : : */
1782 : : void
6045 1783 : 205 : CacheInvalidateRelmap(Oid databaseId)
1784 : : {
1785 : : SharedInvalidationMessage msg;
1786 : :
1787 : 205 : msg.rm.id = SHAREDINVALRELMAP_ID;
1788 : 205 : msg.rm.dbId = databaseId;
1789 : : /* check AddCatcacheInvalidationMessage() for an explanation */
1790 : : VALGRIND_MAKE_MEM_DEFINED(&msg, sizeof(msg));
1791 : :
1792 : 205 : SendSharedInvalidMessages(&msg, 1);
1793 : 205 : }
1794 : :
1795 : :
1796 : : /*
1797 : : * CacheRegisterSyscacheCallback
1798 : : * Register the specified function to be called for all future
1799 : : * invalidation events in the specified cache. The cache ID and the
1800 : : * hash value of the tuple being invalidated will be passed to the
1801 : : * function.
1802 : : *
1803 : : * NOTE: Hash value zero will be passed if a cache reset request is received.
1804 : : * In this case the called routines should flush all cached state.
1805 : : * Yes, there's a possibility of a false match to zero, but it doesn't seem
1806 : : * worth troubling over, especially since most of the current callees just
1807 : : * flush all cached state anyway.
1808 : : */
1809 : : void
190 michael@paquier.xyz 1810 : 373731 : CacheRegisterSyscacheCallback(SysCacheIdentifier cacheid,
1811 : : SyscacheCallbackFunction func,
1812 : : Datum arg)
1813 : : {
3394 tgl@sss.pgh.pa.us 1814 [ + - - + ]: 373731 : if (cacheid < 0 || cacheid >= SysCacheSize)
3394 tgl@sss.pgh.pa.us 1815 [ # # ]:UBC 0 : elog(FATAL, "invalid cache ID: %d", cacheid);
6561 tgl@sss.pgh.pa.us 1816 [ - + ]:CBC 373731 : if (syscache_callback_count >= MAX_SYSCACHE_CALLBACKS)
6561 tgl@sss.pgh.pa.us 1817 [ # # ]:UBC 0 : elog(FATAL, "out of syscache_callback_list slots");
1818 : :
3394 tgl@sss.pgh.pa.us 1819 [ + + ]:CBC 373731 : if (syscache_callback_links[cacheid] == 0)
1820 : : {
1821 : : /* first callback for this cache */
1822 : 227324 : syscache_callback_links[cacheid] = syscache_callback_count + 1;
1823 : : }
1824 : : else
1825 : : {
1826 : : /* add to end of chain, so that older callbacks are called first */
1827 : 146407 : int i = syscache_callback_links[cacheid] - 1;
1828 : :
1829 [ + + ]: 234945 : while (syscache_callback_list[i].link > 0)
1830 : 88538 : i = syscache_callback_list[i].link - 1;
1831 : 146407 : syscache_callback_list[i].link = syscache_callback_count + 1;
1832 : : }
1833 : :
6561 1834 : 373731 : syscache_callback_list[syscache_callback_count].id = cacheid;
3394 1835 : 373731 : syscache_callback_list[syscache_callback_count].link = 0;
6561 1836 : 373731 : syscache_callback_list[syscache_callback_count].function = func;
1837 : 373731 : syscache_callback_list[syscache_callback_count].arg = arg;
1838 : :
1839 : 373731 : ++syscache_callback_count;
8886 1840 : 373731 : }
1841 : :
1842 : : /*
1843 : : * CacheRegisterRelcacheCallback
1844 : : * Register the specified function to be called for all future
1845 : : * relcache invalidation events. The OID of the relation being
1846 : : * invalidated will be passed to the function.
1847 : : *
1848 : : * NOTE: InvalidOid will be passed if a cache reset request is received.
1849 : : * In this case the called routines should flush all cached state.
1850 : : */
1851 : : void
6561 1852 : 24867 : CacheRegisterRelcacheCallback(RelcacheCallbackFunction func,
1853 : : Datum arg)
1854 : : {
1855 [ - + ]: 24867 : if (relcache_callback_count >= MAX_RELCACHE_CALLBACKS)
6561 tgl@sss.pgh.pa.us 1856 [ # # ]:UBC 0 : elog(FATAL, "out of relcache_callback_list slots");
1857 : :
6561 tgl@sss.pgh.pa.us 1858 :CBC 24867 : relcache_callback_list[relcache_callback_count].function = func;
1859 : 24867 : relcache_callback_list[relcache_callback_count].arg = arg;
1860 : :
1861 : 24867 : ++relcache_callback_count;
8886 1862 : 24867 : }
1863 : :
1864 : : /*
1865 : : * CacheRegisterRelSyncCallback
1866 : : * Register the specified function to be called for all future
1867 : : * relsynccache invalidation events.
1868 : : *
1869 : : * This function is intended to be call from the logical decoding output
1870 : : * plugins.
1871 : : */
1872 : : void
532 akapila@postgresql.o 1873 : 455 : CacheRegisterRelSyncCallback(RelSyncCallbackFunction func,
1874 : : Datum arg)
1875 : : {
1876 [ - + ]: 455 : if (relsync_callback_count >= MAX_RELSYNC_CALLBACKS)
532 akapila@postgresql.o 1877 [ # # ]:UBC 0 : elog(FATAL, "out of relsync_callback_list slots");
1878 : :
532 akapila@postgresql.o 1879 :CBC 455 : relsync_callback_list[relsync_callback_count].function = func;
1880 : 455 : relsync_callback_list[relsync_callback_count].arg = arg;
1881 : :
1882 : 455 : ++relsync_callback_count;
1883 : 455 : }
1884 : :
1885 : : /*
1886 : : * CallSyscacheCallbacks
1887 : : *
1888 : : * This is exported so that CatalogCacheFlushCatalog can call it, saving
1889 : : * this module from knowing which catcache IDs correspond to which catalogs.
1890 : : */
1891 : : void
190 michael@paquier.xyz 1892 : 15106872 : CallSyscacheCallbacks(SysCacheIdentifier cacheid, uint32 hashvalue)
1893 : : {
1894 : : int i;
1895 : :
3394 tgl@sss.pgh.pa.us 1896 [ + - - + ]: 15106872 : if (cacheid < 0 || cacheid >= SysCacheSize)
3394 tgl@sss.pgh.pa.us 1897 [ # # ]:UBC 0 : elog(ERROR, "invalid cache ID: %d", cacheid);
1898 : :
3394 tgl@sss.pgh.pa.us 1899 :CBC 15106872 : i = syscache_callback_links[cacheid] - 1;
1900 [ + + ]: 17500630 : while (i >= 0)
1901 : : {
6045 1902 : 2393758 : struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i;
1903 : :
3394 1904 [ - + ]: 2393758 : Assert(ccitem->id == cacheid);
3276 peter_e@gmx.net 1905 : 2393758 : ccitem->function(ccitem->arg, cacheid, hashvalue);
3394 tgl@sss.pgh.pa.us 1906 : 2393758 : i = ccitem->link - 1;
1907 : : }
6045 1908 : 15106872 : }
1909 : :
1910 : : /*
1911 : : * CallRelSyncCallbacks
1912 : : */
1913 : : void
532 akapila@postgresql.o 1914 : 31 : CallRelSyncCallbacks(Oid relid)
1915 : : {
1916 [ + + ]: 52 : for (int i = 0; i < relsync_callback_count; i++)
1917 : : {
1918 : 21 : struct RELSYNCCALLBACK *ccitem = relsync_callback_list + i;
1919 : :
1920 : 21 : ccitem->function(ccitem->arg, relid);
1921 : : }
1922 : 31 : }
1923 : :
1924 : : /*
1925 : : * LogLogicalInvalidations
1926 : : *
1927 : : * Emit WAL for invalidations caused by the current command.
1928 : : *
1929 : : * This is currently only used for logging invalidations at the command end
1930 : : * or at commit time if any invalidations are pending.
1931 : : */
1932 : : void
1837 tgl@sss.pgh.pa.us 1933 : 20006 : LogLogicalInvalidations(void)
1934 : : {
1935 : : xl_xact_invals xlrec;
1936 : : InvalidationMsgsGroup *group;
1937 : : int nmsgs;
1938 : :
1939 : : /* Quick exit if we haven't done anything with invalidation messages. */
2226 akapila@postgresql.o 1940 [ + + ]: 20006 : if (transInvalInfo == NULL)
1941 : 13049 : return;
1942 : :
671 noah@leadboat.com 1943 : 6957 : group = &transInvalInfo->ii.CurrentCmdInvalidMsgs;
1837 tgl@sss.pgh.pa.us 1944 : 6957 : nmsgs = NumMessagesInGroup(group);
1945 : :
2226 akapila@postgresql.o 1946 [ + + ]: 6957 : if (nmsgs > 0)
1947 : : {
1948 : : /* prepare record */
1949 : 5477 : memset(&xlrec, 0, MinSizeOfXactInvals);
1950 : 5477 : xlrec.nmsgs = nmsgs;
1951 : :
1952 : : /* perform insertion */
1953 : 5477 : XLogBeginInsert();
562 peter@eisentraut.org 1954 : 5477 : XLogRegisterData(&xlrec, MinSizeOfXactInvals);
1837 tgl@sss.pgh.pa.us 1955 [ + + ]: 5477 : ProcessMessageSubGroupMulti(group, CatCacheMsgs,
1956 : : XLogRegisterData(msgs,
1957 : : n * sizeof(SharedInvalidationMessage)));
1958 [ + + ]: 5477 : ProcessMessageSubGroupMulti(group, RelCacheMsgs,
1959 : : XLogRegisterData(msgs,
1960 : : n * sizeof(SharedInvalidationMessage)));
2226 akapila@postgresql.o 1961 : 5477 : XLogInsert(RM_XACT_ID, XLOG_XACT_INVALIDATIONS);
1962 : : }
1963 : : }
|