]> granicus.if.org Git - postgresql/blob - src/backend/utils/cache/catcache.c
Fix for failure to clean SysCache entry when a relation is deleted
[postgresql] / src / backend / utils / cache / catcache.c
1 /*-------------------------------------------------------------------------
2  *
3  * catcache.c
4  *        System catalog cache for tuples matching a key.
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.43 1999/06/04 02:19:45 tgl Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include <string.h>
15 #include "postgres.h"
16 #include "access/heapam.h"
17 #include "access/genam.h"
18 #include "utils/tqual.h"
19 #include "utils/builtins.h"
20 #include "utils/portal.h"
21 #include "utils/catcache.h"
22 #include "utils/elog.h"
23 #include "utils/palloc.h"
24 #include "utils/mcxt.h"
25 #include "utils/rel.h"
26 #include "storage/bufpage.h"
27 #include "access/valid.h"
28 #include "miscadmin.h"
29 #include "fmgr.h"                               /* for F_BOOLEQ, etc.  DANGER */
30 #include "catalog/pg_type.h"    /* for OID of int28 type */
31 #include "lib/dllist.h"
32
33 static void CatCacheRemoveCTup(CatCache *cache, Dlelem *e);
34 static Index CatalogCacheComputeHashIndex(struct catcache * cacheInP);
35 static Index CatalogCacheComputeTupleHashIndex(struct catcache * cacheInOutP,
36                                                                   Relation relation, HeapTuple tuple);
37 static void CatalogCacheInitializeCache(struct catcache * cache,
38                                                         Relation relation);
39 static long comphash(long l, char *v);
40
41 /* ----------------
42  *              variables, macros and other stuff
43  *
44  *      note CCSIZE allocates 51 buckets .. one was already allocated in
45  *      the catcache structure.
46  * ----------------
47  */
48
49 #ifdef CACHEDEBUG
50 #define CACHE1_elog(a,b)                                elog(a,b)
51 #define CACHE2_elog(a,b,c)                              elog(a,b,c)
52 #define CACHE3_elog(a,b,c,d)                    elog(a,b,c,d)
53 #define CACHE4_elog(a,b,c,d,e)                  elog(a,b,c,d,e)
54 #define CACHE5_elog(a,b,c,d,e,f)                elog(a,b,c,d,e,f)
55 #define CACHE6_elog(a,b,c,d,e,f,g)              elog(a,b,c,d,e,f,g)
56 #else
57 #define CACHE1_elog(a,b)
58 #define CACHE2_elog(a,b,c)
59 #define CACHE3_elog(a,b,c,d)
60 #define CACHE4_elog(a,b,c,d,e)
61 #define CACHE5_elog(a,b,c,d,e,f)
62 #define CACHE6_elog(a,b,c,d,e,f,g)
63 #endif
64
65 static CatCache   *Caches = NULL; /* head of list of caches */
66
67 GlobalMemory CacheCxt;                  /* context in which caches are allocated */
68 /* CacheCxt is global because relcache uses it too. */
69
70
71 /* ----------------
72  *              EQPROC is used in CatalogCacheInitializeCache
73  *              XXX this should be replaced by catalog lookups soon
74  * ----------------
75  */
76 static long eqproc[] = {
77         F_BOOLEQ, 0l, F_CHAREQ, F_NAMEEQ, 0l,
78         F_INT2EQ, F_KEYFIRSTEQ, F_INT4EQ, 0l, F_TEXTEQ,
79         F_OIDEQ, 0l, 0l, 0l, F_OID8EQ
80 };
81
82 #define EQPROC(SYSTEMTYPEOID)   eqproc[(SYSTEMTYPEOID)-16]
83
84 /* ----------------------------------------------------------------
85  *                                      internal support functions
86  * ----------------------------------------------------------------
87  */
88 /* --------------------------------
89  *              CatalogCacheInitializeCache
90  * --------------------------------
91  */
92 #ifdef CACHEDEBUG
93 #define CatalogCacheInitializeCache_DEBUG1 \
94 do { \
95         elog(DEBUG, "CatalogCacheInitializeCache: cache @%08lx", cache); \
96         if (relation) \
97                 elog(DEBUG, "CatalogCacheInitializeCache: called w/relation(inval)"); \
98         else \
99                 elog(DEBUG, "CatalogCacheInitializeCache: called w/relname %s", \
100                         cache->cc_relname) \
101 } while(0)
102
103 #define CatalogCacheInitializeCache_DEBUG2 \
104 do { \
105                 if (cache->cc_key[i] > 0) { \
106                         elog(DEBUG, "CatalogCacheInitializeCache: load %d/%d w/%d, %d", \
107                                 i+1, cache->cc_nkeys, cache->cc_key[i], \
108                                 relation->rd_att->attrs[cache->cc_key[i] - 1]->attlen); \
109                 } else { \
110                         elog(DEBUG, "CatalogCacheInitializeCache: load %d/%d w/%d", \
111                                 i+1, cache->cc_nkeys, cache->cc_key[i]); \
112                 } \
113 } while(0)
114
115 #else
116 #define CatalogCacheInitializeCache_DEBUG1
117 #define CatalogCacheInitializeCache_DEBUG2
118 #endif
119
120 static void
121 CatalogCacheInitializeCache(struct catcache * cache,
122                                                         Relation relation)
123 {
124         MemoryContext oldcxt;
125         short           didopen = 0;
126         short           i;
127         TupleDesc       tupdesc;
128
129         CatalogCacheInitializeCache_DEBUG1;
130
131         /* ----------------
132          *      first switch to the cache context so our allocations
133          *      do not vanish at the end of a transaction
134          * ----------------
135          */
136         if (!CacheCxt)
137                 CacheCxt = CreateGlobalMemory("Cache");
138         oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
139
140         /* ----------------
141          *      If no relation was passed we must open it to get access to
142          *      its fields.  If one of the other caches has already opened
143          *      it we use heap_open() instead of heap_openr()
144          * ----------------
145          */
146         if (!RelationIsValid(relation))
147         {
148                 struct catcache *cp;
149
150                 /* ----------------
151                  *      scan the caches to see if any other cache has opened the relation
152                  * ----------------
153                  */
154                 for (cp = Caches; cp; cp = cp->cc_next)
155                 {
156                         if (strncmp(cp->cc_relname, cache->cc_relname, NAMEDATALEN) == 0)
157                         {
158                                 if (cp->relationId != InvalidOid)
159                                         break;
160                         }
161                 }
162
163                 /* ----------------
164                  *      open the relation by name or by id
165                  * ----------------
166                  */
167                 if (cp)
168                         relation = heap_open(cp->relationId);
169                 else
170                         relation = heap_openr(cache->cc_relname);
171
172                 didopen = 1;
173         }
174
175         /* ----------------
176          *      initialize the cache's relation id
177          * ----------------
178          */
179         Assert(RelationIsValid(relation));
180         cache->relationId = RelationGetRelid(relation);
181         tupdesc = cache->cc_tupdesc = RelationGetDescr(relation);
182
183         CACHE3_elog(DEBUG, "CatalogCacheInitializeCache: relid %u, %d keys",
184                                 cache->relationId, cache->cc_nkeys);
185
186         /* ----------------
187          *      initialize cache's key information
188          * ----------------
189          */
190         for (i = 0; i < cache->cc_nkeys; ++i)
191         {
192                 CatalogCacheInitializeCache_DEBUG2;
193
194                 if (cache->cc_key[i] > 0)
195                 {
196
197                         /*
198                          * Yoiks.  The implementation of the hashing code and the
199                          * implementation of int28's are at loggerheads.  The right
200                          * thing to do is to throw out the implementation of int28's
201                          * altogether; until that happens, we do the right thing here
202                          * to guarantee that the hash key generator doesn't try to
203                          * dereference an int2 by mistake.
204                          */
205
206                         if (tupdesc->attrs[cache->cc_key[i] - 1]->atttypid == INT28OID)
207                                 cache->cc_klen[i] = sizeof(short);
208                         else
209                                 cache->cc_klen[i] = tupdesc->attrs[cache->cc_key[i] - 1]->attlen;
210
211                         cache->cc_skey[i].sk_procedure = EQPROC(tupdesc->attrs[cache->cc_key[i] - 1]->atttypid);
212
213                         fmgr_info(cache->cc_skey[i].sk_procedure,
214                                           &cache->cc_skey[i].sk_func);
215                         cache->cc_skey[i].sk_nargs = cache->cc_skey[i].sk_func.fn_nargs;
216
217                         CACHE5_elog(DEBUG, "CatalogCacheInit %s %d %d %x",
218                                                 &relation->rd_rel->relname,
219                                                 i,
220                                                 tupdesc->attrs[cache->cc_key[i] - 1]->attlen,
221                                                 cache);
222                 }
223         }
224
225         /* ----------------
226          *      close the relation if we opened it
227          * ----------------
228          */
229         if (didopen)
230                 heap_close(relation);
231
232         /* ----------------
233          *      initialize index information for the cache.  this
234          *      should only be done once per cache.
235          * ----------------
236          */
237         if (cache->cc_indname != NULL && cache->indexId == InvalidOid)
238         {
239                 if (RelationGetForm(relation)->relhasindex)
240                 {
241
242                         /*
243                          * If the index doesn't exist we are in trouble.
244                          */
245                         relation = index_openr(cache->cc_indname);
246                         Assert(relation);
247                         cache->indexId = RelationGetRelid(relation);
248                         index_close(relation);
249                 }
250                 else
251                         cache->cc_indname = NULL;
252         }
253
254         /* ----------------
255          *      return to the proper memory context
256          * ----------------
257          */
258         MemoryContextSwitchTo(oldcxt);
259 }
260
261 /* --------------------------------
262  *              CatalogCacheSetId
263  *
264  *              XXX temporary function
265  * --------------------------------
266  */
267 #ifdef NOT_USED
268 void
269 CatalogCacheSetId(CatCache *cacheInOutP, int id)
270 {
271         Assert(id == InvalidCatalogCacheId || id >= 0);
272         cacheInOutP->id = id;
273 }
274
275 #endif
276
277 /* ----------------
278  * comphash
279  *              Compute a hash value, somehow.
280  *
281  * XXX explain algorithm here.
282  *
283  * l is length of the attribute value, v
284  * v is the attribute value ("Datum")
285  * ----------------
286  */
287 static long
288 comphash(long l, char *v)
289 {
290         long            i;
291         NameData        n;
292
293         CACHE3_elog(DEBUG, "comphash (%d,%x)", l, v);
294
295         switch (l)
296         {
297                 case 1:
298                 case 2:
299                 case 4:
300                         return (long) v;
301         }
302
303         if (l == NAMEDATALEN)
304         {
305
306                 /*
307                  * if it's a name, make sure that the values are null-padded.
308                  *
309                  * Note that this other fixed-length types can also have the same
310                  * typelen so this may break them         - XXX
311                  */
312                 namestrcpy(&n, v);
313                 v = n.data;
314         }
315         else if (l < 0)
316                 l = VARSIZE(v);
317
318         i = 0;
319         while (l--)
320                 i += *v++;
321         return i;
322 }
323
324 /* --------------------------------
325  *              CatalogCacheComputeHashIndex
326  * --------------------------------
327  */
328 static Index
329 CatalogCacheComputeHashIndex(struct catcache * cacheInP)
330 {
331         Index           hashIndex;
332
333         hashIndex = 0x0;
334         CACHE6_elog(DEBUG, "CatalogCacheComputeHashIndex %s %d %d %d %x",
335                                 cacheInP->cc_relname,
336                                 cacheInP->cc_nkeys,
337                                 cacheInP->cc_klen[0],
338                                 cacheInP->cc_klen[1],
339                                 cacheInP);
340
341         switch (cacheInP->cc_nkeys)
342         {
343                 case 4:
344                         hashIndex ^= comphash(cacheInP->cc_klen[3],
345                                                  (char *) cacheInP->cc_skey[3].sk_argument) << 9;
346                         /* FALLTHROUGH */
347                 case 3:
348                         hashIndex ^= comphash(cacheInP->cc_klen[2],
349                                                  (char *) cacheInP->cc_skey[2].sk_argument) << 6;
350                         /* FALLTHROUGH */
351                 case 2:
352                         hashIndex ^= comphash(cacheInP->cc_klen[1],
353                                                  (char *) cacheInP->cc_skey[1].sk_argument) << 3;
354                         /* FALLTHROUGH */
355                 case 1:
356                         hashIndex ^= comphash(cacheInP->cc_klen[0],
357                                                           (char *) cacheInP->cc_skey[0].sk_argument);
358                         break;
359                 default:
360                         elog(FATAL, "CCComputeHashIndex: %d cc_nkeys", cacheInP->cc_nkeys);
361                         break;
362         }
363         hashIndex %= cacheInP->cc_size;
364         return hashIndex;
365 }
366
367 /* --------------------------------
368  *              CatalogCacheComputeTupleHashIndex
369  * --------------------------------
370  */
371 static Index
372 CatalogCacheComputeTupleHashIndex(struct catcache * cacheInOutP,
373                                                                   Relation relation,
374                                                                   HeapTuple tuple)
375 {
376         bool            isNull = '\0';
377
378         if (cacheInOutP->relationId == InvalidOid)
379                 CatalogCacheInitializeCache(cacheInOutP, relation);
380         switch (cacheInOutP->cc_nkeys)
381         {
382                 case 4:
383                         cacheInOutP->cc_skey[3].sk_argument =
384                                 (cacheInOutP->cc_key[3] == ObjectIdAttributeNumber)
385                                 ? (Datum) tuple->t_data->t_oid
386                                 : fastgetattr(tuple,
387                                                           cacheInOutP->cc_key[3],
388                                                           RelationGetDescr(relation),
389                                                           &isNull);
390                         Assert(!isNull);
391                         /* FALLTHROUGH */
392                 case 3:
393                         cacheInOutP->cc_skey[2].sk_argument =
394                                 (cacheInOutP->cc_key[2] == ObjectIdAttributeNumber)
395                                 ? (Datum) tuple->t_data->t_oid
396                                 : fastgetattr(tuple,
397                                                           cacheInOutP->cc_key[2],
398                                                           RelationGetDescr(relation),
399                                                           &isNull);
400                         Assert(!isNull);
401                         /* FALLTHROUGH */
402                 case 2:
403                         cacheInOutP->cc_skey[1].sk_argument =
404                                 (cacheInOutP->cc_key[1] == ObjectIdAttributeNumber)
405                                 ? (Datum) tuple->t_data->t_oid
406                                 : fastgetattr(tuple,
407                                                           cacheInOutP->cc_key[1],
408                                                           RelationGetDescr(relation),
409                                                           &isNull);
410                         Assert(!isNull);
411                         /* FALLTHROUGH */
412                 case 1:
413                         cacheInOutP->cc_skey[0].sk_argument =
414                                 (cacheInOutP->cc_key[0] == ObjectIdAttributeNumber)
415                                 ? (Datum) tuple->t_data->t_oid
416                                 : fastgetattr(tuple,
417                                                           cacheInOutP->cc_key[0],
418                                                           RelationGetDescr(relation),
419                                                           &isNull);
420                         Assert(!isNull);
421                         break;
422                 default:
423                         elog(FATAL, "CCComputeTupleHashIndex: %d cc_nkeys",
424                                  cacheInOutP->cc_nkeys
425                                 );
426                         break;
427         }
428
429         return CatalogCacheComputeHashIndex(cacheInOutP);
430 }
431
432 /* --------------------------------
433  *              CatCacheRemoveCTup
434  * --------------------------------
435  */
436 static void
437 CatCacheRemoveCTup(CatCache *cache, Dlelem *elt)
438 {
439         CatCTup    *ct;
440         CatCTup    *other_ct;
441         Dlelem     *other_elt;
442
443         if (elt)
444                 ct = (CatCTup *) DLE_VAL(elt);
445         else
446                 return;
447
448         other_elt = ct->ct_node;
449         other_ct = (CatCTup *) DLE_VAL(other_elt);
450         DLRemove(other_elt);
451         DLFreeElem(other_elt);
452         free(other_ct);
453         DLRemove(elt);
454         DLFreeElem(elt);
455         free(ct);
456         --cache->cc_ntup;
457 }
458
459 /* --------------------------------
460  *      CatalogCacheIdInvalidate()
461  *
462  *      Invalidate a tuple given a cache id.  In this case the id should always
463  *      be found (whether the cache has opened its relation or not).  Of course,
464  *      if the cache has yet to open its relation, there will be no tuples so
465  *      no problem.
466  * --------------------------------
467  */
468 void
469 CatalogCacheIdInvalidate(int cacheId,   /* XXX */
470                                                  Index hashIndex,
471                                                  ItemPointer pointer)
472 {
473         CatCache   *ccp;
474         CatCTup    *ct;
475         Dlelem     *elt;
476         MemoryContext oldcxt;
477
478         /* ----------------
479          *      sanity checks
480          * ----------------
481          */
482         Assert(hashIndex < NCCBUCK);
483         Assert(ItemPointerIsValid(pointer));
484         CACHE1_elog(DEBUG, "CatalogCacheIdInvalidate: called");
485
486         /* ----------------
487          *      switch to the cache context for our memory allocations
488          * ----------------
489          */
490         if (!CacheCxt)
491                 CacheCxt = CreateGlobalMemory("Cache");
492         oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
493
494         /* ----------------
495          *      inspect every cache that could contain the tuple
496          * ----------------
497          */
498         for (ccp = Caches; ccp; ccp = ccp->cc_next)
499         {
500                 if (cacheId != ccp->id)
501                         continue;
502                 /* ----------------
503                  *      inspect the hash bucket until we find a match or exhaust
504                  * ----------------
505                  */
506                 for (elt = DLGetHead(ccp->cc_cache[hashIndex]);
507                          elt;
508                          elt = DLGetSucc(elt))
509                 {
510                         ct = (CatCTup *) DLE_VAL(elt);
511                         if (ItemPointerEquals(pointer, &ct->ct_tup->t_self))
512                                 break;
513                 }
514
515                 /* ----------------
516                  *      if we found a matching tuple, invalidate it.
517                  * ----------------
518                  */
519
520                 if (elt)
521                 {
522                         CatCacheRemoveCTup(ccp, elt);
523
524                         CACHE1_elog(DEBUG, "CatalogCacheIdInvalidate: invalidated");
525                 }
526
527                 if (cacheId != InvalidCatalogCacheId)
528                         break;
529         }
530
531         /* ----------------
532          *      return to the proper memory context
533          * ----------------
534          */
535         MemoryContextSwitchTo(oldcxt);
536         /* sendpm('I', "Invalidated tuple"); */
537 }
538
539 /* ----------------------------------------------------------------
540  *                                         public functions
541  *
542  *              ResetSystemCache
543  *              InitIndexedSysCache
544  *              InitSysCache
545  *              SearchSysCache
546  *              RelationInvalidateCatalogCacheTuple
547  * ----------------------------------------------------------------
548  */
549 /* --------------------------------
550  *              ResetSystemCache
551  * --------------------------------
552  */
553 void
554 ResetSystemCache()
555 {
556         MemoryContext oldcxt;
557         struct catcache *cache;
558
559         CACHE1_elog(DEBUG, "ResetSystemCache called");
560
561         /* ----------------
562          *      first switch to the cache context so our allocations
563          *      do not vanish at the end of a transaction
564          * ----------------
565          */
566         if (!CacheCxt)
567                 CacheCxt = CreateGlobalMemory("Cache");
568
569         oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
570
571         /* ----------------
572          *      here we purge the contents of all the caches
573          *
574          *      for each system cache
575          *         for each hash bucket
576          *                 for each tuple in hash bucket
577          *                         remove the tuple
578          * ----------------
579          */
580         for (cache = Caches; PointerIsValid(cache); cache = cache->cc_next)
581         {
582                 int                     hash;
583
584                 for (hash = 0; hash < NCCBUCK; hash += 1)
585                 {
586                         Dlelem     *elt,
587                                            *nextelt;
588
589                         for (elt = DLGetHead(cache->cc_cache[hash]); elt; elt = nextelt)
590                         {
591                                 nextelt = DLGetSucc(elt);
592                                 CatCacheRemoveCTup(cache, elt);
593                                 if (cache->cc_ntup < 0)
594                                         elog(NOTICE,
595                                                  "ResetSystemCache: cc_ntup<0 (software error)");
596                         }
597                 }
598                 cache->cc_ntup = 0;             /* in case of WARN error above */
599                 cache->busy = false;    /* to recover from recursive-use error */
600         }
601
602         CACHE1_elog(DEBUG, "end of ResetSystemCache call");
603
604         /* ----------------
605          *      back to the old context before we return...
606          * ----------------
607          */
608         MemoryContextSwitchTo(oldcxt);
609 }
610
611 /* --------------------------------
612  *              SystemCacheRelationFlushed
613  *
614  *      This is called by RelationFlushRelation() to clear out cached information
615  *      about a relation being dropped.  (This could be a DROP TABLE command,
616  *      or a temp table being dropped at end of transaction, or a table created
617  *      during the current transaction that is being dropped because of abort.)
618  *      Remove all cache entries relevant to the specified relation OID.
619  *
620  *      A special case occurs when relId is itself one of the cacheable system
621  *      tables --- although those'll never be dropped, they can get flushed from
622  *      the relcache (VACUUM causes this, for example).  In that case we need to
623  *      force the next SearchSysCache() call to reinitialize the cache itself,
624  *      because we have info (such as cc_tupdesc) that is pointing at the about-
625  *      to-be-deleted relcache entry.
626  * --------------------------------
627  */
628 void
629 SystemCacheRelationFlushed(Oid relId)
630 {
631         struct catcache *cache;
632
633         /*
634          * XXX Ideally we'd search the caches and just zap entries that actually
635          * refer to the indicated relation.  For now, we take the brute-force
636          * approach: just flush the caches entirely.
637          */
638         ResetSystemCache();
639
640         /*
641          * If relcache is dropping a system relation's cache entry, mark the
642          * associated cache structures invalid, so we can rebuild them from
643          * scratch (not just repopulate them) next time they are used.
644          */
645         for (cache = Caches; PointerIsValid(cache); cache = cache->cc_next)
646         {
647                 if (cache->relationId == relId)
648                         cache->relationId = InvalidOid;
649         }
650 }
651
652 /* --------------------------------
653  *              InitIndexedSysCache
654  *
655  *      This allocates and initializes a cache for a system catalog relation.
656  *      Actually, the cache is only partially initialized to avoid opening the
657  *      relation.  The relation will be opened and the rest of the cache
658  *      structure initialized on the first access.
659  * --------------------------------
660  */
661 #ifdef CACHEDEBUG
662 #define InitSysCache_DEBUG1 \
663 do { \
664         elog(DEBUG, "InitSysCache: rid=%u id=%d nkeys=%d size=%d\n", \
665                 cp->relationId, cp->id, cp->cc_nkeys, cp->cc_size); \
666         for (i = 0; i < nkeys; i += 1) \
667         { \
668                 elog(DEBUG, "InitSysCache: key=%d len=%d skey=[%d %d %d %d]\n", \
669                          cp->cc_key[i], cp->cc_klen[i], \
670                          cp->cc_skey[i].sk_flags, \
671                          cp->cc_skey[i].sk_attno, \
672                          cp->cc_skey[i].sk_procedure, \
673                          cp->cc_skey[i].sk_argument); \
674         } \
675 } while(0)
676
677 #else
678 #define InitSysCache_DEBUG1
679 #endif
680
681 CatCache   *
682 InitSysCache(char *relname,
683                          char *iname,
684                          int id,
685                          int nkeys,
686                          int *key,
687                          HeapTuple (*iScanfuncP) ())
688 {
689         CatCache   *cp;
690         int                     i;
691         MemoryContext oldcxt;
692
693         char       *indname;
694
695         indname = (iname) ? iname : NULL;
696
697         /* ----------------
698          *      first switch to the cache context so our allocations
699          *      do not vanish at the end of a transaction
700          * ----------------
701          */
702         if (!CacheCxt)
703                 CacheCxt = CreateGlobalMemory("Cache");
704
705         oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
706
707         /* ----------------
708          *      allocate a new cache structure
709          * ----------------
710          */
711         cp = (CatCache *) palloc(sizeof(CatCache));
712         MemSet((char *) cp, 0, sizeof(CatCache));
713
714         /* ----------------
715          *      initialize the cache buckets (each bucket is a list header)
716          *      and the LRU tuple list
717          * ----------------
718          */
719         {
720
721                 /*
722                  * We can only do this optimization because the number of hash
723                  * buckets never changes.  Without it, we call malloc() too much.
724                  * We could move this to dllist.c, but the way we do this is not
725                  * dynamic/portabl, so why allow other routines to use it.
726                  */
727                 Dllist     *cache_begin = malloc((NCCBUCK + 1) * sizeof(Dllist));
728
729                 for (i = 0; i <= NCCBUCK; ++i)
730                 {
731                         cp->cc_cache[i] = &cache_begin[i];
732                         cp->cc_cache[i]->dll_head = 0;
733                         cp->cc_cache[i]->dll_tail = 0;
734                 }
735         }
736
737         cp->cc_lrulist = DLNewList();
738
739         /* ----------------
740          *      Caches is the pointer to the head of the list of all the
741          *      system caches.  here we add the new cache to the top of the list.
742          * ----------------
743          */
744         cp->cc_next = Caches;           /* list of caches (single link) */
745         Caches = cp;
746
747         /* ----------------
748          *      initialize the cache's relation information for the relation
749          *      corresponding to this cache and initialize some of the the new
750          *      cache's other internal fields.
751          * ----------------
752          */
753         cp->relationId = InvalidOid;
754         cp->indexId = InvalidOid;
755         cp->cc_relname = relname;
756         cp->cc_indname = indname;
757         cp->cc_tupdesc = (TupleDesc) NULL;
758         cp->id = id;
759         cp->busy = false;
760         cp->cc_maxtup = MAXTUP;
761         cp->cc_size = NCCBUCK;
762         cp->cc_nkeys = nkeys;
763         cp->cc_iscanfunc = iScanfuncP;
764
765         /* ----------------
766          *      initialize the cache's key information
767          * ----------------
768          */
769         for (i = 0; i < nkeys; ++i)
770         {
771                 cp->cc_key[i] = key[i];
772                 if (!key[i])
773                         elog(FATAL, "InitSysCache: called with 0 key[%d]", i);
774                 if (key[i] < 0)
775                 {
776                         if (key[i] != ObjectIdAttributeNumber)
777                                 elog(FATAL, "InitSysCache: called with %d key[%d]", key[i], i);
778                         else
779                         {
780                                 cp->cc_klen[i] = sizeof(Oid);
781
782                                 /*
783                                  * ScanKeyEntryData and struct skey are equivalent. It
784                                  * looks like a move was made to obsolete struct skey, but
785                                  * it didn't reach this file.  Someday we should clean up
786                                  * this code and consolidate to ScanKeyEntry - mer 10 Nov
787                                  * 1991
788                                  */
789                                 ScanKeyEntryInitialize(&cp->cc_skey[i],
790                                                                            (bits16) 0,
791                                                                            (AttrNumber) key[i],
792                                                                            (RegProcedure) F_OIDEQ,
793                                                                            (Datum) 0);
794                                 continue;
795                         }
796                 }
797
798                 cp->cc_skey[i].sk_attno = key[i];
799         }
800
801         /* ----------------
802          *      all done.  new cache is initialized.  print some debugging
803          *      information, if appropriate.
804          * ----------------
805          */
806         InitSysCache_DEBUG1;
807
808         /* ----------------
809          *      back to the old context before we return...
810          * ----------------
811          */
812         MemoryContextSwitchTo(oldcxt);
813         return cp;
814 }
815
816
817 /* --------------------------------
818  *              SearchSysCache
819  *
820  *              This call searches a system cache for a tuple, opening the relation
821  *              if necessary (the first access to a particular cache).
822  * --------------------------------
823  */
824 HeapTuple
825 SearchSysCache(struct catcache * cache,
826                            Datum v1,
827                            Datum v2,
828                            Datum v3,
829                            Datum v4)
830 {
831         unsigned        hash;
832         CatCTup    *ct = NULL;
833         CatCTup    *nct;
834         CatCTup    *nct2;
835         Dlelem     *elt;
836         HeapTuple       ntp = 0;
837
838         Relation        relation;
839         MemoryContext oldcxt;
840
841         /* ----------------
842          *      sanity checks
843          * ----------------
844          */
845         if (cache->relationId == InvalidOid)
846                 CatalogCacheInitializeCache(cache, NULL);
847
848         /* ----------------
849          *      initialize the search key information
850          * ----------------
851          */
852         cache->cc_skey[0].sk_argument = v1;
853         cache->cc_skey[1].sk_argument = v2;
854         cache->cc_skey[2].sk_argument = v3;
855         cache->cc_skey[3].sk_argument = v4;
856
857         /* ----------------
858          *      find the hash bucket in which to look for the tuple
859          * ----------------
860          */
861         hash = CatalogCacheComputeHashIndex(cache);
862
863         /* ----------------
864          *      scan the hash bucket until we find a match or exhaust our tuples
865          * ----------------
866          */
867         for (elt = DLGetHead(cache->cc_cache[hash]);
868                  elt;
869                  elt = DLGetSucc(elt))
870         {
871                 bool            res;
872
873                 ct = (CatCTup *) DLE_VAL(elt);
874                 /* ----------------
875                  *      see if the cached tuple matches our key.
876                  *      (should we be worried about time ranges? -cim 10/2/90)
877                  * ----------------
878                  */
879                 HeapKeyTest(ct->ct_tup,
880                                         cache->cc_tupdesc,
881                                         cache->cc_nkeys,
882                                         cache->cc_skey,
883                                         res);
884                 if (res)
885                         break;
886         }
887
888         /* ----------------
889          *      if we found a tuple in the cache, move it to the top of the
890          *      lru list, and return it.  We also move it to the front of the
891          *      list for its hashbucket, in order to speed subsequent searches.
892          *      (The most frequently accessed elements in any hashbucket will
893          *      tend to be near the front of the hashbucket's list.)
894          * ----------------
895          */
896         if (elt)
897         {
898                 Dlelem     *old_lru_elt = ((CatCTup *) DLE_VAL(elt))->ct_node;
899
900                 DLMoveToFront(old_lru_elt);
901                 DLMoveToFront(elt);
902
903 #ifdef CACHEDEBUG
904                 relation = heap_open(cache->relationId);
905                 CACHE3_elog(DEBUG, "SearchSysCache(%s): found in bucket %d",
906                                         RelationGetRelationName(relation), hash);
907                 heap_close(relation);
908 #endif   /* CACHEDEBUG */
909
910                 return ct->ct_tup;
911         }
912
913         /* ----------------
914          *      Tuple was not found in cache, so we have to try and
915          *      retrieve it directly from the relation.  If it's found,
916          *      we add it to the cache.
917          *
918          *      To guard against possible infinite recursion, we mark this cache
919          *      "busy" while trying to load a new entry for it.  It is OK to
920          *      recursively invoke SearchSysCache for a different cache, but
921          *      a recursive call for the same cache will error out.  (We could
922          *      store the specific key(s) being looked for, and consider only
923          *      a recursive request for the same key to be an error, but this
924          *      simple scheme is sufficient for now.)
925          * ----------------
926          */
927
928         if (cache->busy)
929         {
930                 elog(ERROR, "SearchSysCache: recursive use of cache %d", cache->id);
931         }
932         cache->busy = true;
933
934         /* ----------------
935          *      open the relation associated with the cache
936          * ----------------
937          */
938         relation = heap_open(cache->relationId);
939         CACHE2_elog(DEBUG, "SearchSysCache(%s)",
940                                 RelationGetRelationName(relation));
941
942         /* ----------------
943          *      Switch to the cache memory context.
944          * ----------------
945          */
946
947         if (!CacheCxt)
948                 CacheCxt = CreateGlobalMemory("Cache");
949
950         oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
951
952         /* ----------------
953          *      Scan the relation to find the tuple.  If there's an index, and
954          *      if this isn't bootstrap (initdb) time, use the index.
955          * ----------------
956          */
957         CACHE2_elog(DEBUG, "SearchSysCache: performing scan (override==%d)",
958                                 heapisoverride());
959
960         if ((RelationGetForm(relation))->relhasindex
961                 && !IsBootstrapProcessingMode())
962         {
963                 /* ----------
964                  *      Switch back to old memory context so memory not freed
965                  *      in the scan function will go away at transaction end.
966                  *      wieck - 10/18/1996
967                  * ----------
968                  */
969                 MemoryContextSwitchTo(oldcxt);
970                 Assert(cache->cc_iscanfunc);
971                 switch (cache->cc_nkeys)
972                 {
973                         case 4:
974                                 ntp = cache->cc_iscanfunc(relation, v1, v2, v3, v4);
975                                 break;
976                         case 3:
977                                 ntp = cache->cc_iscanfunc(relation, v1, v2, v3);
978                                 break;
979                         case 2:
980                                 ntp = cache->cc_iscanfunc(relation, v1, v2);
981                                 break;
982                         case 1:
983                                 ntp = cache->cc_iscanfunc(relation, v1);
984                                 break;
985                 }
986                 /* ----------
987                  *      Back to Cache context. If we got a tuple copy it
988                  *      into our context.
989                  *      wieck - 10/18/1996
990                  * ----------
991                  */
992                 MemoryContextSwitchTo((MemoryContext) CacheCxt);
993                 if (HeapTupleIsValid(ntp))
994                         ntp = heap_copytuple(ntp);
995         }
996         else
997         {
998                 HeapScanDesc sd;
999
1000                 /* ----------
1001                  *      As above do the lookup in the callers memory
1002                  *      context.
1003                  *      wieck - 10/18/1996
1004                  * ----------
1005                  */
1006                 MemoryContextSwitchTo(oldcxt);
1007
1008                 sd = heap_beginscan(relation, 0, SnapshotNow,
1009                                                         cache->cc_nkeys, cache->cc_skey);
1010
1011                 ntp = heap_getnext(sd, 0);
1012
1013                 MemoryContextSwitchTo((MemoryContext) CacheCxt);
1014
1015                 if (HeapTupleIsValid(ntp))
1016                 {
1017                         CACHE1_elog(DEBUG, "SearchSysCache: found tuple");
1018                         ntp = heap_copytuple(ntp);
1019                 }
1020
1021                 MemoryContextSwitchTo(oldcxt);
1022
1023                 heap_endscan(sd);
1024
1025                 MemoryContextSwitchTo((MemoryContext) CacheCxt);
1026         }
1027
1028         cache->busy = false;
1029
1030         /* ----------------
1031          *      scan is complete.  if tup is valid, we copy it and add the copy to
1032          *      the cache.
1033          * ----------------
1034          */
1035         if (HeapTupleIsValid(ntp))
1036         {
1037                 /* ----------------
1038                  *      allocate a new cache tuple holder, store the pointer
1039                  *      to the heap tuple there and initialize the list pointers.
1040                  * ----------------
1041                  */
1042                 Dlelem     *lru_elt;
1043
1044                 /*
1045                  * this is a little cumbersome here because we want the Dlelem's
1046                  * in both doubly linked lists to point to one another. That makes
1047                  * it easier to remove something from both the cache bucket and
1048                  * the lru list at the same time
1049                  */
1050                 nct = (CatCTup *) malloc(sizeof(CatCTup));
1051                 nct->ct_tup = ntp;
1052                 elt = DLNewElem(nct);
1053                 nct2 = (CatCTup *) malloc(sizeof(CatCTup));
1054                 nct2->ct_tup = ntp;
1055                 lru_elt = DLNewElem(nct2);
1056                 nct2->ct_node = elt;
1057                 nct->ct_node = lru_elt;
1058
1059                 DLAddHead(cache->cc_lrulist, lru_elt);
1060                 DLAddHead(cache->cc_cache[hash], elt);
1061
1062                 /* ----------------
1063                  *      If we've exceeded the desired size of this cache,
1064                  *      throw away the least recently used entry.
1065                  * ----------------
1066                  */
1067                 if (++cache->cc_ntup > cache->cc_maxtup)
1068                 {
1069                         CatCTup    *ct;
1070
1071                         elt = DLGetTail(cache->cc_lrulist);
1072                         ct = (CatCTup *) DLE_VAL(elt);
1073
1074                         if (ct != nct)          /* shouldn't be possible, but be safe... */
1075                         {
1076                                 CACHE2_elog(DEBUG, "SearchSysCache(%s): Overflow, LRU removal",
1077                                                         RelationGetRelationName(relation));
1078
1079                                 CatCacheRemoveCTup(cache, elt);
1080                         }
1081                 }
1082
1083                 CACHE4_elog(DEBUG, "SearchSysCache(%s): Contains %d/%d tuples",
1084                                         RelationGetRelationName(relation),
1085                                         cache->cc_ntup, cache->cc_maxtup);
1086                 CACHE3_elog(DEBUG, "SearchSysCache(%s): put in bucket %d",
1087                                         RelationGetRelationName(relation), hash);
1088         }
1089
1090         /* ----------------
1091          *      close the relation, switch back to the original memory context
1092          *      and return the tuple we found (or NULL)
1093          * ----------------
1094          */
1095         heap_close(relation);
1096
1097         MemoryContextSwitchTo(oldcxt);
1098         return ntp;
1099 }
1100
1101 /* --------------------------------
1102  *      RelationInvalidateCatalogCacheTuple()
1103  *
1104  *      Invalidate a tuple from a specific relation.  This call determines the
1105  *      cache in question and calls CatalogCacheIdInvalidate().  It is -ok-
1106  *      if the relation cannot be found, it simply means this backend has yet
1107  *      to open it.
1108  * --------------------------------
1109  */
1110 void
1111 RelationInvalidateCatalogCacheTuple(Relation relation,
1112                                                                         HeapTuple tuple,
1113                                                           void (*function) (int, Index, ItemPointer))
1114 {
1115         struct catcache *ccp;
1116         MemoryContext oldcxt;
1117         Oid                     relationId;
1118
1119         /* ----------------
1120          *      sanity checks
1121          * ----------------
1122          */
1123         Assert(RelationIsValid(relation));
1124         Assert(HeapTupleIsValid(tuple));
1125         Assert(PointerIsValid(function));
1126         CACHE1_elog(DEBUG, "RelationInvalidateCatalogCacheTuple: called");
1127
1128         /* ----------------
1129          *      switch to the cache memory context
1130          * ----------------
1131          */
1132         if (!CacheCxt)
1133                 CacheCxt = CreateGlobalMemory("Cache");
1134         oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
1135
1136         /* ----------------
1137          *      for each cache
1138          *         if the cache contains tuples from the specified relation
1139          *                 call the invalidation function on the tuples
1140          *                 in the proper hash bucket
1141          * ----------------
1142          */
1143         relationId = RelationGetRelid(relation);
1144
1145         for (ccp = Caches; ccp; ccp = ccp->cc_next)
1146         {
1147                 if (relationId != ccp->relationId)
1148                         continue;
1149
1150 #ifdef NOT_USED
1151                 /* OPT inline simplification of CatalogCacheIdInvalidate */
1152                 if (!PointerIsValid(function))
1153                         function = CatalogCacheIdInvalidate;
1154 #endif
1155
1156                 (*function) (ccp->id,
1157                                  CatalogCacheComputeTupleHashIndex(ccp, relation, tuple),
1158                                          &tuple->t_self);
1159
1160                 heap_close(relation);
1161         }
1162
1163         /* ----------------
1164          *      return to the proper memory context
1165          * ----------------
1166          */
1167         MemoryContextSwitchTo(oldcxt);
1168
1169         /* sendpm('I', "Invalidated tuple"); */
1170 }