LCOV - code coverage report
Current view: top level - src/backend/utils/cache - ts_cache.c (source / functions) Hit Total Coverage
Test: PostgreSQL 14devel Lines: 199 220 90.5 %
Date: 2020-11-27 12:05:55 Functions: 8 8 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * ts_cache.c
       4             :  *    Tsearch related object caches.
       5             :  *
       6             :  * Tsearch performance is very sensitive to performance of parsers,
       7             :  * dictionaries and mapping, so lookups should be cached as much
       8             :  * as possible.
       9             :  *
      10             :  * Once a backend has created a cache entry for a particular TS object OID,
      11             :  * the cache entry will exist for the life of the backend; hence it is
      12             :  * safe to hold onto a pointer to the cache entry while doing things that
      13             :  * might result in recognizing a cache invalidation.  Beware however that
      14             :  * subsidiary information might be deleted and reallocated somewhere else
      15             :  * if a cache inval and reval happens!  This does not look like it will be
      16             :  * a big problem as long as parser and dictionary methods do not attempt
      17             :  * any database access.
      18             :  *
      19             :  *
      20             :  * Copyright (c) 2006-2020, PostgreSQL Global Development Group
      21             :  *
      22             :  * IDENTIFICATION
      23             :  *    src/backend/utils/cache/ts_cache.c
      24             :  *
      25             :  *-------------------------------------------------------------------------
      26             :  */
      27             : #include "postgres.h"
      28             : 
      29             : #include "access/genam.h"
      30             : #include "access/htup_details.h"
      31             : #include "access/table.h"
      32             : #include "access/xact.h"
      33             : #include "catalog/namespace.h"
      34             : #include "catalog/pg_ts_config.h"
      35             : #include "catalog/pg_ts_config_map.h"
      36             : #include "catalog/pg_ts_dict.h"
      37             : #include "catalog/pg_ts_parser.h"
      38             : #include "catalog/pg_ts_template.h"
      39             : #include "commands/defrem.h"
      40             : #include "miscadmin.h"
      41             : #include "tsearch/ts_cache.h"
      42             : #include "utils/builtins.h"
      43             : #include "utils/catcache.h"
      44             : #include "utils/fmgroids.h"
      45             : #include "utils/inval.h"
      46             : #include "utils/lsyscache.h"
      47             : #include "utils/memutils.h"
      48             : #include "utils/regproc.h"
      49             : #include "utils/syscache.h"
      50             : 
      51             : 
      52             : /*
      53             :  * MAXTOKENTYPE/MAXDICTSPERTT are arbitrary limits on the workspace size
      54             :  * used in lookup_ts_config_cache().  We could avoid hardwiring a limit
      55             :  * by making the workspace dynamically enlargeable, but it seems unlikely
      56             :  * to be worth the trouble.
      57             :  */
      58             : #define MAXTOKENTYPE    256
      59             : #define MAXDICTSPERTT   100
      60             : 
      61             : 
      62             : static HTAB *TSParserCacheHash = NULL;
      63             : static TSParserCacheEntry *lastUsedParser = NULL;
      64             : 
      65             : static HTAB *TSDictionaryCacheHash = NULL;
      66             : static TSDictionaryCacheEntry *lastUsedDictionary = NULL;
      67             : 
      68             : static HTAB *TSConfigCacheHash = NULL;
      69             : static TSConfigCacheEntry *lastUsedConfig = NULL;
      70             : 
      71             : /*
      72             :  * GUC default_text_search_config, and a cache of the current config's OID
      73             :  */
      74             : char       *TSCurrentConfig = NULL;
      75             : 
      76             : static Oid  TSCurrentConfigCache = InvalidOid;
      77             : 
      78             : 
      79             : /*
      80             :  * We use this syscache callback to detect when a visible change to a TS
      81             :  * catalog entry has been made, by either our own backend or another one.
      82             :  *
      83             :  * In principle we could just flush the specific cache entry that changed,
      84             :  * but given that TS configuration changes are probably infrequent, it
      85             :  * doesn't seem worth the trouble to determine that; we just flush all the
      86             :  * entries of the related hash table.
      87             :  *
      88             :  * We can use the same function for all TS caches by passing the hash
      89             :  * table address as the "arg".
      90             :  */
      91             : static void
      92        1104 : InvalidateTSCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
      93             : {
      94        1104 :     HTAB       *hash = (HTAB *) DatumGetPointer(arg);
      95             :     HASH_SEQ_STATUS status;
      96             :     TSAnyCacheEntry *entry;
      97             : 
      98        1104 :     hash_seq_init(&status, hash);
      99        3416 :     while ((entry = (TSAnyCacheEntry *) hash_seq_search(&status)) != NULL)
     100        2312 :         entry->isvalid = false;
     101             : 
     102             :     /* Also invalidate the current-config cache if it's pg_ts_config */
     103        1104 :     if (hash == TSConfigCacheHash)
     104         984 :         TSCurrentConfigCache = InvalidOid;
     105        1104 : }
     106             : 
     107             : /*
     108             :  * Fetch parser cache entry
     109             :  */
     110             : TSParserCacheEntry *
     111       33372 : lookup_ts_parser_cache(Oid prsId)
     112             : {
     113             :     TSParserCacheEntry *entry;
     114             : 
     115       33372 :     if (TSParserCacheHash == NULL)
     116             :     {
     117             :         /* First time through: initialize the hash table */
     118             :         HASHCTL     ctl;
     119             : 
     120        6136 :         MemSet(&ctl, 0, sizeof(ctl));
     121         472 :         ctl.keysize = sizeof(Oid);
     122         472 :         ctl.entrysize = sizeof(TSParserCacheEntry);
     123         472 :         TSParserCacheHash = hash_create("Tsearch parser cache", 4,
     124             :                                         &ctl, HASH_ELEM | HASH_BLOBS);
     125             :         /* Flush cache on pg_ts_parser changes */
     126         472 :         CacheRegisterSyscacheCallback(TSPARSEROID, InvalidateTSCacheCallBack,
     127             :                                       PointerGetDatum(TSParserCacheHash));
     128             : 
     129             :         /* Also make sure CacheMemoryContext exists */
     130         472 :         if (!CacheMemoryContext)
     131           0 :             CreateCacheMemoryContext();
     132             :     }
     133             : 
     134             :     /* Check single-entry cache */
     135       33372 :     if (lastUsedParser && lastUsedParser->prsId == prsId &&
     136       32900 :         lastUsedParser->isvalid)
     137       32900 :         return lastUsedParser;
     138             : 
     139             :     /* Try to look up an existing entry */
     140         472 :     entry = (TSParserCacheEntry *) hash_search(TSParserCacheHash,
     141             :                                                (void *) &prsId,
     142             :                                                HASH_FIND, NULL);
     143         472 :     if (entry == NULL || !entry->isvalid)
     144             :     {
     145             :         /*
     146             :          * If we didn't find one, we want to make one. But first look up the
     147             :          * object to be sure the OID is real.
     148             :          */
     149             :         HeapTuple   tp;
     150             :         Form_pg_ts_parser prs;
     151             : 
     152         472 :         tp = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
     153         472 :         if (!HeapTupleIsValid(tp))
     154           0 :             elog(ERROR, "cache lookup failed for text search parser %u",
     155             :                  prsId);
     156         472 :         prs = (Form_pg_ts_parser) GETSTRUCT(tp);
     157             : 
     158             :         /*
     159             :          * Sanity checks
     160             :          */
     161         472 :         if (!OidIsValid(prs->prsstart))
     162           0 :             elog(ERROR, "text search parser %u has no prsstart method", prsId);
     163         472 :         if (!OidIsValid(prs->prstoken))
     164           0 :             elog(ERROR, "text search parser %u has no prstoken method", prsId);
     165         472 :         if (!OidIsValid(prs->prsend))
     166           0 :             elog(ERROR, "text search parser %u has no prsend method", prsId);
     167             : 
     168         472 :         if (entry == NULL)
     169             :         {
     170             :             bool        found;
     171             : 
     172             :             /* Now make the cache entry */
     173             :             entry = (TSParserCacheEntry *)
     174         472 :                 hash_search(TSParserCacheHash,
     175             :                             (void *) &prsId,
     176             :                             HASH_ENTER, &found);
     177             :             Assert(!found);     /* it wasn't there a moment ago */
     178             :         }
     179             : 
     180       13688 :         MemSet(entry, 0, sizeof(TSParserCacheEntry));
     181         472 :         entry->prsId = prsId;
     182         472 :         entry->startOid = prs->prsstart;
     183         472 :         entry->tokenOid = prs->prstoken;
     184         472 :         entry->endOid = prs->prsend;
     185         472 :         entry->headlineOid = prs->prsheadline;
     186         472 :         entry->lextypeOid = prs->prslextype;
     187             : 
     188         472 :         ReleaseSysCache(tp);
     189             : 
     190         472 :         fmgr_info_cxt(entry->startOid, &entry->prsstart, CacheMemoryContext);
     191         472 :         fmgr_info_cxt(entry->tokenOid, &entry->prstoken, CacheMemoryContext);
     192         472 :         fmgr_info_cxt(entry->endOid, &entry->prsend, CacheMemoryContext);
     193         472 :         if (OidIsValid(entry->headlineOid))
     194         472 :             fmgr_info_cxt(entry->headlineOid, &entry->prsheadline,
     195             :                           CacheMemoryContext);
     196             : 
     197         472 :         entry->isvalid = true;
     198             :     }
     199             : 
     200         472 :     lastUsedParser = entry;
     201             : 
     202         472 :     return entry;
     203             : }
     204             : 
     205             : /*
     206             :  * Fetch dictionary cache entry
     207             :  */
     208             : TSDictionaryCacheEntry *
     209        8910 : lookup_ts_dictionary_cache(Oid dictId)
     210             : {
     211             :     TSDictionaryCacheEntry *entry;
     212             : 
     213        8910 :     if (TSDictionaryCacheHash == NULL)
     214             :     {
     215             :         /* First time through: initialize the hash table */
     216             :         HASHCTL     ctl;
     217             : 
     218         390 :         MemSet(&ctl, 0, sizeof(ctl));
     219          30 :         ctl.keysize = sizeof(Oid);
     220          30 :         ctl.entrysize = sizeof(TSDictionaryCacheEntry);
     221          30 :         TSDictionaryCacheHash = hash_create("Tsearch dictionary cache", 8,
     222             :                                             &ctl, HASH_ELEM | HASH_BLOBS);
     223             :         /* Flush cache on pg_ts_dict and pg_ts_template changes */
     224          30 :         CacheRegisterSyscacheCallback(TSDICTOID, InvalidateTSCacheCallBack,
     225             :                                       PointerGetDatum(TSDictionaryCacheHash));
     226          30 :         CacheRegisterSyscacheCallback(TSTEMPLATEOID, InvalidateTSCacheCallBack,
     227             :                                       PointerGetDatum(TSDictionaryCacheHash));
     228             : 
     229             :         /* Also make sure CacheMemoryContext exists */
     230          30 :         if (!CacheMemoryContext)
     231           0 :             CreateCacheMemoryContext();
     232             :     }
     233             : 
     234             :     /* Check single-entry cache */
     235        8910 :     if (lastUsedDictionary && lastUsedDictionary->dictId == dictId &&
     236        7272 :         lastUsedDictionary->isvalid)
     237        7242 :         return lastUsedDictionary;
     238             : 
     239             :     /* Try to look up an existing entry */
     240        1668 :     entry = (TSDictionaryCacheEntry *) hash_search(TSDictionaryCacheHash,
     241             :                                                    (void *) &dictId,
     242             :                                                    HASH_FIND, NULL);
     243        1668 :     if (entry == NULL || !entry->isvalid)
     244             :     {
     245             :         /*
     246             :          * If we didn't find one, we want to make one. But first look up the
     247             :          * object to be sure the OID is real.
     248             :          */
     249             :         HeapTuple   tpdict,
     250             :                     tptmpl;
     251             :         Form_pg_ts_dict dict;
     252             :         Form_pg_ts_template template;
     253             :         MemoryContext saveCtx;
     254             : 
     255         120 :         tpdict = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
     256         120 :         if (!HeapTupleIsValid(tpdict))
     257           0 :             elog(ERROR, "cache lookup failed for text search dictionary %u",
     258             :                  dictId);
     259         120 :         dict = (Form_pg_ts_dict) GETSTRUCT(tpdict);
     260             : 
     261             :         /*
     262             :          * Sanity checks
     263             :          */
     264         120 :         if (!OidIsValid(dict->dicttemplate))
     265           0 :             elog(ERROR, "text search dictionary %u has no template", dictId);
     266             : 
     267             :         /*
     268             :          * Retrieve dictionary's template
     269             :          */
     270         120 :         tptmpl = SearchSysCache1(TSTEMPLATEOID,
     271         120 :                                  ObjectIdGetDatum(dict->dicttemplate));
     272         120 :         if (!HeapTupleIsValid(tptmpl))
     273           0 :             elog(ERROR, "cache lookup failed for text search template %u",
     274             :                  dict->dicttemplate);
     275         120 :         template = (Form_pg_ts_template) GETSTRUCT(tptmpl);
     276             : 
     277             :         /*
     278             :          * Sanity checks
     279             :          */
     280         120 :         if (!OidIsValid(template->tmpllexize))
     281           0 :             elog(ERROR, "text search template %u has no lexize method",
     282             :                  template->tmpllexize);
     283             : 
     284         120 :         if (entry == NULL)
     285             :         {
     286             :             bool        found;
     287             : 
     288             :             /* Now make the cache entry */
     289             :             entry = (TSDictionaryCacheEntry *)
     290          70 :                 hash_search(TSDictionaryCacheHash,
     291             :                             (void *) &dictId,
     292             :                             HASH_ENTER, &found);
     293             :             Assert(!found);     /* it wasn't there a moment ago */
     294             : 
     295             :             /* Create private memory context the first time through */
     296          70 :             saveCtx = AllocSetContextCreate(CacheMemoryContext,
     297             :                                             "TS dictionary",
     298             :                                             ALLOCSET_SMALL_SIZES);
     299          70 :             MemoryContextCopyAndSetIdentifier(saveCtx, NameStr(dict->dictname));
     300             :         }
     301             :         else
     302             :         {
     303             :             /* Clear the existing entry's private context */
     304          50 :             saveCtx = entry->dictCtx;
     305             :             /* Don't let context's ident pointer dangle while we reset it */
     306          50 :             MemoryContextSetIdentifier(saveCtx, NULL);
     307          50 :             MemoryContextReset(saveCtx);
     308          50 :             MemoryContextCopyAndSetIdentifier(saveCtx, NameStr(dict->dictname));
     309             :         }
     310             : 
     311        1320 :         MemSet(entry, 0, sizeof(TSDictionaryCacheEntry));
     312         120 :         entry->dictId = dictId;
     313         120 :         entry->dictCtx = saveCtx;
     314             : 
     315         120 :         entry->lexizeOid = template->tmpllexize;
     316             : 
     317         120 :         if (OidIsValid(template->tmplinit))
     318             :         {
     319             :             List       *dictoptions;
     320             :             Datum       opt;
     321             :             bool        isnull;
     322             :             MemoryContext oldcontext;
     323             : 
     324             :             /*
     325             :              * Init method runs in dictionary's private memory context, and we
     326             :              * make sure the options are stored there too
     327             :              */
     328         120 :             oldcontext = MemoryContextSwitchTo(entry->dictCtx);
     329             : 
     330         120 :             opt = SysCacheGetAttr(TSDICTOID, tpdict,
     331             :                                   Anum_pg_ts_dict_dictinitoption,
     332             :                                   &isnull);
     333         120 :             if (isnull)
     334          20 :                 dictoptions = NIL;
     335             :             else
     336         100 :                 dictoptions = deserialize_deflist(opt);
     337             : 
     338         120 :             entry->dictData =
     339         120 :                 DatumGetPointer(OidFunctionCall1(template->tmplinit,
     340             :                                                  PointerGetDatum(dictoptions)));
     341             : 
     342         120 :             MemoryContextSwitchTo(oldcontext);
     343             :         }
     344             : 
     345         120 :         ReleaseSysCache(tptmpl);
     346         120 :         ReleaseSysCache(tpdict);
     347             : 
     348         120 :         fmgr_info_cxt(entry->lexizeOid, &entry->lexize, entry->dictCtx);
     349             : 
     350         120 :         entry->isvalid = true;
     351             :     }
     352             : 
     353        1668 :     lastUsedDictionary = entry;
     354             : 
     355        1668 :     return entry;
     356             : }
     357             : 
     358             : /*
     359             :  * Initialize config cache and prepare callbacks.  This is split out of
     360             :  * lookup_ts_config_cache because we need to activate the callback before
     361             :  * caching TSCurrentConfigCache, too.
     362             :  */
     363             : static void
     364          22 : init_ts_config_cache(void)
     365             : {
     366             :     HASHCTL     ctl;
     367             : 
     368         286 :     MemSet(&ctl, 0, sizeof(ctl));
     369          22 :     ctl.keysize = sizeof(Oid);
     370          22 :     ctl.entrysize = sizeof(TSConfigCacheEntry);
     371          22 :     TSConfigCacheHash = hash_create("Tsearch configuration cache", 16,
     372             :                                     &ctl, HASH_ELEM | HASH_BLOBS);
     373             :     /* Flush cache on pg_ts_config and pg_ts_config_map changes */
     374          22 :     CacheRegisterSyscacheCallback(TSCONFIGOID, InvalidateTSCacheCallBack,
     375             :                                   PointerGetDatum(TSConfigCacheHash));
     376          22 :     CacheRegisterSyscacheCallback(TSCONFIGMAP, InvalidateTSCacheCallBack,
     377             :                                   PointerGetDatum(TSConfigCacheHash));
     378             : 
     379             :     /* Also make sure CacheMemoryContext exists */
     380          22 :     if (!CacheMemoryContext)
     381           0 :         CreateCacheMemoryContext();
     382          22 : }
     383             : 
     384             : /*
     385             :  * Fetch configuration cache entry
     386             :  */
     387             : TSConfigCacheEntry *
     388        3250 : lookup_ts_config_cache(Oid cfgId)
     389             : {
     390             :     TSConfigCacheEntry *entry;
     391             : 
     392        3250 :     if (TSConfigCacheHash == NULL)
     393             :     {
     394             :         /* First time through: initialize the hash table */
     395          14 :         init_ts_config_cache();
     396             :     }
     397             : 
     398             :     /* Check single-entry cache */
     399        3250 :     if (lastUsedConfig && lastUsedConfig->cfgId == cfgId &&
     400        3156 :         lastUsedConfig->isvalid)
     401        3148 :         return lastUsedConfig;
     402             : 
     403             :     /* Try to look up an existing entry */
     404         102 :     entry = (TSConfigCacheEntry *) hash_search(TSConfigCacheHash,
     405             :                                                (void *) &cfgId,
     406             :                                                HASH_FIND, NULL);
     407         102 :     if (entry == NULL || !entry->isvalid)
     408             :     {
     409             :         /*
     410             :          * If we didn't find one, we want to make one. But first look up the
     411             :          * object to be sure the OID is real.
     412             :          */
     413             :         HeapTuple   tp;
     414             :         Form_pg_ts_config cfg;
     415             :         Relation    maprel;
     416             :         Relation    mapidx;
     417             :         ScanKeyData mapskey;
     418             :         SysScanDesc mapscan;
     419             :         HeapTuple   maptup;
     420             :         ListDictionary maplists[MAXTOKENTYPE + 1];
     421             :         Oid         mapdicts[MAXDICTSPERTT];
     422             :         int         maxtokentype;
     423             :         int         ndicts;
     424             :         int         i;
     425             : 
     426          58 :         tp = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
     427          58 :         if (!HeapTupleIsValid(tp))
     428           0 :             elog(ERROR, "cache lookup failed for text search configuration %u",
     429             :                  cfgId);
     430          58 :         cfg = (Form_pg_ts_config) GETSTRUCT(tp);
     431             : 
     432             :         /*
     433             :          * Sanity checks
     434             :          */
     435          58 :         if (!OidIsValid(cfg->cfgparser))
     436           0 :             elog(ERROR, "text search configuration %u has no parser", cfgId);
     437             : 
     438          58 :         if (entry == NULL)
     439             :         {
     440             :             bool        found;
     441             : 
     442             :             /* Now make the cache entry */
     443             :             entry = (TSConfigCacheEntry *)
     444          50 :                 hash_search(TSConfigCacheHash,
     445             :                             (void *) &cfgId,
     446             :                             HASH_ENTER, &found);
     447             :             Assert(!found);     /* it wasn't there a moment ago */
     448             :         }
     449             :         else
     450             :         {
     451             :             /* Cleanup old contents */
     452           8 :             if (entry->map)
     453             :             {
     454         192 :                 for (i = 0; i < entry->lenmap; i++)
     455         184 :                     if (entry->map[i].dictIds)
     456         152 :                         pfree(entry->map[i].dictIds);
     457           8 :                 pfree(entry->map);
     458             :             }
     459             :         }
     460             : 
     461         232 :         MemSet(entry, 0, sizeof(TSConfigCacheEntry));
     462          58 :         entry->cfgId = cfgId;
     463          58 :         entry->prsId = cfg->cfgparser;
     464             : 
     465          58 :         ReleaseSysCache(tp);
     466             : 
     467             :         /*
     468             :          * Scan pg_ts_config_map to gather dictionary list for each token type
     469             :          *
     470             :          * Because the index is on (mapcfg, maptokentype, mapseqno), we will
     471             :          * see the entries in maptokentype order, and in mapseqno order for
     472             :          * each token type, even though we didn't explicitly ask for that.
     473             :          */
     474          58 :         MemSet(maplists, 0, sizeof(maplists));
     475          58 :         maxtokentype = 0;
     476          58 :         ndicts = 0;
     477             : 
     478          58 :         ScanKeyInit(&mapskey,
     479             :                     Anum_pg_ts_config_map_mapcfg,
     480             :                     BTEqualStrategyNumber, F_OIDEQ,
     481             :                     ObjectIdGetDatum(cfgId));
     482             : 
     483          58 :         maprel = table_open(TSConfigMapRelationId, AccessShareLock);
     484          58 :         mapidx = index_open(TSConfigMapIndexId, AccessShareLock);
     485          58 :         mapscan = systable_beginscan_ordered(maprel, mapidx,
     486             :                                              NULL, 1, &mapskey);
     487             : 
     488        1304 :         while ((maptup = systable_getnext_ordered(mapscan, ForwardScanDirection)) != NULL)
     489             :         {
     490        1246 :             Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
     491        1246 :             int         toktype = cfgmap->maptokentype;
     492             : 
     493        1246 :             if (toktype <= 0 || toktype > MAXTOKENTYPE)
     494           0 :                 elog(ERROR, "maptokentype value %d is out of range", toktype);
     495        1246 :             if (toktype < maxtokentype)
     496           0 :                 elog(ERROR, "maptokentype entries are out of order");
     497        1246 :             if (toktype > maxtokentype)
     498             :             {
     499             :                 /* starting a new token type, but first save the prior data */
     500        1066 :                 if (ndicts > 0)
     501             :                 {
     502        1008 :                     maplists[maxtokentype].len = ndicts;
     503        1008 :                     maplists[maxtokentype].dictIds = (Oid *)
     504        1008 :                         MemoryContextAlloc(CacheMemoryContext,
     505             :                                            sizeof(Oid) * ndicts);
     506        1008 :                     memcpy(maplists[maxtokentype].dictIds, mapdicts,
     507             :                            sizeof(Oid) * ndicts);
     508             :                 }
     509        1066 :                 maxtokentype = toktype;
     510        1066 :                 mapdicts[0] = cfgmap->mapdict;
     511        1066 :                 ndicts = 1;
     512             :             }
     513             :             else
     514             :             {
     515             :                 /* continuing data for current token type */
     516         180 :                 if (ndicts >= MAXDICTSPERTT)
     517           0 :                     elog(ERROR, "too many pg_ts_config_map entries for one token type");
     518         180 :                 mapdicts[ndicts++] = cfgmap->mapdict;
     519             :             }
     520             :         }
     521             : 
     522          58 :         systable_endscan_ordered(mapscan);
     523          58 :         index_close(mapidx, AccessShareLock);
     524          58 :         table_close(maprel, AccessShareLock);
     525             : 
     526          58 :         if (ndicts > 0)
     527             :         {
     528             :             /* save the last token type's dictionaries */
     529          58 :             maplists[maxtokentype].len = ndicts;
     530          58 :             maplists[maxtokentype].dictIds = (Oid *)
     531          58 :                 MemoryContextAlloc(CacheMemoryContext,
     532             :                                    sizeof(Oid) * ndicts);
     533          58 :             memcpy(maplists[maxtokentype].dictIds, mapdicts,
     534             :                    sizeof(Oid) * ndicts);
     535             :             /* and save the overall map */
     536          58 :             entry->lenmap = maxtokentype + 1;
     537          58 :             entry->map = (ListDictionary *)
     538          58 :                 MemoryContextAlloc(CacheMemoryContext,
     539          58 :                                    sizeof(ListDictionary) * entry->lenmap);
     540          58 :             memcpy(entry->map, maplists,
     541          58 :                    sizeof(ListDictionary) * entry->lenmap);
     542             :         }
     543             : 
     544          58 :         entry->isvalid = true;
     545             :     }
     546             : 
     547         102 :     lastUsedConfig = entry;
     548             : 
     549         102 :     return entry;
     550             : }
     551             : 
     552             : 
     553             : /*---------------------------------------------------
     554             :  * GUC variable "default_text_search_config"
     555             :  *---------------------------------------------------
     556             :  */
     557             : 
     558             : Oid
     559         260 : getTSCurrentConfig(bool emitError)
     560             : {
     561             :     /* if we have a cached value, return it */
     562         260 :     if (OidIsValid(TSCurrentConfigCache))
     563         240 :         return TSCurrentConfigCache;
     564             : 
     565             :     /* fail if GUC hasn't been set up yet */
     566          20 :     if (TSCurrentConfig == NULL || *TSCurrentConfig == '\0')
     567             :     {
     568           0 :         if (emitError)
     569           0 :             elog(ERROR, "text search configuration isn't set");
     570             :         else
     571           0 :             return InvalidOid;
     572             :     }
     573             : 
     574          20 :     if (TSConfigCacheHash == NULL)
     575             :     {
     576             :         /* First time through: initialize the tsconfig inval callback */
     577           8 :         init_ts_config_cache();
     578             :     }
     579             : 
     580             :     /* Look up the config */
     581          20 :     TSCurrentConfigCache =
     582          20 :         get_ts_config_oid(stringToQualifiedNameList(TSCurrentConfig),
     583          20 :                           !emitError);
     584             : 
     585          20 :     return TSCurrentConfigCache;
     586             : }
     587             : 
     588             : /* GUC check_hook for default_text_search_config */
     589             : bool
     590        7532 : check_TSCurrentConfig(char **newval, void **extra, GucSource source)
     591             : {
     592             :     /*
     593             :      * If we aren't inside a transaction, or connected to a database, we
     594             :      * cannot do the catalog accesses necessary to verify the config name.
     595             :      * Must accept it on faith.
     596             :      */
     597        7532 :     if (IsTransactionState() && MyDatabaseId != InvalidOid)
     598             :     {
     599             :         Oid         cfgId;
     600             :         HeapTuple   tuple;
     601             :         Form_pg_ts_config cfg;
     602             :         char       *buf;
     603             : 
     604        3234 :         cfgId = get_ts_config_oid(stringToQualifiedNameList(*newval), true);
     605             : 
     606             :         /*
     607             :          * When source == PGC_S_TEST, don't throw a hard error for a
     608             :          * nonexistent configuration, only a NOTICE.  See comments in guc.h.
     609             :          */
     610        3234 :         if (!OidIsValid(cfgId))
     611             :         {
     612          18 :             if (source == PGC_S_TEST)
     613             :             {
     614          10 :                 ereport(NOTICE,
     615             :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
     616             :                          errmsg("text search configuration \"%s\" does not exist", *newval)));
     617          10 :                 return true;
     618             :             }
     619             :             else
     620           8 :                 return false;
     621             :         }
     622             : 
     623             :         /*
     624             :          * Modify the actually stored value to be fully qualified, to ensure
     625             :          * later changes of search_path don't affect it.
     626             :          */
     627        3216 :         tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
     628        3216 :         if (!HeapTupleIsValid(tuple))
     629           0 :             elog(ERROR, "cache lookup failed for text search configuration %u",
     630             :                  cfgId);
     631        3216 :         cfg = (Form_pg_ts_config) GETSTRUCT(tuple);
     632             : 
     633        3216 :         buf = quote_qualified_identifier(get_namespace_name(cfg->cfgnamespace),
     634        3216 :                                          NameStr(cfg->cfgname));
     635             : 
     636        3216 :         ReleaseSysCache(tuple);
     637             : 
     638             :         /* GUC wants it malloc'd not palloc'd */
     639        3216 :         free(*newval);
     640        3216 :         *newval = strdup(buf);
     641        3216 :         pfree(buf);
     642        3216 :         if (!*newval)
     643           0 :             return false;
     644             :     }
     645             : 
     646        7514 :     return true;
     647             : }
     648             : 
     649             : /* GUC assign_hook for default_text_search_config */
     650             : void
     651        7510 : assign_TSCurrentConfig(const char *newval, void *extra)
     652             : {
     653             :     /* Just reset the cache to force a lookup on first use */
     654        7510 :     TSCurrentConfigCache = InvalidOid;
     655        7510 : }

Generated by: LCOV version 1.13