]> granicus.if.org Git - postgresql/blob - src/backend/utils/cache/inval.c
pgindent run for 9.4
[postgresql] / src / backend / utils / cache / inval.c
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 time qualification 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 utils/time/tqual.c, and note that system catalogs are generally
12  *      scanned under the most current snapshot available, rather than the
13  *      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 or pg_attribute tuple,
55  *      we register a relcache flush operation for the relation described by that
56  *      tuple.
57  *
58  *      We keep the relcache flush requests in lists separate from the catcache
59  *      tuple flush requests.  This allows us to issue all the pending catcache
60  *      flushes before we issue relcache flushes, which saves us from loading
61  *      a catcache tuple during relcache load only to flush it again right away.
62  *      Also, we avoid queuing multiple relcache flush requests for the same
63  *      relation, since a relcache flush is relatively expensive to do.
64  *      (XXX is it worth testing likewise for duplicate catcache flush entries?
65  *      Probably not.)
66  *
67  *      If a relcache flush is issued for a system relation that we preload
68  *      from the relcache init file, we must also delete the init file so that
69  *      it will be rebuilt during the next backend restart.  The actual work of
70  *      manipulating the init file is in relcache.c, but we keep track of the
71  *      need for it here.
72  *
73  *      The request lists proper are kept in CurTransactionContext of their
74  *      creating (sub)transaction, since they can be forgotten on abort of that
75  *      transaction but must be kept till top-level commit otherwise.  For
76  *      simplicity we keep the controlling list-of-lists in TopTransactionContext.
77  *
78  *      Currently, inval messages are sent without regard for the possibility
79  *      that the object described by the catalog tuple might be a session-local
80  *      object such as a temporary table.  This is because (1) this code has
81  *      no practical way to tell the difference, and (2) it is not certain that
82  *      other backends don't have catalog cache or even relcache entries for
83  *      such tables, anyway; there is nothing that prevents that.  It might be
84  *      worth trying to avoid sending such inval traffic in the future, if those
85  *      problems can be overcome cheaply.
86  *
87  *
88  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
89  * Portions Copyright (c) 1994, Regents of the University of California
90  *
91  * IDENTIFICATION
92  *        src/backend/utils/cache/inval.c
93  *
94  *-------------------------------------------------------------------------
95  */
96 #include "postgres.h"
97
98 #include "access/htup_details.h"
99 #include "access/xact.h"
100 #include "catalog/catalog.h"
101 #include "miscadmin.h"
102 #include "storage/sinval.h"
103 #include "storage/smgr.h"
104 #include "utils/catcache.h"
105 #include "utils/inval.h"
106 #include "utils/memutils.h"
107 #include "utils/rel.h"
108 #include "utils/relmapper.h"
109 #include "utils/snapmgr.h"
110 #include "utils/syscache.h"
111
112
113 /*
114  * To minimize palloc traffic, we keep pending requests in successively-
115  * larger chunks (a slightly more sophisticated version of an expansible
116  * array).  All request types can be stored as SharedInvalidationMessage
117  * records.  The ordering of requests within a list is never significant.
118  */
119 typedef struct InvalidationChunk
120 {
121         struct InvalidationChunk *next;         /* list link */
122         int                     nitems;                 /* # items currently stored in chunk */
123         int                     maxitems;               /* size of allocated array in this chunk */
124         SharedInvalidationMessage msgs[1];      /* VARIABLE LENGTH ARRAY */
125 } InvalidationChunk;                    /* VARIABLE LENGTH STRUCTURE */
126
127 typedef struct InvalidationListHeader
128 {
129         InvalidationChunk *cclist;      /* list of chunks holding catcache msgs */
130         InvalidationChunk *rclist;      /* list of chunks holding relcache msgs */
131 } InvalidationListHeader;
132
133 /*----------------
134  * Invalidation info is divided into two lists:
135  *      1) events so far in current command, not yet reflected to caches.
136  *      2) events in previous commands of current transaction; these have
137  *         been reflected to local caches, and must be either broadcast to
138  *         other backends or rolled back from local cache when we commit
139  *         or abort the transaction.
140  * Actually, we need two such lists for each level of nested transaction,
141  * so that we can discard events from an aborted subtransaction.  When
142  * a subtransaction commits, we append its lists to the parent's lists.
143  *
144  * The relcache-file-invalidated flag can just be a simple boolean,
145  * since we only act on it at transaction commit; we don't care which
146  * command of the transaction set it.
147  *----------------
148  */
149
150 typedef struct TransInvalidationInfo
151 {
152         /* Back link to parent transaction's info */
153         struct TransInvalidationInfo *parent;
154
155         /* Subtransaction nesting depth */
156         int                     my_level;
157
158         /* head of current-command event list */
159         InvalidationListHeader CurrentCmdInvalidMsgs;
160
161         /* head of previous-commands event list */
162         InvalidationListHeader PriorCmdInvalidMsgs;
163
164         /* init file must be invalidated? */
165         bool            RelcacheInitFileInval;
166 } TransInvalidationInfo;
167
168 static TransInvalidationInfo *transInvalInfo = NULL;
169
170 static SharedInvalidationMessage *SharedInvalidMessagesArray;
171 static int      numSharedInvalidMessagesArray;
172 static int      maxSharedInvalidMessagesArray;
173
174
175 /*
176  * Dynamically-registered callback functions.  Current implementation
177  * assumes there won't be very many of these at once; could improve if needed.
178  */
179
180 #define MAX_SYSCACHE_CALLBACKS 32
181 #define MAX_RELCACHE_CALLBACKS 10
182
183 static struct SYSCACHECALLBACK
184 {
185         int16           id;                             /* cache number */
186         SyscacheCallbackFunction function;
187         Datum           arg;
188 }       syscache_callback_list[MAX_SYSCACHE_CALLBACKS];
189
190 static int      syscache_callback_count = 0;
191
192 static struct RELCACHECALLBACK
193 {
194         RelcacheCallbackFunction function;
195         Datum           arg;
196 }       relcache_callback_list[MAX_RELCACHE_CALLBACKS];
197
198 static int      relcache_callback_count = 0;
199
200 /* ----------------------------------------------------------------
201  *                              Invalidation list support functions
202  *
203  * These three routines encapsulate processing of the "chunked"
204  * representation of what is logically just a list of messages.
205  * ----------------------------------------------------------------
206  */
207
208 /*
209  * AddInvalidationMessage
210  *              Add an invalidation message to a list (of chunks).
211  *
212  * Note that we do not pay any great attention to maintaining the original
213  * ordering of the messages.
214  */
215 static void
216 AddInvalidationMessage(InvalidationChunk **listHdr,
217                                            SharedInvalidationMessage *msg)
218 {
219         InvalidationChunk *chunk = *listHdr;
220
221         if (chunk == NULL)
222         {
223                 /* First time through; create initial chunk */
224 #define FIRSTCHUNKSIZE 32
225                 chunk = (InvalidationChunk *)
226                         MemoryContextAlloc(CurTransactionContext,
227                                                            sizeof(InvalidationChunk) +
228                                         (FIRSTCHUNKSIZE - 1) *sizeof(SharedInvalidationMessage));
229                 chunk->nitems = 0;
230                 chunk->maxitems = FIRSTCHUNKSIZE;
231                 chunk->next = *listHdr;
232                 *listHdr = chunk;
233         }
234         else if (chunk->nitems >= chunk->maxitems)
235         {
236                 /* Need another chunk; double size of last chunk */
237                 int                     chunksize = 2 * chunk->maxitems;
238
239                 chunk = (InvalidationChunk *)
240                         MemoryContextAlloc(CurTransactionContext,
241                                                            sizeof(InvalidationChunk) +
242                                                  (chunksize - 1) *sizeof(SharedInvalidationMessage));
243                 chunk->nitems = 0;
244                 chunk->maxitems = chunksize;
245                 chunk->next = *listHdr;
246                 *listHdr = chunk;
247         }
248         /* Okay, add message to current chunk */
249         chunk->msgs[chunk->nitems] = *msg;
250         chunk->nitems++;
251 }
252
253 /*
254  * Append one list of invalidation message chunks to another, resetting
255  * the source chunk-list pointer to NULL.
256  */
257 static void
258 AppendInvalidationMessageList(InvalidationChunk **destHdr,
259                                                           InvalidationChunk **srcHdr)
260 {
261         InvalidationChunk *chunk = *srcHdr;
262
263         if (chunk == NULL)
264                 return;                                 /* nothing to do */
265
266         while (chunk->next != NULL)
267                 chunk = chunk->next;
268
269         chunk->next = *destHdr;
270
271         *destHdr = *srcHdr;
272
273         *srcHdr = NULL;
274 }
275
276 /*
277  * Process a list of invalidation messages.
278  *
279  * This is a macro that executes the given code fragment for each message in
280  * a message chunk list.  The fragment should refer to the message as *msg.
281  */
282 #define ProcessMessageList(listHdr, codeFragment) \
283         do { \
284                 InvalidationChunk *_chunk; \
285                 for (_chunk = (listHdr); _chunk != NULL; _chunk = _chunk->next) \
286                 { \
287                         int             _cindex; \
288                         for (_cindex = 0; _cindex < _chunk->nitems; _cindex++) \
289                         { \
290                                 SharedInvalidationMessage *msg = &_chunk->msgs[_cindex]; \
291                                 codeFragment; \
292                         } \
293                 } \
294         } while (0)
295
296 /*
297  * Process a list of invalidation messages group-wise.
298  *
299  * As above, but the code fragment can handle an array of messages.
300  * The fragment should refer to the messages as msgs[], with n entries.
301  */
302 #define ProcessMessageListMulti(listHdr, codeFragment) \
303         do { \
304                 InvalidationChunk *_chunk; \
305                 for (_chunk = (listHdr); _chunk != NULL; _chunk = _chunk->next) \
306                 { \
307                         SharedInvalidationMessage *msgs = _chunk->msgs; \
308                         int             n = _chunk->nitems; \
309                         codeFragment; \
310                 } \
311         } while (0)
312
313
314 /* ----------------------------------------------------------------
315  *                              Invalidation set support functions
316  *
317  * These routines understand about the division of a logical invalidation
318  * list into separate physical lists for catcache and relcache entries.
319  * ----------------------------------------------------------------
320  */
321
322 /*
323  * Add a catcache inval entry
324  */
325 static void
326 AddCatcacheInvalidationMessage(InvalidationListHeader *hdr,
327                                                            int id, uint32 hashValue, Oid dbId)
328 {
329         SharedInvalidationMessage msg;
330
331         Assert(id < CHAR_MAX);
332         msg.cc.id = (int8) id;
333         msg.cc.dbId = dbId;
334         msg.cc.hashValue = hashValue;
335         AddInvalidationMessage(&hdr->cclist, &msg);
336 }
337
338 /*
339  * Add a whole-catalog inval entry
340  */
341 static void
342 AddCatalogInvalidationMessage(InvalidationListHeader *hdr,
343                                                           Oid dbId, Oid catId)
344 {
345         SharedInvalidationMessage msg;
346
347         msg.cat.id = SHAREDINVALCATALOG_ID;
348         msg.cat.dbId = dbId;
349         msg.cat.catId = catId;
350         AddInvalidationMessage(&hdr->cclist, &msg);
351 }
352
353 /*
354  * Add a relcache inval entry
355  */
356 static void
357 AddRelcacheInvalidationMessage(InvalidationListHeader *hdr,
358                                                            Oid dbId, Oid relId)
359 {
360         SharedInvalidationMessage msg;
361
362         /* Don't add a duplicate item */
363         /* We assume dbId need not be checked because it will never change */
364         ProcessMessageList(hdr->rclist,
365                                            if (msg->rc.id == SHAREDINVALRELCACHE_ID &&
366                                                    msg->rc.relId == relId)
367                                            return);
368
369         /* OK, add the item */
370         msg.rc.id = SHAREDINVALRELCACHE_ID;
371         msg.rc.dbId = dbId;
372         msg.rc.relId = relId;
373         AddInvalidationMessage(&hdr->rclist, &msg);
374 }
375
376 /*
377  * Add a snapshot inval entry
378  */
379 static void
380 AddSnapshotInvalidationMessage(InvalidationListHeader *hdr,
381                                                            Oid dbId, Oid relId)
382 {
383         SharedInvalidationMessage msg;
384
385         /* Don't add a duplicate item */
386         /* We assume dbId need not be checked because it will never change */
387         ProcessMessageList(hdr->rclist,
388                                            if (msg->sn.id == SHAREDINVALSNAPSHOT_ID &&
389                                                    msg->sn.relId == relId)
390                                            return);
391
392         /* OK, add the item */
393         msg.sn.id = SHAREDINVALSNAPSHOT_ID;
394         msg.sn.dbId = dbId;
395         msg.sn.relId = relId;
396         AddInvalidationMessage(&hdr->rclist, &msg);
397 }
398
399 /*
400  * Append one list of invalidation messages to another, resetting
401  * the source list to empty.
402  */
403 static void
404 AppendInvalidationMessages(InvalidationListHeader *dest,
405                                                    InvalidationListHeader *src)
406 {
407         AppendInvalidationMessageList(&dest->cclist, &src->cclist);
408         AppendInvalidationMessageList(&dest->rclist, &src->rclist);
409 }
410
411 /*
412  * Execute the given function for all the messages in an invalidation list.
413  * The list is not altered.
414  *
415  * catcache entries are processed first, for reasons mentioned above.
416  */
417 static void
418 ProcessInvalidationMessages(InvalidationListHeader *hdr,
419                                                         void (*func) (SharedInvalidationMessage *msg))
420 {
421         ProcessMessageList(hdr->cclist, func(msg));
422         ProcessMessageList(hdr->rclist, func(msg));
423 }
424
425 /*
426  * As above, but the function is able to process an array of messages
427  * rather than just one at a time.
428  */
429 static void
430 ProcessInvalidationMessagesMulti(InvalidationListHeader *hdr,
431                                  void (*func) (const SharedInvalidationMessage *msgs, int n))
432 {
433         ProcessMessageListMulti(hdr->cclist, func(msgs, n));
434         ProcessMessageListMulti(hdr->rclist, func(msgs, n));
435 }
436
437 /* ----------------------------------------------------------------
438  *                                        private support functions
439  * ----------------------------------------------------------------
440  */
441
442 /*
443  * RegisterCatcacheInvalidation
444  *
445  * Register an invalidation event for a catcache tuple entry.
446  */
447 static void
448 RegisterCatcacheInvalidation(int cacheId,
449                                                          uint32 hashValue,
450                                                          Oid dbId)
451 {
452         AddCatcacheInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
453                                                                    cacheId, hashValue, dbId);
454 }
455
456 /*
457  * RegisterCatalogInvalidation
458  *
459  * Register an invalidation event for all catcache entries from a catalog.
460  */
461 static void
462 RegisterCatalogInvalidation(Oid dbId, Oid catId)
463 {
464         AddCatalogInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
465                                                                   dbId, catId);
466 }
467
468 /*
469  * RegisterRelcacheInvalidation
470  *
471  * As above, but register a relcache invalidation event.
472  */
473 static void
474 RegisterRelcacheInvalidation(Oid dbId, Oid relId)
475 {
476         AddRelcacheInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
477                                                                    dbId, relId);
478
479         /*
480          * Most of the time, relcache invalidation is associated with system
481          * catalog updates, but there are a few cases where it isn't.  Quick hack
482          * to ensure that the next CommandCounterIncrement() will think that we
483          * need to do CommandEndInvalidationMessages().
484          */
485         (void) GetCurrentCommandId(true);
486
487         /*
488          * If the relation being invalidated is one of those cached in the
489          * relcache init file, mark that we need to zap that file at commit.
490          */
491         if (RelationIdIsInInitFile(relId))
492                 transInvalInfo->RelcacheInitFileInval = true;
493 }
494
495 /*
496  * RegisterSnapshotInvalidation
497  *
498  * Register a invalidation event for MVCC scans against a given catalog.
499  * Only needed for catalogs that don't have catcaches.
500  */
501 static void
502 RegisterSnapshotInvalidation(Oid dbId, Oid relId)
503 {
504         AddSnapshotInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
505                                                                    dbId, relId);
506 }
507
508 /*
509  * LocalExecuteInvalidationMessage
510  *
511  * Process a single invalidation message (which could be of any type).
512  * Only the local caches are flushed; this does not transmit the message
513  * to other backends.
514  */
515 void
516 LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
517 {
518         if (msg->id >= 0)
519         {
520                 if (msg->cc.dbId == MyDatabaseId || msg->cc.dbId == InvalidOid)
521                 {
522                         InvalidateCatalogSnapshot();
523
524                         CatalogCacheIdInvalidate(msg->cc.id, msg->cc.hashValue);
525
526                         CallSyscacheCallbacks(msg->cc.id, msg->cc.hashValue);
527                 }
528         }
529         else if (msg->id == SHAREDINVALCATALOG_ID)
530         {
531                 if (msg->cat.dbId == MyDatabaseId || msg->cat.dbId == InvalidOid)
532                 {
533                         InvalidateCatalogSnapshot();
534
535                         CatalogCacheFlushCatalog(msg->cat.catId);
536
537                         /* CatalogCacheFlushCatalog calls CallSyscacheCallbacks as needed */
538                 }
539         }
540         else if (msg->id == SHAREDINVALRELCACHE_ID)
541         {
542                 if (msg->rc.dbId == MyDatabaseId || msg->rc.dbId == InvalidOid)
543                 {
544                         int                     i;
545
546                         RelationCacheInvalidateEntry(msg->rc.relId);
547
548                         for (i = 0; i < relcache_callback_count; i++)
549                         {
550                                 struct RELCACHECALLBACK *ccitem = relcache_callback_list + i;
551
552                                 (*ccitem->function) (ccitem->arg, msg->rc.relId);
553                         }
554                 }
555         }
556         else if (msg->id == SHAREDINVALSMGR_ID)
557         {
558                 /*
559                  * We could have smgr entries for relations of other databases, so no
560                  * short-circuit test is possible here.
561                  */
562                 RelFileNodeBackend rnode;
563
564                 rnode.node = msg->sm.rnode;
565                 rnode.backend = (msg->sm.backend_hi << 16) | (int) msg->sm.backend_lo;
566                 smgrclosenode(rnode);
567         }
568         else if (msg->id == SHAREDINVALRELMAP_ID)
569         {
570                 /* We only care about our own database and shared catalogs */
571                 if (msg->rm.dbId == InvalidOid)
572                         RelationMapInvalidate(true);
573                 else if (msg->rm.dbId == MyDatabaseId)
574                         RelationMapInvalidate(false);
575         }
576         else if (msg->id == SHAREDINVALSNAPSHOT_ID)
577         {
578                 /* We only care about our own database and shared catalogs */
579                 if (msg->rm.dbId == InvalidOid)
580                         InvalidateCatalogSnapshot();
581                 else if (msg->rm.dbId == MyDatabaseId)
582                         InvalidateCatalogSnapshot();
583         }
584         else
585                 elog(FATAL, "unrecognized SI message ID: %d", msg->id);
586 }
587
588 /*
589  *              InvalidateSystemCaches
590  *
591  *              This blows away all tuples in the system catalog caches and
592  *              all the cached relation descriptors and smgr cache entries.
593  *              Relation descriptors that have positive refcounts are then rebuilt.
594  *
595  *              We call this when we see a shared-inval-queue overflow signal,
596  *              since that tells us we've lost some shared-inval messages and hence
597  *              don't know what needs to be invalidated.
598  */
599 void
600 InvalidateSystemCaches(void)
601 {
602         int                     i;
603
604         InvalidateCatalogSnapshot();
605         ResetCatalogCaches();
606         RelationCacheInvalidate();      /* gets smgr and relmap too */
607
608         for (i = 0; i < syscache_callback_count; i++)
609         {
610                 struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i;
611
612                 (*ccitem->function) (ccitem->arg, ccitem->id, 0);
613         }
614
615         for (i = 0; i < relcache_callback_count; i++)
616         {
617                 struct RELCACHECALLBACK *ccitem = relcache_callback_list + i;
618
619                 (*ccitem->function) (ccitem->arg, InvalidOid);
620         }
621 }
622
623
624 /* ----------------------------------------------------------------
625  *                                        public functions
626  * ----------------------------------------------------------------
627  */
628
629 /*
630  * AcceptInvalidationMessages
631  *              Read and process invalidation messages from the shared invalidation
632  *              message queue.
633  *
634  * Note:
635  *              This should be called as the first step in processing a transaction.
636  */
637 void
638 AcceptInvalidationMessages(void)
639 {
640         ReceiveSharedInvalidMessages(LocalExecuteInvalidationMessage,
641                                                                  InvalidateSystemCaches);
642
643         /*
644          * Test code to force cache flushes anytime a flush could happen.
645          *
646          * If used with CLOBBER_FREED_MEMORY, CLOBBER_CACHE_ALWAYS provides a
647          * fairly thorough test that the system contains no cache-flush hazards.
648          * However, it also makes the system unbelievably slow --- the regression
649          * tests take about 100 times longer than normal.
650          *
651          * If you're a glutton for punishment, try CLOBBER_CACHE_RECURSIVELY. This
652          * slows things by at least a factor of 10000, so I wouldn't suggest
653          * trying to run the entire regression tests that way.  It's useful to try
654          * a few simple tests, to make sure that cache reload isn't subject to
655          * internal cache-flush hazards, but after you've done a few thousand
656          * recursive reloads it's unlikely you'll learn more.
657          */
658 #if defined(CLOBBER_CACHE_ALWAYS)
659         {
660                 static bool in_recursion = false;
661
662                 if (!in_recursion)
663                 {
664                         in_recursion = true;
665                         InvalidateSystemCaches();
666                         in_recursion = false;
667                 }
668         }
669 #elif defined(CLOBBER_CACHE_RECURSIVELY)
670         InvalidateSystemCaches();
671 #endif
672 }
673
674 /*
675  * AtStart_Inval
676  *              Initialize inval lists at start of a main transaction.
677  */
678 void
679 AtStart_Inval(void)
680 {
681         Assert(transInvalInfo == NULL);
682         transInvalInfo = (TransInvalidationInfo *)
683                 MemoryContextAllocZero(TopTransactionContext,
684                                                            sizeof(TransInvalidationInfo));
685         transInvalInfo->my_level = GetCurrentTransactionNestLevel();
686         SharedInvalidMessagesArray = NULL;
687         numSharedInvalidMessagesArray = 0;
688 }
689
690 /*
691  * PostPrepare_Inval
692  *              Clean up after successful PREPARE.
693  *
694  * Here, we want to act as though the transaction aborted, so that we will
695  * undo any syscache changes it made, thereby bringing us into sync with the
696  * outside world, which doesn't believe the transaction committed yet.
697  *
698  * If the prepared transaction is later aborted, there is nothing more to
699  * do; if it commits, we will receive the consequent inval messages just
700  * like everyone else.
701  */
702 void
703 PostPrepare_Inval(void)
704 {
705         AtEOXact_Inval(false);
706 }
707
708 /*
709  * AtSubStart_Inval
710  *              Initialize inval lists at start of a subtransaction.
711  */
712 void
713 AtSubStart_Inval(void)
714 {
715         TransInvalidationInfo *myInfo;
716
717         Assert(transInvalInfo != NULL);
718         myInfo = (TransInvalidationInfo *)
719                 MemoryContextAllocZero(TopTransactionContext,
720                                                            sizeof(TransInvalidationInfo));
721         myInfo->parent = transInvalInfo;
722         myInfo->my_level = GetCurrentTransactionNestLevel();
723         transInvalInfo = myInfo;
724 }
725
726 /*
727  * Collect invalidation messages into SharedInvalidMessagesArray array.
728  */
729 static void
730 MakeSharedInvalidMessagesArray(const SharedInvalidationMessage *msgs, int n)
731 {
732         /*
733          * Initialise array first time through in each commit
734          */
735         if (SharedInvalidMessagesArray == NULL)
736         {
737                 maxSharedInvalidMessagesArray = FIRSTCHUNKSIZE;
738                 numSharedInvalidMessagesArray = 0;
739
740                 /*
741                  * Although this is being palloc'd we don't actually free it directly.
742                  * We're so close to EOXact that we now we're going to lose it anyhow.
743                  */
744                 SharedInvalidMessagesArray = palloc(maxSharedInvalidMessagesArray
745                                                                                 * sizeof(SharedInvalidationMessage));
746         }
747
748         if ((numSharedInvalidMessagesArray + n) > maxSharedInvalidMessagesArray)
749         {
750                 while ((numSharedInvalidMessagesArray + n) > maxSharedInvalidMessagesArray)
751                         maxSharedInvalidMessagesArray *= 2;
752
753                 SharedInvalidMessagesArray = repalloc(SharedInvalidMessagesArray,
754                                                                                           maxSharedInvalidMessagesArray
755                                                                                 * sizeof(SharedInvalidationMessage));
756         }
757
758         /*
759          * Append the next chunk onto the array
760          */
761         memcpy(SharedInvalidMessagesArray + numSharedInvalidMessagesArray,
762                    msgs, n * sizeof(SharedInvalidationMessage));
763         numSharedInvalidMessagesArray += n;
764 }
765
766 /*
767  * xactGetCommittedInvalidationMessages() is executed by
768  * RecordTransactionCommit() to add invalidation messages onto the
769  * commit record. This applies only to commit message types, never to
770  * abort records. Must always run before AtEOXact_Inval(), since that
771  * removes the data we need to see.
772  *
773  * Remember that this runs before we have officially committed, so we
774  * must not do anything here to change what might occur *if* we should
775  * fail between here and the actual commit.
776  *
777  * see also xact_redo_commit() and xact_desc_commit()
778  */
779 int
780 xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs,
781                                                                          bool *RelcacheInitFileInval)
782 {
783         MemoryContext oldcontext;
784
785         /* Must be at top of stack */
786         Assert(transInvalInfo != NULL && transInvalInfo->parent == NULL);
787
788         /*
789          * Relcache init file invalidation requires processing both before and
790          * after we send the SI messages.  However, we need not do anything unless
791          * we committed.
792          */
793         *RelcacheInitFileInval = transInvalInfo->RelcacheInitFileInval;
794
795         /*
796          * Walk through TransInvalidationInfo to collect all the messages into a
797          * single contiguous array of invalidation messages. It must be contiguous
798          * so we can copy directly into WAL message. Maintain the order that they
799          * would be processed in by AtEOXact_Inval(), to ensure emulated behaviour
800          * in redo is as similar as possible to original. We want the same bugs,
801          * if any, not new ones.
802          */
803         oldcontext = MemoryContextSwitchTo(CurTransactionContext);
804
805         ProcessInvalidationMessagesMulti(&transInvalInfo->CurrentCmdInvalidMsgs,
806                                                                          MakeSharedInvalidMessagesArray);
807         ProcessInvalidationMessagesMulti(&transInvalInfo->PriorCmdInvalidMsgs,
808                                                                          MakeSharedInvalidMessagesArray);
809         MemoryContextSwitchTo(oldcontext);
810
811         Assert(!(numSharedInvalidMessagesArray > 0 &&
812                          SharedInvalidMessagesArray == NULL));
813
814         *msgs = SharedInvalidMessagesArray;
815
816         return numSharedInvalidMessagesArray;
817 }
818
819 /*
820  * ProcessCommittedInvalidationMessages is executed by xact_redo_commit()
821  * to process invalidation messages added to commit records.
822  *
823  * Relcache init file invalidation requires processing both
824  * before and after we send the SI messages. See AtEOXact_Inval()
825  */
826 void
827 ProcessCommittedInvalidationMessages(SharedInvalidationMessage *msgs,
828                                                                          int nmsgs, bool RelcacheInitFileInval,
829                                                                          Oid dbid, Oid tsid)
830 {
831         if (nmsgs <= 0)
832                 return;
833
834         elog(trace_recovery(DEBUG4), "replaying commit with %d messages%s", nmsgs,
835                  (RelcacheInitFileInval ? " and relcache file invalidation" : ""));
836
837         if (RelcacheInitFileInval)
838         {
839                 /*
840                  * RelationCacheInitFilePreInvalidate requires DatabasePath to be set,
841                  * but we should not use SetDatabasePath during recovery, since it is
842                  * intended to be used only once by normal backends.  Hence, a quick
843                  * hack: set DatabasePath directly then unset after use.
844                  */
845                 DatabasePath = GetDatabasePath(dbid, tsid);
846                 elog(trace_recovery(DEBUG4), "removing relcache init file in \"%s\"",
847                          DatabasePath);
848                 RelationCacheInitFilePreInvalidate();
849                 pfree(DatabasePath);
850                 DatabasePath = NULL;
851         }
852
853         SendSharedInvalidMessages(msgs, nmsgs);
854
855         if (RelcacheInitFileInval)
856                 RelationCacheInitFilePostInvalidate();
857 }
858
859 /*
860  * AtEOXact_Inval
861  *              Process queued-up invalidation messages at end of main transaction.
862  *
863  * If isCommit, we must send out the messages in our PriorCmdInvalidMsgs list
864  * to the shared invalidation message queue.  Note that these will be read
865  * not only by other backends, but also by our own backend at the next
866  * transaction start (via AcceptInvalidationMessages).  This means that
867  * we can skip immediate local processing of anything that's still in
868  * CurrentCmdInvalidMsgs, and just send that list out too.
869  *
870  * If not isCommit, we are aborting, and must locally process the messages
871  * in PriorCmdInvalidMsgs.  No messages need be sent to other backends,
872  * since they'll not have seen our changed tuples anyway.  We can forget
873  * about CurrentCmdInvalidMsgs too, since those changes haven't touched
874  * the caches yet.
875  *
876  * In any case, reset the various lists to empty.  We need not physically
877  * free memory here, since TopTransactionContext is about to be emptied
878  * anyway.
879  *
880  * Note:
881  *              This should be called as the last step in processing a transaction.
882  */
883 void
884 AtEOXact_Inval(bool isCommit)
885 {
886         if (isCommit)
887         {
888                 /* Must be at top of stack */
889                 Assert(transInvalInfo != NULL && transInvalInfo->parent == NULL);
890
891                 /*
892                  * Relcache init file invalidation requires processing both before and
893                  * after we send the SI messages.  However, we need not do anything
894                  * unless we committed.
895                  */
896                 if (transInvalInfo->RelcacheInitFileInval)
897                         RelationCacheInitFilePreInvalidate();
898
899                 AppendInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
900                                                                    &transInvalInfo->CurrentCmdInvalidMsgs);
901
902                 ProcessInvalidationMessagesMulti(&transInvalInfo->PriorCmdInvalidMsgs,
903                                                                                  SendSharedInvalidMessages);
904
905                 if (transInvalInfo->RelcacheInitFileInval)
906                         RelationCacheInitFilePostInvalidate();
907         }
908         else if (transInvalInfo != NULL)
909         {
910                 /* Must be at top of stack */
911                 Assert(transInvalInfo->parent == NULL);
912
913                 ProcessInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
914                                                                         LocalExecuteInvalidationMessage);
915         }
916
917         /* Need not free anything explicitly */
918         transInvalInfo = NULL;
919 }
920
921 /*
922  * AtEOSubXact_Inval
923  *              Process queued-up invalidation messages at end of subtransaction.
924  *
925  * If isCommit, process CurrentCmdInvalidMsgs if any (there probably aren't),
926  * and then attach both CurrentCmdInvalidMsgs and PriorCmdInvalidMsgs to the
927  * parent's PriorCmdInvalidMsgs list.
928  *
929  * If not isCommit, we are aborting, and must locally process the messages
930  * in PriorCmdInvalidMsgs.  No messages need be sent to other backends.
931  * We can forget about CurrentCmdInvalidMsgs too, since those changes haven't
932  * touched the caches yet.
933  *
934  * In any case, pop the transaction stack.  We need not physically free memory
935  * here, since CurTransactionContext is about to be emptied anyway
936  * (if aborting).  Beware of the possibility of aborting the same nesting
937  * level twice, though.
938  */
939 void
940 AtEOSubXact_Inval(bool isCommit)
941 {
942         int                     my_level = GetCurrentTransactionNestLevel();
943         TransInvalidationInfo *myInfo = transInvalInfo;
944
945         if (isCommit)
946         {
947                 /* Must be at non-top of stack */
948                 Assert(myInfo != NULL && myInfo->parent != NULL);
949                 Assert(myInfo->my_level == my_level);
950
951                 /* If CurrentCmdInvalidMsgs still has anything, fix it */
952                 CommandEndInvalidationMessages();
953
954                 /* Pass up my inval messages to parent */
955                 AppendInvalidationMessages(&myInfo->parent->PriorCmdInvalidMsgs,
956                                                                    &myInfo->PriorCmdInvalidMsgs);
957
958                 /* Pending relcache inval becomes parent's problem too */
959                 if (myInfo->RelcacheInitFileInval)
960                         myInfo->parent->RelcacheInitFileInval = true;
961
962                 /* Pop the transaction state stack */
963                 transInvalInfo = myInfo->parent;
964
965                 /* Need not free anything else explicitly */
966                 pfree(myInfo);
967         }
968         else if (myInfo != NULL && myInfo->my_level == my_level)
969         {
970                 /* Must be at non-top of stack */
971                 Assert(myInfo->parent != NULL);
972
973                 ProcessInvalidationMessages(&myInfo->PriorCmdInvalidMsgs,
974                                                                         LocalExecuteInvalidationMessage);
975
976                 /* Pop the transaction state stack */
977                 transInvalInfo = myInfo->parent;
978
979                 /* Need not free anything else explicitly */
980                 pfree(myInfo);
981         }
982 }
983
984 /*
985  * CommandEndInvalidationMessages
986  *              Process queued-up invalidation messages at end of one command
987  *              in a transaction.
988  *
989  * Here, we send no messages to the shared queue, since we don't know yet if
990  * we will commit.  We do need to locally process the CurrentCmdInvalidMsgs
991  * list, so as to flush our caches of any entries we have outdated in the
992  * current command.  We then move the current-cmd list over to become part
993  * of the prior-cmds list.
994  *
995  * Note:
996  *              This should be called during CommandCounterIncrement(),
997  *              after we have advanced the command ID.
998  */
999 void
1000 CommandEndInvalidationMessages(void)
1001 {
1002         /*
1003          * You might think this shouldn't be called outside any transaction, but
1004          * bootstrap does it, and also ABORT issued when not in a transaction. So
1005          * just quietly return if no state to work on.
1006          */
1007         if (transInvalInfo == NULL)
1008                 return;
1009
1010         ProcessInvalidationMessages(&transInvalInfo->CurrentCmdInvalidMsgs,
1011                                                                 LocalExecuteInvalidationMessage);
1012         AppendInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
1013                                                            &transInvalInfo->CurrentCmdInvalidMsgs);
1014 }
1015
1016
1017 /*
1018  * CacheInvalidateHeapTuple
1019  *              Register the given tuple for invalidation at end of command
1020  *              (ie, current command is creating or outdating this tuple).
1021  *              Also, detect whether a relcache invalidation is implied.
1022  *
1023  * For an insert or delete, tuple is the target tuple and newtuple is NULL.
1024  * For an update, we are called just once, with tuple being the old tuple
1025  * version and newtuple the new version.  This allows avoidance of duplicate
1026  * effort during an update.
1027  */
1028 void
1029 CacheInvalidateHeapTuple(Relation relation,
1030                                                  HeapTuple tuple,
1031                                                  HeapTuple newtuple)
1032 {
1033         Oid                     tupleRelId;
1034         Oid                     databaseId;
1035         Oid                     relationId;
1036
1037         /* Do nothing during bootstrap */
1038         if (IsBootstrapProcessingMode())
1039                 return;
1040
1041         /*
1042          * We only need to worry about invalidation for tuples that are in system
1043          * catalogs; user-relation tuples are never in catcaches and can't affect
1044          * the relcache either.
1045          */
1046         if (!IsCatalogRelation(relation))
1047                 return;
1048
1049         /*
1050          * IsCatalogRelation() will return true for TOAST tables of system
1051          * catalogs, but we don't care about those, either.
1052          */
1053         if (IsToastRelation(relation))
1054                 return;
1055
1056         /*
1057          * First let the catcache do its thing
1058          */
1059         tupleRelId = RelationGetRelid(relation);
1060         if (RelationInvalidatesSnapshotsOnly(tupleRelId))
1061         {
1062                 databaseId = IsSharedRelation(tupleRelId) ? InvalidOid : MyDatabaseId;
1063                 RegisterSnapshotInvalidation(databaseId, tupleRelId);
1064         }
1065         else
1066                 PrepareToInvalidateCacheTuple(relation, tuple, newtuple,
1067                                                                           RegisterCatcacheInvalidation);
1068
1069         /*
1070          * Now, is this tuple one of the primary definers of a relcache entry?
1071          *
1072          * Note we ignore newtuple here; we assume an update cannot move a tuple
1073          * from being part of one relcache entry to being part of another.
1074          */
1075         if (tupleRelId == RelationRelationId)
1076         {
1077                 Form_pg_class classtup = (Form_pg_class) GETSTRUCT(tuple);
1078
1079                 relationId = HeapTupleGetOid(tuple);
1080                 if (classtup->relisshared)
1081                         databaseId = InvalidOid;
1082                 else
1083                         databaseId = MyDatabaseId;
1084         }
1085         else if (tupleRelId == AttributeRelationId)
1086         {
1087                 Form_pg_attribute atttup = (Form_pg_attribute) GETSTRUCT(tuple);
1088
1089                 relationId = atttup->attrelid;
1090
1091                 /*
1092                  * KLUGE ALERT: we always send the relcache event with MyDatabaseId,
1093                  * even if the rel in question is shared (which we can't easily tell).
1094                  * This essentially means that only backends in this same database
1095                  * will react to the relcache flush request.  This is in fact
1096                  * appropriate, since only those backends could see our pg_attribute
1097                  * change anyway.  It looks a bit ugly though.  (In practice, shared
1098                  * relations can't have schema changes after bootstrap, so we should
1099                  * never come here for a shared rel anyway.)
1100                  */
1101                 databaseId = MyDatabaseId;
1102         }
1103         else if (tupleRelId == IndexRelationId)
1104         {
1105                 Form_pg_index indextup = (Form_pg_index) GETSTRUCT(tuple);
1106
1107                 /*
1108                  * When a pg_index row is updated, we should send out a relcache inval
1109                  * for the index relation.  As above, we don't know the shared status
1110                  * of the index, but in practice it doesn't matter since indexes of
1111                  * shared catalogs can't have such updates.
1112                  */
1113                 relationId = indextup->indexrelid;
1114                 databaseId = MyDatabaseId;
1115         }
1116         else
1117                 return;
1118
1119         /*
1120          * Yes.  We need to register a relcache invalidation event.
1121          */
1122         RegisterRelcacheInvalidation(databaseId, relationId);
1123 }
1124
1125 /*
1126  * CacheInvalidateCatalog
1127  *              Register invalidation of the whole content of a system catalog.
1128  *
1129  * This is normally used in VACUUM FULL/CLUSTER, where we haven't so much
1130  * changed any tuples as moved them around.  Some uses of catcache entries
1131  * expect their TIDs to be correct, so we have to blow away the entries.
1132  *
1133  * Note: we expect caller to verify that the rel actually is a system
1134  * catalog.  If it isn't, no great harm is done, just a wasted sinval message.
1135  */
1136 void
1137 CacheInvalidateCatalog(Oid catalogId)
1138 {
1139         Oid                     databaseId;
1140
1141         if (IsSharedRelation(catalogId))
1142                 databaseId = InvalidOid;
1143         else
1144                 databaseId = MyDatabaseId;
1145
1146         RegisterCatalogInvalidation(databaseId, catalogId);
1147 }
1148
1149 /*
1150  * CacheInvalidateRelcache
1151  *              Register invalidation of the specified relation's relcache entry
1152  *              at end of command.
1153  *
1154  * This is used in places that need to force relcache rebuild but aren't
1155  * changing any of the tuples recognized as contributors to the relcache
1156  * entry by CacheInvalidateHeapTuple.  (An example is dropping an index.)
1157  */
1158 void
1159 CacheInvalidateRelcache(Relation relation)
1160 {
1161         Oid                     databaseId;
1162         Oid                     relationId;
1163
1164         relationId = RelationGetRelid(relation);
1165         if (relation->rd_rel->relisshared)
1166                 databaseId = InvalidOid;
1167         else
1168                 databaseId = MyDatabaseId;
1169
1170         RegisterRelcacheInvalidation(databaseId, relationId);
1171 }
1172
1173 /*
1174  * CacheInvalidateRelcacheByTuple
1175  *              As above, but relation is identified by passing its pg_class tuple.
1176  */
1177 void
1178 CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
1179 {
1180         Form_pg_class classtup = (Form_pg_class) GETSTRUCT(classTuple);
1181         Oid                     databaseId;
1182         Oid                     relationId;
1183
1184         relationId = HeapTupleGetOid(classTuple);
1185         if (classtup->relisshared)
1186                 databaseId = InvalidOid;
1187         else
1188                 databaseId = MyDatabaseId;
1189         RegisterRelcacheInvalidation(databaseId, relationId);
1190 }
1191
1192 /*
1193  * CacheInvalidateRelcacheByRelid
1194  *              As above, but relation is identified by passing its OID.
1195  *              This is the least efficient of the three options; use one of
1196  *              the above routines if you have a Relation or pg_class tuple.
1197  */
1198 void
1199 CacheInvalidateRelcacheByRelid(Oid relid)
1200 {
1201         HeapTuple       tup;
1202
1203         tup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
1204         if (!HeapTupleIsValid(tup))
1205                 elog(ERROR, "cache lookup failed for relation %u", relid);
1206         CacheInvalidateRelcacheByTuple(tup);
1207         ReleaseSysCache(tup);
1208 }
1209
1210
1211 /*
1212  * CacheInvalidateSmgr
1213  *              Register invalidation of smgr references to a physical relation.
1214  *
1215  * Sending this type of invalidation msg forces other backends to close open
1216  * smgr entries for the rel.  This should be done to flush dangling open-file
1217  * references when the physical rel is being dropped or truncated.  Because
1218  * these are nontransactional (i.e., not-rollback-able) operations, we just
1219  * send the inval message immediately without any queuing.
1220  *
1221  * Note: in most cases there will have been a relcache flush issued against
1222  * the rel at the logical level.  We need a separate smgr-level flush because
1223  * it is possible for backends to have open smgr entries for rels they don't
1224  * have a relcache entry for, e.g. because the only thing they ever did with
1225  * the rel is write out dirty shared buffers.
1226  *
1227  * Note: because these messages are nontransactional, they won't be captured
1228  * in commit/abort WAL entries.  Instead, calls to CacheInvalidateSmgr()
1229  * should happen in low-level smgr.c routines, which are executed while
1230  * replaying WAL as well as when creating it.
1231  *
1232  * Note: In order to avoid bloating SharedInvalidationMessage, we store only
1233  * three bytes of the backend ID using what would otherwise be padding space.
1234  * Thus, the maximum possible backend ID is 2^23-1.
1235  */
1236 void
1237 CacheInvalidateSmgr(RelFileNodeBackend rnode)
1238 {
1239         SharedInvalidationMessage msg;
1240
1241         msg.sm.id = SHAREDINVALSMGR_ID;
1242         msg.sm.backend_hi = rnode.backend >> 16;
1243         msg.sm.backend_lo = rnode.backend & 0xffff;
1244         msg.sm.rnode = rnode.node;
1245         SendSharedInvalidMessages(&msg, 1);
1246 }
1247
1248 /*
1249  * CacheInvalidateRelmap
1250  *              Register invalidation of the relation mapping for a database,
1251  *              or for the shared catalogs if databaseId is zero.
1252  *
1253  * Sending this type of invalidation msg forces other backends to re-read
1254  * the indicated relation mapping file.  It is also necessary to send a
1255  * relcache inval for the specific relations whose mapping has been altered,
1256  * else the relcache won't get updated with the new filenode data.
1257  *
1258  * Note: because these messages are nontransactional, they won't be captured
1259  * in commit/abort WAL entries.  Instead, calls to CacheInvalidateRelmap()
1260  * should happen in low-level relmapper.c routines, which are executed while
1261  * replaying WAL as well as when creating it.
1262  */
1263 void
1264 CacheInvalidateRelmap(Oid databaseId)
1265 {
1266         SharedInvalidationMessage msg;
1267
1268         msg.rm.id = SHAREDINVALRELMAP_ID;
1269         msg.rm.dbId = databaseId;
1270         SendSharedInvalidMessages(&msg, 1);
1271 }
1272
1273
1274 /*
1275  * CacheRegisterSyscacheCallback
1276  *              Register the specified function to be called for all future
1277  *              invalidation events in the specified cache.  The cache ID and the
1278  *              hash value of the tuple being invalidated will be passed to the
1279  *              function.
1280  *
1281  * NOTE: Hash value zero will be passed if a cache reset request is received.
1282  * In this case the called routines should flush all cached state.
1283  * Yes, there's a possibility of a false match to zero, but it doesn't seem
1284  * worth troubling over, especially since most of the current callees just
1285  * flush all cached state anyway.
1286  */
1287 void
1288 CacheRegisterSyscacheCallback(int cacheid,
1289                                                           SyscacheCallbackFunction func,
1290                                                           Datum arg)
1291 {
1292         if (syscache_callback_count >= MAX_SYSCACHE_CALLBACKS)
1293                 elog(FATAL, "out of syscache_callback_list slots");
1294
1295         syscache_callback_list[syscache_callback_count].id = cacheid;
1296         syscache_callback_list[syscache_callback_count].function = func;
1297         syscache_callback_list[syscache_callback_count].arg = arg;
1298
1299         ++syscache_callback_count;
1300 }
1301
1302 /*
1303  * CacheRegisterRelcacheCallback
1304  *              Register the specified function to be called for all future
1305  *              relcache invalidation events.  The OID of the relation being
1306  *              invalidated will be passed to the function.
1307  *
1308  * NOTE: InvalidOid will be passed if a cache reset request is received.
1309  * In this case the called routines should flush all cached state.
1310  */
1311 void
1312 CacheRegisterRelcacheCallback(RelcacheCallbackFunction func,
1313                                                           Datum arg)
1314 {
1315         if (relcache_callback_count >= MAX_RELCACHE_CALLBACKS)
1316                 elog(FATAL, "out of relcache_callback_list slots");
1317
1318         relcache_callback_list[relcache_callback_count].function = func;
1319         relcache_callback_list[relcache_callback_count].arg = arg;
1320
1321         ++relcache_callback_count;
1322 }
1323
1324 /*
1325  * CallSyscacheCallbacks
1326  *
1327  * This is exported so that CatalogCacheFlushCatalog can call it, saving
1328  * this module from knowing which catcache IDs correspond to which catalogs.
1329  */
1330 void
1331 CallSyscacheCallbacks(int cacheid, uint32 hashvalue)
1332 {
1333         int                     i;
1334
1335         for (i = 0; i < syscache_callback_count; i++)
1336         {
1337                 struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i;
1338
1339                 if (ccitem->id == cacheid)
1340                         (*ccitem->function) (ccitem->arg, cacheid, hashvalue);
1341         }
1342 }