1 /*-------------------------------------------------------------------------
4 * System catalog cache for tuples matching a key.
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.38 1999/02/03 21:17:30 momjian Exp $
13 * XXX This needs to use exception.h to handle recovery when
14 * an abort occurs during DisableCache.
16 *-------------------------------------------------------------------------
20 #include "access/heapam.h"
21 #include "access/genam.h"
22 #include "utils/tqual.h"
23 #include "utils/builtins.h"
24 #include "utils/portal.h"
25 #include "utils/catcache.h"
26 #include "utils/elog.h"
27 #include "utils/palloc.h"
28 #include "utils/mcxt.h"
29 #include "utils/rel.h"
30 #include "storage/bufpage.h"
31 #include "access/valid.h"
32 #include "miscadmin.h"
33 #include "fmgr.h" /* for F_BOOLEQ, etc. DANGER */
34 #include "catalog/pg_type.h" /* for OID of int28 type */
35 #include "lib/dllist.h"
37 static void CatCacheRemoveCTup(CatCache *cache, Dlelem *e);
38 static Index CatalogCacheComputeHashIndex(struct catcache * cacheInP);
39 static Index CatalogCacheComputeTupleHashIndex(struct catcache * cacheInOutP,
40 Relation relation, HeapTuple tuple);
41 static void CatalogCacheInitializeCache(struct catcache * cache,
43 static long comphash(long l, char *v);
46 * variables, macros and other stuff
48 * note CCSIZE allocates 51 buckets .. one was already allocated in
49 * the catcache structure.
54 #define CACHE1_elog(a,b) elog(a,b)
55 #define CACHE2_elog(a,b,c) elog(a,b,c)
56 #define CACHE3_elog(a,b,c,d) elog(a,b,c,d)
57 #define CACHE4_elog(a,b,c,d,e) elog(a,b,c,d,e)
58 #define CACHE5_elog(a,b,c,d,e,f) elog(a,b,c,d,e,f)
59 #define CACHE6_elog(a,b,c,d,e,f,g) elog(a,b,c,d,e,f,g)
61 #define CACHE1_elog(a,b)
62 #define CACHE2_elog(a,b,c)
63 #define CACHE3_elog(a,b,c,d)
64 #define CACHE4_elog(a,b,c,d,e)
65 #define CACHE5_elog(a,b,c,d,e,f)
66 #define CACHE6_elog(a,b,c,d,e,f,g)
69 CatCache *Caches = NULL;
70 GlobalMemory CacheCxt;
72 static int DisableCache;
75 * EQPROC is used in CatalogCacheInitializeCache
76 * XXX this should be replaced by catalog lookups soon
79 static long eqproc[] = {
80 F_BOOLEQ, 0l, F_CHAREQ, F_NAMEEQ, 0l,
81 F_INT2EQ, F_KEYFIRSTEQ, F_INT4EQ, 0l, F_TEXTEQ,
82 F_OIDEQ, 0l, 0l, 0l, F_OID8EQ
85 #define EQPROC(SYSTEMTYPEOID) eqproc[(SYSTEMTYPEOID)-16]
87 /* ----------------------------------------------------------------
88 * internal support functions
89 * ----------------------------------------------------------------
91 /* --------------------------------
92 * CatalogCacheInitializeCache
93 * --------------------------------
96 #define CatalogCacheInitializeCache_DEBUG1 \
98 elog(DEBUG, "CatalogCacheInitializeCache: cache @%08lx", cache); \
100 elog(DEBUG, "CatalogCacheInitializeCache: called w/relation(inval)"); \
102 elog(DEBUG, "CatalogCacheInitializeCache: called w/relname %s", \
106 #define CatalogCacheInitializeCache_DEBUG2 \
108 if (cache->cc_key[i] > 0) { \
109 elog(DEBUG, "CatalogCacheInitializeCache: load %d/%d w/%d, %d", \
110 i+1, cache->cc_nkeys, cache->cc_key[i], \
111 relation->rd_att->attrs[cache->cc_key[i] - 1]->attlen); \
113 elog(DEBUG, "CatalogCacheInitializeCache: load %d/%d w/%d", \
114 i+1, cache->cc_nkeys, cache->cc_key[i]); \
119 #define CatalogCacheInitializeCache_DEBUG1
120 #define CatalogCacheInitializeCache_DEBUG2
124 CatalogCacheInitializeCache(struct catcache * cache,
127 MemoryContext oldcxt;
132 CatalogCacheInitializeCache_DEBUG1;
135 * first switch to the cache context so our allocations
136 * do not vanish at the end of a transaction
140 CacheCxt = CreateGlobalMemory("Cache");
141 oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
144 * If no relation was passed we must open it to get access to
145 * its fields. If one of the other caches has already opened
146 * it we use heap_open() instead of heap_openr()
149 if (!RelationIsValid(relation))
154 * scan the caches to see if any other cache has opened the relation
157 for (cp = Caches; cp; cp = cp->cc_next)
159 if (strncmp(cp->cc_relname, cache->cc_relname, NAMEDATALEN) == 0)
161 if (cp->relationId != InvalidOid)
167 * open the relation by name or by id
171 relation = heap_open(cp->relationId);
173 relation = heap_openr(cache->cc_relname);
179 * initialize the cache's relation id
182 Assert(RelationIsValid(relation));
183 cache->relationId = RelationGetRelid(relation);
184 tupdesc = cache->cc_tupdesc = RelationGetDescr(relation);
186 CACHE3_elog(DEBUG, "CatalogCacheInitializeCache: relid %d, %d keys",
187 cache->relationId, cache->cc_nkeys);
190 * initialize cache's key information
193 for (i = 0; i < cache->cc_nkeys; ++i)
195 CatalogCacheInitializeCache_DEBUG2;
197 if (cache->cc_key[i] > 0)
200 * Yoiks. The implementation of the hashing code and the
201 * implementation of int28's are at loggerheads. The right
202 * thing to do is to throw out the implementation of int28's
203 * altogether; until that happens, we do the right thing here
204 * to guarantee that the hash key generator doesn't try to
205 * dereference an int2 by mistake.
208 if (tupdesc->attrs[cache->cc_key[i] - 1]->atttypid == INT28OID)
209 cache->cc_klen[i] = sizeof(short);
211 cache->cc_klen[i] = tupdesc->attrs[cache->cc_key[i] - 1]->attlen;
213 cache->cc_skey[i].sk_procedure = EQPROC(tupdesc->attrs[cache->cc_key[i] - 1]->atttypid);
215 fmgr_info(cache->cc_skey[i].sk_procedure,
216 &cache->cc_skey[i].sk_func);
217 cache->cc_skey[i].sk_nargs = cache->cc_skey[i].sk_func.fn_nargs;
219 CACHE5_elog(DEBUG, "CatalogCacheInit %s %d %d %x",
220 &relation->rd_rel->relname,
222 tupdesc->attrs[cache->cc_key[i] - 1]->attlen,
228 * close the relation if we opened it
232 heap_close(relation);
235 * initialize index information for the cache. this
236 * should only be done once per cache.
239 if (cache->cc_indname != NULL && cache->indexId == InvalidOid)
241 if (RelationGetForm(relation)->relhasindex)
245 * If the index doesn't exist we are in trouble.
247 relation = index_openr(cache->cc_indname);
249 cache->indexId = RelationGetRelid(relation);
250 index_close(relation);
253 cache->cc_indname = NULL;
257 * return to the proper memory context
260 MemoryContextSwitchTo(oldcxt);
263 /* --------------------------------
266 * XXX temporary function
267 * --------------------------------
271 CatalogCacheSetId(CatCache *cacheInOutP, int id)
273 Assert(id == InvalidCatalogCacheId || id >= 0);
274 cacheInOutP->id = id;
281 * Compute a hash value, somehow.
283 * XXX explain algorithm here.
285 * l is length of the attribute value, v
286 * v is the attribute value ("Datum")
290 comphash(long l, char *v)
295 CACHE3_elog(DEBUG, "comphash (%d,%x)", l, v);
305 if (l == NAMEDATALEN)
309 * if it's a name, make sure that the values are null-padded.
311 * Note that this other fixed-length types can also have the same
312 * typelen so this may break them - XXX
326 /* --------------------------------
327 * CatalogCacheComputeHashIndex
328 * --------------------------------
331 CatalogCacheComputeHashIndex(struct catcache * cacheInP)
336 CACHE6_elog(DEBUG, "CatalogCacheComputeHashIndex %s %d %d %d %x",
337 cacheInP->cc_relname,
339 cacheInP->cc_klen[0],
340 cacheInP->cc_klen[1],
343 switch (cacheInP->cc_nkeys)
346 hashIndex ^= comphash(cacheInP->cc_klen[3],
347 (char *) cacheInP->cc_skey[3].sk_argument) << 9;
350 hashIndex ^= comphash(cacheInP->cc_klen[2],
351 (char *) cacheInP->cc_skey[2].sk_argument) << 6;
354 hashIndex ^= comphash(cacheInP->cc_klen[1],
355 (char *) cacheInP->cc_skey[1].sk_argument) << 3;
358 hashIndex ^= comphash(cacheInP->cc_klen[0],
359 (char *) cacheInP->cc_skey[0].sk_argument);
362 elog(FATAL, "CCComputeHashIndex: %d cc_nkeys", cacheInP->cc_nkeys);
365 hashIndex %= cacheInP->cc_size;
369 /* --------------------------------
370 * CatalogCacheComputeTupleHashIndex
371 * --------------------------------
374 CatalogCacheComputeTupleHashIndex(struct catcache * cacheInOutP,
380 if (cacheInOutP->relationId == InvalidOid)
381 CatalogCacheInitializeCache(cacheInOutP, relation);
382 switch (cacheInOutP->cc_nkeys)
385 cacheInOutP->cc_skey[3].sk_argument =
386 (cacheInOutP->cc_key[3] == ObjectIdAttributeNumber)
387 ? (Datum) tuple->t_data->t_oid
389 cacheInOutP->cc_key[3],
390 RelationGetDescr(relation),
395 cacheInOutP->cc_skey[2].sk_argument =
396 (cacheInOutP->cc_key[2] == ObjectIdAttributeNumber)
397 ? (Datum) tuple->t_data->t_oid
399 cacheInOutP->cc_key[2],
400 RelationGetDescr(relation),
405 cacheInOutP->cc_skey[1].sk_argument =
406 (cacheInOutP->cc_key[1] == ObjectIdAttributeNumber)
407 ? (Datum) tuple->t_data->t_oid
409 cacheInOutP->cc_key[1],
410 RelationGetDescr(relation),
415 cacheInOutP->cc_skey[0].sk_argument =
416 (cacheInOutP->cc_key[0] == ObjectIdAttributeNumber)
417 ? (Datum) tuple->t_data->t_oid
419 cacheInOutP->cc_key[0],
420 RelationGetDescr(relation),
425 elog(FATAL, "CCComputeTupleHashIndex: %d cc_nkeys",
426 cacheInOutP->cc_nkeys
431 return CatalogCacheComputeHashIndex(cacheInOutP);
434 /* --------------------------------
436 * --------------------------------
439 CatCacheRemoveCTup(CatCache *cache, Dlelem *elt)
446 ct = (CatCTup *) DLE_VAL(elt);
450 other_elt = ct->ct_node;
451 other_ct = (CatCTup *) DLE_VAL(other_elt);
453 DLFreeElem(other_elt);
461 /* --------------------------------
462 * CatalogCacheIdInvalidate()
464 * Invalidate a tuple given a cache id. In this case the id should always
465 * be found (whether the cache has opened its relation or not). Of course,
466 * if the cache has yet to open its relation, there will be no tuples so
468 * --------------------------------
471 CatalogCacheIdInvalidate(int cacheId, /* XXX */
478 MemoryContext oldcxt;
484 Assert(hashIndex < NCCBUCK);
485 Assert(ItemPointerIsValid(pointer));
486 CACHE1_elog(DEBUG, "CatalogCacheIdInvalidate: called");
489 * switch to the cache context for our memory allocations
493 CacheCxt = CreateGlobalMemory("Cache");
494 oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
497 * inspect every cache that could contain the tuple
500 for (ccp = Caches; ccp; ccp = ccp->cc_next)
502 if (cacheId != ccp->id)
505 * inspect the hash bucket until we find a match or exhaust
508 for (elt = DLGetHead(ccp->cc_cache[hashIndex]);
510 elt = DLGetSucc(elt))
512 ct = (CatCTup *) DLE_VAL(elt);
513 if (ItemPointerEquals(pointer, &ct->ct_tup->t_self))
518 * if we found a matching tuple, invalidate it.
524 CatCacheRemoveCTup(ccp, elt);
526 CACHE1_elog(DEBUG, "CatalogCacheIdInvalidate: invalidated");
529 if (cacheId != InvalidCatalogCacheId)
534 * return to the proper memory context
537 MemoryContextSwitchTo(oldcxt);
538 /* sendpm('I', "Invalidated tuple"); */
541 /* ----------------------------------------------------------------
545 * InitIndexedSysCache
548 * RelationInvalidateCatalogCacheTuple
549 * ----------------------------------------------------------------
551 /* --------------------------------
553 * --------------------------------
558 MemoryContext oldcxt;
559 struct catcache *cache;
565 CACHE1_elog(DEBUG, "ResetSystemCache called");
568 elog(ERROR, "ResetSystemCache: Called while cache disabled");
573 * first switch to the cache context so our allocations
574 * do not vanish at the end of a transaction
578 CacheCxt = CreateGlobalMemory("Cache");
580 oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
583 * here we purge the contents of all the caches
585 * for each system cache
586 * for each hash bucket
587 * for each tuple in hash bucket
591 for (cache = Caches; PointerIsValid(cache); cache = cache->cc_next)
595 for (hash = 0; hash < NCCBUCK; hash += 1)
600 for (elt = DLGetHead(cache->cc_cache[hash]); elt; elt = nextelt)
602 nextelt = DLGetSucc(elt);
603 CatCacheRemoveCTup(cache, elt);
604 if (cache->cc_ntup == -1)
605 elog(ERROR, "ResetSystemCache: cc_ntup<0 (software error)");
608 cache->cc_ntup = 0; /* in case of WARN error above */
611 CACHE1_elog(DEBUG, "end of ResetSystemCache call");
614 * back to the old context before we return...
617 MemoryContextSwitchTo(oldcxt);
620 /* --------------------------------
621 * SystemCacheRelationFlushed
623 * RelationFlushRelation() frees some information referenced in the
624 * cache structures. So we get informed when this is done and arrange
625 * for the next SearchSysCache() call that this information is setup
627 * --------------------------------
630 SystemCacheRelationFlushed(Oid relId)
632 struct catcache *cache;
634 for (cache = Caches; PointerIsValid(cache); cache = cache->cc_next)
636 if (cache->relationId == relId)
637 cache->relationId = InvalidOid;
641 /* --------------------------------
642 * InitIndexedSysCache
644 * This allocates and initializes a cache for a system catalog relation.
645 * Actually, the cache is only partially initialized to avoid opening the
646 * relation. The relation will be opened and the rest of the cache
647 * structure initialized on the first access.
648 * --------------------------------
651 #define InitSysCache_DEBUG1 \
653 elog(DEBUG, "InitSysCache: rid=%d id=%d nkeys=%d size=%d\n", \
654 cp->relationId, cp->id, cp->cc_nkeys, cp->cc_size); \
655 for (i = 0; i < nkeys; i += 1) \
657 elog(DEBUG, "InitSysCache: key=%d len=%d skey=[%d %d %d %d]\n", \
658 cp->cc_key[i], cp->cc_klen[i], \
659 cp->cc_skey[i].sk_flags, \
660 cp->cc_skey[i].sk_attno, \
661 cp->cc_skey[i].sk_procedure, \
662 cp->cc_skey[i].sk_argument); \
667 #define InitSysCache_DEBUG1
671 InitSysCache(char *relname,
676 HeapTuple (*iScanfuncP) ())
680 MemoryContext oldcxt;
684 indname = (iname) ? iname : NULL;
687 * first switch to the cache context so our allocations
688 * do not vanish at the end of a transaction
692 CacheCxt = CreateGlobalMemory("Cache");
694 oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
697 * allocate a new cache structure
700 cp = (CatCache *) palloc(sizeof(CatCache));
701 MemSet((char *) cp, 0, sizeof(CatCache));
704 * initialize the cache buckets (each bucket is a list header)
705 * and the LRU tuple list
711 * We can only do this optimization because the number of hash
712 * buckets never changes. Without it, we call malloc() too much.
713 * We could move this to dllist.c, but the way we do this is not
714 * dynamic/portabl, so why allow other routines to use it.
716 Dllist *cache_begin = malloc((NCCBUCK + 1) * sizeof(Dllist));
718 for (i = 0; i <= NCCBUCK; ++i)
720 cp->cc_cache[i] = &cache_begin[i];
721 cp->cc_cache[i]->dll_head = 0;
722 cp->cc_cache[i]->dll_tail = 0;
726 cp->cc_lrulist = DLNewList();
729 * Caches is the pointer to the head of the list of all the
730 * system caches. here we add the new cache to the top of the list.
733 cp->cc_next = Caches; /* list of caches (single link) */
737 * initialize the cache's relation information for the relation
738 * corresponding to this cache and initialize some of the the new
739 * cache's other internal fields.
742 cp->relationId = InvalidOid;
743 cp->indexId = InvalidOid;
744 cp->cc_relname = relname;
745 cp->cc_indname = indname;
746 cp->cc_tupdesc = (TupleDesc) NULL;
748 cp->cc_maxtup = MAXTUP;
749 cp->cc_size = NCCBUCK;
750 cp->cc_nkeys = nkeys;
751 cp->cc_iscanfunc = iScanfuncP;
754 * initialize the cache's key information
757 for (i = 0; i < nkeys; ++i)
759 cp->cc_key[i] = key[i];
761 elog(FATAL, "InitSysCache: called with 0 key[%d]", i);
764 if (key[i] != ObjectIdAttributeNumber)
765 elog(FATAL, "InitSysCache: called with %d key[%d]", key[i], i);
768 cp->cc_klen[i] = sizeof(Oid);
771 * ScanKeyEntryData and struct skey are equivalent. It
772 * looks like a move was made to obsolete struct skey, but
773 * it didn't reach this file. Someday we should clean up
774 * this code and consolidate to ScanKeyEntry - mer 10 Nov
777 ScanKeyEntryInitialize(&cp->cc_skey[i],
780 (RegProcedure) F_OIDEQ,
786 cp->cc_skey[i].sk_attno = key[i];
790 * all done. new cache is initialized. print some debugging
791 * information, if appropriate.
797 * back to the old context before we return...
800 MemoryContextSwitchTo(oldcxt);
805 /* --------------------------------
808 * This call searches a system cache for a tuple, opening the relation
809 * if necessary (the first access to a particular cache).
810 * --------------------------------
813 SearchSysCache(struct catcache * cache,
827 MemoryContext oldcxt;
833 if (cache->relationId == InvalidOid)
834 CatalogCacheInitializeCache(cache, NULL);
837 * initialize the search key information
840 cache->cc_skey[0].sk_argument = v1;
841 cache->cc_skey[1].sk_argument = v2;
842 cache->cc_skey[2].sk_argument = v3;
843 cache->cc_skey[3].sk_argument = v4;
846 * find the hash bucket in which to look for the tuple
849 hash = CatalogCacheComputeHashIndex(cache);
852 * scan the hash bucket until we find a match or exhaust our tuples
855 for (elt = DLGetHead(cache->cc_cache[hash]);
857 elt = DLGetSucc(elt))
861 ct = (CatCTup *) DLE_VAL(elt);
863 * see if the cached tuple matches our key.
864 * (should we be worried about time ranges? -cim 10/2/90)
867 HeapKeyTest(ct->ct_tup,
877 * if we found a tuple in the cache, move it to the top of the
878 * lru list, and return it.
885 old_lru_elt = ((CatCTup *) DLE_VAL(elt))->ct_node;
886 DLRemove(old_lru_elt);
887 DLAddHead(cache->cc_lrulist, old_lru_elt);
890 relation = heap_open(cache->relationId);
891 CACHE3_elog(DEBUG, "SearchSysCache(%s): found in bucket %d",
892 RelationGetRelationName(relation), hash);
893 heap_close(relation);
894 #endif /* CACHEDEBUG */
900 * Tuple was not found in cache, so we have to try and
901 * retrieve it directly from the relation. If it's found,
902 * we add it to the cache. We must avoid recursion here,
903 * so we disable cache operations. If operations are
904 * currently disabled and we couldn't find the requested item
905 * in the cache, then this may be a recursive request, and we
906 * abort with an error.
912 elog(ERROR, "SearchSysCache: Called while cache disabled");
913 return (HeapTuple) NULL;
917 * open the relation associated with the cache
920 relation = heap_open(cache->relationId);
921 CACHE2_elog(DEBUG, "SearchSysCache(%s)",
922 RelationGetRelationName(relation));
925 * DisableCache and then switch to the cache memory context.
931 CacheCxt = CreateGlobalMemory("Cache");
933 oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
936 * Scan the relation to find the tuple. If there's an index, and
937 * if this isn't bootstrap (initdb) time, use the index.
940 CACHE2_elog(DEBUG, "SearchSysCache: performing scan (override==%d)",
943 if ((RelationGetForm(relation))->relhasindex
944 && !IsBootstrapProcessingMode())
947 * Switch back to old memory context so memory not freed
948 * in the scan function will go away at transaction end.
952 MemoryContextSwitchTo(oldcxt);
953 Assert(cache->cc_iscanfunc);
954 switch (cache->cc_nkeys)
957 ntp = cache->cc_iscanfunc(relation, v1, v2, v3, v4);
960 ntp = cache->cc_iscanfunc(relation, v1, v2, v3);
963 ntp = cache->cc_iscanfunc(relation, v1, v2);
966 ntp = cache->cc_iscanfunc(relation, v1);
970 * Back to Cache context. If we got a tuple copy it
975 MemoryContextSwitchTo((MemoryContext) CacheCxt);
976 if (HeapTupleIsValid(ntp))
977 ntp = heap_copytuple(ntp);
984 * As above do the lookup in the callers memory
989 MemoryContextSwitchTo(oldcxt);
991 sd = heap_beginscan(relation, 0, SnapshotNow,
992 cache->cc_nkeys, cache->cc_skey);
994 ntp = heap_getnext(sd, 0);
996 MemoryContextSwitchTo((MemoryContext) CacheCxt);
998 if (HeapTupleIsValid(ntp))
1000 CACHE1_elog(DEBUG, "SearchSysCache: found tuple");
1001 ntp = heap_copytuple(ntp);
1004 MemoryContextSwitchTo(oldcxt);
1008 MemoryContextSwitchTo((MemoryContext) CacheCxt);
1014 * scan is complete. if tup is valid, we copy it and add the copy to
1018 if (HeapTupleIsValid(ntp))
1021 * allocate a new cache tuple holder, store the pointer
1022 * to the heap tuple there and initialize the list pointers.
1028 * this is a little cumbersome here because we want the Dlelem's
1029 * in both doubly linked lists to point to one another. That makes
1030 * it easier to remove something from both the cache bucket and
1031 * the lru list at the same time
1033 nct = (CatCTup *) malloc(sizeof(CatCTup));
1035 elt = DLNewElem(nct);
1036 nct2 = (CatCTup *) malloc(sizeof(CatCTup));
1038 lru_elt = DLNewElem(nct2);
1039 nct2->ct_node = elt;
1040 nct->ct_node = lru_elt;
1042 DLAddHead(cache->cc_lrulist, lru_elt);
1043 DLAddHead(cache->cc_cache[hash], elt);
1046 * deal with hash bucket overflow
1049 if (++cache->cc_ntup > cache->cc_maxtup)
1053 elt = DLGetTail(cache->cc_lrulist);
1054 ct = (CatCTup *) DLE_VAL(elt);
1058 CACHE2_elog(DEBUG, "SearchSysCache(%s): Overflow, LRU removal",
1059 RelationGetRelationName(relation));
1061 CatCacheRemoveCTup(cache, elt);
1066 CACHE4_elog(DEBUG, "SearchSysCache(%s): Contains %d/%d tuples",
1067 RelationGetRelationName(relation),
1068 cache->cc_ntup, cache->cc_maxtup);
1069 CACHE3_elog(DEBUG, "SearchSysCache(%s): put in bucket %d",
1070 RelationGetRelationName(relation), hash);
1074 * close the relation, switch back to the original memory context
1075 * and return the tuple we found (or NULL)
1078 heap_close(relation);
1080 MemoryContextSwitchTo(oldcxt);
1084 /* --------------------------------
1085 * RelationInvalidateCatalogCacheTuple()
1087 * Invalidate a tuple from a specific relation. This call determines the
1088 * cache in question and calls CatalogCacheIdInvalidate(). It is -ok-
1089 * if the relation cannot be found, it simply means this backend has yet
1091 * --------------------------------
1094 RelationInvalidateCatalogCacheTuple(Relation relation,
1096 void (*function) (int, Index, ItemPointer))
1098 struct catcache *ccp;
1099 MemoryContext oldcxt;
1106 Assert(RelationIsValid(relation));
1107 Assert(HeapTupleIsValid(tuple));
1108 Assert(PointerIsValid(function));
1109 CACHE1_elog(DEBUG, "RelationInvalidateCatalogCacheTuple: called");
1112 * switch to the cache memory context
1116 CacheCxt = CreateGlobalMemory("Cache");
1117 oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
1121 * if the cache contains tuples from the specified relation
1122 * call the invalidation function on the tuples
1123 * in the proper hash bucket
1126 relationId = RelationGetRelid(relation);
1128 for (ccp = Caches; ccp; ccp = ccp->cc_next)
1130 if (relationId != ccp->relationId)
1134 /* OPT inline simplification of CatalogCacheIdInvalidate */
1135 if (!PointerIsValid(function))
1136 function = CatalogCacheIdInvalidate;
1139 (*function) (ccp->id,
1140 CatalogCacheComputeTupleHashIndex(ccp, relation, tuple),
1143 heap_close(relation);
1147 * return to the proper memory context
1150 MemoryContextSwitchTo(oldcxt);
1152 /* sendpm('I', "Invalidated tuple"); */