OSDN Git Service

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