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