LCOV - code coverage report
Current view: top level - src/backend/utils/cache - ts_cache.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 88.3 % 230 203
Test Date: 2026-08-25 14:15:54 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 57.3 % 192 110

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

Generated by: LCOV version 2.0-1