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