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