LCOV - differential code coverage report
Current view: top level - src/backend/utils/cache - ts_cache.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 88.2 % 228 201 6 2 5 14 3 33 12 153 8 43 2 3
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 8 8 2 3 3 4
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  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-2023, 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 GIC         823 : InvalidateTSCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
      95                 : {
      96 CBC         823 :     HTAB       *hash = (HTAB *) DatumGetPointer(arg);
      97                 :     HASH_SEQ_STATUS status;
      98 ECB             :     TSAnyCacheEntry *entry;
      99                 : 
     100 GIC         823 :     hash_seq_init(&status, hash);
     101            2552 :     while ((entry = (TSAnyCacheEntry *) hash_seq_search(&status)) != NULL)
     102 CBC        1729 :         entry->isvalid = false;
     103 ECB             : 
     104                 :     /* Also invalidate the current-config cache if it's pg_ts_config */
     105 GIC         823 :     if (hash == TSConfigCacheHash)
     106             738 :         TSCurrentConfigCache = InvalidOid;
     107 CBC         823 : }
     108 ECB             : 
     109                 : /*
     110                 :  * Fetch parser cache entry
     111                 :  */
     112                 : TSParserCacheEntry *
     113 GIC       28198 : lookup_ts_parser_cache(Oid prsId)
     114                 : {
     115 ECB             :     TSParserCacheEntry *entry;
     116                 : 
     117 GIC       28198 :     if (TSParserCacheHash == NULL)
     118                 :     {
     119 ECB             :         /* First time through: initialize the hash table */
     120                 :         HASHCTL     ctl;
     121                 : 
     122 GIC         365 :         ctl.keysize = sizeof(Oid);
     123             365 :         ctl.entrysize = sizeof(TSParserCacheEntry);
     124 CBC         365 :         TSParserCacheHash = hash_create("Tsearch parser cache", 4,
     125 ECB             :                                         &ctl, HASH_ELEM | HASH_BLOBS);
     126                 :         /* Flush cache on pg_ts_parser changes */
     127 GIC         365 :         CacheRegisterSyscacheCallback(TSPARSEROID, InvalidateTSCacheCallBack,
     128                 :                                       PointerGetDatum(TSParserCacheHash));
     129 ECB             : 
     130                 :         /* Also make sure CacheMemoryContext exists */
     131 GIC         365 :         if (!CacheMemoryContext)
     132 UIC           0 :             CreateCacheMemoryContext();
     133 ECB             :     }
     134 EUB             : 
     135                 :     /* Check single-entry cache */
     136 GIC       28198 :     if (lastUsedParser && lastUsedParser->prsId == prsId &&
     137           27833 :         lastUsedParser->isvalid)
     138 CBC       27830 :         return lastUsedParser;
     139 ECB             : 
     140                 :     /* Try to look up an existing entry */
     141 GIC         368 :     entry = (TSParserCacheEntry *) hash_search(TSParserCacheHash,
     142                 :                                                &prsId,
     143 ECB             :                                                HASH_FIND, NULL);
     144 GIC         368 :     if (entry == NULL || !entry->isvalid)
     145                 :     {
     146 ECB             :         /*
     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 GIC         368 :         tp = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
     154             368 :         if (!HeapTupleIsValid(tp))
     155 LBC           0 :             elog(ERROR, "cache lookup failed for text search parser %u",
     156 ECB             :                  prsId);
     157 GBC         368 :         prs = (Form_pg_ts_parser) GETSTRUCT(tp);
     158                 : 
     159 ECB             :         /*
     160                 :          * Sanity checks
     161                 :          */
     162 GIC         368 :         if (!OidIsValid(prs->prsstart))
     163 UIC           0 :             elog(ERROR, "text search parser %u has no prsstart method", prsId);
     164 CBC         368 :         if (!OidIsValid(prs->prstoken))
     165 UBC           0 :             elog(ERROR, "text search parser %u has no prstoken method", prsId);
     166 CBC         368 :         if (!OidIsValid(prs->prsend))
     167 UBC           0 :             elog(ERROR, "text search parser %u has no prsend method", prsId);
     168 ECB             : 
     169 GBC         368 :         if (entry == NULL)
     170                 :         {
     171 ECB             :             bool        found;
     172                 : 
     173                 :             /* Now make the cache entry */
     174                 :             entry = (TSParserCacheEntry *)
     175 GNC         365 :                 hash_search(TSParserCacheHash, &prsId, HASH_ENTER, &found);
     176 CBC         365 :             Assert(!found);     /* it wasn't there a moment ago */
     177                 :         }
     178                 : 
     179           10672 :         MemSet(entry, 0, sizeof(TSParserCacheEntry));
     180             368 :         entry->prsId = prsId;
     181             368 :         entry->startOid = prs->prsstart;
     182             368 :         entry->tokenOid = prs->prstoken;
     183             368 :         entry->endOid = prs->prsend;
     184             368 :         entry->headlineOid = prs->prsheadline;
     185             368 :         entry->lextypeOid = prs->prslextype;
     186                 : 
     187             368 :         ReleaseSysCache(tp);
     188                 : 
     189             368 :         fmgr_info_cxt(entry->startOid, &entry->prsstart, CacheMemoryContext);
     190             368 :         fmgr_info_cxt(entry->tokenOid, &entry->prstoken, CacheMemoryContext);
     191             368 :         fmgr_info_cxt(entry->endOid, &entry->prsend, CacheMemoryContext);
     192             368 :         if (OidIsValid(entry->headlineOid))
     193             368 :             fmgr_info_cxt(entry->headlineOid, &entry->prsheadline,
     194                 :                           CacheMemoryContext);
     195                 : 
     196             368 :         entry->isvalid = true;
     197                 :     }
     198                 : 
     199             368 :     lastUsedParser = entry;
     200                 : 
     201             368 :     return entry;
     202                 : }
     203                 : 
     204                 : /*
     205                 :  * Fetch dictionary cache entry
     206                 :  */
     207                 : TSDictionaryCacheEntry *
     208            7554 : lookup_ts_dictionary_cache(Oid dictId)
     209                 : {
     210                 :     TSDictionaryCacheEntry *entry;
     211                 : 
     212            7554 :     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 UBC           0 :             CreateCacheMemoryContext();
     230                 :     }
     231                 : 
     232                 :     /* Check single-entry cache */
     233 CBC        7554 :     if (lastUsedDictionary && lastUsedDictionary->dictId == dictId &&
     234            6289 :         lastUsedDictionary->isvalid)
     235            6271 :         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 UBC           0 :             elog(ERROR, "cache lookup failed for text search dictionary %u",
     256                 :                  dictId);
     257 CBC          86 :         dict = (Form_pg_ts_dict) GETSTRUCT(tpdict);
     258                 : 
     259                 :         /*
     260                 :          * Sanity checks
     261                 :          */
     262              86 :         if (!OidIsValid(dict->dicttemplate))
     263 UBC           0 :             elog(ERROR, "text search dictionary %u has no template", dictId);
     264                 : 
     265                 :         /*
     266                 :          * Retrieve dictionary's template
     267                 :          */
     268 CBC          86 :         tptmpl = SearchSysCache1(TSTEMPLATEOID,
     269                 :                                  ObjectIdGetDatum(dict->dicttemplate));
     270              86 :         if (!HeapTupleIsValid(tptmpl))
     271 UBC           0 :             elog(ERROR, "cache lookup failed for text search template %u",
     272                 :                  dict->dicttemplate);
     273 CBC          86 :         template = (Form_pg_ts_template) GETSTRUCT(tptmpl);
     274                 : 
     275                 :         /*
     276                 :          * Sanity checks
     277                 :          */
     278              86 :         if (!OidIsValid(template->tmpllexize))
     279 UBC           0 :             elog(ERROR, "text search template %u has no lexize method",
     280                 :                  template->tmpllexize);
     281                 : 
     282 CBC          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              53 :             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
     325                 :              */
     326              86 :             oldcontext = MemoryContextSwitchTo(entry->dictCtx);
     327                 : 
     328              86 :             opt = SysCacheGetAttr(TSDICTOID, tpdict,
     329                 :                                   Anum_pg_ts_dict_dictinitoption,
     330                 :                                   &isnull);
     331              86 :             if (isnull)
     332              17 :                 dictoptions = NIL;
     333                 :             else
     334              69 :                 dictoptions = deserialize_deflist(opt);
     335                 : 
     336              86 :             entry->dictData =
     337              86 :                 DatumGetPointer(OidFunctionCall1(template->tmplinit,
     338                 :                                                  PointerGetDatum(dictoptions)));
     339                 : 
     340              86 :             MemoryContextSwitchTo(oldcontext);
     341                 :         }
     342                 : 
     343              86 :         ReleaseSysCache(tptmpl);
     344              86 :         ReleaseSysCache(tpdict);
     345                 : 
     346              86 :         fmgr_info_cxt(entry->lexizeOid, &entry->lexize, entry->dictCtx);
     347                 : 
     348              86 :         entry->isvalid = true;
     349                 :     }
     350                 : 
     351            1283 :     lastUsedDictionary = entry;
     352                 : 
     353            1283 :     return entry;
     354                 : }
     355                 : 
     356                 : /*
     357                 :  * Initialize config cache and prepare callbacks.  This is split out of
     358                 :  * lookup_ts_config_cache because we need to activate the callback before
     359                 :  * caching TSCurrentConfigCache, too.
     360                 :  */
     361                 : static void
     362              19 : init_ts_config_cache(void)
     363                 : {
     364                 :     HASHCTL     ctl;
     365                 : 
     366              19 :     ctl.keysize = sizeof(Oid);
     367              19 :     ctl.entrysize = sizeof(TSConfigCacheEntry);
     368              19 :     TSConfigCacheHash = hash_create("Tsearch configuration cache", 16,
     369                 :                                     &ctl, HASH_ELEM | HASH_BLOBS);
     370                 :     /* Flush cache on pg_ts_config and pg_ts_config_map changes */
     371              19 :     CacheRegisterSyscacheCallback(TSCONFIGOID, InvalidateTSCacheCallBack,
     372                 :                                   PointerGetDatum(TSConfigCacheHash));
     373              19 :     CacheRegisterSyscacheCallback(TSCONFIGMAP, InvalidateTSCacheCallBack,
     374                 :                                   PointerGetDatum(TSConfigCacheHash));
     375                 : 
     376                 :     /* Also make sure CacheMemoryContext exists */
     377              19 :     if (!CacheMemoryContext)
     378 UBC           0 :         CreateCacheMemoryContext();
     379 CBC          19 : }
     380                 : 
     381                 : /*
     382                 :  * Fetch configuration cache entry
     383                 :  */
     384                 : TSConfigCacheEntry *
     385            2463 : lookup_ts_config_cache(Oid cfgId)
     386                 : {
     387                 :     TSConfigCacheEntry *entry;
     388                 : 
     389            2463 :     if (TSConfigCacheHash == NULL)
     390                 :     {
     391                 :         /* First time through: initialize the hash table */
     392              13 :         init_ts_config_cache();
     393                 :     }
     394                 : 
     395                 :     /* Check single-entry cache */
     396            2463 :     if (lastUsedConfig && lastUsedConfig->cfgId == cfgId &&
     397            2390 :         lastUsedConfig->isvalid)
     398            2384 :         return lastUsedConfig;
     399                 : 
     400                 :     /* Try to look up an existing entry */
     401              79 :     entry = (TSConfigCacheEntry *) hash_search(TSConfigCacheHash,
     402                 :                                                &cfgId,
     403                 :                                                HASH_FIND, NULL);
     404              79 :     if (entry == NULL || !entry->isvalid)
     405                 :     {
     406                 :         /*
     407                 :          * If we didn't find one, we want to make one. But first look up the
     408                 :          * object to be sure the OID is real.
     409                 :          */
     410                 :         HeapTuple   tp;
     411                 :         Form_pg_ts_config cfg;
     412                 :         Relation    maprel;
     413                 :         Relation    mapidx;
     414                 :         ScanKeyData mapskey;
     415                 :         SysScanDesc mapscan;
     416                 :         HeapTuple   maptup;
     417                 :         ListDictionary maplists[MAXTOKENTYPE + 1];
     418                 :         Oid         mapdicts[MAXDICTSPERTT];
     419                 :         int         maxtokentype;
     420                 :         int         ndicts;
     421                 :         int         i;
     422                 : 
     423              46 :         tp = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
     424              46 :         if (!HeapTupleIsValid(tp))
     425 UBC           0 :             elog(ERROR, "cache lookup failed for text search configuration %u",
     426                 :                  cfgId);
     427 CBC          46 :         cfg = (Form_pg_ts_config) GETSTRUCT(tp);
     428                 : 
     429                 :         /*
     430                 :          * Sanity checks
     431                 :          */
     432              46 :         if (!OidIsValid(cfg->cfgparser))
     433 UBC           0 :             elog(ERROR, "text search configuration %u has no parser", cfgId);
     434                 : 
     435 CBC          46 :         if (entry == NULL)
     436                 :         {
     437                 :             bool        found;
     438                 : 
     439                 :             /* Now make the cache entry */
     440                 :             entry = (TSConfigCacheEntry *)
     441              40 :                 hash_search(TSConfigCacheHash,
     442                 :                             &cfgId,
     443                 :                             HASH_ENTER, &found);
     444              40 :             Assert(!found);     /* it wasn't there a moment ago */
     445                 :         }
     446                 :         else
     447                 :         {
     448                 :             /* Cleanup old contents */
     449               6 :             if (entry->map)
     450                 :             {
     451             144 :                 for (i = 0; i < entry->lenmap; i++)
     452             138 :                     if (entry->map[i].dictIds)
     453             114 :                         pfree(entry->map[i].dictIds);
     454               6 :                 pfree(entry->map);
     455                 :             }
     456                 :         }
     457                 : 
     458             184 :         MemSet(entry, 0, sizeof(TSConfigCacheEntry));
     459              46 :         entry->cfgId = cfgId;
     460              46 :         entry->prsId = cfg->cfgparser;
     461                 : 
     462              46 :         ReleaseSysCache(tp);
     463                 : 
     464                 :         /*
     465                 :          * Scan pg_ts_config_map to gather dictionary list for each token type
     466                 :          *
     467                 :          * Because the index is on (mapcfg, maptokentype, mapseqno), we will
     468                 :          * see the entries in maptokentype order, and in mapseqno order for
     469                 :          * each token type, even though we didn't explicitly ask for that.
     470                 :          */
     471              46 :         MemSet(maplists, 0, sizeof(maplists));
     472              46 :         maxtokentype = 0;
     473              46 :         ndicts = 0;
     474                 : 
     475              46 :         ScanKeyInit(&mapskey,
     476                 :                     Anum_pg_ts_config_map_mapcfg,
     477                 :                     BTEqualStrategyNumber, F_OIDEQ,
     478                 :                     ObjectIdGetDatum(cfgId));
     479                 : 
     480              46 :         maprel = table_open(TSConfigMapRelationId, AccessShareLock);
     481              46 :         mapidx = index_open(TSConfigMapIndexId, AccessShareLock);
     482              46 :         mapscan = systable_beginscan_ordered(maprel, mapidx,
     483                 :                                              NULL, 1, &mapskey);
     484                 : 
     485            1037 :         while ((maptup = systable_getnext_ordered(mapscan, ForwardScanDirection)) != NULL)
     486                 :         {
     487             991 :             Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
     488             991 :             int         toktype = cfgmap->maptokentype;
     489                 : 
     490             991 :             if (toktype <= 0 || toktype > MAXTOKENTYPE)
     491 UBC           0 :                 elog(ERROR, "maptokentype value %d is out of range", toktype);
     492 CBC         991 :             if (toktype < maxtokentype)
     493 UBC           0 :                 elog(ERROR, "maptokentype entries are out of order");
     494 CBC         991 :             if (toktype > maxtokentype)
     495                 :             {
     496                 :                 /* starting a new token type, but first save the prior data */
     497             856 :                 if (ndicts > 0)
     498                 :                 {
     499             810 :                     maplists[maxtokentype].len = ndicts;
     500             810 :                     maplists[maxtokentype].dictIds = (Oid *)
     501             810 :                         MemoryContextAlloc(CacheMemoryContext,
     502                 :                                            sizeof(Oid) * ndicts);
     503             810 :                     memcpy(maplists[maxtokentype].dictIds, mapdicts,
     504                 :                            sizeof(Oid) * ndicts);
     505                 :                 }
     506             856 :                 maxtokentype = toktype;
     507             856 :                 mapdicts[0] = cfgmap->mapdict;
     508             856 :                 ndicts = 1;
     509                 :             }
     510                 :             else
     511                 :             {
     512                 :                 /* continuing data for current token type */
     513             135 :                 if (ndicts >= MAXDICTSPERTT)
     514 UBC           0 :                     elog(ERROR, "too many pg_ts_config_map entries for one token type");
     515 CBC         135 :                 mapdicts[ndicts++] = cfgmap->mapdict;
     516                 :             }
     517                 :         }
     518                 : 
     519              46 :         systable_endscan_ordered(mapscan);
     520              46 :         index_close(mapidx, AccessShareLock);
     521              46 :         table_close(maprel, AccessShareLock);
     522                 : 
     523              46 :         if (ndicts > 0)
     524                 :         {
     525                 :             /* save the last token type's dictionaries */
     526              46 :             maplists[maxtokentype].len = ndicts;
     527              46 :             maplists[maxtokentype].dictIds = (Oid *)
     528              46 :                 MemoryContextAlloc(CacheMemoryContext,
     529                 :                                    sizeof(Oid) * ndicts);
     530              46 :             memcpy(maplists[maxtokentype].dictIds, mapdicts,
     531                 :                    sizeof(Oid) * ndicts);
     532                 :             /* and save the overall map */
     533              46 :             entry->lenmap = maxtokentype + 1;
     534              46 :             entry->map = (ListDictionary *)
     535              46 :                 MemoryContextAlloc(CacheMemoryContext,
     536              46 :                                    sizeof(ListDictionary) * entry->lenmap);
     537              46 :             memcpy(entry->map, maplists,
     538              46 :                    sizeof(ListDictionary) * entry->lenmap);
     539                 :         }
     540                 : 
     541              46 :         entry->isvalid = true;
     542                 :     }
     543                 : 
     544              79 :     lastUsedConfig = entry;
     545                 : 
     546              79 :     return entry;
     547                 : }
     548                 : 
     549                 : 
     550                 : /*---------------------------------------------------
     551                 :  * GUC variable "default_text_search_config"
     552                 :  *---------------------------------------------------
     553                 :  */
     554                 : 
     555                 : Oid
     556             195 : getTSCurrentConfig(bool emitError)
     557                 : {
     558                 :     List       *namelist;
     559                 : 
     560                 :     /* if we have a cached value, return it */
     561 GIC         195 :     if (OidIsValid(TSCurrentConfigCache))
     562             180 :         return TSCurrentConfigCache;
     563 ECB             : 
     564                 :     /* fail if GUC hasn't been set up yet */
     565 GIC          15 :     if (TSCurrentConfig == NULL || *TSCurrentConfig == '\0')
     566                 :     {
     567 LBC           0 :         if (emitError)
     568 UIC           0 :             elog(ERROR, "text search configuration isn't set");
     569 EUB             :         else
     570 UBC           0 :             return InvalidOid;
     571                 :     }
     572 EUB             : 
     573 GIC          15 :     if (TSConfigCacheHash == NULL)
     574                 :     {
     575 ECB             :         /* First time through: initialize the tsconfig inval callback */
     576 GIC           6 :         init_ts_config_cache();
     577                 :     }
     578 ECB             : 
     579                 :     /* Look up the config */
     580 GNC          15 :     if (emitError)
     581                 :     {
     582              15 :         namelist = stringToQualifiedNameList(TSCurrentConfig, NULL);
     583              15 :         TSCurrentConfigCache = get_ts_config_oid(namelist, false);
     584                 :     }
     585                 :     else
     586                 :     {
     587 UNC           0 :         ErrorSaveContext escontext = {T_ErrorSaveContext};
     588                 : 
     589               0 :         namelist = stringToQualifiedNameList(TSCurrentConfig,
     590                 :                                              (Node *) &escontext);
     591               0 :         if (namelist != NIL)
     592               0 :             TSCurrentConfigCache = get_ts_config_oid(namelist, true);
     593                 :         else
     594               0 :             TSCurrentConfigCache = InvalidOid;  /* bad name list syntax */
     595                 :     }
     596                 : 
     597 CBC          15 :     return TSCurrentConfigCache;
     598 ECB             : }
     599                 : 
     600                 : /* GUC check_hook for default_text_search_config */
     601                 : bool
     602 GNC        6037 : check_default_text_search_config(char **newval, void **extra, GucSource source)
     603                 : {
     604 EUB             :     /*
     605                 :      * If we aren't inside a transaction, or connected to a database, we
     606                 :      * cannot do the catalog accesses necessary to verify the config name.
     607                 :      * Must accept it on faith.
     608                 :      */
     609 GBC        6037 :     if (IsTransactionState() && MyDatabaseId != InvalidOid)
     610                 :     {
     611 GNC        2618 :         ErrorSaveContext escontext = {T_ErrorSaveContext};
     612                 :         List       *namelist;
     613                 :         Oid         cfgId;
     614 ECB             :         HeapTuple   tuple;
     615                 :         Form_pg_ts_config cfg;
     616                 :         char       *buf;
     617                 : 
     618 GNC        2618 :         namelist = stringToQualifiedNameList(*newval,
     619                 :                                              (Node *) &escontext);
     620            2618 :         if (namelist != NIL)
     621            2618 :             cfgId = get_ts_config_oid(namelist, true);
     622                 :         else
     623 UNC           0 :             cfgId = InvalidOid; /* bad name list syntax */
     624 ECB             : 
     625                 :         /*
     626                 :          * When source == PGC_S_TEST, don't throw a hard error for a
     627                 :          * nonexistent configuration, only a NOTICE.  See comments in guc.h.
     628                 :          */
     629 GIC        2618 :         if (!OidIsValid(cfgId))
     630                 :         {
     631 CBC          13 :             if (source == PGC_S_TEST)
     632                 :             {
     633               7 :                 ereport(NOTICE,
     634                 :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
     635                 :                          errmsg("text search configuration \"%s\" does not exist", *newval)));
     636 GIC          13 :                 return true;
     637                 :             }
     638                 :             else
     639               6 :                 return false;
     640 ECB             :         }
     641                 : 
     642                 :         /*
     643                 :          * Modify the actually stored value to be fully qualified, to ensure
     644                 :          * later changes of search_path don't affect it.
     645 EUB             :          */
     646 GIC        2605 :         tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
     647            2605 :         if (!HeapTupleIsValid(tuple))
     648 UIC           0 :             elog(ERROR, "cache lookup failed for text search configuration %u",
     649                 :                  cfgId);
     650 GIC        2605 :         cfg = (Form_pg_ts_config) GETSTRUCT(tuple);
     651 ECB             : 
     652 GIC        2605 :         buf = quote_qualified_identifier(get_namespace_name(cfg->cfgnamespace),
     653 CBC        2605 :                                          NameStr(cfg->cfgname));
     654                 : 
     655            2605 :         ReleaseSysCache(tuple);
     656                 : 
     657                 :         /* GUC wants it guc_malloc'd not palloc'd */
     658 GNC        2605 :         guc_free(*newval);
     659            2605 :         *newval = guc_strdup(LOG, buf);
     660 GIC        2605 :         pfree(buf);
     661 CBC        2605 :         if (!*newval)
     662 UIC           0 :             return false;
     663                 :     }
     664                 : 
     665 GIC        6024 :     return true;
     666                 : }
     667                 : 
     668 ECB             : /* GUC assign_hook for default_text_search_config */
     669                 : void
     670 GNC        6021 : assign_default_text_search_config(const char *newval, void *extra)
     671                 : {
     672 ECB             :     /* Just reset the cache to force a lookup on first use */
     673 GIC        6021 :     TSCurrentConfigCache = InvalidOid;
     674 CBC        6021 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a