LCOV - code coverage report
Current view: top level - src/backend/utils/cache - ts_cache.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 88.0 % 225 198
Test Date: 2026-04-07 14:16:30 Functions: 100.0 % 8 8
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-2026, 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 "nodes/miscnodes.h"
      42              : #include "tsearch/ts_cache.h"
      43              : #include "utils/builtins.h"
      44              : #include "utils/catcache.h"
      45              : #include "utils/fmgroids.h"
      46              : #include "utils/guc_hooks.h"
      47              : #include "utils/hsearch.h"
      48              : #include "utils/inval.h"
      49              : #include "utils/lsyscache.h"
      50              : #include "utils/memutils.h"
      51              : #include "utils/regproc.h"
      52              : #include "utils/syscache.h"
      53              : 
      54              : 
      55              : /*
      56              :  * MAXTOKENTYPE/MAXDICTSPERTT are arbitrary limits on the workspace size
      57              :  * used in lookup_ts_config_cache().  We could avoid hardwiring a limit
      58              :  * by making the workspace dynamically enlargeable, but it seems unlikely
      59              :  * to be worth the trouble.
      60              :  */
      61              : #define MAXTOKENTYPE    256
      62              : #define MAXDICTSPERTT   100
      63              : 
      64              : 
      65              : static HTAB *TSParserCacheHash = NULL;
      66              : static TSParserCacheEntry *lastUsedParser = NULL;
      67              : 
      68              : static HTAB *TSDictionaryCacheHash = NULL;
      69              : static TSDictionaryCacheEntry *lastUsedDictionary = NULL;
      70              : 
      71              : static HTAB *TSConfigCacheHash = NULL;
      72              : static TSConfigCacheEntry *lastUsedConfig = NULL;
      73              : 
      74              : /*
      75              :  * GUC default_text_search_config, and a cache of the current config's OID
      76              :  */
      77              : char       *TSCurrentConfig = NULL;
      78              : 
      79              : static Oid  TSCurrentConfigCache = InvalidOid;
      80              : 
      81              : 
      82              : /*
      83              :  * We use this syscache callback to detect when a visible change to a TS
      84              :  * catalog entry has been made, by either our own backend or another one.
      85              :  *
      86              :  * In principle we could just flush the specific cache entry that changed,
      87              :  * but given that TS configuration changes are probably infrequent, it
      88              :  * doesn't seem worth the trouble to determine that; we just flush all the
      89              :  * entries of the related hash table.
      90              :  *
      91              :  * We can use the same function for all TS caches by passing the hash
      92              :  * table address as the "arg".
      93              :  */
      94              : static void
      95         1388 : InvalidateTSCacheCallBack(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
      96              : {
      97         1388 :     HTAB       *hash = (HTAB *) DatumGetPointer(arg);
      98              :     HASH_SEQ_STATUS status;
      99              :     TSAnyCacheEntry *entry;
     100              : 
     101         1388 :     hash_seq_init(&status, hash);
     102         4800 :     while ((entry = (TSAnyCacheEntry *) hash_seq_search(&status)) != NULL)
     103         3412 :         entry->isvalid = false;
     104              : 
     105              :     /* Also invalidate the current-config cache if it's pg_ts_config */
     106         1388 :     if (hash == TSConfigCacheHash)
     107         1256 :         TSCurrentConfigCache = InvalidOid;
     108         1388 : }
     109              : 
     110              : /*
     111              :  * Fetch parser cache entry
     112              :  */
     113              : TSParserCacheEntry *
     114         9624 : lookup_ts_parser_cache(Oid prsId)
     115              : {
     116              :     TSParserCacheEntry *entry;
     117              : 
     118         9624 :     if (TSParserCacheHash == NULL)
     119              :     {
     120              :         /* First time through: initialize the hash table */
     121              :         HASHCTL     ctl;
     122              : 
     123          126 :         ctl.keysize = sizeof(Oid);
     124          126 :         ctl.entrysize = sizeof(TSParserCacheEntry);
     125          126 :         TSParserCacheHash = hash_create("Tsearch parser cache", 4,
     126              :                                         &ctl, HASH_ELEM | HASH_BLOBS);
     127              :         /* Flush cache on pg_ts_parser changes */
     128          126 :         CacheRegisterSyscacheCallback(TSPARSEROID, InvalidateTSCacheCallBack,
     129              :                                       PointerGetDatum(TSParserCacheHash));
     130              : 
     131              :         /* Also make sure CacheMemoryContext exists */
     132          126 :         if (!CacheMemoryContext)
     133            0 :             CreateCacheMemoryContext();
     134              :     }
     135              : 
     136              :     /* Check single-entry cache */
     137         9624 :     if (lastUsedParser && lastUsedParser->prsId == prsId &&
     138         9498 :         lastUsedParser->isvalid)
     139         9498 :         return lastUsedParser;
     140              : 
     141              :     /* Try to look up an existing entry */
     142          126 :     entry = (TSParserCacheEntry *) hash_search(TSParserCacheHash,
     143              :                                                &prsId,
     144              :                                                HASH_FIND, NULL);
     145          126 :     if (entry == NULL || !entry->isvalid)
     146              :     {
     147              :         /*
     148              :          * If we didn't find one, we want to make one. But first look up the
     149              :          * object to be sure the OID is real.
     150              :          */
     151              :         HeapTuple   tp;
     152              :         Form_pg_ts_parser prs;
     153              : 
     154          126 :         tp = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
     155          126 :         if (!HeapTupleIsValid(tp))
     156            0 :             elog(ERROR, "cache lookup failed for text search parser %u",
     157              :                  prsId);
     158          126 :         prs = (Form_pg_ts_parser) GETSTRUCT(tp);
     159              : 
     160              :         /*
     161              :          * Sanity checks
     162              :          */
     163          126 :         if (!OidIsValid(prs->prsstart))
     164            0 :             elog(ERROR, "text search parser %u has no prsstart method", prsId);
     165          126 :         if (!OidIsValid(prs->prstoken))
     166            0 :             elog(ERROR, "text search parser %u has no prstoken method", prsId);
     167          126 :         if (!OidIsValid(prs->prsend))
     168            0 :             elog(ERROR, "text search parser %u has no prsend method", prsId);
     169              : 
     170          126 :         if (entry == NULL)
     171              :         {
     172              :             bool        found;
     173              : 
     174              :             /* Now make the cache entry */
     175              :             entry = (TSParserCacheEntry *)
     176          126 :                 hash_search(TSParserCacheHash, &prsId, HASH_ENTER, &found);
     177              :             Assert(!found);     /* it wasn't there a moment ago */
     178              :         }
     179              : 
     180         3654 :         MemSet(entry, 0, sizeof(TSParserCacheEntry));
     181          126 :         entry->prsId = prsId;
     182          126 :         entry->startOid = prs->prsstart;
     183          126 :         entry->tokenOid = prs->prstoken;
     184          126 :         entry->endOid = prs->prsend;
     185          126 :         entry->headlineOid = prs->prsheadline;
     186          126 :         entry->lextypeOid = prs->prslextype;
     187              : 
     188          126 :         ReleaseSysCache(tp);
     189              : 
     190          126 :         fmgr_info_cxt(entry->startOid, &entry->prsstart, CacheMemoryContext);
     191          126 :         fmgr_info_cxt(entry->tokenOid, &entry->prstoken, CacheMemoryContext);
     192          126 :         fmgr_info_cxt(entry->endOid, &entry->prsend, CacheMemoryContext);
     193          126 :         if (OidIsValid(entry->headlineOid))
     194          126 :             fmgr_info_cxt(entry->headlineOid, &entry->prsheadline,
     195              :                           CacheMemoryContext);
     196              : 
     197          126 :         entry->isvalid = true;
     198              :     }
     199              : 
     200          126 :     lastUsedParser = entry;
     201              : 
     202          126 :     return entry;
     203              : }
     204              : 
     205              : /*
     206              :  * Fetch dictionary cache entry
     207              :  */
     208              : TSDictionaryCacheEntry *
     209        12363 : lookup_ts_dictionary_cache(Oid dictId)
     210              : {
     211              :     TSDictionaryCacheEntry *entry;
     212              : 
     213        12363 :     if (TSDictionaryCacheHash == NULL)
     214              :     {
     215              :         /* First time through: initialize the hash table */
     216              :         HASHCTL     ctl;
     217              : 
     218           32 :         ctl.keysize = sizeof(Oid);
     219           32 :         ctl.entrysize = sizeof(TSDictionaryCacheEntry);
     220           32 :         TSDictionaryCacheHash = hash_create("Tsearch dictionary cache", 8,
     221              :                                             &ctl, HASH_ELEM | HASH_BLOBS);
     222              :         /* Flush cache on pg_ts_dict and pg_ts_template changes */
     223           32 :         CacheRegisterSyscacheCallback(TSDICTOID, InvalidateTSCacheCallBack,
     224              :                                       PointerGetDatum(TSDictionaryCacheHash));
     225           32 :         CacheRegisterSyscacheCallback(TSTEMPLATEOID, InvalidateTSCacheCallBack,
     226              :                                       PointerGetDatum(TSDictionaryCacheHash));
     227              : 
     228              :         /* Also make sure CacheMemoryContext exists */
     229           32 :         if (!CacheMemoryContext)
     230            0 :             CreateCacheMemoryContext();
     231              :     }
     232              : 
     233              :     /* Check single-entry cache */
     234        12363 :     if (lastUsedDictionary && lastUsedDictionary->dictId == dictId &&
     235        10318 :         lastUsedDictionary->isvalid)
     236        10297 :         return lastUsedDictionary;
     237              : 
     238              :     /* Try to look up an existing entry */
     239         2066 :     entry = (TSDictionaryCacheEntry *) hash_search(TSDictionaryCacheHash,
     240              :                                                    &dictId,
     241              :                                                    HASH_FIND, NULL);
     242         2066 :     if (entry == NULL || !entry->isvalid)
     243              :     {
     244              :         /*
     245              :          * If we didn't find one, we want to make one. But first look up the
     246              :          * object to be sure the OID is real.
     247              :          */
     248              :         HeapTuple   tpdict,
     249              :                     tptmpl;
     250              :         Form_pg_ts_dict dict;
     251              :         Form_pg_ts_template template;
     252              :         MemoryContext saveCtx;
     253              : 
     254          113 :         tpdict = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
     255          113 :         if (!HeapTupleIsValid(tpdict))
     256            0 :             elog(ERROR, "cache lookup failed for text search dictionary %u",
     257              :                  dictId);
     258          113 :         dict = (Form_pg_ts_dict) GETSTRUCT(tpdict);
     259              : 
     260              :         /*
     261              :          * Sanity checks
     262              :          */
     263          113 :         if (!OidIsValid(dict->dicttemplate))
     264            0 :             elog(ERROR, "text search dictionary %u has no template", dictId);
     265              : 
     266              :         /*
     267              :          * Retrieve dictionary's template
     268              :          */
     269          113 :         tptmpl = SearchSysCache1(TSTEMPLATEOID,
     270              :                                  ObjectIdGetDatum(dict->dicttemplate));
     271          113 :         if (!HeapTupleIsValid(tptmpl))
     272            0 :             elog(ERROR, "cache lookup failed for text search template %u",
     273              :                  dict->dicttemplate);
     274          113 :         template = (Form_pg_ts_template) GETSTRUCT(tptmpl);
     275              : 
     276              :         /*
     277              :          * Sanity checks
     278              :          */
     279          113 :         if (!OidIsValid(template->tmpllexize))
     280            0 :             elog(ERROR, "text search template %u has no lexize method",
     281              :                  template->tmpllexize);
     282              : 
     283          113 :         if (entry == NULL)
     284              :         {
     285              :             bool        found;
     286              : 
     287              :             /* Now make the cache entry */
     288              :             entry = (TSDictionaryCacheEntry *)
     289           72 :                 hash_search(TSDictionaryCacheHash,
     290              :                             &dictId,
     291              :                             HASH_ENTER, &found);
     292              :             Assert(!found);     /* it wasn't there a moment ago */
     293              : 
     294              :             /* Create private memory context the first time through */
     295           72 :             saveCtx = AllocSetContextCreate(CacheMemoryContext,
     296              :                                             "TS dictionary",
     297              :                                             ALLOCSET_SMALL_SIZES);
     298           72 :             MemoryContextCopyAndSetIdentifier(saveCtx, NameStr(dict->dictname));
     299              :         }
     300              :         else
     301              :         {
     302              :             /* Clear the existing entry's private context */
     303           41 :             saveCtx = entry->dictCtx;
     304              :             /* Don't let context's ident pointer dangle while we reset it */
     305           41 :             MemoryContextSetIdentifier(saveCtx, NULL);
     306           41 :             MemoryContextReset(saveCtx);
     307           41 :             MemoryContextCopyAndSetIdentifier(saveCtx, NameStr(dict->dictname));
     308              :         }
     309              : 
     310         1243 :         MemSet(entry, 0, sizeof(TSDictionaryCacheEntry));
     311          113 :         entry->dictId = dictId;
     312          113 :         entry->dictCtx = saveCtx;
     313              : 
     314          113 :         entry->lexizeOid = template->tmpllexize;
     315              : 
     316          113 :         if (OidIsValid(template->tmplinit))
     317              :         {
     318              :             List       *dictoptions;
     319              :             Datum       opt;
     320              :             bool        isnull;
     321              :             MemoryContext oldcontext;
     322              : 
     323              :             /*
     324              :              * Init method runs in dictionary's private memory context, and we
     325              :              * make sure the options are stored there too.  This typically
     326              :              * results in a small amount of memory leakage, but it's not worth
     327              :              * complicating the API for tmplinit functions to avoid it.
     328              :              */
     329          113 :             oldcontext = MemoryContextSwitchTo(entry->dictCtx);
     330              : 
     331          113 :             opt = SysCacheGetAttr(TSDICTOID, tpdict,
     332              :                                   Anum_pg_ts_dict_dictinitoption,
     333              :                                   &isnull);
     334          113 :             if (isnull)
     335           21 :                 dictoptions = NIL;
     336              :             else
     337           92 :                 dictoptions = deserialize_deflist(opt);
     338              : 
     339          113 :             entry->dictData =
     340          113 :                 DatumGetPointer(OidFunctionCall1(template->tmplinit,
     341              :                                                  PointerGetDatum(dictoptions)));
     342              : 
     343          113 :             MemoryContextSwitchTo(oldcontext);
     344              :         }
     345              : 
     346          113 :         ReleaseSysCache(tptmpl);
     347          113 :         ReleaseSysCache(tpdict);
     348              : 
     349          113 :         fmgr_info_cxt(entry->lexizeOid, &entry->lexize, entry->dictCtx);
     350              : 
     351          113 :         entry->isvalid = true;
     352              :     }
     353              : 
     354         2066 :     lastUsedDictionary = entry;
     355              : 
     356         2066 :     return entry;
     357              : }
     358              : 
     359              : /*
     360              :  * Initialize config cache and prepare callbacks.  This is split out of
     361              :  * lookup_ts_config_cache because we need to activate the callback before
     362              :  * caching TSCurrentConfigCache, too.
     363              :  */
     364              : static void
     365           28 : init_ts_config_cache(void)
     366              : {
     367              :     HASHCTL     ctl;
     368              : 
     369           28 :     ctl.keysize = sizeof(Oid);
     370           28 :     ctl.entrysize = sizeof(TSConfigCacheEntry);
     371           28 :     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           28 :     CacheRegisterSyscacheCallback(TSCONFIGOID, InvalidateTSCacheCallBack,
     375              :                                   PointerGetDatum(TSConfigCacheHash));
     376           28 :     CacheRegisterSyscacheCallback(TSCONFIGMAP, InvalidateTSCacheCallBack,
     377              :                                   PointerGetDatum(TSConfigCacheHash));
     378              : 
     379              :     /* Also make sure CacheMemoryContext exists */
     380           28 :     if (!CacheMemoryContext)
     381            0 :         CreateCacheMemoryContext();
     382           28 : }
     383              : 
     384              : /*
     385              :  * Fetch configuration cache entry
     386              :  */
     387              : TSConfigCacheEntry *
     388         4041 : lookup_ts_config_cache(Oid cfgId)
     389              : {
     390              :     TSConfigCacheEntry *entry;
     391              : 
     392         4041 :     if (TSConfigCacheHash == NULL)
     393              :     {
     394              :         /* First time through: initialize the hash table */
     395           16 :         init_ts_config_cache();
     396              :     }
     397              : 
     398              :     /* Check single-entry cache */
     399         4041 :     if (lastUsedConfig && lastUsedConfig->cfgId == cfgId &&
     400         3941 :         lastUsedConfig->isvalid)
     401         3933 :         return lastUsedConfig;
     402              : 
     403              :     /* Try to look up an existing entry */
     404          108 :     entry = (TSConfigCacheEntry *) hash_search(TSConfigCacheHash,
     405              :                                                &cfgId,
     406              :                                                HASH_FIND, NULL);
     407          108 :     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           64 :         tp = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
     427           64 :         if (!HeapTupleIsValid(tp))
     428            0 :             elog(ERROR, "cache lookup failed for text search configuration %u",
     429              :                  cfgId);
     430           64 :         cfg = (Form_pg_ts_config) GETSTRUCT(tp);
     431              : 
     432              :         /*
     433              :          * Sanity checks
     434              :          */
     435           64 :         if (!OidIsValid(cfg->cfgparser))
     436            0 :             elog(ERROR, "text search configuration %u has no parser", cfgId);
     437              : 
     438           64 :         if (entry == NULL)
     439              :         {
     440              :             bool        found;
     441              : 
     442              :             /* Now make the cache entry */
     443              :             entry = (TSConfigCacheEntry *)
     444           56 :                 hash_search(TSConfigCacheHash,
     445              :                             &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          256 :         MemSet(entry, 0, sizeof(TSConfigCacheEntry));
     462           64 :         entry->cfgId = cfgId;
     463           64 :         entry->prsId = cfg->cfgparser;
     464              : 
     465           64 :         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           64 :         MemSet(maplists, 0, sizeof(maplists));
     475           64 :         maxtokentype = 0;
     476           64 :         ndicts = 0;
     477              : 
     478           64 :         ScanKeyInit(&mapskey,
     479              :                     Anum_pg_ts_config_map_mapcfg,
     480              :                     BTEqualStrategyNumber, F_OIDEQ,
     481              :                     ObjectIdGetDatum(cfgId));
     482              : 
     483           64 :         maprel = table_open(TSConfigMapRelationId, AccessShareLock);
     484           64 :         mapidx = index_open(TSConfigMapIndexId, AccessShareLock);
     485           64 :         mapscan = systable_beginscan_ordered(maprel, mapidx,
     486              :                                              NULL, 1, &mapskey);
     487              : 
     488         1442 :         while ((maptup = systable_getnext_ordered(mapscan, ForwardScanDirection)) != NULL)
     489              :         {
     490         1378 :             Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
     491         1378 :             int         toktype = cfgmap->maptokentype;
     492              : 
     493         1378 :             if (toktype <= 0 || toktype > MAXTOKENTYPE)
     494            0 :                 elog(ERROR, "maptokentype value %d is out of range", toktype);
     495         1378 :             if (toktype < maxtokentype)
     496            0 :                 elog(ERROR, "maptokentype entries are out of order");
     497         1378 :             if (toktype > maxtokentype)
     498              :             {
     499              :                 /* starting a new token type, but first save the prior data */
     500         1198 :                 if (ndicts > 0)
     501              :                 {
     502         1134 :                     maplists[maxtokentype].len = ndicts;
     503         1134 :                     maplists[maxtokentype].dictIds = (Oid *)
     504         1134 :                         MemoryContextAlloc(CacheMemoryContext,
     505              :                                            sizeof(Oid) * ndicts);
     506         1134 :                     memcpy(maplists[maxtokentype].dictIds, mapdicts,
     507              :                            sizeof(Oid) * ndicts);
     508              :                 }
     509         1198 :                 maxtokentype = toktype;
     510         1198 :                 mapdicts[0] = cfgmap->mapdict;
     511         1198 :                 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           64 :         systable_endscan_ordered(mapscan);
     523           64 :         index_close(mapidx, AccessShareLock);
     524           64 :         table_close(maprel, AccessShareLock);
     525              : 
     526           64 :         if (ndicts > 0)
     527              :         {
     528              :             /* save the last token type's dictionaries */
     529           64 :             maplists[maxtokentype].len = ndicts;
     530           64 :             maplists[maxtokentype].dictIds = (Oid *)
     531           64 :                 MemoryContextAlloc(CacheMemoryContext,
     532              :                                    sizeof(Oid) * ndicts);
     533           64 :             memcpy(maplists[maxtokentype].dictIds, mapdicts,
     534              :                    sizeof(Oid) * ndicts);
     535              :             /* and save the overall map */
     536           64 :             entry->lenmap = maxtokentype + 1;
     537           64 :             entry->map = (ListDictionary *)
     538           64 :                 MemoryContextAlloc(CacheMemoryContext,
     539           64 :                                    sizeof(ListDictionary) * entry->lenmap);
     540           64 :             memcpy(entry->map, maplists,
     541           64 :                    sizeof(ListDictionary) * entry->lenmap);
     542              :         }
     543              : 
     544           64 :         entry->isvalid = true;
     545              :     }
     546              : 
     547          108 :     lastUsedConfig = entry;
     548              : 
     549          108 :     return entry;
     550              : }
     551              : 
     552              : 
     553              : /*---------------------------------------------------
     554              :  * GUC variable "default_text_search_config"
     555              :  *---------------------------------------------------
     556              :  */
     557              : 
     558              : Oid
     559          300 : getTSCurrentConfig(bool emitError)
     560              : {
     561              :     List       *namelist;
     562              : 
     563              :     /* if we have a cached value, return it */
     564          300 :     if (OidIsValid(TSCurrentConfigCache))
     565          276 :         return TSCurrentConfigCache;
     566              : 
     567              :     /* fail if GUC hasn't been set up yet */
     568           24 :     if (TSCurrentConfig == NULL || *TSCurrentConfig == '\0')
     569              :     {
     570            0 :         if (emitError)
     571            0 :             elog(ERROR, "text search configuration isn't set");
     572              :         else
     573            0 :             return InvalidOid;
     574              :     }
     575              : 
     576           24 :     if (TSConfigCacheHash == NULL)
     577              :     {
     578              :         /* First time through: initialize the tsconfig inval callback */
     579           12 :         init_ts_config_cache();
     580              :     }
     581              : 
     582              :     /* Look up the config */
     583           24 :     if (emitError)
     584              :     {
     585           24 :         namelist = stringToQualifiedNameList(TSCurrentConfig, NULL);
     586           24 :         TSCurrentConfigCache = get_ts_config_oid(namelist, false);
     587              :     }
     588              :     else
     589              :     {
     590            0 :         ErrorSaveContext escontext = {T_ErrorSaveContext};
     591              : 
     592            0 :         namelist = stringToQualifiedNameList(TSCurrentConfig,
     593              :                                              (Node *) &escontext);
     594            0 :         if (namelist != NIL)
     595            0 :             TSCurrentConfigCache = get_ts_config_oid(namelist, true);
     596              :         else
     597            0 :             TSCurrentConfigCache = InvalidOid;  /* bad name list syntax */
     598              :     }
     599              : 
     600           24 :     return TSCurrentConfigCache;
     601              : }
     602              : 
     603              : /* GUC check_hook for default_text_search_config */
     604              : bool
     605         7375 : check_default_text_search_config(char **newval, void **extra, GucSource source)
     606              : {
     607              :     /*
     608              :      * If we aren't inside a transaction, or connected to a database, we
     609              :      * cannot do the catalog accesses necessary to verify the config name.
     610              :      * Must accept it on faith.
     611              :      */
     612         7375 :     if (IsTransactionState() && MyDatabaseId != InvalidOid)
     613              :     {
     614         4047 :         ErrorSaveContext escontext = {T_ErrorSaveContext};
     615              :         List       *namelist;
     616              :         Oid         cfgId;
     617              :         HeapTuple   tuple;
     618              :         Form_pg_ts_config cfg;
     619              :         char       *buf;
     620              : 
     621         4047 :         namelist = stringToQualifiedNameList(*newval,
     622              :                                              (Node *) &escontext);
     623         4047 :         if (namelist != NIL)
     624         4047 :             cfgId = get_ts_config_oid(namelist, true);
     625              :         else
     626            0 :             cfgId = InvalidOid; /* bad name list syntax */
     627              : 
     628              :         /*
     629              :          * When source == PGC_S_TEST, don't throw a hard error for a
     630              :          * nonexistent configuration, only a NOTICE.  See comments in guc.h.
     631              :          */
     632         4047 :         if (!OidIsValid(cfgId))
     633              :         {
     634           17 :             if (source == PGC_S_TEST)
     635              :             {
     636            9 :                 ereport(NOTICE,
     637              :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
     638              :                          errmsg("text search configuration \"%s\" does not exist", *newval)));
     639           17 :                 return true;
     640              :             }
     641              :             else
     642            8 :                 return false;
     643              :         }
     644              : 
     645              :         /*
     646              :          * Modify the actually stored value to be fully qualified, to ensure
     647              :          * later changes of search_path don't affect it.
     648              :          */
     649         4030 :         tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
     650         4030 :         if (!HeapTupleIsValid(tuple))
     651            0 :             elog(ERROR, "cache lookup failed for text search configuration %u",
     652              :                  cfgId);
     653         4030 :         cfg = (Form_pg_ts_config) GETSTRUCT(tuple);
     654              : 
     655         4030 :         buf = quote_qualified_identifier(get_namespace_name(cfg->cfgnamespace),
     656         4030 :                                          NameStr(cfg->cfgname));
     657              : 
     658         4030 :         ReleaseSysCache(tuple);
     659              : 
     660              :         /* GUC wants it guc_malloc'd not palloc'd */
     661         4030 :         guc_free(*newval);
     662         4030 :         *newval = guc_strdup(LOG, buf);
     663         4030 :         pfree(buf);
     664         4030 :         if (!*newval)
     665            0 :             return false;
     666              :     }
     667              : 
     668         7358 :     return true;
     669              : }
     670              : 
     671              : /* GUC assign_hook for default_text_search_config */
     672              : void
     673         7354 : assign_default_text_search_config(const char *newval, void *extra)
     674              : {
     675              :     /* Just reset the cache to force a lookup on first use */
     676         7354 :     TSCurrentConfigCache = InvalidOid;
     677         7354 : }
        

Generated by: LCOV version 2.0-1