LCOV - code coverage report
Current view: top level - src/backend/utils/cache - ts_cache.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 90.5 % 304 275
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 49.4 % 239 118

             Branch data     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                 :         334 : InvalidateTSCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
      95                 :             : {
      96                 :         334 :         HTAB       *hash = (HTAB *) DatumGetPointer(arg);
      97                 :         334 :         HASH_SEQ_STATUS status;
      98                 :         334 :         TSAnyCacheEntry *entry;
      99                 :             : 
     100                 :         334 :         hash_seq_init(&status, hash);
     101         [ +  + ]:        1174 :         while ((entry = (TSAnyCacheEntry *) hash_seq_search(&status)) != NULL)
     102                 :         840 :                 entry->isvalid = false;
     103                 :             : 
     104                 :             :         /* Also invalidate the current-config cache if it's pg_ts_config */
     105         [ +  + ]:         334 :         if (hash == TSConfigCacheHash)
     106                 :         314 :                 TSCurrentConfigCache = InvalidOid;
     107                 :         334 : }
     108                 :             : 
     109                 :             : /*
     110                 :             :  * Fetch parser cache entry
     111                 :             :  */
     112                 :             : TSParserCacheEntry *
     113                 :         975 : lookup_ts_parser_cache(Oid prsId)
     114                 :             : {
     115                 :         975 :         TSParserCacheEntry *entry;
     116                 :             : 
     117         [ +  + ]:         975 :         if (TSParserCacheHash == NULL)
     118                 :             :         {
     119                 :             :                 /* First time through: initialize the hash table */
     120                 :           6 :                 HASHCTL         ctl;
     121                 :             : 
     122                 :           6 :                 ctl.keysize = sizeof(Oid);
     123                 :           6 :                 ctl.entrysize = sizeof(TSParserCacheEntry);
     124                 :           6 :                 TSParserCacheHash = hash_create("Tsearch parser cache", 4,
     125                 :             :                                                                                 &ctl, HASH_ELEM | HASH_BLOBS);
     126                 :             :                 /* Flush cache on pg_ts_parser changes */
     127                 :           6 :                 CacheRegisterSyscacheCallback(TSPARSEROID, InvalidateTSCacheCallBack,
     128                 :           6 :                                                                           PointerGetDatum(TSParserCacheHash));
     129                 :             : 
     130                 :             :                 /* Also make sure CacheMemoryContext exists */
     131         [ +  - ]:           6 :                 if (!CacheMemoryContext)
     132                 :           0 :                         CreateCacheMemoryContext();
     133                 :           6 :         }
     134                 :             : 
     135                 :             :         /* Check single-entry cache */
     136   [ +  +  +  -  :         975 :         if (lastUsedParser && lastUsedParser->prsId == prsId &&
                   -  + ]
     137                 :         969 :                 lastUsedParser->isvalid)
     138                 :         969 :                 return lastUsedParser;
     139                 :             : 
     140                 :             :         /* Try to look up an existing entry */
     141                 :           6 :         entry = (TSParserCacheEntry *) hash_search(TSParserCacheHash,
     142                 :             :                                                                                            &prsId,
     143                 :             :                                                                                            HASH_FIND, NULL);
     144   [ -  +  #  # ]:           6 :         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                 :           6 :                 HeapTuple       tp;
     151                 :           6 :                 Form_pg_ts_parser prs;
     152                 :             : 
     153                 :           6 :                 tp = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
     154         [ +  - ]:           6 :                 if (!HeapTupleIsValid(tp))
     155   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for text search parser %u",
     156                 :             :                                  prsId);
     157                 :           6 :                 prs = (Form_pg_ts_parser) GETSTRUCT(tp);
     158                 :             : 
     159                 :             :                 /*
     160                 :             :                  * Sanity checks
     161                 :             :                  */
     162         [ +  - ]:           6 :                 if (!OidIsValid(prs->prsstart))
     163   [ #  #  #  # ]:           0 :                         elog(ERROR, "text search parser %u has no prsstart method", prsId);
     164         [ +  - ]:           6 :                 if (!OidIsValid(prs->prstoken))
     165   [ #  #  #  # ]:           0 :                         elog(ERROR, "text search parser %u has no prstoken method", prsId);
     166         [ +  - ]:           6 :                 if (!OidIsValid(prs->prsend))
     167   [ #  #  #  # ]:           0 :                         elog(ERROR, "text search parser %u has no prsend method", prsId);
     168                 :             : 
     169         [ -  + ]:           6 :                 if (entry == NULL)
     170                 :             :                 {
     171                 :           6 :                         bool            found;
     172                 :             : 
     173                 :             :                         /* Now make the cache entry */
     174                 :           6 :                         entry = (TSParserCacheEntry *)
     175                 :           6 :                                 hash_search(TSParserCacheHash, &prsId, HASH_ENTER, &found);
     176         [ +  - ]:           6 :                         Assert(!found);         /* it wasn't there a moment ago */
     177                 :           6 :                 }
     178                 :             : 
     179   [ +  -  +  -  :         174 :                 MemSet(entry, 0, sizeof(TSParserCacheEntry));
          +  -  -  +  +  
                      + ]
     180                 :           6 :                 entry->prsId = prsId;
     181                 :           6 :                 entry->startOid = prs->prsstart;
     182                 :           6 :                 entry->tokenOid = prs->prstoken;
     183                 :           6 :                 entry->endOid = prs->prsend;
     184                 :           6 :                 entry->headlineOid = prs->prsheadline;
     185                 :           6 :                 entry->lextypeOid = prs->prslextype;
     186                 :             : 
     187                 :           6 :                 ReleaseSysCache(tp);
     188                 :             : 
     189                 :           6 :                 fmgr_info_cxt(entry->startOid, &entry->prsstart, CacheMemoryContext);
     190                 :           6 :                 fmgr_info_cxt(entry->tokenOid, &entry->prstoken, CacheMemoryContext);
     191                 :           6 :                 fmgr_info_cxt(entry->endOid, &entry->prsend, CacheMemoryContext);
     192         [ -  + ]:           6 :                 if (OidIsValid(entry->headlineOid))
     193                 :          12 :                         fmgr_info_cxt(entry->headlineOid, &entry->prsheadline,
     194                 :           6 :                                                   CacheMemoryContext);
     195                 :             : 
     196                 :           6 :                 entry->isvalid = true;
     197                 :           6 :         }
     198                 :             : 
     199                 :           6 :         lastUsedParser = entry;
     200                 :             : 
     201                 :           6 :         return entry;
     202                 :         975 : }
     203                 :             : 
     204                 :             : /*
     205                 :             :  * Fetch dictionary cache entry
     206                 :             :  */
     207                 :             : TSDictionaryCacheEntry *
     208                 :        2482 : lookup_ts_dictionary_cache(Oid dictId)
     209                 :             : {
     210                 :        2482 :         TSDictionaryCacheEntry *entry;
     211                 :             : 
     212         [ +  + ]:        2482 :         if (TSDictionaryCacheHash == NULL)
     213                 :             :         {
     214                 :             :                 /* First time through: initialize the hash table */
     215                 :           5 :                 HASHCTL         ctl;
     216                 :             : 
     217                 :           5 :                 ctl.keysize = sizeof(Oid);
     218                 :           5 :                 ctl.entrysize = sizeof(TSDictionaryCacheEntry);
     219                 :           5 :                 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                 :           5 :                 CacheRegisterSyscacheCallback(TSDICTOID, InvalidateTSCacheCallBack,
     223                 :           5 :                                                                           PointerGetDatum(TSDictionaryCacheHash));
     224                 :           5 :                 CacheRegisterSyscacheCallback(TSTEMPLATEOID, InvalidateTSCacheCallBack,
     225                 :           5 :                                                                           PointerGetDatum(TSDictionaryCacheHash));
     226                 :             : 
     227                 :             :                 /* Also make sure CacheMemoryContext exists */
     228         [ +  - ]:           5 :                 if (!CacheMemoryContext)
     229                 :           0 :                         CreateCacheMemoryContext();
     230                 :           5 :         }
     231                 :             : 
     232                 :             :         /* Check single-entry cache */
     233   [ +  +  +  +  :        2482 :         if (lastUsedDictionary && lastUsedDictionary->dictId == dictId &&
                   +  + ]
     234                 :        2063 :                 lastUsedDictionary->isvalid)
     235                 :        2060 :                 return lastUsedDictionary;
     236                 :             : 
     237                 :             :         /* Try to look up an existing entry */
     238                 :         422 :         entry = (TSDictionaryCacheEntry *) hash_search(TSDictionaryCacheHash,
     239                 :             :                                                                                                    &dictId,
     240                 :             :                                                                                                    HASH_FIND, NULL);
     241   [ +  +  +  + ]:         422 :         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                 :          23 :                 HeapTuple       tpdict,
     248                 :             :                                         tptmpl;
     249                 :          23 :                 Form_pg_ts_dict dict;
     250                 :          23 :                 Form_pg_ts_template template;
     251                 :          23 :                 MemoryContext saveCtx;
     252                 :             : 
     253                 :          23 :                 tpdict = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
     254         [ +  - ]:          23 :                 if (!HeapTupleIsValid(tpdict))
     255   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for text search dictionary %u",
     256                 :             :                                  dictId);
     257                 :          23 :                 dict = (Form_pg_ts_dict) GETSTRUCT(tpdict);
     258                 :             : 
     259                 :             :                 /*
     260                 :             :                  * Sanity checks
     261                 :             :                  */
     262         [ +  - ]:          23 :                 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                 :          23 :                 tptmpl = SearchSysCache1(TSTEMPLATEOID,
     269                 :          23 :                                                                  ObjectIdGetDatum(dict->dicttemplate));
     270         [ +  - ]:          23 :                 if (!HeapTupleIsValid(tptmpl))
     271   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for text search template %u",
     272                 :             :                                  dict->dicttemplate);
     273                 :          23 :                 template = (Form_pg_ts_template) GETSTRUCT(tptmpl);
     274                 :             : 
     275                 :             :                 /*
     276                 :             :                  * Sanity checks
     277                 :             :                  */
     278         [ +  - ]:          23 :                 if (!OidIsValid(template->tmpllexize))
     279   [ #  #  #  # ]:           0 :                         elog(ERROR, "text search template %u has no lexize method",
     280                 :             :                                  template->tmpllexize);
     281                 :             : 
     282         [ +  + ]:          23 :                 if (entry == NULL)
     283                 :             :                 {
     284                 :          15 :                         bool            found;
     285                 :             : 
     286                 :             :                         /* Now make the cache entry */
     287                 :          15 :                         entry = (TSDictionaryCacheEntry *)
     288                 :          15 :                                 hash_search(TSDictionaryCacheHash,
     289                 :             :                                                         &dictId,
     290                 :             :                                                         HASH_ENTER, &found);
     291         [ +  - ]:          15 :                         Assert(!found);         /* it wasn't there a moment ago */
     292                 :             : 
     293                 :             :                         /* Create private memory context the first time through */
     294                 :          15 :                         saveCtx = AllocSetContextCreate(CacheMemoryContext,
     295                 :             :                                                                                         "TS dictionary",
     296                 :             :                                                                                         ALLOCSET_SMALL_SIZES);
     297                 :          15 :                         MemoryContextCopyAndSetIdentifier(saveCtx, NameStr(dict->dictname));
     298                 :          15 :                 }
     299                 :             :                 else
     300                 :             :                 {
     301                 :             :                         /* Clear the existing entry's private context */
     302                 :           8 :                         saveCtx = entry->dictCtx;
     303                 :             :                         /* Don't let context's ident pointer dangle while we reset it */
     304                 :           8 :                         MemoryContextSetIdentifier(saveCtx, NULL);
     305                 :           8 :                         MemoryContextReset(saveCtx);
     306                 :           8 :                         MemoryContextCopyAndSetIdentifier(saveCtx, NameStr(dict->dictname));
     307                 :             :                 }
     308                 :             : 
     309   [ +  -  +  -  :         253 :                 MemSet(entry, 0, sizeof(TSDictionaryCacheEntry));
          +  -  -  +  +  
                      + ]
     310                 :          23 :                 entry->dictId = dictId;
     311                 :          23 :                 entry->dictCtx = saveCtx;
     312                 :             : 
     313                 :          23 :                 entry->lexizeOid = template->tmpllexize;
     314                 :             : 
     315         [ -  + ]:          23 :                 if (OidIsValid(template->tmplinit))
     316                 :             :                 {
     317                 :          23 :                         List       *dictoptions;
     318                 :          23 :                         Datum           opt;
     319                 :          23 :                         bool            isnull;
     320                 :          23 :                         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                 :          23 :                         oldcontext = MemoryContextSwitchTo(entry->dictCtx);
     329                 :             : 
     330                 :          23 :                         opt = SysCacheGetAttr(TSDICTOID, tpdict,
     331                 :             :                                                                   Anum_pg_ts_dict_dictinitoption,
     332                 :             :                                                                   &isnull);
     333         [ +  + ]:          23 :                         if (isnull)
     334                 :           4 :                                 dictoptions = NIL;
     335                 :             :                         else
     336                 :          19 :                                 dictoptions = deserialize_deflist(opt);
     337                 :             : 
     338                 :          23 :                         entry->dictData =
     339                 :          23 :                                 DatumGetPointer(OidFunctionCall1(template->tmplinit,
     340                 :             :                                                                                                  PointerGetDatum(dictoptions)));
     341                 :             : 
     342                 :          23 :                         MemoryContextSwitchTo(oldcontext);
     343                 :          23 :                 }
     344                 :             : 
     345                 :          23 :                 ReleaseSysCache(tptmpl);
     346                 :          23 :                 ReleaseSysCache(tpdict);
     347                 :             : 
     348                 :          23 :                 fmgr_info_cxt(entry->lexizeOid, &entry->lexize, entry->dictCtx);
     349                 :             : 
     350                 :          23 :                 entry->isvalid = true;
     351                 :          23 :         }
     352                 :             : 
     353                 :         422 :         lastUsedDictionary = entry;
     354                 :             : 
     355                 :         422 :         return entry;
     356                 :        2482 : }
     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                 :           5 : init_ts_config_cache(void)
     365                 :             : {
     366                 :           5 :         HASHCTL         ctl;
     367                 :             : 
     368                 :           5 :         ctl.keysize = sizeof(Oid);
     369                 :           5 :         ctl.entrysize = sizeof(TSConfigCacheEntry);
     370                 :           5 :         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                 :           5 :         CacheRegisterSyscacheCallback(TSCONFIGOID, InvalidateTSCacheCallBack,
     374                 :           5 :                                                                   PointerGetDatum(TSConfigCacheHash));
     375                 :           5 :         CacheRegisterSyscacheCallback(TSCONFIGMAP, InvalidateTSCacheCallBack,
     376                 :           5 :                                                                   PointerGetDatum(TSConfigCacheHash));
     377                 :             : 
     378                 :             :         /* Also make sure CacheMemoryContext exists */
     379         [ +  - ]:           5 :         if (!CacheMemoryContext)
     380                 :           0 :                 CreateCacheMemoryContext();
     381                 :           5 : }
     382                 :             : 
     383                 :             : /*
     384                 :             :  * Fetch configuration cache entry
     385                 :             :  */
     386                 :             : TSConfigCacheEntry *
     387                 :         822 : lookup_ts_config_cache(Oid cfgId)
     388                 :             : {
     389                 :         822 :         TSConfigCacheEntry *entry;
     390                 :             : 
     391         [ +  + ]:         822 :         if (TSConfigCacheHash == NULL)
     392                 :             :         {
     393                 :             :                 /* First time through: initialize the hash table */
     394                 :           3 :                 init_ts_config_cache();
     395                 :           3 :         }
     396                 :             : 
     397                 :             :         /* Check single-entry cache */
     398   [ +  +  +  +  :         822 :         if (lastUsedConfig && lastUsedConfig->cfgId == cfgId &&
                   +  + ]
     399                 :         799 :                 lastUsedConfig->isvalid)
     400                 :         797 :                 return lastUsedConfig;
     401                 :             : 
     402                 :             :         /* Try to look up an existing entry */
     403                 :          25 :         entry = (TSConfigCacheEntry *) hash_search(TSConfigCacheHash,
     404                 :             :                                                                                            &cfgId,
     405                 :             :                                                                                            HASH_FIND, NULL);
     406   [ +  +  +  + ]:          25 :         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                 :          14 :                 HeapTuple       tp;
     413                 :          14 :                 Form_pg_ts_config cfg;
     414                 :          14 :                 Relation        maprel;
     415                 :          14 :                 Relation        mapidx;
     416                 :          14 :                 ScanKeyData mapskey;
     417                 :          14 :                 SysScanDesc mapscan;
     418                 :          14 :                 HeapTuple       maptup;
     419                 :          14 :                 ListDictionary maplists[MAXTOKENTYPE + 1];
     420                 :          14 :                 Oid                     mapdicts[MAXDICTSPERTT];
     421                 :          14 :                 int                     maxtokentype;
     422                 :          14 :                 int                     ndicts;
     423                 :          14 :                 int                     i;
     424                 :             : 
     425                 :          14 :                 tp = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
     426         [ +  - ]:          14 :                 if (!HeapTupleIsValid(tp))
     427   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for text search configuration %u",
     428                 :             :                                  cfgId);
     429                 :          14 :                 cfg = (Form_pg_ts_config) GETSTRUCT(tp);
     430                 :             : 
     431                 :             :                 /*
     432                 :             :                  * Sanity checks
     433                 :             :                  */
     434         [ +  - ]:          14 :                 if (!OidIsValid(cfg->cfgparser))
     435   [ #  #  #  # ]:           0 :                         elog(ERROR, "text search configuration %u has no parser", cfgId);
     436                 :             : 
     437         [ +  + ]:          14 :                 if (entry == NULL)
     438                 :             :                 {
     439                 :          12 :                         bool            found;
     440                 :             : 
     441                 :             :                         /* Now make the cache entry */
     442                 :          12 :                         entry = (TSConfigCacheEntry *)
     443                 :          12 :                                 hash_search(TSConfigCacheHash,
     444                 :             :                                                         &cfgId,
     445                 :             :                                                         HASH_ENTER, &found);
     446         [ +  - ]:          12 :                         Assert(!found);         /* it wasn't there a moment ago */
     447                 :          12 :                 }
     448                 :             :                 else
     449                 :             :                 {
     450                 :             :                         /* Cleanup old contents */
     451         [ -  + ]:           2 :                         if (entry->map)
     452                 :             :                         {
     453         [ +  + ]:          48 :                                 for (i = 0; i < entry->lenmap; i++)
     454         [ +  + ]:          84 :                                         if (entry->map[i].dictIds)
     455                 :          38 :                                                 pfree(entry->map[i].dictIds);
     456                 :           2 :                                 pfree(entry->map);
     457                 :           2 :                         }
     458                 :             :                 }
     459                 :             : 
     460   [ +  -  +  -  :          56 :                 MemSet(entry, 0, sizeof(TSConfigCacheEntry));
          +  -  -  +  +  
                      + ]
     461                 :          14 :                 entry->cfgId = cfgId;
     462                 :          14 :                 entry->prsId = cfg->cfgparser;
     463                 :             : 
     464                 :          14 :                 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   [ +  -  +  -  :          14 :                 MemSet(maplists, 0, sizeof(maplists));
          +  -  +  -  #  
                      # ]
     474                 :          14 :                 maxtokentype = 0;
     475                 :          14 :                 ndicts = 0;
     476                 :             : 
     477                 :          14 :                 ScanKeyInit(&mapskey,
     478                 :             :                                         Anum_pg_ts_config_map_mapcfg,
     479                 :             :                                         BTEqualStrategyNumber, F_OIDEQ,
     480                 :          14 :                                         ObjectIdGetDatum(cfgId));
     481                 :             : 
     482                 :          14 :                 maprel = table_open(TSConfigMapRelationId, AccessShareLock);
     483                 :          14 :                 mapidx = index_open(TSConfigMapIndexId, AccessShareLock);
     484                 :          14 :                 mapscan = systable_beginscan_ordered(maprel, mapidx,
     485                 :             :                                                                                          NULL, 1, &mapskey);
     486                 :             : 
     487         [ +  + ]:         325 :                 while ((maptup = systable_getnext_ordered(mapscan, ForwardScanDirection)) != NULL)
     488                 :             :                 {
     489                 :         311 :                         Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
     490                 :         311 :                         int                     toktype = cfgmap->maptokentype;
     491                 :             : 
     492         [ +  - ]:         311 :                         if (toktype <= 0 || toktype > MAXTOKENTYPE)
     493   [ #  #  #  # ]:           0 :                                 elog(ERROR, "maptokentype value %d is out of range", toktype);
     494         [ +  - ]:         311 :                         if (toktype < maxtokentype)
     495   [ #  #  #  # ]:           0 :                                 elog(ERROR, "maptokentype entries are out of order");
     496         [ +  + ]:         311 :                         if (toktype > maxtokentype)
     497                 :             :                         {
     498                 :             :                                 /* starting a new token type, but first save the prior data */
     499         [ +  + ]:         266 :                                 if (ndicts > 0)
     500                 :             :                                 {
     501                 :         252 :                                         maplists[maxtokentype].len = ndicts;
     502                 :         252 :                                         maplists[maxtokentype].dictIds = (Oid *)
     503                 :         504 :                                                 MemoryContextAlloc(CacheMemoryContext,
     504                 :         252 :                                                                                    sizeof(Oid) * ndicts);
     505                 :         252 :                                         memcpy(maplists[maxtokentype].dictIds, mapdicts,
     506                 :             :                                                    sizeof(Oid) * ndicts);
     507                 :         252 :                                 }
     508                 :         266 :                                 maxtokentype = toktype;
     509                 :         266 :                                 mapdicts[0] = cfgmap->mapdict;
     510                 :         266 :                                 ndicts = 1;
     511                 :         266 :                         }
     512                 :             :                         else
     513                 :             :                         {
     514                 :             :                                 /* continuing data for current token type */
     515         [ +  - ]:          45 :                                 if (ndicts >= MAXDICTSPERTT)
     516   [ #  #  #  # ]:           0 :                                         elog(ERROR, "too many pg_ts_config_map entries for one token type");
     517                 :          45 :                                 mapdicts[ndicts++] = cfgmap->mapdict;
     518                 :             :                         }
     519                 :         311 :                 }
     520                 :             : 
     521                 :          14 :                 systable_endscan_ordered(mapscan);
     522                 :          14 :                 index_close(mapidx, AccessShareLock);
     523                 :          14 :                 table_close(maprel, AccessShareLock);
     524                 :             : 
     525         [ -  + ]:          14 :                 if (ndicts > 0)
     526                 :             :                 {
     527                 :             :                         /* save the last token type's dictionaries */
     528                 :          14 :                         maplists[maxtokentype].len = ndicts;
     529                 :          14 :                         maplists[maxtokentype].dictIds = (Oid *)
     530                 :          28 :                                 MemoryContextAlloc(CacheMemoryContext,
     531                 :          14 :                                                                    sizeof(Oid) * ndicts);
     532                 :          14 :                         memcpy(maplists[maxtokentype].dictIds, mapdicts,
     533                 :             :                                    sizeof(Oid) * ndicts);
     534                 :             :                         /* and save the overall map */
     535                 :          14 :                         entry->lenmap = maxtokentype + 1;
     536                 :          14 :                         entry->map = (ListDictionary *)
     537                 :          28 :                                 MemoryContextAlloc(CacheMemoryContext,
     538                 :          14 :                                                                    sizeof(ListDictionary) * entry->lenmap);
     539                 :          14 :                         memcpy(entry->map, maplists,
     540                 :             :                                    sizeof(ListDictionary) * entry->lenmap);
     541                 :          14 :                 }
     542                 :             : 
     543                 :          14 :                 entry->isvalid = true;
     544                 :          14 :         }
     545                 :             : 
     546                 :          25 :         lastUsedConfig = entry;
     547                 :             : 
     548                 :          25 :         return entry;
     549                 :         822 : }
     550                 :             : 
     551                 :             : 
     552                 :             : /*---------------------------------------------------
     553                 :             :  * GUC variable "default_text_search_config"
     554                 :             :  *---------------------------------------------------
     555                 :             :  */
     556                 :             : 
     557                 :             : Oid
     558                 :          65 : getTSCurrentConfig(bool emitError)
     559                 :             : {
     560                 :          65 :         List       *namelist;
     561                 :             : 
     562                 :             :         /* if we have a cached value, return it */
     563         [ +  + ]:          65 :         if (OidIsValid(TSCurrentConfigCache))
     564                 :          60 :                 return TSCurrentConfigCache;
     565                 :             : 
     566                 :             :         /* fail if GUC hasn't been set up yet */
     567   [ +  -  +  - ]:           5 :         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                 :           0 :         }
     574                 :             : 
     575         [ +  + ]:           5 :         if (TSConfigCacheHash == NULL)
     576                 :             :         {
     577                 :             :                 /* First time through: initialize the tsconfig inval callback */
     578                 :           2 :                 init_ts_config_cache();
     579                 :           2 :         }
     580                 :             : 
     581                 :             :         /* Look up the config */
     582         [ +  - ]:           5 :         if (emitError)
     583                 :             :         {
     584                 :           5 :                 namelist = stringToQualifiedNameList(TSCurrentConfig, NULL);
     585                 :           5 :                 TSCurrentConfigCache = get_ts_config_oid(namelist, false);
     586                 :           5 :         }
     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                 :           0 :         }
     598                 :             : 
     599                 :           5 :         return TSCurrentConfigCache;
     600                 :          65 : }
     601                 :             : 
     602                 :             : /* GUC check_hook for default_text_search_config */
     603                 :             : bool
     604                 :         971 : 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   [ +  +  -  + ]:         971 :         if (IsTransactionState() && MyDatabaseId != InvalidOid)
     612                 :             :         {
     613                 :         961 :                 ErrorSaveContext escontext = {T_ErrorSaveContext};
     614                 :         961 :                 List       *namelist;
     615                 :         961 :                 Oid                     cfgId;
     616                 :         961 :                 HeapTuple       tuple;
     617                 :         961 :                 Form_pg_ts_config cfg;
     618                 :         961 :                 char       *buf;
     619                 :             : 
     620                 :         961 :                 namelist = stringToQualifiedNameList(*newval,
     621                 :             :                                                                                          (Node *) &escontext);
     622         [ +  - ]:         961 :                 if (namelist != NIL)
     623                 :         961 :                         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         [ +  + ]:         961 :                 if (!OidIsValid(cfgId))
     632                 :             :                 {
     633         [ +  + ]:           4 :                         if (source == PGC_S_TEST)
     634                 :             :                         {
     635   [ -  +  +  - ]:           2 :                                 ereport(NOTICE,
     636                 :             :                                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     637                 :             :                                                  errmsg("text search configuration \"%s\" does not exist", *newval)));
     638                 :           2 :                                 return true;
     639                 :             :                         }
     640                 :             :                         else
     641                 :           2 :                                 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                 :         957 :                 tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
     649         [ +  - ]:         957 :                 if (!HeapTupleIsValid(tuple))
     650   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for text search configuration %u",
     651                 :             :                                  cfgId);
     652                 :         957 :                 cfg = (Form_pg_ts_config) GETSTRUCT(tuple);
     653                 :             : 
     654                 :        1914 :                 buf = quote_qualified_identifier(get_namespace_name(cfg->cfgnamespace),
     655                 :         957 :                                                                                  NameStr(cfg->cfgname));
     656                 :             : 
     657                 :         957 :                 ReleaseSysCache(tuple);
     658                 :             : 
     659                 :             :                 /* GUC wants it guc_malloc'd not palloc'd */
     660                 :         957 :                 guc_free(*newval);
     661                 :         957 :                 *newval = guc_strdup(LOG, buf);
     662                 :         957 :                 pfree(buf);
     663         [ +  - ]:         957 :                 if (!*newval)
     664                 :           0 :                         return false;
     665      [ -  +  + ]:         961 :         }
     666                 :             : 
     667                 :         967 :         return true;
     668                 :         971 : }
     669                 :             : 
     670                 :             : /* GUC assign_hook for default_text_search_config */
     671                 :             : void
     672                 :         966 : assign_default_text_search_config(const char *newval, void *extra)
     673                 :             : {
     674                 :             :         /* Just reset the cache to force a lookup on first use */
     675                 :         966 :         TSCurrentConfigCache = InvalidOid;
     676                 :         966 : }
        

Generated by: LCOV version 2.3.2-1