Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * ts_cache.c
4 : : * Tsearch related object caches.
5 : : *
6 : : * Tsearch performance is very sensitive to performance of parsers,
7 : : * dictionaries and mapping, so lookups should be cached as much
8 : : * as possible.
9 : : *
10 : : * Once a backend has created a cache entry for a particular TS object OID,
11 : : * the cache entry will exist for the life of the backend; hence it is
12 : : * safe to hold onto a pointer to the cache entry while doing things that
13 : : * might result in recognizing a cache invalidation. Beware however that
14 : : * subsidiary information might be deleted and reallocated somewhere else
15 : : * if a cache inval and reval happens! This does not look like it will be
16 : : * a big problem as long as parser and dictionary methods do not attempt
17 : : * any database access.
18 : : *
19 : : *
20 : : * Copyright (c) 2006-2024, 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
4625 tgl@sss.pgh.pa.us 94 :CBC 1027 : InvalidateTSCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
95 : : {
6081 96 : 1027 : HTAB *hash = (HTAB *) DatumGetPointer(arg);
97 : : HASH_SEQ_STATUS status;
98 : : TSAnyCacheEntry *entry;
99 : :
100 : 1027 : hash_seq_init(&status, hash);
101 [ + + ]: 3572 : while ((entry = (TSAnyCacheEntry *) hash_seq_search(&status)) != NULL)
102 : 2545 : entry->isvalid = false;
103 : :
104 : : /* Also invalidate the current-config cache if it's pg_ts_config */
105 [ + + ]: 1027 : if (hash == TSConfigCacheHash)
106 : 942 : TSCurrentConfigCache = InvalidOid;
107 : 1027 : }
108 : :
109 : : /*
110 : : * Fetch parser cache entry
111 : : */
112 : : TSParserCacheEntry *
113 : 5906 : lookup_ts_parser_cache(Oid prsId)
114 : : {
115 : : TSParserCacheEntry *entry;
116 : :
117 [ + + ]: 5906 : if (TSParserCacheHash == NULL)
118 : : {
119 : : /* First time through: initialize the hash table */
120 : : HASHCTL ctl;
121 : :
122 : 100 : ctl.keysize = sizeof(Oid);
123 : 100 : ctl.entrysize = sizeof(TSParserCacheEntry);
124 : 100 : TSParserCacheHash = hash_create("Tsearch parser cache", 4,
125 : : &ctl, HASH_ELEM | HASH_BLOBS);
126 : : /* Flush cache on pg_ts_parser changes */
127 : 100 : CacheRegisterSyscacheCallback(TSPARSEROID, InvalidateTSCacheCallBack,
128 : : PointerGetDatum(TSParserCacheHash));
129 : :
130 : : /* Also make sure CacheMemoryContext exists */
5222 131 [ - + ]: 100 : if (!CacheMemoryContext)
5222 tgl@sss.pgh.pa.us 132 :UBC 0 : CreateCacheMemoryContext();
133 : : }
134 : :
135 : : /* Check single-entry cache */
6081 tgl@sss.pgh.pa.us 136 [ + + + - ]:CBC 5906 : if (lastUsedParser && lastUsedParser->prsId == prsId &&
137 [ + - ]: 5806 : lastUsedParser->isvalid)
138 : 5806 : return lastUsedParser;
139 : :
140 : : /* Try to look up an existing entry */
141 : 100 : entry = (TSParserCacheEntry *) hash_search(TSParserCacheHash,
142 : : &prsId,
143 : : HASH_FIND, NULL);
144 [ - + - - ]: 100 : if (entry == NULL || !entry->isvalid)
145 : : {
146 : : /*
147 : : * If we didn't find one, we want to make one. But first look up the
148 : : * object to be sure the OID is real.
149 : : */
150 : : HeapTuple tp;
151 : : Form_pg_ts_parser prs;
152 : :
5173 rhaas@postgresql.org 153 : 100 : tp = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
6081 tgl@sss.pgh.pa.us 154 [ - + ]: 100 : if (!HeapTupleIsValid(tp))
6081 tgl@sss.pgh.pa.us 155 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for text search parser %u",
156 : : prsId);
6081 tgl@sss.pgh.pa.us 157 :CBC 100 : prs = (Form_pg_ts_parser) GETSTRUCT(tp);
158 : :
159 : : /*
160 : : * Sanity checks
161 : : */
162 [ - + ]: 100 : if (!OidIsValid(prs->prsstart))
6081 tgl@sss.pgh.pa.us 163 [ # # ]:UBC 0 : elog(ERROR, "text search parser %u has no prsstart method", prsId);
6081 tgl@sss.pgh.pa.us 164 [ - + ]:CBC 100 : if (!OidIsValid(prs->prstoken))
6081 tgl@sss.pgh.pa.us 165 [ # # ]:UBC 0 : elog(ERROR, "text search parser %u has no prstoken method", prsId);
6081 tgl@sss.pgh.pa.us 166 [ - + ]:CBC 100 : if (!OidIsValid(prs->prsend))
6081 tgl@sss.pgh.pa.us 167 [ # # ]:UBC 0 : elog(ERROR, "text search parser %u has no prsend method", prsId);
168 : :
6081 tgl@sss.pgh.pa.us 169 [ + - ]:CBC 100 : if (entry == NULL)
170 : : {
171 : : bool found;
172 : :
173 : : /* Now make the cache entry */
174 : : entry = (TSParserCacheEntry *)
433 peter@eisentraut.org 175 : 100 : hash_search(TSParserCacheHash, &prsId, HASH_ENTER, &found);
6081 tgl@sss.pgh.pa.us 176 [ - + ]: 100 : Assert(!found); /* it wasn't there a moment ago */
177 : : }
178 : :
179 [ + - + - : 2900 : MemSet(entry, 0, sizeof(TSParserCacheEntry));
+ - + - +
+ ]
180 : 100 : entry->prsId = prsId;
181 : 100 : entry->startOid = prs->prsstart;
182 : 100 : entry->tokenOid = prs->prstoken;
183 : 100 : entry->endOid = prs->prsend;
184 : 100 : entry->headlineOid = prs->prsheadline;
185 : 100 : entry->lextypeOid = prs->prslextype;
186 : :
187 : 100 : ReleaseSysCache(tp);
188 : :
189 : 100 : fmgr_info_cxt(entry->startOid, &entry->prsstart, CacheMemoryContext);
190 : 100 : fmgr_info_cxt(entry->tokenOid, &entry->prstoken, CacheMemoryContext);
191 : 100 : fmgr_info_cxt(entry->endOid, &entry->prsend, CacheMemoryContext);
192 [ + - ]: 100 : if (OidIsValid(entry->headlineOid))
193 : 100 : fmgr_info_cxt(entry->headlineOid, &entry->prsheadline,
194 : : CacheMemoryContext);
195 : :
196 : 100 : entry->isvalid = true;
197 : : }
198 : :
199 : 100 : lastUsedParser = entry;
200 : :
201 : 100 : return entry;
202 : : }
203 : :
204 : : /*
205 : : * Fetch dictionary cache entry
206 : : */
207 : : TSDictionaryCacheEntry *
208 : 7560 : lookup_ts_dictionary_cache(Oid dictId)
209 : : {
210 : : TSDictionaryCacheEntry *entry;
211 : :
212 [ + + ]: 7560 : if (TSDictionaryCacheHash == NULL)
213 : : {
214 : : /* First time through: initialize the hash table */
215 : : HASHCTL ctl;
216 : :
217 : 23 : ctl.keysize = sizeof(Oid);
218 : 23 : ctl.entrysize = sizeof(TSDictionaryCacheEntry);
219 : 23 : TSDictionaryCacheHash = hash_create("Tsearch dictionary cache", 8,
220 : : &ctl, HASH_ELEM | HASH_BLOBS);
221 : : /* Flush cache on pg_ts_dict and pg_ts_template changes */
222 : 23 : CacheRegisterSyscacheCallback(TSDICTOID, InvalidateTSCacheCallBack,
223 : : PointerGetDatum(TSDictionaryCacheHash));
224 : 23 : CacheRegisterSyscacheCallback(TSTEMPLATEOID, InvalidateTSCacheCallBack,
225 : : PointerGetDatum(TSDictionaryCacheHash));
226 : :
227 : : /* Also make sure CacheMemoryContext exists */
5222 228 [ - + ]: 23 : if (!CacheMemoryContext)
5222 tgl@sss.pgh.pa.us 229 :UBC 0 : CreateCacheMemoryContext();
230 : : }
231 : :
232 : : /* Check single-entry cache */
6081 tgl@sss.pgh.pa.us 233 [ + + + + ]:CBC 7560 : if (lastUsedDictionary && lastUsedDictionary->dictId == dictId &&
234 [ + + ]: 6295 : lastUsedDictionary->isvalid)
235 : 6277 : return lastUsedDictionary;
236 : :
237 : : /* Try to look up an existing entry */
238 : 1283 : entry = (TSDictionaryCacheEntry *) hash_search(TSDictionaryCacheHash,
239 : : &dictId,
240 : : HASH_FIND, NULL);
241 [ + + + + ]: 1283 : if (entry == NULL || !entry->isvalid)
242 : : {
243 : : /*
244 : : * If we didn't find one, we want to make one. But first look up the
245 : : * object to be sure the OID is real.
246 : : */
247 : : HeapTuple tpdict,
248 : : tptmpl;
249 : : Form_pg_ts_dict dict;
250 : : Form_pg_ts_template template;
251 : : MemoryContext saveCtx;
252 : :
5173 rhaas@postgresql.org 253 : 86 : tpdict = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
6081 tgl@sss.pgh.pa.us 254 [ - + ]: 86 : if (!HeapTupleIsValid(tpdict))
6081 tgl@sss.pgh.pa.us 255 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for text search dictionary %u",
256 : : dictId);
6081 tgl@sss.pgh.pa.us 257 :CBC 86 : dict = (Form_pg_ts_dict) GETSTRUCT(tpdict);
258 : :
259 : : /*
260 : : * Sanity checks
261 : : */
262 [ - + ]: 86 : if (!OidIsValid(dict->dicttemplate))
6081 tgl@sss.pgh.pa.us 263 [ # # ]:UBC 0 : elog(ERROR, "text search dictionary %u has no template", dictId);
264 : :
265 : : /*
266 : : * Retrieve dictionary's template
267 : : */
5173 rhaas@postgresql.org 268 :CBC 86 : tptmpl = SearchSysCache1(TSTEMPLATEOID,
269 : : ObjectIdGetDatum(dict->dicttemplate));
6081 tgl@sss.pgh.pa.us 270 [ - + ]: 86 : if (!HeapTupleIsValid(tptmpl))
6081 tgl@sss.pgh.pa.us 271 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for text search template %u",
272 : : dict->dicttemplate);
6081 tgl@sss.pgh.pa.us 273 :CBC 86 : template = (Form_pg_ts_template) GETSTRUCT(tptmpl);
274 : :
275 : : /*
276 : : * Sanity checks
277 : : */
278 [ - + ]: 86 : if (!OidIsValid(template->tmpllexize))
6081 tgl@sss.pgh.pa.us 279 [ # # ]:UBC 0 : elog(ERROR, "text search template %u has no lexize method",
280 : : template->tmpllexize);
281 : :
6081 tgl@sss.pgh.pa.us 282 [ + + ]:CBC 86 : if (entry == NULL)
283 : : {
284 : : bool found;
285 : :
286 : : /* Now make the cache entry */
287 : : entry = (TSDictionaryCacheEntry *)
288 : 53 : hash_search(TSDictionaryCacheHash,
289 : : &dictId,
290 : : HASH_ENTER, &found);
291 [ - + ]: 53 : Assert(!found); /* it wasn't there a moment ago */
292 : :
293 : : /* Create private memory context the first time through */
2210 294 : 53 : saveCtx = AllocSetContextCreate(CacheMemoryContext,
295 : : "TS dictionary",
296 : : ALLOCSET_SMALL_SIZES);
2200 peter_e@gmx.net 297 : 53 : MemoryContextCopyAndSetIdentifier(saveCtx, NameStr(dict->dictname));
298 : : }
299 : : else
300 : : {
301 : : /* Clear the existing entry's private context */
6081 tgl@sss.pgh.pa.us 302 : 33 : saveCtx = entry->dictCtx;
303 : : /* Don't let context's ident pointer dangle while we reset it */
2210 304 : 33 : MemoryContextSetIdentifier(saveCtx, NULL);
305 : 33 : MemoryContextReset(saveCtx);
2200 peter_e@gmx.net 306 : 33 : MemoryContextCopyAndSetIdentifier(saveCtx, NameStr(dict->dictname));
307 : : }
308 : :
6081 tgl@sss.pgh.pa.us 309 [ + - + - : 946 : MemSet(entry, 0, sizeof(TSDictionaryCacheEntry));
+ - + - +
+ ]
310 : 86 : entry->dictId = dictId;
311 : 86 : entry->dictCtx = saveCtx;
312 : :
313 : 86 : entry->lexizeOid = template->tmpllexize;
314 : :
315 [ + - ]: 86 : if (OidIsValid(template->tmplinit))
316 : : {
317 : : List *dictoptions;
318 : : Datum opt;
319 : : bool isnull;
320 : : MemoryContext oldcontext;
321 : :
322 : : /*
323 : : * Init method runs in dictionary's private memory context, and we
324 : : * make sure the options are stored there too
325 : : */
6080 326 : 86 : oldcontext = MemoryContextSwitchTo(entry->dictCtx);
327 : :
6081 328 : 86 : opt = SysCacheGetAttr(TSDICTOID, tpdict,
329 : : Anum_pg_ts_dict_dictinitoption,
330 : : &isnull);
331 [ + + ]: 86 : if (isnull)
6080 332 : 17 : dictoptions = NIL;
333 : : else
334 : 69 : dictoptions = deserialize_deflist(opt);
335 : :
336 : 86 : entry->dictData =
337 : 86 : DatumGetPointer(OidFunctionCall1(template->tmplinit,
338 : : PointerGetDatum(dictoptions)));
339 : :
340 : 86 : MemoryContextSwitchTo(oldcontext);
341 : : }
342 : :
6081 343 : 86 : ReleaseSysCache(tptmpl);
344 : 86 : ReleaseSysCache(tpdict);
345 : :
346 : 86 : fmgr_info_cxt(entry->lexizeOid, &entry->lexize, entry->dictCtx);
347 : :
348 : 86 : entry->isvalid = true;
349 : : }
350 : :
351 : 1283 : lastUsedDictionary = entry;
352 : :
353 : 1283 : return entry;
354 : : }
355 : :
356 : : /*
357 : : * Initialize config cache and prepare callbacks. This is split out of
358 : : * lookup_ts_config_cache because we need to activate the callback before
359 : : * caching TSCurrentConfigCache, too.
360 : : */
361 : : static void
362 : 19 : init_ts_config_cache(void)
363 : : {
364 : : HASHCTL ctl;
365 : :
366 : 19 : ctl.keysize = sizeof(Oid);
367 : 19 : ctl.entrysize = sizeof(TSConfigCacheEntry);
368 : 19 : TSConfigCacheHash = hash_create("Tsearch configuration cache", 16,
369 : : &ctl, HASH_ELEM | HASH_BLOBS);
370 : : /* Flush cache on pg_ts_config and pg_ts_config_map changes */
371 : 19 : CacheRegisterSyscacheCallback(TSCONFIGOID, InvalidateTSCacheCallBack,
372 : : PointerGetDatum(TSConfigCacheHash));
373 : 19 : CacheRegisterSyscacheCallback(TSCONFIGMAP, InvalidateTSCacheCallBack,
374 : : PointerGetDatum(TSConfigCacheHash));
375 : :
376 : : /* Also make sure CacheMemoryContext exists */
5222 377 [ - + ]: 19 : if (!CacheMemoryContext)
5222 tgl@sss.pgh.pa.us 378 :UBC 0 : CreateCacheMemoryContext();
6081 tgl@sss.pgh.pa.us 379 :CBC 19 : }
380 : :
381 : : /*
382 : : * Fetch configuration cache entry
383 : : */
384 : : TSConfigCacheEntry *
385 : 2463 : lookup_ts_config_cache(Oid cfgId)
386 : : {
387 : : TSConfigCacheEntry *entry;
388 : :
389 [ + + ]: 2463 : if (TSConfigCacheHash == NULL)
390 : : {
391 : : /* First time through: initialize the hash table */
392 : 13 : init_ts_config_cache();
393 : : }
394 : :
395 : : /* Check single-entry cache */
396 [ + + + + ]: 2463 : if (lastUsedConfig && lastUsedConfig->cfgId == cfgId &&
397 [ + + ]: 2390 : lastUsedConfig->isvalid)
398 : 2384 : return lastUsedConfig;
399 : :
400 : : /* Try to look up an existing entry */
401 : 79 : entry = (TSConfigCacheEntry *) hash_search(TSConfigCacheHash,
402 : : &cfgId,
403 : : HASH_FIND, NULL);
404 [ + + + + ]: 79 : if (entry == NULL || !entry->isvalid)
405 : : {
406 : : /*
407 : : * If we didn't find one, we want to make one. But first look up the
408 : : * object to be sure the OID is real.
409 : : */
410 : : HeapTuple tp;
411 : : Form_pg_ts_config cfg;
412 : : Relation maprel;
413 : : Relation mapidx;
414 : : ScanKeyData mapskey;
415 : : SysScanDesc mapscan;
416 : : HeapTuple maptup;
417 : : ListDictionary maplists[MAXTOKENTYPE + 1];
418 : : Oid mapdicts[MAXDICTSPERTT];
419 : : int maxtokentype;
420 : : int ndicts;
421 : : int i;
422 : :
5173 rhaas@postgresql.org 423 : 46 : tp = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
6081 tgl@sss.pgh.pa.us 424 [ - + ]: 46 : if (!HeapTupleIsValid(tp))
6081 tgl@sss.pgh.pa.us 425 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for text search configuration %u",
426 : : cfgId);
6081 tgl@sss.pgh.pa.us 427 :CBC 46 : cfg = (Form_pg_ts_config) GETSTRUCT(tp);
428 : :
429 : : /*
430 : : * Sanity checks
431 : : */
432 [ - + ]: 46 : if (!OidIsValid(cfg->cfgparser))
6081 tgl@sss.pgh.pa.us 433 [ # # ]:UBC 0 : elog(ERROR, "text search configuration %u has no parser", cfgId);
434 : :
6081 tgl@sss.pgh.pa.us 435 [ + + ]:CBC 46 : if (entry == NULL)
436 : : {
437 : : bool found;
438 : :
439 : : /* Now make the cache entry */
440 : : entry = (TSConfigCacheEntry *)
441 : 40 : hash_search(TSConfigCacheHash,
442 : : &cfgId,
443 : : HASH_ENTER, &found);
444 [ - + ]: 40 : Assert(!found); /* it wasn't there a moment ago */
445 : : }
446 : : else
447 : : {
448 : : /* Cleanup old contents */
449 [ + - ]: 6 : if (entry->map)
450 : : {
451 [ + + ]: 144 : for (i = 0; i < entry->lenmap; i++)
452 [ + + ]: 138 : if (entry->map[i].dictIds)
453 : 114 : pfree(entry->map[i].dictIds);
454 : 6 : pfree(entry->map);
455 : : }
456 : : }
457 : :
458 [ + - + - : 184 : MemSet(entry, 0, sizeof(TSConfigCacheEntry));
+ - + - +
+ ]
459 : 46 : entry->cfgId = cfgId;
460 : 46 : entry->prsId = cfg->cfgparser;
461 : :
462 : 46 : ReleaseSysCache(tp);
463 : :
464 : : /*
465 : : * Scan pg_ts_config_map to gather dictionary list for each token type
466 : : *
467 : : * Because the index is on (mapcfg, maptokentype, mapseqno), we will
468 : : * see the entries in maptokentype order, and in mapseqno order for
469 : : * each token type, even though we didn't explicitly ask for that.
470 : : */
471 [ + - + - : 46 : MemSet(maplists, 0, sizeof(maplists));
+ - - + -
- ]
472 : 46 : maxtokentype = 0;
473 : 46 : ndicts = 0;
474 : :
475 : 46 : ScanKeyInit(&mapskey,
476 : : Anum_pg_ts_config_map_mapcfg,
477 : : BTEqualStrategyNumber, F_OIDEQ,
478 : : ObjectIdGetDatum(cfgId));
479 : :
1910 andres@anarazel.de 480 : 46 : maprel = table_open(TSConfigMapRelationId, AccessShareLock);
6081 tgl@sss.pgh.pa.us 481 : 46 : mapidx = index_open(TSConfigMapIndexId, AccessShareLock);
5846 482 : 46 : mapscan = systable_beginscan_ordered(maprel, mapidx,
483 : : NULL, 1, &mapskey);
484 : :
485 [ + + ]: 1037 : while ((maptup = systable_getnext_ordered(mapscan, ForwardScanDirection)) != NULL)
486 : : {
6081 487 : 991 : Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
5995 bruce@momjian.us 488 : 991 : int toktype = cfgmap->maptokentype;
489 : :
6081 tgl@sss.pgh.pa.us 490 [ + - - + ]: 991 : if (toktype <= 0 || toktype > MAXTOKENTYPE)
6081 tgl@sss.pgh.pa.us 491 [ # # ]:UBC 0 : elog(ERROR, "maptokentype value %d is out of range", toktype);
6081 tgl@sss.pgh.pa.us 492 [ - + ]:CBC 991 : if (toktype < maxtokentype)
6081 tgl@sss.pgh.pa.us 493 [ # # ]:UBC 0 : elog(ERROR, "maptokentype entries are out of order");
6081 tgl@sss.pgh.pa.us 494 [ + + ]:CBC 991 : if (toktype > maxtokentype)
495 : : {
496 : : /* starting a new token type, but first save the prior data */
497 [ + + ]: 856 : if (ndicts > 0)
498 : : {
499 : 810 : maplists[maxtokentype].len = ndicts;
500 : 810 : maplists[maxtokentype].dictIds = (Oid *)
501 : 810 : MemoryContextAlloc(CacheMemoryContext,
502 : : sizeof(Oid) * ndicts);
503 : 810 : memcpy(maplists[maxtokentype].dictIds, mapdicts,
504 : : sizeof(Oid) * ndicts);
505 : : }
506 : 856 : maxtokentype = toktype;
507 : 856 : mapdicts[0] = cfgmap->mapdict;
508 : 856 : ndicts = 1;
509 : : }
510 : : else
511 : : {
512 : : /* continuing data for current token type */
513 [ - + ]: 135 : if (ndicts >= MAXDICTSPERTT)
6081 tgl@sss.pgh.pa.us 514 [ # # ]:UBC 0 : elog(ERROR, "too many pg_ts_config_map entries for one token type");
6081 tgl@sss.pgh.pa.us 515 :CBC 135 : mapdicts[ndicts++] = cfgmap->mapdict;
516 : : }
517 : : }
518 : :
5846 519 : 46 : systable_endscan_ordered(mapscan);
6081 520 : 46 : index_close(mapidx, AccessShareLock);
1910 andres@anarazel.de 521 : 46 : table_close(maprel, AccessShareLock);
522 : :
6081 tgl@sss.pgh.pa.us 523 [ + - ]: 46 : if (ndicts > 0)
524 : : {
525 : : /* save the last token type's dictionaries */
526 : 46 : maplists[maxtokentype].len = ndicts;
527 : 46 : maplists[maxtokentype].dictIds = (Oid *)
528 : 46 : MemoryContextAlloc(CacheMemoryContext,
529 : : sizeof(Oid) * ndicts);
530 : 46 : memcpy(maplists[maxtokentype].dictIds, mapdicts,
531 : : sizeof(Oid) * ndicts);
532 : : /* and save the overall map */
533 : 46 : entry->lenmap = maxtokentype + 1;
534 : 46 : entry->map = (ListDictionary *)
535 : 46 : MemoryContextAlloc(CacheMemoryContext,
536 : 46 : sizeof(ListDictionary) * entry->lenmap);
537 : 46 : memcpy(entry->map, maplists,
538 : 46 : sizeof(ListDictionary) * entry->lenmap);
539 : : }
540 : :
541 : 46 : entry->isvalid = true;
542 : : }
543 : :
544 : 79 : lastUsedConfig = entry;
545 : :
546 : 79 : return entry;
547 : : }
548 : :
549 : :
550 : : /*---------------------------------------------------
551 : : * GUC variable "default_text_search_config"
552 : : *---------------------------------------------------
553 : : */
554 : :
555 : : Oid
556 : 195 : getTSCurrentConfig(bool emitError)
557 : : {
558 : : List *namelist;
559 : :
560 : : /* if we have a cached value, return it */
561 [ + + ]: 195 : if (OidIsValid(TSCurrentConfigCache))
562 : 180 : return TSCurrentConfigCache;
563 : :
564 : : /* fail if GUC hasn't been set up yet */
565 [ + - - + ]: 15 : if (TSCurrentConfig == NULL || *TSCurrentConfig == '\0')
566 : : {
6081 tgl@sss.pgh.pa.us 567 [ # # ]:UBC 0 : if (emitError)
568 [ # # ]: 0 : elog(ERROR, "text search configuration isn't set");
569 : : else
570 : 0 : return InvalidOid;
571 : : }
572 : :
6081 tgl@sss.pgh.pa.us 573 [ + + ]:CBC 15 : if (TSConfigCacheHash == NULL)
574 : : {
575 : : /* First time through: initialize the tsconfig inval callback */
576 : 6 : init_ts_config_cache();
577 : : }
578 : :
579 : : /* Look up the config */
474 580 [ + - ]: 15 : if (emitError)
581 : : {
582 : 15 : namelist = stringToQualifiedNameList(TSCurrentConfig, NULL);
583 : 15 : TSCurrentConfigCache = get_ts_config_oid(namelist, false);
584 : : }
585 : : else
586 : : {
474 tgl@sss.pgh.pa.us 587 :UBC 0 : ErrorSaveContext escontext = {T_ErrorSaveContext};
588 : :
589 : 0 : namelist = stringToQualifiedNameList(TSCurrentConfig,
590 : : (Node *) &escontext);
591 [ # # ]: 0 : if (namelist != NIL)
592 : 0 : TSCurrentConfigCache = get_ts_config_oid(namelist, true);
593 : : else
594 : 0 : TSCurrentConfigCache = InvalidOid; /* bad name list syntax */
595 : : }
596 : :
6081 tgl@sss.pgh.pa.us 597 :CBC 15 : return TSCurrentConfigCache;
598 : : }
599 : :
600 : : /* GUC check_hook for default_text_search_config */
601 : : bool
579 602 : 5227 : check_default_text_search_config(char **newval, void **extra, GucSource source)
603 : : {
604 : : /*
605 : : * If we aren't inside a transaction, or connected to a database, we
606 : : * cannot do the catalog accesses necessary to verify the config name.
607 : : * Must accept it on faith.
608 : : */
1770 andres@anarazel.de 609 [ + + + - ]: 5227 : if (IsTransactionState() && MyDatabaseId != InvalidOid)
610 : : {
474 tgl@sss.pgh.pa.us 611 : 2667 : ErrorSaveContext escontext = {T_ErrorSaveContext};
612 : : List *namelist;
613 : : Oid cfgId;
614 : : HeapTuple tuple;
615 : : Form_pg_ts_config cfg;
616 : : char *buf;
617 : :
618 : 2667 : namelist = stringToQualifiedNameList(*newval,
619 : : (Node *) &escontext);
620 [ + - ]: 2667 : if (namelist != NIL)
621 : 2667 : cfgId = get_ts_config_oid(namelist, true);
622 : : else
474 tgl@sss.pgh.pa.us 623 :UBC 0 : cfgId = InvalidOid; /* bad name list syntax */
624 : :
625 : : /*
626 : : * When source == PGC_S_TEST, don't throw a hard error for a
627 : : * nonexistent configuration, only a NOTICE. See comments in guc.h.
628 : : */
6081 tgl@sss.pgh.pa.us 629 [ + + ]:CBC 2667 : if (!OidIsValid(cfgId))
630 : : {
4458 heikki.linnakangas@i 631 [ + + ]: 13 : if (source == PGC_S_TEST)
632 : : {
633 [ + + ]: 7 : ereport(NOTICE,
634 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
635 : : errmsg("text search configuration \"%s\" does not exist", *newval)));
636 : 13 : return true;
637 : : }
638 : : else
639 : 6 : return false;
640 : : }
641 : :
642 : : /*
643 : : * Modify the actually stored value to be fully qualified, to ensure
644 : : * later changes of search_path don't affect it.
645 : : */
5173 rhaas@postgresql.org 646 : 2654 : tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
6081 tgl@sss.pgh.pa.us 647 [ - + ]: 2654 : if (!HeapTupleIsValid(tuple))
6081 tgl@sss.pgh.pa.us 648 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for text search configuration %u",
649 : : cfgId);
6081 tgl@sss.pgh.pa.us 650 :CBC 2654 : cfg = (Form_pg_ts_config) GETSTRUCT(tuple);
651 : :
652 : 2654 : buf = quote_qualified_identifier(get_namespace_name(cfg->cfgnamespace),
653 : 2654 : NameStr(cfg->cfgname));
654 : :
655 : 2654 : ReleaseSysCache(tuple);
656 : :
657 : : /* GUC wants it guc_malloc'd not palloc'd */
548 658 : 2654 : guc_free(*newval);
659 : 2654 : *newval = guc_strdup(LOG, buf);
6081 660 : 2654 : pfree(buf);
4102 661 [ - + ]: 2654 : if (!*newval)
4756 tgl@sss.pgh.pa.us 662 :UBC 0 : return false;
663 : : }
664 : :
4756 tgl@sss.pgh.pa.us 665 :CBC 5214 : return true;
666 : : }
667 : :
668 : : /* GUC assign_hook for default_text_search_config */
669 : : void
579 670 : 5211 : assign_default_text_search_config(const char *newval, void *extra)
671 : : {
672 : : /* Just reset the cache to force a lookup on first use */
4756 673 : 5211 : TSCurrentConfigCache = InvalidOid;
6081 674 : 5211 : }
|