mirror of
https://github.com/postgres/postgres.git
synced 2025-07-02 09:02:37 +03:00
Tsearch2 functionality migrates to core. The bulk of this work is by
Oleg Bartunov and Teodor Sigaev, but I did a lot of editorializing, so anything that's broken is probably my fault. Documentation is nonexistent as yet, but let's land the patch so we can get some portability testing done.
This commit is contained in:
4
src/backend/utils/cache/Makefile
vendored
4
src/backend/utils/cache/Makefile
vendored
@ -4,7 +4,7 @@
|
||||
# Makefile for utils/cache
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $PostgreSQL: pgsql/src/backend/utils/cache/Makefile,v 1.21 2007/03/13 00:33:42 tgl Exp $
|
||||
# $PostgreSQL: pgsql/src/backend/utils/cache/Makefile,v 1.22 2007/08/21 01:11:19 tgl Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@ -13,7 +13,7 @@ top_builddir = ../../../..
|
||||
include $(top_builddir)/src/Makefile.global
|
||||
|
||||
OBJS = catcache.o inval.o plancache.o relcache.o \
|
||||
syscache.o lsyscache.o typcache.o
|
||||
syscache.o lsyscache.o typcache.o ts_cache.o
|
||||
|
||||
all: SUBSYS.o
|
||||
|
||||
|
4
src/backend/utils/cache/catcache.c
vendored
4
src/backend/utils/cache/catcache.c
vendored
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.137 2007/04/21 04:49:20 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.138 2007/08/21 01:11:19 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -134,6 +134,8 @@ GetCCHashEqFuncs(Oid keytype, PGFunction *hashfunc, RegProcedure *eqfunc)
|
||||
case REGOPERATOROID:
|
||||
case REGCLASSOID:
|
||||
case REGTYPEOID:
|
||||
case REGCONFIGOID:
|
||||
case REGDICTIONARYOID:
|
||||
*hashfunc = hashoid;
|
||||
*eqfunc = F_OIDEQ;
|
||||
break;
|
||||
|
115
src/backend/utils/cache/syscache.c
vendored
115
src/backend/utils/cache/syscache.c
vendored
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.112 2007/04/02 03:49:39 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.113 2007/08/21 01:11:19 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* These routines allow the parser/planner/executor to perform
|
||||
@ -40,6 +40,11 @@
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_rewrite.h"
|
||||
#include "catalog/pg_statistic.h"
|
||||
#include "catalog/pg_ts_config.h"
|
||||
#include "catalog/pg_ts_config_map.h"
|
||||
#include "catalog/pg_ts_dict.h"
|
||||
#include "catalog/pg_ts_parser.h"
|
||||
#include "catalog/pg_ts_template.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
@ -540,6 +545,114 @@ static const struct cachedesc cacheinfo[] = {
|
||||
},
|
||||
1024
|
||||
},
|
||||
{TSConfigMapRelationId, /* TSCONFIGMAP */
|
||||
TSConfigMapIndexId,
|
||||
0,
|
||||
3,
|
||||
{
|
||||
Anum_pg_ts_config_map_mapcfg,
|
||||
Anum_pg_ts_config_map_maptokentype,
|
||||
Anum_pg_ts_config_map_mapseqno,
|
||||
0
|
||||
},
|
||||
4
|
||||
},
|
||||
{TSConfigRelationId, /* TSCONFIGNAMENSP */
|
||||
TSConfigNameNspIndexId,
|
||||
0,
|
||||
2,
|
||||
{
|
||||
Anum_pg_ts_config_cfgname,
|
||||
Anum_pg_ts_config_cfgnamespace,
|
||||
0,
|
||||
0
|
||||
},
|
||||
16
|
||||
},
|
||||
{TSConfigRelationId, /* TSCONFIGOID */
|
||||
TSConfigOidIndexId,
|
||||
0,
|
||||
1,
|
||||
{
|
||||
ObjectIdAttributeNumber,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
16
|
||||
},
|
||||
{TSDictionaryRelationId, /* TSDICTNAMENSP */
|
||||
TSDictionaryNameNspIndexId,
|
||||
0,
|
||||
2,
|
||||
{
|
||||
Anum_pg_ts_dict_dictname,
|
||||
Anum_pg_ts_dict_dictnamespace,
|
||||
0,
|
||||
0
|
||||
},
|
||||
16
|
||||
},
|
||||
{TSDictionaryRelationId, /* TSDICTOID */
|
||||
TSDictionaryOidIndexId,
|
||||
0,
|
||||
1,
|
||||
{
|
||||
ObjectIdAttributeNumber,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
16
|
||||
},
|
||||
{TSParserRelationId, /* TSPARSERNAMENSP */
|
||||
TSParserNameNspIndexId,
|
||||
0,
|
||||
2,
|
||||
{
|
||||
Anum_pg_ts_parser_prsname,
|
||||
Anum_pg_ts_parser_prsnamespace,
|
||||
0,
|
||||
0
|
||||
},
|
||||
4
|
||||
},
|
||||
{TSParserRelationId, /* TSPARSEROID */
|
||||
TSParserOidIndexId,
|
||||
0,
|
||||
1,
|
||||
{
|
||||
ObjectIdAttributeNumber,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
4
|
||||
},
|
||||
{TSTemplateRelationId, /* TSTEMPLATENAMENSP */
|
||||
TSTemplateNameNspIndexId,
|
||||
0,
|
||||
2,
|
||||
{
|
||||
Anum_pg_ts_template_tmplname,
|
||||
Anum_pg_ts_template_tmplnamespace,
|
||||
0,
|
||||
0
|
||||
},
|
||||
16
|
||||
},
|
||||
{TSTemplateRelationId, /* TSTEMPLATEOID */
|
||||
TSTemplateOidIndexId,
|
||||
0,
|
||||
1,
|
||||
{
|
||||
ObjectIdAttributeNumber,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
16
|
||||
},
|
||||
{TypeRelationId, /* TYPENAMENSP */
|
||||
TypeNameNspIndexId,
|
||||
Anum_pg_type_typrelid,
|
||||
|
641
src/backend/utils/cache/ts_cache.c
vendored
Normal file
641
src/backend/utils/cache/ts_cache.c
vendored
Normal file
@ -0,0 +1,641 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* ts_cache.c
|
||||
* Tsearch related object caches.
|
||||
*
|
||||
* Tsearch performance is very sensitive to performance of parsers,
|
||||
* dictionaries and mapping, so lookups should be cached as much
|
||||
* as possible.
|
||||
*
|
||||
* Once a backend has created a cache entry for a particular TS object OID,
|
||||
* the cache entry will exist for the life of the backend; hence it is
|
||||
* safe to hold onto a pointer to the cache entry while doing things that
|
||||
* might result in recognizing a cache invalidation. Beware however that
|
||||
* subsidiary information might be deleted and reallocated somewhere else
|
||||
* if a cache inval and reval happens! This does not look like it will be
|
||||
* a big problem as long as parser and dictionary methods do not attempt
|
||||
* any database access.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2006-2007, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/ts_cache.c,v 1.1 2007/08/21 01:11:19 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/genam.h"
|
||||
#include "access/heapam.h"
|
||||
#include "access/xact.h"
|
||||
#include "catalog/indexing.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_ts_config.h"
|
||||
#include "catalog/pg_ts_config_map.h"
|
||||
#include "catalog/pg_ts_dict.h"
|
||||
#include "catalog/pg_ts_parser.h"
|
||||
#include "catalog/pg_ts_template.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "miscadmin.h"
|
||||
#include "tsearch/ts_cache.h"
|
||||
#include "tsearch/ts_utils.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/catcache.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/inval.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
/*
|
||||
* MAXTOKENTYPE/MAXDICTSPERTT are arbitrary limits on the workspace size
|
||||
* used in lookup_ts_config_cache(). We could avoid hardwiring a limit
|
||||
* by making the workspace dynamically enlargeable, but it seems unlikely
|
||||
* to be worth the trouble.
|
||||
*/
|
||||
#define MAXTOKENTYPE 256
|
||||
#define MAXDICTSPERTT 100
|
||||
|
||||
|
||||
static HTAB *TSParserCacheHash = NULL;
|
||||
static TSParserCacheEntry *lastUsedParser = NULL;
|
||||
|
||||
static HTAB *TSDictionaryCacheHash = NULL;
|
||||
static TSDictionaryCacheEntry *lastUsedDictionary = NULL;
|
||||
|
||||
static HTAB *TSConfigCacheHash = NULL;
|
||||
static TSConfigCacheEntry *lastUsedConfig = NULL;
|
||||
|
||||
/*
|
||||
* GUC default_text_search_config, and a cache of the current config's OID
|
||||
*/
|
||||
char *TSCurrentConfig = NULL;
|
||||
|
||||
static Oid TSCurrentConfigCache = InvalidOid;
|
||||
|
||||
|
||||
/*
|
||||
* We use this catcache callback to detect when a visible change to a TS
|
||||
* catalog entry has been made, by either our own backend or another one.
|
||||
* We don't get enough information to know *which* specific catalog row
|
||||
* changed, so we have to invalidate all related cache entries. Fortunately,
|
||||
* it seems unlikely that TS configuration changes will occur often enough
|
||||
* for this to be a performance problem.
|
||||
*
|
||||
* We can use the same function for all TS caches by passing the hash
|
||||
* table address as the "arg".
|
||||
*/
|
||||
static void
|
||||
InvalidateTSCacheCallBack(Datum arg, Oid relid)
|
||||
{
|
||||
HTAB *hash = (HTAB *) DatumGetPointer(arg);
|
||||
HASH_SEQ_STATUS status;
|
||||
TSAnyCacheEntry *entry;
|
||||
|
||||
hash_seq_init(&status, hash);
|
||||
while ((entry = (TSAnyCacheEntry *) hash_seq_search(&status)) != NULL)
|
||||
entry->isvalid = false;
|
||||
|
||||
/* Also invalidate the current-config cache if it's pg_ts_config */
|
||||
if (hash == TSConfigCacheHash)
|
||||
TSCurrentConfigCache = InvalidOid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch parser cache entry
|
||||
*/
|
||||
TSParserCacheEntry *
|
||||
lookup_ts_parser_cache(Oid prsId)
|
||||
{
|
||||
TSParserCacheEntry *entry;
|
||||
|
||||
if (TSParserCacheHash == NULL)
|
||||
{
|
||||
/* First time through: initialize the hash table */
|
||||
HASHCTL ctl;
|
||||
|
||||
if (!CacheMemoryContext)
|
||||
CreateCacheMemoryContext();
|
||||
|
||||
MemSet(&ctl, 0, sizeof(ctl));
|
||||
ctl.keysize = sizeof(Oid);
|
||||
ctl.entrysize = sizeof(TSParserCacheEntry);
|
||||
ctl.hash = oid_hash;
|
||||
TSParserCacheHash = hash_create("Tsearch parser cache", 4,
|
||||
&ctl, HASH_ELEM | HASH_FUNCTION);
|
||||
/* Flush cache on pg_ts_parser changes */
|
||||
CacheRegisterSyscacheCallback(TSPARSEROID, InvalidateTSCacheCallBack,
|
||||
PointerGetDatum(TSParserCacheHash));
|
||||
}
|
||||
|
||||
/* Check single-entry cache */
|
||||
if (lastUsedParser && lastUsedParser->prsId == prsId &&
|
||||
lastUsedParser->isvalid)
|
||||
return lastUsedParser;
|
||||
|
||||
/* Try to look up an existing entry */
|
||||
entry = (TSParserCacheEntry *) hash_search(TSParserCacheHash,
|
||||
(void *) &prsId,
|
||||
HASH_FIND, NULL);
|
||||
if (entry == NULL || !entry->isvalid)
|
||||
{
|
||||
/*
|
||||
* If we didn't find one, we want to make one.
|
||||
* But first look up the object to be sure the OID is real.
|
||||
*/
|
||||
HeapTuple tp;
|
||||
Form_pg_ts_parser prs;
|
||||
|
||||
tp = SearchSysCache(TSPARSEROID,
|
||||
ObjectIdGetDatum(prsId),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tp))
|
||||
elog(ERROR, "cache lookup failed for text search parser %u",
|
||||
prsId);
|
||||
prs = (Form_pg_ts_parser) GETSTRUCT(tp);
|
||||
|
||||
/*
|
||||
* Sanity checks
|
||||
*/
|
||||
if (!OidIsValid(prs->prsstart))
|
||||
elog(ERROR, "text search parser %u has no prsstart method", prsId);
|
||||
if (!OidIsValid(prs->prstoken))
|
||||
elog(ERROR, "text search parser %u has no prstoken method", prsId);
|
||||
if (!OidIsValid(prs->prsend))
|
||||
elog(ERROR, "text search parser %u has no prsend method", prsId);
|
||||
|
||||
if (entry == NULL)
|
||||
{
|
||||
bool found;
|
||||
|
||||
/* Now make the cache entry */
|
||||
entry = (TSParserCacheEntry *)
|
||||
hash_search(TSParserCacheHash,
|
||||
(void *) &prsId,
|
||||
HASH_ENTER, &found);
|
||||
Assert(!found); /* it wasn't there a moment ago */
|
||||
}
|
||||
|
||||
MemSet(entry, 0, sizeof(TSParserCacheEntry));
|
||||
entry->prsId = prsId;
|
||||
entry->startOid = prs->prsstart;
|
||||
entry->tokenOid = prs->prstoken;
|
||||
entry->endOid = prs->prsend;
|
||||
entry->headlineOid = prs->prsheadline;
|
||||
entry->lextypeOid = prs->prslextype;
|
||||
|
||||
ReleaseSysCache(tp);
|
||||
|
||||
fmgr_info_cxt(entry->startOid, &entry->prsstart, CacheMemoryContext);
|
||||
fmgr_info_cxt(entry->tokenOid, &entry->prstoken, CacheMemoryContext);
|
||||
fmgr_info_cxt(entry->endOid, &entry->prsend, CacheMemoryContext);
|
||||
if (OidIsValid(entry->headlineOid))
|
||||
fmgr_info_cxt(entry->headlineOid, &entry->prsheadline,
|
||||
CacheMemoryContext);
|
||||
|
||||
entry->isvalid = true;
|
||||
}
|
||||
|
||||
lastUsedParser = entry;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch dictionary cache entry
|
||||
*/
|
||||
TSDictionaryCacheEntry *
|
||||
lookup_ts_dictionary_cache(Oid dictId)
|
||||
{
|
||||
TSDictionaryCacheEntry *entry;
|
||||
|
||||
if (TSDictionaryCacheHash == NULL)
|
||||
{
|
||||
/* First time through: initialize the hash table */
|
||||
HASHCTL ctl;
|
||||
|
||||
if (!CacheMemoryContext)
|
||||
CreateCacheMemoryContext();
|
||||
|
||||
MemSet(&ctl, 0, sizeof(ctl));
|
||||
ctl.keysize = sizeof(Oid);
|
||||
ctl.entrysize = sizeof(TSDictionaryCacheEntry);
|
||||
ctl.hash = oid_hash;
|
||||
TSDictionaryCacheHash = hash_create("Tsearch dictionary cache", 8,
|
||||
&ctl, HASH_ELEM | HASH_FUNCTION);
|
||||
/* Flush cache on pg_ts_dict and pg_ts_template changes */
|
||||
CacheRegisterSyscacheCallback(TSDICTOID, InvalidateTSCacheCallBack,
|
||||
PointerGetDatum(TSDictionaryCacheHash));
|
||||
CacheRegisterSyscacheCallback(TSTEMPLATEOID, InvalidateTSCacheCallBack,
|
||||
PointerGetDatum(TSDictionaryCacheHash));
|
||||
}
|
||||
|
||||
/* Check single-entry cache */
|
||||
if (lastUsedDictionary && lastUsedDictionary->dictId == dictId &&
|
||||
lastUsedDictionary->isvalid)
|
||||
return lastUsedDictionary;
|
||||
|
||||
/* Try to look up an existing entry */
|
||||
entry = (TSDictionaryCacheEntry *) hash_search(TSDictionaryCacheHash,
|
||||
(void *) &dictId,
|
||||
HASH_FIND, NULL);
|
||||
if (entry == NULL || !entry->isvalid)
|
||||
{
|
||||
/*
|
||||
* If we didn't find one, we want to make one.
|
||||
* But first look up the object to be sure the OID is real.
|
||||
*/
|
||||
HeapTuple tpdict,
|
||||
tptmpl;
|
||||
Form_pg_ts_dict dict;
|
||||
Form_pg_ts_template template;
|
||||
MemoryContext saveCtx = NULL;
|
||||
|
||||
tpdict = SearchSysCache(TSDICTOID,
|
||||
ObjectIdGetDatum(dictId),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tpdict))
|
||||
elog(ERROR, "cache lookup failed for text search dictionary %u",
|
||||
dictId);
|
||||
dict = (Form_pg_ts_dict) GETSTRUCT(tpdict);
|
||||
|
||||
/*
|
||||
* Sanity checks
|
||||
*/
|
||||
if (!OidIsValid(dict->dicttemplate))
|
||||
elog(ERROR, "text search dictionary %u has no template", dictId);
|
||||
|
||||
/*
|
||||
* Retrieve dictionary's template
|
||||
*/
|
||||
tptmpl = SearchSysCache(TSTEMPLATEOID,
|
||||
ObjectIdGetDatum(dict->dicttemplate),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tptmpl))
|
||||
elog(ERROR, "cache lookup failed for text search template %u",
|
||||
dict->dicttemplate);
|
||||
template = (Form_pg_ts_template) GETSTRUCT(tptmpl);
|
||||
|
||||
/*
|
||||
* Sanity checks
|
||||
*/
|
||||
if (!OidIsValid(template->tmpllexize))
|
||||
elog(ERROR, "text search template %u has no lexize method",
|
||||
template->tmpllexize);
|
||||
|
||||
if (entry == NULL)
|
||||
{
|
||||
bool found;
|
||||
|
||||
/* Now make the cache entry */
|
||||
entry = (TSDictionaryCacheEntry *)
|
||||
hash_search(TSDictionaryCacheHash,
|
||||
(void *) &dictId,
|
||||
HASH_ENTER, &found);
|
||||
Assert(!found); /* it wasn't there a moment ago */
|
||||
|
||||
/* Create private memory context the first time through */
|
||||
saveCtx = AllocSetContextCreate(CacheMemoryContext,
|
||||
NameStr(dict->dictname),
|
||||
ALLOCSET_SMALL_MINSIZE,
|
||||
ALLOCSET_SMALL_INITSIZE,
|
||||
ALLOCSET_SMALL_MAXSIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Clear the existing entry's private context */
|
||||
saveCtx = entry->dictCtx;
|
||||
MemoryContextResetAndDeleteChildren(saveCtx);
|
||||
}
|
||||
|
||||
MemSet(entry, 0, sizeof(TSDictionaryCacheEntry));
|
||||
entry->dictId = dictId;
|
||||
entry->dictCtx = saveCtx;
|
||||
|
||||
entry->lexizeOid = template->tmpllexize;
|
||||
|
||||
if (OidIsValid(template->tmplinit))
|
||||
{
|
||||
bool isnull;
|
||||
Datum opt;
|
||||
|
||||
opt = SysCacheGetAttr(TSDICTOID, tpdict,
|
||||
Anum_pg_ts_dict_dictinitoption,
|
||||
&isnull);
|
||||
if (isnull)
|
||||
opt = PointerGetDatum(NULL);
|
||||
|
||||
/*
|
||||
* Init method runs in dictionary's private memory context
|
||||
*/
|
||||
saveCtx = MemoryContextSwitchTo(entry->dictCtx);
|
||||
entry->dictData = DatumGetPointer(OidFunctionCall1(template->tmplinit, opt));
|
||||
MemoryContextSwitchTo(saveCtx);
|
||||
}
|
||||
|
||||
ReleaseSysCache(tptmpl);
|
||||
ReleaseSysCache(tpdict);
|
||||
|
||||
fmgr_info_cxt(entry->lexizeOid, &entry->lexize, entry->dictCtx);
|
||||
|
||||
entry->isvalid = true;
|
||||
}
|
||||
|
||||
lastUsedDictionary = entry;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize config cache and prepare callbacks. This is split out of
|
||||
* lookup_ts_config_cache because we need to activate the callback before
|
||||
* caching TSCurrentConfigCache, too.
|
||||
*/
|
||||
static void
|
||||
init_ts_config_cache(void)
|
||||
{
|
||||
HASHCTL ctl;
|
||||
|
||||
if (!CacheMemoryContext)
|
||||
CreateCacheMemoryContext();
|
||||
|
||||
MemSet(&ctl, 0, sizeof(ctl));
|
||||
ctl.keysize = sizeof(Oid);
|
||||
ctl.entrysize = sizeof(TSConfigCacheEntry);
|
||||
ctl.hash = oid_hash;
|
||||
TSConfigCacheHash = hash_create("Tsearch configuration cache", 16,
|
||||
&ctl, HASH_ELEM | HASH_FUNCTION);
|
||||
/* Flush cache on pg_ts_config and pg_ts_config_map changes */
|
||||
CacheRegisterSyscacheCallback(TSCONFIGOID, InvalidateTSCacheCallBack,
|
||||
PointerGetDatum(TSConfigCacheHash));
|
||||
CacheRegisterSyscacheCallback(TSCONFIGMAP, InvalidateTSCacheCallBack,
|
||||
PointerGetDatum(TSConfigCacheHash));
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch configuration cache entry
|
||||
*/
|
||||
TSConfigCacheEntry *
|
||||
lookup_ts_config_cache(Oid cfgId)
|
||||
{
|
||||
TSConfigCacheEntry *entry;
|
||||
|
||||
if (TSConfigCacheHash == NULL)
|
||||
{
|
||||
/* First time through: initialize the hash table */
|
||||
init_ts_config_cache();
|
||||
}
|
||||
|
||||
/* Check single-entry cache */
|
||||
if (lastUsedConfig && lastUsedConfig->cfgId == cfgId &&
|
||||
lastUsedConfig->isvalid)
|
||||
return lastUsedConfig;
|
||||
|
||||
/* Try to look up an existing entry */
|
||||
entry = (TSConfigCacheEntry *) hash_search(TSConfigCacheHash,
|
||||
(void *) &cfgId,
|
||||
HASH_FIND, NULL);
|
||||
if (entry == NULL || !entry->isvalid)
|
||||
{
|
||||
/*
|
||||
* If we didn't find one, we want to make one.
|
||||
* But first look up the object to be sure the OID is real.
|
||||
*/
|
||||
HeapTuple tp;
|
||||
Form_pg_ts_config cfg;
|
||||
Relation maprel;
|
||||
Relation mapidx;
|
||||
ScanKeyData mapskey;
|
||||
IndexScanDesc mapscan;
|
||||
HeapTuple maptup;
|
||||
ListDictionary maplists[MAXTOKENTYPE + 1];
|
||||
Oid mapdicts[MAXDICTSPERTT];
|
||||
int maxtokentype;
|
||||
int ndicts;
|
||||
int i;
|
||||
|
||||
tp = SearchSysCache(TSCONFIGOID,
|
||||
ObjectIdGetDatum(cfgId),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tp))
|
||||
elog(ERROR, "cache lookup failed for text search configuration %u",
|
||||
cfgId);
|
||||
cfg = (Form_pg_ts_config) GETSTRUCT(tp);
|
||||
|
||||
/*
|
||||
* Sanity checks
|
||||
*/
|
||||
if (!OidIsValid(cfg->cfgparser))
|
||||
elog(ERROR, "text search configuration %u has no parser", cfgId);
|
||||
|
||||
if (entry == NULL)
|
||||
{
|
||||
bool found;
|
||||
|
||||
/* Now make the cache entry */
|
||||
entry = (TSConfigCacheEntry *)
|
||||
hash_search(TSConfigCacheHash,
|
||||
(void *) &cfgId,
|
||||
HASH_ENTER, &found);
|
||||
Assert(!found); /* it wasn't there a moment ago */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Cleanup old contents */
|
||||
if (entry->map)
|
||||
{
|
||||
for (i = 0; i < entry->lenmap; i++)
|
||||
if (entry->map[i].dictIds)
|
||||
pfree(entry->map[i].dictIds);
|
||||
pfree(entry->map);
|
||||
}
|
||||
}
|
||||
|
||||
MemSet(entry, 0, sizeof(TSConfigCacheEntry));
|
||||
entry->cfgId = cfgId;
|
||||
entry->prsId = cfg->cfgparser;
|
||||
|
||||
ReleaseSysCache(tp);
|
||||
|
||||
/*
|
||||
* Scan pg_ts_config_map to gather dictionary list for each token type
|
||||
*
|
||||
* Because the index is on (mapcfg, maptokentype, mapseqno), we will
|
||||
* see the entries in maptokentype order, and in mapseqno order for
|
||||
* each token type, even though we didn't explicitly ask for that.
|
||||
*/
|
||||
MemSet(maplists, 0, sizeof(maplists));
|
||||
maxtokentype = 0;
|
||||
ndicts = 0;
|
||||
|
||||
ScanKeyInit(&mapskey,
|
||||
Anum_pg_ts_config_map_mapcfg,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(cfgId));
|
||||
|
||||
maprel = heap_open(TSConfigMapRelationId, AccessShareLock);
|
||||
mapidx = index_open(TSConfigMapIndexId, AccessShareLock);
|
||||
mapscan = index_beginscan(maprel, mapidx, SnapshotNow, 1, &mapskey);
|
||||
|
||||
while ((maptup = index_getnext(mapscan, ForwardScanDirection)) != NULL)
|
||||
{
|
||||
Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
|
||||
int toktype = cfgmap->maptokentype;
|
||||
|
||||
if (toktype <= 0 || toktype > MAXTOKENTYPE)
|
||||
elog(ERROR, "maptokentype value %d is out of range", toktype);
|
||||
if (toktype < maxtokentype)
|
||||
elog(ERROR, "maptokentype entries are out of order");
|
||||
if (toktype > maxtokentype)
|
||||
{
|
||||
/* starting a new token type, but first save the prior data */
|
||||
if (ndicts > 0)
|
||||
{
|
||||
maplists[maxtokentype].len = ndicts;
|
||||
maplists[maxtokentype].dictIds = (Oid *)
|
||||
MemoryContextAlloc(CacheMemoryContext,
|
||||
sizeof(Oid) * ndicts);
|
||||
memcpy(maplists[maxtokentype].dictIds, mapdicts,
|
||||
sizeof(Oid) * ndicts);
|
||||
}
|
||||
maxtokentype = toktype;
|
||||
mapdicts[0] = cfgmap->mapdict;
|
||||
ndicts = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* continuing data for current token type */
|
||||
if (ndicts >= MAXDICTSPERTT)
|
||||
elog(ERROR, "too many pg_ts_config_map entries for one token type");
|
||||
mapdicts[ndicts++] = cfgmap->mapdict;
|
||||
}
|
||||
}
|
||||
|
||||
index_endscan(mapscan);
|
||||
index_close(mapidx, AccessShareLock);
|
||||
heap_close(maprel, AccessShareLock);
|
||||
|
||||
if (ndicts > 0)
|
||||
{
|
||||
/* save the last token type's dictionaries */
|
||||
maplists[maxtokentype].len = ndicts;
|
||||
maplists[maxtokentype].dictIds = (Oid *)
|
||||
MemoryContextAlloc(CacheMemoryContext,
|
||||
sizeof(Oid) * ndicts);
|
||||
memcpy(maplists[maxtokentype].dictIds, mapdicts,
|
||||
sizeof(Oid) * ndicts);
|
||||
/* and save the overall map */
|
||||
entry->lenmap = maxtokentype + 1;
|
||||
entry->map = (ListDictionary *)
|
||||
MemoryContextAlloc(CacheMemoryContext,
|
||||
sizeof(ListDictionary) * entry->lenmap);
|
||||
memcpy(entry->map, maplists,
|
||||
sizeof(ListDictionary) * entry->lenmap);
|
||||
}
|
||||
|
||||
entry->isvalid = true;
|
||||
}
|
||||
|
||||
lastUsedConfig = entry;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------
|
||||
* GUC variable "default_text_search_config"
|
||||
*---------------------------------------------------
|
||||
*/
|
||||
|
||||
Oid
|
||||
getTSCurrentConfig(bool emitError)
|
||||
{
|
||||
/* if we have a cached value, return it */
|
||||
if (OidIsValid(TSCurrentConfigCache))
|
||||
return TSCurrentConfigCache;
|
||||
|
||||
/* fail if GUC hasn't been set up yet */
|
||||
if (TSCurrentConfig == NULL || *TSCurrentConfig == '\0')
|
||||
{
|
||||
if (emitError)
|
||||
elog(ERROR, "text search configuration isn't set");
|
||||
else
|
||||
return InvalidOid;
|
||||
}
|
||||
|
||||
if (TSConfigCacheHash == NULL)
|
||||
{
|
||||
/* First time through: initialize the tsconfig inval callback */
|
||||
init_ts_config_cache();
|
||||
}
|
||||
|
||||
/* Look up the config */
|
||||
TSCurrentConfigCache =
|
||||
TSConfigGetCfgid(stringToQualifiedNameList(TSCurrentConfig),
|
||||
!emitError);
|
||||
|
||||
return TSCurrentConfigCache;
|
||||
}
|
||||
|
||||
const char *
|
||||
assignTSCurrentConfig(const char *newval, bool doit, GucSource source)
|
||||
{
|
||||
/* do nothing during initial GUC setup */
|
||||
if (newval == NULL)
|
||||
{
|
||||
if (doit)
|
||||
TSCurrentConfigCache = InvalidOid;
|
||||
return newval;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we aren't inside a transaction, we cannot do database access so
|
||||
* cannot verify the config name. Must accept it on faith.
|
||||
*/
|
||||
if (IsTransactionState())
|
||||
{
|
||||
Oid cfgId;
|
||||
HeapTuple tuple;
|
||||
Form_pg_ts_config cfg;
|
||||
char *buf;
|
||||
|
||||
cfgId = TSConfigGetCfgid(stringToQualifiedNameList(newval), true);
|
||||
|
||||
if (!OidIsValid(cfgId))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Modify the actually stored value to be fully qualified, to ensure
|
||||
* later changes of search_path don't affect it.
|
||||
*/
|
||||
tuple = SearchSysCache(TSCONFIGOID,
|
||||
ObjectIdGetDatum(cfgId),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "cache lookup failed for text search configuration %u",
|
||||
cfgId);
|
||||
cfg = (Form_pg_ts_config) GETSTRUCT(tuple);
|
||||
|
||||
buf = quote_qualified_identifier(get_namespace_name(cfg->cfgnamespace),
|
||||
NameStr(cfg->cfgname));
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/* GUC wants it malloc'd not palloc'd */
|
||||
newval = strdup(buf);
|
||||
pfree(buf);
|
||||
|
||||
if (doit)
|
||||
TSCurrentConfigCache = cfgId;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (doit)
|
||||
TSCurrentConfigCache = InvalidOid;
|
||||
}
|
||||
|
||||
return newval;
|
||||
}
|
Reference in New Issue
Block a user