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

Generated by: LCOV version 2.0-1