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