/* * RelfilenodeMapInvalidateCallback * Initialize cache, either on first use or after a reset. */ static void InitializeRelfilenodeMap(void) { HASHCTL ctl; static bool initial_init_done = false; int i; /* Make sure we've initialized CacheMemoryContext. */ if (CacheMemoryContext == NULL) CreateCacheMemoryContext(); /* Initialize the hash table. */ MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(RelfilenodeMapKey); ctl.entrysize = sizeof(RelfilenodeMapEntry); ctl.hash = tag_hash; ctl.hcxt = CacheMemoryContext; RelfilenodeMapHash = hash_create("RelfilenodeMap cache", 1024, &ctl, HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); /* * For complete resets we simply delete the entire hash, but there's no * need to do the other stuff multiple times. Especially the initialization * of the relcche invalidation should only be done once. */ if (initial_init_done) return; /* build skey */ MemSet(&relfilenode_skey, 0, sizeof(relfilenode_skey)); for (i = 0; i < 2; i++) { fmgr_info_cxt(F_OIDEQ, &relfilenode_skey[i].sk_func, CacheMemoryContext); relfilenode_skey[i].sk_strategy = BTEqualStrategyNumber; relfilenode_skey[i].sk_subtype = InvalidOid; relfilenode_skey[i].sk_collation = InvalidOid; } relfilenode_skey[0].sk_attno = Anum_pg_class_reltablespace; relfilenode_skey[1].sk_attno = Anum_pg_class_relfilenode; /* Watch for invalidation events. */ CacheRegisterRelcacheCallback(RelfilenodeMapInvalidateCallback, (Datum) 0); initial_init_done = true; }
/* * RelfilenodeMapInvalidateCallback * Initialize cache, either on first use or after a reset. */ static void InitializeRelfilenodeMap(void) { HASHCTL ctl; int i; /* Make sure we've initialized CacheMemoryContext. */ if (CacheMemoryContext == NULL) CreateCacheMemoryContext(); /* build skey */ MemSet(&relfilenode_skey, 0, sizeof(relfilenode_skey)); for (i = 0; i < 2; i++) { fmgr_info_cxt(F_OIDEQ, &relfilenode_skey[i].sk_func, CacheMemoryContext); relfilenode_skey[i].sk_strategy = BTEqualStrategyNumber; relfilenode_skey[i].sk_subtype = InvalidOid; relfilenode_skey[i].sk_collation = InvalidOid; } relfilenode_skey[0].sk_attno = Anum_pg_class_reltablespace; relfilenode_skey[1].sk_attno = Anum_pg_class_relfilenode; /* Initialize the hash table. */ MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(RelfilenodeMapKey); ctl.entrysize = sizeof(RelfilenodeMapEntry); ctl.hash = tag_hash; ctl.hcxt = CacheMemoryContext; /* * Only create the RelfilenodeMapHash now, so we don't end up partially * initialized when fmgr_info_cxt() above ERRORs out with an out of memory * error. */ RelfilenodeMapHash = hash_create("RelfilenodeMap cache", 1024, &ctl, HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); /* Watch for invalidation events. */ CacheRegisterRelcacheCallback(RelfilenodeMapInvalidateCallback, (Datum) 0); }
/* * Fetch dictionary cache entry */ TSDictionaryCacheEntry * lookup_ts_dictionary_cache(Oid dictId) { TSDictionaryCacheEntry *entry; if (TSDictionaryCacheHash == NULL) { /* First time through: initialize the hash table */ HASHCTL ctl; MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(Oid); ctl.entrysize = sizeof(TSDictionaryCacheEntry); ctl.hash = oid_hash; TSDictionaryCacheHash = hash_create("Tsearch dictionary cache", 8, &ctl, HASH_ELEM | HASH_FUNCTION); /* Flush cache on pg_ts_dict and pg_ts_template changes */ CacheRegisterSyscacheCallback(TSDICTOID, InvalidateTSCacheCallBack, PointerGetDatum(TSDictionaryCacheHash)); CacheRegisterSyscacheCallback(TSTEMPLATEOID, InvalidateTSCacheCallBack, PointerGetDatum(TSDictionaryCacheHash)); /* Also make sure CacheMemoryContext exists */ if (!CacheMemoryContext) CreateCacheMemoryContext(); } /* Check single-entry cache */ if (lastUsedDictionary && lastUsedDictionary->dictId == dictId && lastUsedDictionary->isvalid) return lastUsedDictionary; /* Try to look up an existing entry */ entry = (TSDictionaryCacheEntry *) hash_search(TSDictionaryCacheHash, (void *) &dictId, HASH_FIND, NULL); if (entry == NULL || !entry->isvalid) { /* * If we didn't find one, we want to make one. But first look up the * object to be sure the OID is real. */ HeapTuple tpdict, tptmpl; Form_pg_ts_dict dict; Form_pg_ts_template template;
/* * Initialize config cache and prepare callbacks. This is split out of * lookup_ts_config_cache because we need to activate the callback before * caching TSCurrentConfigCache, too. */ static void init_ts_config_cache(void) { HASHCTL ctl; MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(Oid); ctl.entrysize = sizeof(TSConfigCacheEntry); TSConfigCacheHash = hash_create("Tsearch configuration cache", 16, &ctl, HASH_ELEM | HASH_BLOBS); /* Flush cache on pg_ts_config and pg_ts_config_map changes */ CacheRegisterSyscacheCallback(TSCONFIGOID, InvalidateTSCacheCallBack, PointerGetDatum(TSConfigCacheHash)); CacheRegisterSyscacheCallback(TSCONFIGMAP, InvalidateTSCacheCallBack, PointerGetDatum(TSConfigCacheHash)); /* Also make sure CacheMemoryContext exists */ if (!CacheMemoryContext) CreateCacheMemoryContext(); }
/* * InitializeTableSpaceCache * Initialize the tablespace cache. */ static void InitializeTableSpaceCache(void) { HASHCTL ctl; /* Initialize the hash table. */ MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(Oid); ctl.entrysize = sizeof(TableSpaceCacheEntry); TableSpaceCacheHash = hash_create("TableSpace cache", 16, &ctl, HASH_ELEM | HASH_BLOBS); /* Make sure we've initialized CacheMemoryContext. */ if (!CacheMemoryContext) CreateCacheMemoryContext(); /* Watch for invalidation events. */ CacheRegisterSyscacheCallback(TABLESPACEOID, InvalidateTableSpaceCacheCallback, (Datum) 0); }
/* * find_oper_cache_entry * * Look for a cache entry matching the given key. If found, return the * contained operator OID, else return InvalidOid. */ static Oid find_oper_cache_entry(OprCacheKey *key) { OprCacheEntry *oprentry; if (OprCacheHash == NULL) { /* First time through: initialize the hash table */ HASHCTL ctl; if (!CacheMemoryContext) CreateCacheMemoryContext(); MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(OprCacheKey); ctl.entrysize = sizeof(OprCacheEntry); ctl.hash = tag_hash; OprCacheHash = hash_create("Operator lookup cache", 256, &ctl, HASH_ELEM | HASH_FUNCTION); /* Arrange to flush cache on pg_operator and pg_cast changes */ CacheRegisterSyscacheCallback(OPERNAMENSP, InvalidateOprCacheCallBack, (Datum) 0); CacheRegisterSyscacheCallback(CASTSOURCETARGET, InvalidateOprCacheCallBack, (Datum) 0); } /* Look for an existing entry */ oprentry = (OprCacheEntry *) hash_search(OprCacheHash, (void *) key, HASH_FIND, NULL); if (oprentry == NULL) return InvalidOid; return oprentry->opr_oid; }
/* * InitializeAttoptCache * Initialize the tablespace cache. */ static void InitializeAttoptCache(void) { HASHCTL ctl; /* Initialize the hash table. */ MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(AttoptCacheKey); ctl.entrysize = sizeof(AttoptCacheEntry); ctl.hash = tag_hash; AttoptCacheHash = hash_create("Attopt cache", 256, &ctl, HASH_ELEM | HASH_FUNCTION); /* Make sure we've initialized CacheMemoryContext. */ if (!CacheMemoryContext) CreateCacheMemoryContext(); /* Watch for invalidation events. */ CacheRegisterSyscacheCallback(ATTNUM, InvalidateAttoptCacheCallback, (Datum) 0); }
/* * Fetch parser cache entry */ TSParserCacheEntry * lookup_ts_parser_cache(Oid prsId) { TSParserCacheEntry *entry; if (TSParserCacheHash == NULL) { /* First time through: initialize the hash table */ HASHCTL ctl; MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(Oid); ctl.entrysize = sizeof(TSParserCacheEntry); ctl.hash = oid_hash; TSParserCacheHash = hash_create("Tsearch parser cache", 4, &ctl, HASH_ELEM | HASH_FUNCTION); /* Flush cache on pg_ts_parser changes */ CacheRegisterSyscacheCallback(TSPARSEROID, InvalidateTSCacheCallBack, PointerGetDatum(TSParserCacheHash)); /* Also make sure CacheMemoryContext exists */ if (!CacheMemoryContext) CreateCacheMemoryContext(); } /* Check single-entry cache */ if (lastUsedParser && lastUsedParser->prsId == prsId && lastUsedParser->isvalid) return lastUsedParser; /* Try to look up an existing entry */ entry = (TSParserCacheEntry *) hash_search(TSParserCacheHash, (void *) &prsId, HASH_FIND, NULL); if (entry == NULL || !entry->isvalid) { /* * If we didn't find one, we want to make one. But first look up the * object to be sure the OID is real. */ HeapTuple tp; Form_pg_ts_parser prs; tp = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for text search parser %u", prsId); prs = (Form_pg_ts_parser) GETSTRUCT(tp); /* * Sanity checks */ if (!OidIsValid(prs->prsstart)) elog(ERROR, "text search parser %u has no prsstart method", prsId); if (!OidIsValid(prs->prstoken)) elog(ERROR, "text search parser %u has no prstoken method", prsId); if (!OidIsValid(prs->prsend)) elog(ERROR, "text search parser %u has no prsend method", prsId); if (entry == NULL) { bool found; /* Now make the cache entry */ entry = (TSParserCacheEntry *) hash_search(TSParserCacheHash, (void *) &prsId, HASH_ENTER, &found); Assert(!found); /* it wasn't there a moment ago */ } MemSet(entry, 0, sizeof(TSParserCacheEntry)); entry->prsId = prsId; entry->startOid = prs->prsstart; entry->tokenOid = prs->prstoken; entry->endOid = prs->prsend; entry->headlineOid = prs->prsheadline; entry->lextypeOid = prs->prslextype; ReleaseSysCache(tp); fmgr_info_cxt(entry->startOid, &entry->prsstart, CacheMemoryContext); fmgr_info_cxt(entry->tokenOid, &entry->prstoken, CacheMemoryContext); fmgr_info_cxt(entry->endOid, &entry->prsend, CacheMemoryContext); if (OidIsValid(entry->headlineOid)) fmgr_info_cxt(entry->headlineOid, &entry->prsheadline, CacheMemoryContext); entry->isvalid = true; } lastUsedParser = entry; return entry; }
CatCache * InitCatCache(int id, Oid reloid, Oid indexoid, int nkeys, const int *key, int nbuckets) { CatCache *cp; MemoryContext oldcxt; int i; /* * nbuckets is the number of hash buckets to use in this catcache. * Currently we just use a hard-wired estimate of an appropriate size for * each cache; maybe later make them dynamically resizable? * * nbuckets must be a power of two. We check this via Assert rather than * a full runtime check because the values will be coming from constant * tables. * * If you're confused by the power-of-two check, see comments in * bitmapset.c for an explanation. */ Assert(nbuckets > 0 && (nbuckets & -nbuckets) == nbuckets); /* * first switch to the cache context so our allocations do not vanish at * the end of a transaction */ if (!CacheMemoryContext) CreateCacheMemoryContext(); oldcxt = MemoryContextSwitchTo(CacheMemoryContext); /* * if first time through, initialize the cache group header */ if (CacheHdr == NULL) { CacheHdr = (CatCacheHeader *) palloc(sizeof(CatCacheHeader)); CacheHdr->ch_caches = NULL; CacheHdr->ch_ntup = 0; #ifdef CATCACHE_STATS /* set up to dump stats at backend exit */ on_proc_exit(CatCachePrintStats, 0); #endif } /* * allocate a new cache structure * * Note: we assume zeroing initializes the Dllist headers correctly */ cp = (CatCache *) palloc0(sizeof(CatCache) + nbuckets * sizeof(Dllist)); /* * initialize the cache's relation information for the relation * corresponding to this cache, and initialize some of the new cache's * other internal fields. But don't open the relation yet. */ cp->id = id; cp->cc_relname = "(not known yet)"; cp->cc_reloid = reloid; cp->cc_indexoid = indexoid; cp->cc_relisshared = false; /* temporary */ cp->cc_tupdesc = (TupleDesc) NULL; cp->cc_ntup = 0; cp->cc_nbuckets = nbuckets; cp->cc_nkeys = nkeys; for (i = 0; i < nkeys; ++i) cp->cc_key[i] = key[i]; /* * new cache is initialized as far as we can go for now. print some * debugging information, if appropriate. */ InitCatCache_DEBUG2; /* * add completed cache to top of group header's list */ cp->cc_next = CacheHdr->ch_caches; CacheHdr->ch_caches = cp; /* * back to the old context before we return... */ MemoryContextSwitchTo(oldcxt); return cp; }
/* * Rebuild the event trigger cache. */ static void BuildEventTriggerCache(void) { HASHCTL ctl; HTAB *cache; MemoryContext oldcontext; Relation rel; Relation irel; SysScanDesc scan; if (EventTriggerCacheContext != NULL) { /* * Free up any memory already allocated in EventTriggerCacheContext. * This can happen either because a previous rebuild failed, or * because an invalidation happened before the rebuild was complete. */ MemoryContextResetAndDeleteChildren(EventTriggerCacheContext); } else { /* * This is our first time attempting to build the cache, so we need * to set up the memory context and register a syscache callback to * capture future invalidation events. */ if (CacheMemoryContext == NULL) CreateCacheMemoryContext(); EventTriggerCacheContext = AllocSetContextCreate(CacheMemoryContext, "EventTriggerCache", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); CacheRegisterSyscacheCallback(EVENTTRIGGEROID, InvalidateEventCacheCallback, (Datum) 0); } /* Switch to correct memory context. */ oldcontext = MemoryContextSwitchTo(EventTriggerCacheContext); /* Prevent the memory context from being nuked while we're rebuilding. */ EventTriggerCacheState = ETCS_REBUILD_STARTED; /* Create new hash table. */ MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(EventTriggerEvent); ctl.entrysize = sizeof(EventTriggerCacheEntry); ctl.hash = tag_hash; ctl.hcxt = EventTriggerCacheContext; cache = hash_create("Event Trigger Cache", 32, &ctl, HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); /* * Prepare to scan pg_event_trigger in name order. We use an MVCC * snapshot to avoid getting inconsistent results if the table is * being concurrently updated. */ rel = relation_open(EventTriggerRelationId, AccessShareLock); irel = index_open(EventTriggerNameIndexId, AccessShareLock); scan = systable_beginscan_ordered(rel, irel, GetLatestSnapshot(), 0, NULL); /* * Build a cache item for each pg_event_trigger tuple, and append each * one to the appropriate cache entry. */ for (;;) { HeapTuple tup; Form_pg_event_trigger form; char *evtevent; EventTriggerEvent event; EventTriggerCacheItem *item; Datum evttags; bool evttags_isnull; EventTriggerCacheEntry *entry; bool found; /* Get next tuple. */ tup = systable_getnext_ordered(scan, ForwardScanDirection); if (!HeapTupleIsValid(tup)) break; /* Skip trigger if disabled. */ form = (Form_pg_event_trigger) GETSTRUCT(tup); if (form->evtenabled == TRIGGER_DISABLED) continue; /* Decode event name. */ evtevent = NameStr(form->evtevent); if (strcmp(evtevent, "ddl_command_start") == 0) event = EVT_DDLCommandStart; else continue; /* Allocate new cache item. */ item = palloc0(sizeof(EventTriggerCacheItem)); item->fnoid = form->evtfoid; item->enabled = form->evtenabled; /* Decode and sort tags array. */ evttags = heap_getattr(tup, Anum_pg_event_trigger_evttags, RelationGetDescr(rel), &evttags_isnull); if (!evttags_isnull) { item->ntags = DecodeTextArrayToCString(evttags, &item->tag); qsort(item->tag, item->ntags, sizeof(char *), pg_qsort_strcmp); } /* Add to cache entry. */ entry = hash_search(cache, &event, HASH_ENTER, &found); if (found) entry->triggerlist = lappend(entry->triggerlist, item); else entry->triggerlist = list_make1(item); } /* Done with pg_event_trigger scan. */ systable_endscan_ordered(scan); index_close(irel, AccessShareLock); relation_close(rel, AccessShareLock); /* Restore previous memory context. */ MemoryContextSwitchTo(oldcontext); /* Install new cache. */ EventTriggerCache = cache; /* * If the cache has been invalidated since we entered this routine, we * still use and return the cache we just finished constructing, to avoid * infinite loops, but we leave the cache marked stale so that we'll * rebuild it again on next access. Otherwise, we mark the cache valid. */ if (EventTriggerCacheState == ETCS_REBUILD_STARTED) EventTriggerCacheState = ETCS_VALID; }
/* * Fetch dictionary cache entry */ TSDictionaryCacheEntry * lookup_ts_dictionary_cache(Oid dictId) { TSDictionaryCacheEntry *entry; if (TSDictionaryCacheHash == NULL) { /* First time through: initialize the hash table */ HASHCTL ctl; MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(Oid); ctl.entrysize = sizeof(TSDictionaryCacheEntry); TSDictionaryCacheHash = hash_create("Tsearch dictionary cache", 8, &ctl, HASH_ELEM | HASH_BLOBS); /* Flush cache on pg_ts_dict and pg_ts_template changes */ CacheRegisterSyscacheCallback(TSDICTOID, InvalidateTSCacheCallBack, PointerGetDatum(TSDictionaryCacheHash)); CacheRegisterSyscacheCallback(TSTEMPLATEOID, InvalidateTSCacheCallBack, PointerGetDatum(TSDictionaryCacheHash)); /* Also make sure CacheMemoryContext exists */ if (!CacheMemoryContext) CreateCacheMemoryContext(); } /* Check single-entry cache */ if (lastUsedDictionary && lastUsedDictionary->dictId == dictId && lastUsedDictionary->isvalid) return lastUsedDictionary; /* Try to look up an existing entry */ entry = (TSDictionaryCacheEntry *) hash_search(TSDictionaryCacheHash, (void *) &dictId, HASH_FIND, NULL); if (entry == NULL || !entry->isvalid) { /* * If we didn't find one, we want to make one. But first look up the * object to be sure the OID is real. */ HeapTuple tpdict, tptmpl; Form_pg_ts_dict dict; Form_pg_ts_template ctemplate; MemoryContext saveCtx; tpdict = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId)); if (!HeapTupleIsValid(tpdict)) elog(ERROR, "cache lookup failed for text search dictionary %u", dictId); dict = (Form_pg_ts_dict) GETSTRUCT(tpdict); /* * Sanity checks */ if (!OidIsValid(dict->dicttemplate)) elog(ERROR, "text search dictionary %u has no ctemplate", dictId); /* * Retrieve dictionary's ctemplate */ tptmpl = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(dict->dicttemplate)); if (!HeapTupleIsValid(tptmpl)) elog(ERROR, "cache lookup failed for text search ctemplate %u", dict->dicttemplate); ctemplate = (Form_pg_ts_template) GETSTRUCT(tptmpl); /* * Sanity checks */ if (!OidIsValid(ctemplate->tmpllexize)) elog(ERROR, "text search ctemplate %u has no lexize method", ctemplate->tmpllexize); if (entry == NULL) { bool found; /* Now make the cache entry */ entry = (TSDictionaryCacheEntry *) hash_search(TSDictionaryCacheHash, (void *) &dictId, HASH_ENTER, &found); Assert(!found); /* it wasn't there a moment ago */ /* Create private___ memory context the first time through */ saveCtx = AllocSetContextCreate(CacheMemoryContext, NameStr(dict->dictname), ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE); } else { /* Clear the existing entry's private___ context */ saveCtx = entry->dictCtx; MemoryContextResetAndDeleteChildren(saveCtx); } MemSet(entry, 0, sizeof(TSDictionaryCacheEntry)); entry->dictId = dictId; entry->dictCtx = saveCtx; entry->lexizeOid = ctemplate->tmpllexize; if (OidIsValid(ctemplate->tmplinit)) { List *dictoptions; Datum opt; bool isnull; MemoryContext oldcontext; /* * Init method runs in dictionary's private___ memory context, and we * make sure the options are stored there too */ oldcontext = MemoryContextSwitchTo(entry->dictCtx); opt = SysCacheGetAttr(TSDICTOID, tpdict, Anum_pg_ts_dict_dictinitoption, &isnull); if (isnull) dictoptions = NIL; else dictoptions = deserialize_deflist(opt); entry->dictData = DatumGetPointer(OidFunctionCall1(ctemplate->tmplinit, PointerGetDatum(dictoptions))); MemoryContextSwitchTo(oldcontext); } ReleaseSysCache(tptmpl); ReleaseSysCache(tpdict); fmgr_info_cxt(entry->lexizeOid, &entry->lexize, entry->dictCtx); entry->isvalid = true; } lastUsedDictionary = entry; return entry; }
/* * Rebuild the event trigger cache. */ static void BuildEventTriggerCache(void) { HASHCTL ctl; HTAB *cache; MemoryContext oldcontext; Relation rel; Relation irel; SysScanDesc scan; if (EventTriggerCacheContext != NULL) { /* * The cache has been previously built, and subsequently invalidated, * and now we're trying to rebuild it. Normally, there won't be * anything in the context at this point, because the invalidation * will have already reset it. But if the previous attempt to rebuild * the cache failed, then there might be some junk lying around * that we want to reclaim. */ MemoryContextReset(EventTriggerCacheContext); } else { /* * This is our first time attempting to build the cache, so we need * to set up the memory context and register a syscache callback to * capture future invalidation events. */ if (CacheMemoryContext == NULL) CreateCacheMemoryContext(); EventTriggerCacheContext = AllocSetContextCreate(CacheMemoryContext, "EventTriggerCache", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); CacheRegisterSyscacheCallback(EVENTTRIGGEROID, InvalidateEventCacheCallback, (Datum) 0); } /* Switch to correct memory context. */ oldcontext = MemoryContextSwitchTo(EventTriggerCacheContext); /* * Create a new hash table, but don't assign it to the global variable * until it accurately represents the state of the catalogs, so that * if we fail midway through this we won't end up with incorrect cache * contents. */ MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(EventTriggerEvent); ctl.entrysize = sizeof(EventTriggerCacheEntry); ctl.hash = tag_hash; cache = hash_create("Event Trigger Cache", 32, &ctl, HASH_ELEM | HASH_FUNCTION); /* * Prepare to scan pg_event_trigger in name order. We use an MVCC * snapshot to avoid getting inconsistent results if the table is * being concurrently updated. */ rel = relation_open(EventTriggerRelationId, AccessShareLock); irel = index_open(EventTriggerNameIndexId, AccessShareLock); scan = systable_beginscan_ordered(rel, irel, GetLatestSnapshot(), 0, NULL); /* * Build a cache item for each pg_event_trigger tuple, and append each * one to the appropriate cache entry. */ for (;;) { HeapTuple tup; Form_pg_event_trigger form; char *evtevent; EventTriggerEvent event; EventTriggerCacheItem *item; Datum evttags; bool evttags_isnull; EventTriggerCacheEntry *entry; bool found; /* Get next tuple. */ tup = systable_getnext_ordered(scan, ForwardScanDirection); if (!HeapTupleIsValid(tup)) break; /* Skip trigger if disabled. */ form = (Form_pg_event_trigger) GETSTRUCT(tup); if (form->evtenabled == TRIGGER_DISABLED) continue; /* Decode event name. */ evtevent = NameStr(form->evtevent); if (strcmp(evtevent, "ddl_command_start") == 0) event = EVT_DDLCommandStart; else continue; /* Allocate new cache item. */ item = palloc0(sizeof(EventTriggerCacheItem)); item->fnoid = form->evtfoid; item->enabled = form->evtenabled; /* Decode and sort tags array. */ evttags = heap_getattr(tup, Anum_pg_event_trigger_evttags, RelationGetDescr(rel), &evttags_isnull); if (!evttags_isnull) { item->ntags = DecodeTextArrayToCString(evttags, &item->tag); qsort(item->tag, item->ntags, sizeof(char *), pg_qsort_strcmp); } /* Add to cache entry. */ entry = hash_search(cache, &event, HASH_ENTER, &found); if (found) entry->triggerlist = lappend(entry->triggerlist, item); else entry->triggerlist = list_make1(item); } /* Done with pg_event_trigger scan. */ systable_endscan_ordered(scan); index_close(irel, AccessShareLock); relation_close(rel, AccessShareLock); /* Restore previous memory context. */ MemoryContextSwitchTo(oldcontext); /* Cache is now valid. */ EventTriggerCache = cache; }
CatCache * InitCatCache(int id, const char *relname, const char *indname, int reloidattr, int nkeys, const int *key) { CatCache *cp; MemoryContext oldcxt; int i; /* * first switch to the cache context so our allocations do not vanish * at the end of a transaction */ if (!CacheMemoryContext) CreateCacheMemoryContext(); oldcxt = MemoryContextSwitchTo(CacheMemoryContext); /* * if first time through, initialize the cache group header, including * global LRU list header */ if (CacheHdr == NULL) { CacheHdr = (CatCacheHeader *) palloc(sizeof(CatCacheHeader)); CacheHdr->ch_caches = NULL; CacheHdr->ch_ntup = 0; CacheHdr->ch_maxtup = MAXCCTUPLES; DLInitList(&CacheHdr->ch_lrulist); #ifdef CATCACHE_STATS on_proc_exit(CatCachePrintStats, 0); #endif } /* * allocate a new cache structure * * Note: we assume zeroing initializes the Dllist headers correctly */ cp = (CatCache *) palloc0(sizeof(CatCache) + NCCBUCKETS * sizeof(Dllist)); /* * initialize the cache's relation information for the relation * corresponding to this cache, and initialize some of the new cache's * other internal fields. But don't open the relation yet. */ cp->id = id; cp->cc_relname = relname; cp->cc_indname = indname; cp->cc_reloid = InvalidOid; /* temporary */ cp->cc_relisshared = false; /* temporary */ cp->cc_tupdesc = (TupleDesc) NULL; cp->cc_reloidattr = reloidattr; cp->cc_ntup = 0; cp->cc_nbuckets = NCCBUCKETS; cp->cc_nkeys = nkeys; for (i = 0; i < nkeys; ++i) cp->cc_key[i] = key[i]; /* * new cache is initialized as far as we can go for now. print some * debugging information, if appropriate. */ InitCatCache_DEBUG2; /* * add completed cache to top of group header's list */ cp->cc_next = CacheHdr->ch_caches; CacheHdr->ch_caches = cp; /* * back to the old context before we return... */ MemoryContextSwitchTo(oldcxt); return cp; }
/* * lookup_type_cache * * Fetch the type cache entry for the specified datatype, and make sure that * all the fields requested by bits in 'flags' are valid. * * The result is never NULL --- we will elog() if the passed type OID is * invalid. Note however that we may fail to find one or more of the * requested opclass-dependent fields; the caller needs to check whether * the fields are InvalidOid or not. */ TypeCacheEntry * lookup_type_cache(Oid type_id, int flags) { TypeCacheEntry *typentry; bool found; if (TypeCacheHash == NULL) { /* First time through: initialize the hash table */ HASHCTL ctl; if (!CacheMemoryContext) CreateCacheMemoryContext(); MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(Oid); ctl.entrysize = sizeof(TypeCacheEntry); ctl.hash = tag_hash; TypeCacheHash = hash_create("Type information cache", 64, &ctl, HASH_ELEM | HASH_FUNCTION); } /* Try to look up an existing entry */ typentry = (TypeCacheEntry *) hash_search(TypeCacheHash, (void *) &type_id, HASH_FIND, NULL); if (typentry == NULL) { /* * If we didn't find one, we want to make one. But first get the * required info from the pg_type row, just to make sure we don't * make a cache entry for an invalid type OID. */ int16 typlen; bool typbyval; char typalign; get_typlenbyvalalign(type_id, &typlen, &typbyval, &typalign); typentry = (TypeCacheEntry *) hash_search(TypeCacheHash, (void *) &type_id, HASH_ENTER, &found); if (typentry == NULL) ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); Assert(!found); /* it wasn't there a moment ago */ MemSet(typentry, 0, sizeof(TypeCacheEntry)); typentry->type_id = type_id; typentry->typlen = typlen; typentry->typbyval = typbyval; typentry->typalign = typalign; } /* If we haven't already found the opclass, try to do so */ if (flags != 0 && typentry->btree_opc == InvalidOid) { typentry->btree_opc = lookup_default_opclass(type_id, BTREE_AM_OID); /* Only care about hash opclass if no btree opclass... */ if (typentry->btree_opc == InvalidOid) { if (typentry->hash_opc == InvalidOid) typentry->hash_opc = lookup_default_opclass(type_id, HASH_AM_OID); } else { /* * If we find a btree opclass where previously we only found * a hash opclass, forget the hash equality operator so we * can use the btree operator instead. */ typentry->eq_opr = InvalidOid; typentry->eq_opr_finfo.fn_oid = InvalidOid; } } /* Look for requested operators and functions */ if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) && typentry->eq_opr == InvalidOid) { if (typentry->btree_opc != InvalidOid) typentry->eq_opr = get_opclass_member(typentry->btree_opc, BTEqualStrategyNumber); if (typentry->eq_opr == InvalidOid && typentry->hash_opc != InvalidOid) typentry->eq_opr = get_opclass_member(typentry->hash_opc, HTEqualStrategyNumber); } if ((flags & TYPECACHE_LT_OPR) && typentry->lt_opr == InvalidOid) { if (typentry->btree_opc != InvalidOid) typentry->lt_opr = get_opclass_member(typentry->btree_opc, BTLessStrategyNumber); } if ((flags & TYPECACHE_GT_OPR) && typentry->gt_opr == InvalidOid) { if (typentry->btree_opc != InvalidOid) typentry->gt_opr = get_opclass_member(typentry->btree_opc, BTGreaterStrategyNumber); } if ((flags & (TYPECACHE_CMP_PROC | TYPECACHE_CMP_PROC_FINFO)) && typentry->cmp_proc == InvalidOid) { if (typentry->btree_opc != InvalidOid) typentry->cmp_proc = get_opclass_proc(typentry->btree_opc, BTORDER_PROC); } /* * Set up fmgr lookup info as requested * * Note: we tell fmgr the finfo structures live in CacheMemoryContext, * which is not quite right (they're really in DynaHashContext) but this * will do for our purposes. */ if ((flags & TYPECACHE_EQ_OPR_FINFO) && typentry->eq_opr_finfo.fn_oid == InvalidOid && typentry->eq_opr != InvalidOid) { Oid eq_opr_func; eq_opr_func = get_opcode(typentry->eq_opr); if (eq_opr_func != InvalidOid) fmgr_info_cxt(eq_opr_func, &typentry->eq_opr_finfo, CacheMemoryContext); } if ((flags & TYPECACHE_CMP_PROC_FINFO) && typentry->cmp_proc_finfo.fn_oid == InvalidOid && typentry->cmp_proc != InvalidOid) { fmgr_info_cxt(typentry->cmp_proc, &typentry->cmp_proc_finfo, CacheMemoryContext); } return typentry; }