00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "postgres.h"
00028
00029 #include "access/genam.h"
00030 #include "access/heapam.h"
00031 #include "access/htup_details.h"
00032 #include "access/xact.h"
00033 #include "catalog/indexing.h"
00034 #include "catalog/namespace.h"
00035 #include "catalog/pg_ts_config.h"
00036 #include "catalog/pg_ts_config_map.h"
00037 #include "catalog/pg_ts_dict.h"
00038 #include "catalog/pg_ts_parser.h"
00039 #include "catalog/pg_ts_template.h"
00040 #include "commands/defrem.h"
00041 #include "tsearch/ts_cache.h"
00042 #include "utils/builtins.h"
00043 #include "utils/catcache.h"
00044 #include "utils/fmgroids.h"
00045 #include "utils/inval.h"
00046 #include "utils/lsyscache.h"
00047 #include "utils/memutils.h"
00048 #include "utils/syscache.h"
00049 #include "utils/tqual.h"
00050
00051
00052
00053
00054
00055
00056
00057
00058 #define MAXTOKENTYPE 256
00059 #define MAXDICTSPERTT 100
00060
00061
00062 static HTAB *TSParserCacheHash = NULL;
00063 static TSParserCacheEntry *lastUsedParser = NULL;
00064
00065 static HTAB *TSDictionaryCacheHash = NULL;
00066 static TSDictionaryCacheEntry *lastUsedDictionary = NULL;
00067
00068 static HTAB *TSConfigCacheHash = NULL;
00069 static TSConfigCacheEntry *lastUsedConfig = NULL;
00070
00071
00072
00073
00074 char *TSCurrentConfig = NULL;
00075
00076 static Oid TSCurrentConfigCache = InvalidOid;
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091 static void
00092 InvalidateTSCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
00093 {
00094 HTAB *hash = (HTAB *) DatumGetPointer(arg);
00095 HASH_SEQ_STATUS status;
00096 TSAnyCacheEntry *entry;
00097
00098 hash_seq_init(&status, hash);
00099 while ((entry = (TSAnyCacheEntry *) hash_seq_search(&status)) != NULL)
00100 entry->isvalid = false;
00101
00102
00103 if (hash == TSConfigCacheHash)
00104 TSCurrentConfigCache = InvalidOid;
00105 }
00106
00107
00108
00109
00110 TSParserCacheEntry *
00111 lookup_ts_parser_cache(Oid prsId)
00112 {
00113 TSParserCacheEntry *entry;
00114
00115 if (TSParserCacheHash == NULL)
00116 {
00117
00118 HASHCTL ctl;
00119
00120 MemSet(&ctl, 0, sizeof(ctl));
00121 ctl.keysize = sizeof(Oid);
00122 ctl.entrysize = sizeof(TSParserCacheEntry);
00123 ctl.hash = oid_hash;
00124 TSParserCacheHash = hash_create("Tsearch parser cache", 4,
00125 &ctl, HASH_ELEM | HASH_FUNCTION);
00126
00127 CacheRegisterSyscacheCallback(TSPARSEROID, InvalidateTSCacheCallBack,
00128 PointerGetDatum(TSParserCacheHash));
00129
00130
00131 if (!CacheMemoryContext)
00132 CreateCacheMemoryContext();
00133 }
00134
00135
00136 if (lastUsedParser && lastUsedParser->prsId == prsId &&
00137 lastUsedParser->isvalid)
00138 return lastUsedParser;
00139
00140
00141 entry = (TSParserCacheEntry *) hash_search(TSParserCacheHash,
00142 (void *) &prsId,
00143 HASH_FIND, NULL);
00144 if (entry == NULL || !entry->isvalid)
00145 {
00146
00147
00148
00149
00150 HeapTuple tp;
00151 Form_pg_ts_parser prs;
00152
00153 tp = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
00154 if (!HeapTupleIsValid(tp))
00155 elog(ERROR, "cache lookup failed for text search parser %u",
00156 prsId);
00157 prs = (Form_pg_ts_parser) GETSTRUCT(tp);
00158
00159
00160
00161
00162 if (!OidIsValid(prs->prsstart))
00163 elog(ERROR, "text search parser %u has no prsstart method", prsId);
00164 if (!OidIsValid(prs->prstoken))
00165 elog(ERROR, "text search parser %u has no prstoken method", prsId);
00166 if (!OidIsValid(prs->prsend))
00167 elog(ERROR, "text search parser %u has no prsend method", prsId);
00168
00169 if (entry == NULL)
00170 {
00171 bool found;
00172
00173
00174 entry = (TSParserCacheEntry *)
00175 hash_search(TSParserCacheHash,
00176 (void *) &prsId,
00177 HASH_ENTER, &found);
00178 Assert(!found);
00179 }
00180
00181 MemSet(entry, 0, sizeof(TSParserCacheEntry));
00182 entry->prsId = prsId;
00183 entry->startOid = prs->prsstart;
00184 entry->tokenOid = prs->prstoken;
00185 entry->endOid = prs->prsend;
00186 entry->headlineOid = prs->prsheadline;
00187 entry->lextypeOid = prs->prslextype;
00188
00189 ReleaseSysCache(tp);
00190
00191 fmgr_info_cxt(entry->startOid, &entry->prsstart, CacheMemoryContext);
00192 fmgr_info_cxt(entry->tokenOid, &entry->prstoken, CacheMemoryContext);
00193 fmgr_info_cxt(entry->endOid, &entry->prsend, CacheMemoryContext);
00194 if (OidIsValid(entry->headlineOid))
00195 fmgr_info_cxt(entry->headlineOid, &entry->prsheadline,
00196 CacheMemoryContext);
00197
00198 entry->isvalid = true;
00199 }
00200
00201 lastUsedParser = entry;
00202
00203 return entry;
00204 }
00205
00206
00207
00208
00209 TSDictionaryCacheEntry *
00210 lookup_ts_dictionary_cache(Oid dictId)
00211 {
00212 TSDictionaryCacheEntry *entry;
00213
00214 if (TSDictionaryCacheHash == NULL)
00215 {
00216
00217 HASHCTL ctl;
00218
00219 MemSet(&ctl, 0, sizeof(ctl));
00220 ctl.keysize = sizeof(Oid);
00221 ctl.entrysize = sizeof(TSDictionaryCacheEntry);
00222 ctl.hash = oid_hash;
00223 TSDictionaryCacheHash = hash_create("Tsearch dictionary cache", 8,
00224 &ctl, HASH_ELEM | HASH_FUNCTION);
00225
00226 CacheRegisterSyscacheCallback(TSDICTOID, InvalidateTSCacheCallBack,
00227 PointerGetDatum(TSDictionaryCacheHash));
00228 CacheRegisterSyscacheCallback(TSTEMPLATEOID, InvalidateTSCacheCallBack,
00229 PointerGetDatum(TSDictionaryCacheHash));
00230
00231
00232 if (!CacheMemoryContext)
00233 CreateCacheMemoryContext();
00234 }
00235
00236
00237 if (lastUsedDictionary && lastUsedDictionary->dictId == dictId &&
00238 lastUsedDictionary->isvalid)
00239 return lastUsedDictionary;
00240
00241
00242 entry = (TSDictionaryCacheEntry *) hash_search(TSDictionaryCacheHash,
00243 (void *) &dictId,
00244 HASH_FIND, NULL);
00245 if (entry == NULL || !entry->isvalid)
00246 {
00247
00248
00249
00250
00251 HeapTuple tpdict,
00252 tptmpl;
00253 Form_pg_ts_dict dict;
00254 Form_pg_ts_template template;
00255 MemoryContext saveCtx;
00256
00257 tpdict = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
00258 if (!HeapTupleIsValid(tpdict))
00259 elog(ERROR, "cache lookup failed for text search dictionary %u",
00260 dictId);
00261 dict = (Form_pg_ts_dict) GETSTRUCT(tpdict);
00262
00263
00264
00265
00266 if (!OidIsValid(dict->dicttemplate))
00267 elog(ERROR, "text search dictionary %u has no template", dictId);
00268
00269
00270
00271
00272 tptmpl = SearchSysCache1(TSTEMPLATEOID,
00273 ObjectIdGetDatum(dict->dicttemplate));
00274 if (!HeapTupleIsValid(tptmpl))
00275 elog(ERROR, "cache lookup failed for text search template %u",
00276 dict->dicttemplate);
00277 template = (Form_pg_ts_template) GETSTRUCT(tptmpl);
00278
00279
00280
00281
00282 if (!OidIsValid(template->tmpllexize))
00283 elog(ERROR, "text search template %u has no lexize method",
00284 template->tmpllexize);
00285
00286 if (entry == NULL)
00287 {
00288 bool found;
00289
00290
00291 entry = (TSDictionaryCacheEntry *)
00292 hash_search(TSDictionaryCacheHash,
00293 (void *) &dictId,
00294 HASH_ENTER, &found);
00295 Assert(!found);
00296
00297
00298 saveCtx = AllocSetContextCreate(CacheMemoryContext,
00299 NameStr(dict->dictname),
00300 ALLOCSET_SMALL_MINSIZE,
00301 ALLOCSET_SMALL_INITSIZE,
00302 ALLOCSET_SMALL_MAXSIZE);
00303 }
00304 else
00305 {
00306
00307 saveCtx = entry->dictCtx;
00308 MemoryContextResetAndDeleteChildren(saveCtx);
00309 }
00310
00311 MemSet(entry, 0, sizeof(TSDictionaryCacheEntry));
00312 entry->dictId = dictId;
00313 entry->dictCtx = saveCtx;
00314
00315 entry->lexizeOid = template->tmpllexize;
00316
00317 if (OidIsValid(template->tmplinit))
00318 {
00319 List *dictoptions;
00320 Datum opt;
00321 bool isnull;
00322 MemoryContext oldcontext;
00323
00324
00325
00326
00327
00328 oldcontext = MemoryContextSwitchTo(entry->dictCtx);
00329
00330 opt = SysCacheGetAttr(TSDICTOID, tpdict,
00331 Anum_pg_ts_dict_dictinitoption,
00332 &isnull);
00333 if (isnull)
00334 dictoptions = NIL;
00335 else
00336 dictoptions = deserialize_deflist(opt);
00337
00338 entry->dictData =
00339 DatumGetPointer(OidFunctionCall1(template->tmplinit,
00340 PointerGetDatum(dictoptions)));
00341
00342 MemoryContextSwitchTo(oldcontext);
00343 }
00344
00345 ReleaseSysCache(tptmpl);
00346 ReleaseSysCache(tpdict);
00347
00348 fmgr_info_cxt(entry->lexizeOid, &entry->lexize, entry->dictCtx);
00349
00350 entry->isvalid = true;
00351 }
00352
00353 lastUsedDictionary = entry;
00354
00355 return entry;
00356 }
00357
00358
00359
00360
00361
00362
00363 static void
00364 init_ts_config_cache(void)
00365 {
00366 HASHCTL ctl;
00367
00368 MemSet(&ctl, 0, sizeof(ctl));
00369 ctl.keysize = sizeof(Oid);
00370 ctl.entrysize = sizeof(TSConfigCacheEntry);
00371 ctl.hash = oid_hash;
00372 TSConfigCacheHash = hash_create("Tsearch configuration cache", 16,
00373 &ctl, HASH_ELEM | HASH_FUNCTION);
00374
00375 CacheRegisterSyscacheCallback(TSCONFIGOID, InvalidateTSCacheCallBack,
00376 PointerGetDatum(TSConfigCacheHash));
00377 CacheRegisterSyscacheCallback(TSCONFIGMAP, InvalidateTSCacheCallBack,
00378 PointerGetDatum(TSConfigCacheHash));
00379
00380
00381 if (!CacheMemoryContext)
00382 CreateCacheMemoryContext();
00383 }
00384
00385
00386
00387
00388 TSConfigCacheEntry *
00389 lookup_ts_config_cache(Oid cfgId)
00390 {
00391 TSConfigCacheEntry *entry;
00392
00393 if (TSConfigCacheHash == NULL)
00394 {
00395
00396 init_ts_config_cache();
00397 }
00398
00399
00400 if (lastUsedConfig && lastUsedConfig->cfgId == cfgId &&
00401 lastUsedConfig->isvalid)
00402 return lastUsedConfig;
00403
00404
00405 entry = (TSConfigCacheEntry *) hash_search(TSConfigCacheHash,
00406 (void *) &cfgId,
00407 HASH_FIND, NULL);
00408 if (entry == NULL || !entry->isvalid)
00409 {
00410
00411
00412
00413
00414 HeapTuple tp;
00415 Form_pg_ts_config cfg;
00416 Relation maprel;
00417 Relation mapidx;
00418 ScanKeyData mapskey;
00419 SysScanDesc mapscan;
00420 HeapTuple maptup;
00421 ListDictionary maplists[MAXTOKENTYPE + 1];
00422 Oid mapdicts[MAXDICTSPERTT];
00423 int maxtokentype;
00424 int ndicts;
00425 int i;
00426
00427 tp = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
00428 if (!HeapTupleIsValid(tp))
00429 elog(ERROR, "cache lookup failed for text search configuration %u",
00430 cfgId);
00431 cfg = (Form_pg_ts_config) GETSTRUCT(tp);
00432
00433
00434
00435
00436 if (!OidIsValid(cfg->cfgparser))
00437 elog(ERROR, "text search configuration %u has no parser", cfgId);
00438
00439 if (entry == NULL)
00440 {
00441 bool found;
00442
00443
00444 entry = (TSConfigCacheEntry *)
00445 hash_search(TSConfigCacheHash,
00446 (void *) &cfgId,
00447 HASH_ENTER, &found);
00448 Assert(!found);
00449 }
00450 else
00451 {
00452
00453 if (entry->map)
00454 {
00455 for (i = 0; i < entry->lenmap; i++)
00456 if (entry->map[i].dictIds)
00457 pfree(entry->map[i].dictIds);
00458 pfree(entry->map);
00459 }
00460 }
00461
00462 MemSet(entry, 0, sizeof(TSConfigCacheEntry));
00463 entry->cfgId = cfgId;
00464 entry->prsId = cfg->cfgparser;
00465
00466 ReleaseSysCache(tp);
00467
00468
00469
00470
00471
00472
00473
00474
00475 MemSet(maplists, 0, sizeof(maplists));
00476 maxtokentype = 0;
00477 ndicts = 0;
00478
00479 ScanKeyInit(&mapskey,
00480 Anum_pg_ts_config_map_mapcfg,
00481 BTEqualStrategyNumber, F_OIDEQ,
00482 ObjectIdGetDatum(cfgId));
00483
00484 maprel = heap_open(TSConfigMapRelationId, AccessShareLock);
00485 mapidx = index_open(TSConfigMapIndexId, AccessShareLock);
00486 mapscan = systable_beginscan_ordered(maprel, mapidx,
00487 SnapshotNow, 1, &mapskey);
00488
00489 while ((maptup = systable_getnext_ordered(mapscan, ForwardScanDirection)) != NULL)
00490 {
00491 Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
00492 int toktype = cfgmap->maptokentype;
00493
00494 if (toktype <= 0 || toktype > MAXTOKENTYPE)
00495 elog(ERROR, "maptokentype value %d is out of range", toktype);
00496 if (toktype < maxtokentype)
00497 elog(ERROR, "maptokentype entries are out of order");
00498 if (toktype > maxtokentype)
00499 {
00500
00501 if (ndicts > 0)
00502 {
00503 maplists[maxtokentype].len = ndicts;
00504 maplists[maxtokentype].dictIds = (Oid *)
00505 MemoryContextAlloc(CacheMemoryContext,
00506 sizeof(Oid) * ndicts);
00507 memcpy(maplists[maxtokentype].dictIds, mapdicts,
00508 sizeof(Oid) * ndicts);
00509 }
00510 maxtokentype = toktype;
00511 mapdicts[0] = cfgmap->mapdict;
00512 ndicts = 1;
00513 }
00514 else
00515 {
00516
00517 if (ndicts >= MAXDICTSPERTT)
00518 elog(ERROR, "too many pg_ts_config_map entries for one token type");
00519 mapdicts[ndicts++] = cfgmap->mapdict;
00520 }
00521 }
00522
00523 systable_endscan_ordered(mapscan);
00524 index_close(mapidx, AccessShareLock);
00525 heap_close(maprel, AccessShareLock);
00526
00527 if (ndicts > 0)
00528 {
00529
00530 maplists[maxtokentype].len = ndicts;
00531 maplists[maxtokentype].dictIds = (Oid *)
00532 MemoryContextAlloc(CacheMemoryContext,
00533 sizeof(Oid) * ndicts);
00534 memcpy(maplists[maxtokentype].dictIds, mapdicts,
00535 sizeof(Oid) * ndicts);
00536
00537 entry->lenmap = maxtokentype + 1;
00538 entry->map = (ListDictionary *)
00539 MemoryContextAlloc(CacheMemoryContext,
00540 sizeof(ListDictionary) * entry->lenmap);
00541 memcpy(entry->map, maplists,
00542 sizeof(ListDictionary) * entry->lenmap);
00543 }
00544
00545 entry->isvalid = true;
00546 }
00547
00548 lastUsedConfig = entry;
00549
00550 return entry;
00551 }
00552
00553
00554
00555
00556
00557
00558
00559 Oid
00560 getTSCurrentConfig(bool emitError)
00561 {
00562
00563 if (OidIsValid(TSCurrentConfigCache))
00564 return TSCurrentConfigCache;
00565
00566
00567 if (TSCurrentConfig == NULL || *TSCurrentConfig == '\0')
00568 {
00569 if (emitError)
00570 elog(ERROR, "text search configuration isn't set");
00571 else
00572 return InvalidOid;
00573 }
00574
00575 if (TSConfigCacheHash == NULL)
00576 {
00577
00578 init_ts_config_cache();
00579 }
00580
00581
00582 TSCurrentConfigCache =
00583 get_ts_config_oid(stringToQualifiedNameList(TSCurrentConfig),
00584 !emitError);
00585
00586 return TSCurrentConfigCache;
00587 }
00588
00589
00590 bool
00591 check_TSCurrentConfig(char **newval, void **extra, GucSource source)
00592 {
00593
00594
00595
00596
00597 if (IsTransactionState())
00598 {
00599 Oid cfgId;
00600 HeapTuple tuple;
00601 Form_pg_ts_config cfg;
00602 char *buf;
00603
00604 cfgId = get_ts_config_oid(stringToQualifiedNameList(*newval), true);
00605
00606
00607
00608
00609
00610
00611
00612
00613 if (!OidIsValid(cfgId))
00614 {
00615 if (source == PGC_S_TEST)
00616 {
00617 ereport(NOTICE,
00618 (errcode(ERRCODE_UNDEFINED_OBJECT),
00619 errmsg("text search configuration \"%s\" does not exist", *newval)));
00620 return true;
00621 }
00622 else
00623 return false;
00624 }
00625
00626
00627
00628
00629
00630 tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
00631 if (!HeapTupleIsValid(tuple))
00632 elog(ERROR, "cache lookup failed for text search configuration %u",
00633 cfgId);
00634 cfg = (Form_pg_ts_config) GETSTRUCT(tuple);
00635
00636 buf = quote_qualified_identifier(get_namespace_name(cfg->cfgnamespace),
00637 NameStr(cfg->cfgname));
00638
00639 ReleaseSysCache(tuple);
00640
00641
00642 free(*newval);
00643 *newval = strdup(buf);
00644 pfree(buf);
00645 if (!*newval)
00646 return false;
00647 }
00648
00649 return true;
00650 }
00651
00652
00653 void
00654 assign_TSCurrentConfig(const char *newval, void *extra)
00655 {
00656
00657 TSCurrentConfigCache = InvalidOid;
00658 }