Commit 553bc416 authored by Teodor Sigaev's avatar Teodor Sigaev

1 add namespaces as Tom suggest http://www.pgsql.ru/db/mw/msg.html?mid=1987703

2 remove select qeury in inserts
parent 7cb55d21
#include "postgres.h" #include "postgres.h"
#include "postgres.h"
#include "fmgr.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_namespace.h"
#include "utils/syscache.h"
#include "ts_cfg.h"
#include "dict.h"
#include "wparser.h"
#include "snmap.h"
#include "common.h"
#include "tsvector.h"
#include "common.h" #include "common.h"
#include "wparser.h" #include "wparser.h"
#include "ts_cfg.h" #include "ts_cfg.h"
#include "dict.h" #include "dict.h"
Oid TSNSP_FunctionOid = InvalidOid;
text * text *
char2text(char *in) char2text(char *in)
{ {
...@@ -100,3 +120,45 @@ text_cmp(text *a, text *b) ...@@ -100,3 +120,45 @@ text_cmp(text *a, text *b)
return (int) VARSIZE(a) - (int) VARSIZE(b); return (int) VARSIZE(a) - (int) VARSIZE(b);
} }
char*
get_namespace(Oid funcoid) {
HeapTuple tuple;
Form_pg_proc proc;
Form_pg_namespace nsp;
Oid nspoid;
char *txt;
tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(funcoid), 0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for proc oid %u", funcoid);
proc=(Form_pg_proc) GETSTRUCT(tuple);
nspoid = proc->pronamespace;
ReleaseSysCache(tuple);
tuple = SearchSysCache(NAMESPACEOID, ObjectIdGetDatum(nspoid), 0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for namespace oid %u", nspoid);
nsp = (Form_pg_namespace) GETSTRUCT(tuple);
txt = pstrdup( NameStr((nsp->nspname)) );
ReleaseSysCache(tuple);
return txt;
}
Oid
get_oidnamespace(Oid funcoid) {
HeapTuple tuple;
Form_pg_proc proc;
Oid nspoid;
tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(funcoid), 0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for proc oid %u", funcoid);
proc=(Form_pg_proc) GETSTRUCT(tuple);
nspoid = proc->pronamespace;
ReleaseSysCache(tuple);
return nspoid;
}
...@@ -21,4 +21,13 @@ int text_cmp(text *a, text *b); ...@@ -21,4 +21,13 @@ int text_cmp(text *a, text *b);
void ts_error(int state, const char *format,...); void ts_error(int state, const char *format,...);
extern Oid TSNSP_FunctionOid; /* oid of called function, needed only for determ namespace, no more */
char* get_namespace(Oid funcoid);
Oid get_oidnamespace(Oid funcoid);
#define SET_FUNCOID() do { \
if ( fcinfo->flinfo && fcinfo->flinfo->fn_oid != InvalidOid ) \
TSNSP_FunctionOid = fcinfo->flinfo->fn_oid; \
} while(0)
#endif #endif
...@@ -19,8 +19,6 @@ ...@@ -19,8 +19,6 @@
/*********top interface**********/ /*********top interface**********/
static void *plan_getdict = NULL;
void void
init_dict(Oid id, DictInfo * dict) init_dict(Oid id, DictInfo * dict)
{ {
...@@ -28,20 +26,22 @@ init_dict(Oid id, DictInfo * dict) ...@@ -28,20 +26,22 @@ init_dict(Oid id, DictInfo * dict)
bool isnull; bool isnull;
Datum pars[1]; Datum pars[1];
int stat; int stat;
void *plan;
char buf[1024];
char *nsp = get_namespace(TSNSP_FunctionOid);
arg[0] = OIDOID; arg[0] = OIDOID;
pars[0] = ObjectIdGetDatum(id); pars[0] = ObjectIdGetDatum(id);
memset(dict, 0, sizeof(DictInfo)); memset(dict, 0, sizeof(DictInfo));
SPI_connect(); SPI_connect();
if (!plan_getdict) sprintf(buf,"select dict_init, dict_initoption, dict_lexize from %s.pg_ts_dict where oid = $1", nsp);
{ pfree(nsp);
plan_getdict = SPI_saveplan(SPI_prepare("select dict_init, dict_initoption, dict_lexize from pg_ts_dict where oid = $1", 1, arg)); plan= SPI_prepare(buf, 1, arg);
if (!plan_getdict) if (!plan)
ts_error(ERROR, "SPI_prepare() failed"); ts_error(ERROR, "SPI_prepare() failed");
}
stat = SPI_execp(plan_getdict, pars, " ", 1); stat = SPI_execp(plan, pars, " ", 1);
if (stat < 0) if (stat < 0)
ts_error(ERROR, "SPI_execp return %d", stat); ts_error(ERROR, "SPI_execp return %d", stat);
if (SPI_processed > 0) if (SPI_processed > 0)
...@@ -63,6 +63,7 @@ init_dict(Oid id, DictInfo * dict) ...@@ -63,6 +63,7 @@ init_dict(Oid id, DictInfo * dict)
} }
else else
ts_error(ERROR, "No dictionary with id %d", id); ts_error(ERROR, "No dictionary with id %d", id);
SPI_freeplan(plan);
SPI_finish(); SPI_finish();
} }
...@@ -133,8 +134,6 @@ finddict(Oid id) ...@@ -133,8 +134,6 @@ finddict(Oid id)
return finddict(id); /* qsort changed order!! */ ; return finddict(id); /* qsort changed order!! */ ;
} }
static void *plan_name2id = NULL;
Oid Oid
name2id_dict(text *name) name2id_dict(text *name)
{ {
...@@ -143,6 +142,8 @@ name2id_dict(text *name) ...@@ -143,6 +142,8 @@ name2id_dict(text *name)
Datum pars[1]; Datum pars[1];
int stat; int stat;
Oid id = findSNMap_t(&(DList.name2id_map), name); Oid id = findSNMap_t(&(DList.name2id_map), name);
void *plan;
char buf[1024], *nsp;
arg[0] = TEXTOID; arg[0] = TEXTOID;
pars[0] = PointerGetDatum(name); pars[0] = PointerGetDatum(name);
...@@ -150,21 +151,22 @@ name2id_dict(text *name) ...@@ -150,21 +151,22 @@ name2id_dict(text *name)
if (id) if (id)
return id; return id;
nsp = get_namespace(TSNSP_FunctionOid);
SPI_connect(); SPI_connect();
if (!plan_name2id) sprintf(buf,"select oid from %s.pg_ts_dict where dict_name = $1", nsp);
{ pfree(nsp);
plan_name2id = SPI_saveplan(SPI_prepare("select oid from pg_ts_dict where dict_name = $1", 1, arg)); plan= SPI_prepare(buf, 1, arg);
if (!plan_name2id) if (!plan)
ts_error(ERROR, "SPI_prepare() failed"); ts_error(ERROR, "SPI_prepare() failed");
}
stat = SPI_execp(plan_name2id, pars, " ", 1); stat = SPI_execp(plan, pars, " ", 1);
if (stat < 0) if (stat < 0)
ts_error(ERROR, "SPI_execp return %d", stat); ts_error(ERROR, "SPI_execp return %d", stat);
if (SPI_processed > 0) if (SPI_processed > 0)
id = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); id = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull));
else else
ts_error(ERROR, "No dictionary with name '%s'", text2char(name)); ts_error(ERROR, "No dictionary with name '%s'", text2char(name));
SPI_freeplan(plan);
SPI_finish(); SPI_finish();
addSNMap_t(&(DList.name2id_map), name, id); addSNMap_t(&(DList.name2id_map), name, id);
return id; return id;
...@@ -179,12 +181,14 @@ Datum ...@@ -179,12 +181,14 @@ Datum
lexize(PG_FUNCTION_ARGS) lexize(PG_FUNCTION_ARGS)
{ {
text *in = PG_GETARG_TEXT_P(1); text *in = PG_GETARG_TEXT_P(1);
DictInfo *dict = finddict(PG_GETARG_OID(0)); DictInfo *dict;
char **res, char **res,
**ptr; **ptr;
Datum *da; Datum *da;
ArrayType *a; ArrayType *a;
SET_FUNCOID();
dict = finddict(PG_GETARG_OID(0));
ptr = res = (char **) DatumGetPointer( ptr = res = (char **) DatumGetPointer(
FunctionCall3(&(dict->lexize_info), FunctionCall3(&(dict->lexize_info),
...@@ -241,8 +245,8 @@ lexize_byname(PG_FUNCTION_ARGS) ...@@ -241,8 +245,8 @@ lexize_byname(PG_FUNCTION_ARGS)
{ {
text *dictname = PG_GETARG_TEXT_P(0); text *dictname = PG_GETARG_TEXT_P(0);
Datum res; Datum res;
SET_FUNCOID();
strdup("simple");
res = DirectFunctionCall3( res = DirectFunctionCall3(
lexize, lexize,
ObjectIdGetDatum(name2id_dict(dictname)), ObjectIdGetDatum(name2id_dict(dictname)),
...@@ -263,6 +267,7 @@ Datum set_curdict(PG_FUNCTION_ARGS); ...@@ -263,6 +267,7 @@ Datum set_curdict(PG_FUNCTION_ARGS);
Datum Datum
set_curdict(PG_FUNCTION_ARGS) set_curdict(PG_FUNCTION_ARGS)
{ {
SET_FUNCOID();
finddict(PG_GETARG_OID(0)); finddict(PG_GETARG_OID(0));
currect_dictionary_id = PG_GETARG_OID(0); currect_dictionary_id = PG_GETARG_OID(0);
PG_RETURN_VOID(); PG_RETURN_VOID();
...@@ -274,7 +279,7 @@ Datum ...@@ -274,7 +279,7 @@ Datum
set_curdict_byname(PG_FUNCTION_ARGS) set_curdict_byname(PG_FUNCTION_ARGS)
{ {
text *dictname = PG_GETARG_TEXT_P(0); text *dictname = PG_GETARG_TEXT_P(0);
SET_FUNCOID();
DirectFunctionCall1( DirectFunctionCall1(
set_curdict, set_curdict,
ObjectIdGetDatum(name2id_dict(dictname)) ObjectIdGetDatum(name2id_dict(dictname))
...@@ -289,7 +294,7 @@ Datum ...@@ -289,7 +294,7 @@ Datum
lexize_bycurrent(PG_FUNCTION_ARGS) lexize_bycurrent(PG_FUNCTION_ARGS)
{ {
Datum res; Datum res;
SET_FUNCOID();
if (currect_dictionary_id == 0) if (currect_dictionary_id == 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
......
...@@ -469,6 +469,7 @@ TS_execute(ITEM * curitem, void *checkval, bool calcnot, bool (*chkcond) (void * ...@@ -469,6 +469,7 @@ TS_execute(ITEM * curitem, void *checkval, bool calcnot, bool (*chkcond) (void *
Datum Datum
rexectsq(PG_FUNCTION_ARGS) rexectsq(PG_FUNCTION_ARGS)
{ {
SET_FUNCOID();
return DirectFunctionCall2( return DirectFunctionCall2(
exectsq, exectsq,
PG_GETARG_DATUM(1), PG_GETARG_DATUM(1),
...@@ -483,7 +484,7 @@ exectsq(PG_FUNCTION_ARGS) ...@@ -483,7 +484,7 @@ exectsq(PG_FUNCTION_ARGS)
QUERYTYPE *query = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(1))); QUERYTYPE *query = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(1)));
CHKVAL chkval; CHKVAL chkval;
bool result; bool result;
SET_FUNCOID();
if (!val->size || !query->size) if (!val->size || !query->size)
{ {
PG_FREE_IF_COPY(val, 0); PG_FREE_IF_COPY(val, 0);
...@@ -638,6 +639,7 @@ static QUERYTYPE * ...@@ -638,6 +639,7 @@ static QUERYTYPE *
Datum Datum
tsquery_in(PG_FUNCTION_ARGS) tsquery_in(PG_FUNCTION_ARGS)
{ {
SET_FUNCOID();
PG_RETURN_POINTER(queryin((char *) PG_GETARG_POINTER(0), pushval_asis, 0)); PG_RETURN_POINTER(queryin((char *) PG_GETARG_POINTER(0), pushval_asis, 0));
} }
...@@ -863,6 +865,7 @@ to_tsquery(PG_FUNCTION_ARGS) ...@@ -863,6 +865,7 @@ to_tsquery(PG_FUNCTION_ARGS)
QUERYTYPE *query; QUERYTYPE *query;
ITEM *res; ITEM *res;
int4 len; int4 len;
SET_FUNCOID();
str = text2char(in); str = text2char(in);
PG_FREE_IF_COPY(in, 1); PG_FREE_IF_COPY(in, 1);
...@@ -884,7 +887,9 @@ Datum ...@@ -884,7 +887,9 @@ Datum
to_tsquery_name(PG_FUNCTION_ARGS) to_tsquery_name(PG_FUNCTION_ARGS)
{ {
text *name = PG_GETARG_TEXT_P(0); text *name = PG_GETARG_TEXT_P(0);
Datum res = DirectFunctionCall2(to_tsquery, Datum res;
SET_FUNCOID();
res = DirectFunctionCall2(to_tsquery,
Int32GetDatum(name2id_cfg(name)), Int32GetDatum(name2id_cfg(name)),
PG_GETARG_DATUM(1)); PG_GETARG_DATUM(1));
...@@ -895,6 +900,7 @@ to_tsquery_name(PG_FUNCTION_ARGS) ...@@ -895,6 +900,7 @@ to_tsquery_name(PG_FUNCTION_ARGS)
Datum Datum
to_tsquery_current(PG_FUNCTION_ARGS) to_tsquery_current(PG_FUNCTION_ARGS)
{ {
SET_FUNCOID();
PG_RETURN_DATUM(DirectFunctionCall2(to_tsquery, PG_RETURN_DATUM(DirectFunctionCall2(to_tsquery,
Int32GetDatum(get_currcfg()), Int32GetDatum(get_currcfg()),
PG_GETARG_DATUM(0))); PG_GETARG_DATUM(0)));
......
...@@ -13,7 +13,12 @@ ...@@ -13,7 +13,12 @@
static int static int
compareSNMapEntry(const void *a, const void *b) compareSNMapEntry(const void *a, const void *b)
{ {
return strcmp(((SNMapEntry *) a)->key, ((SNMapEntry *) b)->key); if ( ((SNMapEntry *) a)->nsp < ((SNMapEntry *) b)->nsp )
return -1;
else if ( ((SNMapEntry *) a)->nsp > ((SNMapEntry *) b)->nsp )
return 1;
else
return strcmp(((SNMapEntry *) a)->key, ((SNMapEntry *) b)->key);
} }
void void
...@@ -37,6 +42,7 @@ addSNMap(SNMap * map, char *key, Oid value) ...@@ -37,6 +42,7 @@ addSNMap(SNMap * map, char *key, Oid value)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY), (errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory"))); errmsg("out of memory")));
map->list[map->len].nsp = get_oidnamespace(TSNSP_FunctionOid);
map->list[map->len].value = value; map->list[map->len].value = value;
map->len++; map->len++;
if (map->len > 1) if (map->len > 1)
...@@ -59,6 +65,7 @@ findSNMap(SNMap * map, char *key) ...@@ -59,6 +65,7 @@ findSNMap(SNMap * map, char *key)
SNMapEntry ks; SNMapEntry ks;
ks.key = key; ks.key = key;
ks.nsp = get_oidnamespace(TSNSP_FunctionOid);
ks.value = 0; ks.value = 0;
if (map->len == 0 || !map->list) if (map->len == 0 || !map->list)
......
...@@ -7,6 +7,7 @@ typedef struct ...@@ -7,6 +7,7 @@ typedef struct
{ {
char *key; char *key;
Oid value; Oid value;
Oid nsp;
} SNMapEntry; } SNMapEntry;
typedef struct typedef struct
......
...@@ -25,10 +25,6 @@ ...@@ -25,10 +25,6 @@
/*********top interface**********/ /*********top interface**********/
static void *plan_getcfg_bylocale = NULL;
static void *plan_getcfg = NULL;
static void *plan_getmap = NULL;
static void *plan_name2id = NULL;
static Oid current_cfg_id = 0; static Oid current_cfg_id = 0;
void void
...@@ -42,7 +38,10 @@ init_cfg(Oid id, TSCfgInfo * cfg) ...@@ -42,7 +38,10 @@ init_cfg(Oid id, TSCfgInfo * cfg)
j; j;
text *ptr; text *ptr;
text *prsname = NULL; text *prsname = NULL;
char *nsp=get_namespace(TSNSP_FunctionOid);
char buf[1024];
MemoryContext oldcontext; MemoryContext oldcontext;
void *plan;
arg[0] = OIDOID; arg[0] = OIDOID;
arg[1] = OIDOID; arg[1] = OIDOID;
...@@ -51,14 +50,13 @@ init_cfg(Oid id, TSCfgInfo * cfg) ...@@ -51,14 +50,13 @@ init_cfg(Oid id, TSCfgInfo * cfg)
memset(cfg, 0, sizeof(TSCfgInfo)); memset(cfg, 0, sizeof(TSCfgInfo));
SPI_connect(); SPI_connect();
if (!plan_getcfg)
{
plan_getcfg = SPI_saveplan(SPI_prepare("select prs_name from pg_ts_cfg where oid = $1", 1, arg));
if (!plan_getcfg)
ts_error(ERROR, "SPI_prepare() failed");
}
stat = SPI_execp(plan_getcfg, pars, " ", 1); sprintf(buf, "select prs_name from %s.pg_ts_cfg where oid = $1", nsp);
plan= SPI_prepare(buf, 1, arg);
if (!plan)
ts_error(ERROR, "SPI_prepare() failed");
stat = SPI_execp(plan, pars, " ", 1);
if (stat < 0) if (stat < 0)
ts_error(ERROR, "SPI_execp return %d", stat); ts_error(ERROR, "SPI_execp return %d", stat);
if (SPI_processed > 0) if (SPI_processed > 0)
...@@ -75,16 +73,16 @@ init_cfg(Oid id, TSCfgInfo * cfg) ...@@ -75,16 +73,16 @@ init_cfg(Oid id, TSCfgInfo * cfg)
else else
ts_error(ERROR, "No tsearch cfg with id %d", id); ts_error(ERROR, "No tsearch cfg with id %d", id);
SPI_freeplan(plan);
arg[0] = TEXTOID; arg[0] = TEXTOID;
if (!plan_getmap) sprintf(buf, "select lt.tokid, map.dict_name from %s.pg_ts_cfgmap as map, %s.pg_ts_cfg as cfg, %s.token_type( $1 ) as lt where lt.alias = map.tok_alias and map.ts_name = cfg.ts_name and cfg.oid= $2 order by lt.tokid desc;", nsp, nsp, nsp);
{ plan= SPI_prepare(buf, 2, arg);
plan_getmap = SPI_saveplan(SPI_prepare("select lt.tokid, pg_ts_cfgmap.dict_name from pg_ts_cfgmap, pg_ts_cfg, token_type( $1 ) as lt where lt.alias = pg_ts_cfgmap.tok_alias and pg_ts_cfgmap.ts_name = pg_ts_cfg.ts_name and pg_ts_cfg.oid= $2 order by lt.tokid desc;", 2, arg)); if (!plan)
if (!plan_getmap) ts_error(ERROR, "SPI_prepare() failed");
ts_error(ERROR, "SPI_prepare() failed");
}
pars[0] = PointerGetDatum(prsname); pars[0] = PointerGetDatum(prsname);
stat = SPI_execp(plan_getmap, pars, " ", 0); stat = SPI_execp(plan, pars, " ", 0);
if (stat < 0) if (stat < 0)
ts_error(ERROR, "SPI_execp return %d", stat); ts_error(ERROR, "SPI_execp return %d", stat);
if (SPI_processed <= 0) if (SPI_processed <= 0)
...@@ -136,9 +134,11 @@ init_cfg(Oid id, TSCfgInfo * cfg) ...@@ -136,9 +134,11 @@ init_cfg(Oid id, TSCfgInfo * cfg)
pfree(a); pfree(a);
} }
SPI_freeplan(plan);
SPI_finish(); SPI_finish();
cfg->prs_id = name2id_prs(prsname); cfg->prs_id = name2id_prs(prsname);
pfree(prsname); pfree(prsname);
pfree(nsp);
for (i = 0; i < cfg->len; i++) for (i = 0; i < cfg->len; i++)
{ {
for (j = 0; j < cfg->map[i].len; j++) for (j = 0; j < cfg->map[i].len; j++)
...@@ -235,6 +235,9 @@ name2id_cfg(text *name) ...@@ -235,6 +235,9 @@ name2id_cfg(text *name)
Datum pars[1]; Datum pars[1];
int stat; int stat;
Oid id = findSNMap_t(&(CList.name2id_map), name); Oid id = findSNMap_t(&(CList.name2id_map), name);
void *plan;
char *nsp;
char buf[1024];
arg[0] = TEXTOID; arg[0] = TEXTOID;
pars[0] = PointerGetDatum(name); pars[0] = PointerGetDatum(name);
...@@ -242,16 +245,15 @@ name2id_cfg(text *name) ...@@ -242,16 +245,15 @@ name2id_cfg(text *name)
if (id) if (id)
return id; return id;
nsp=get_namespace(TSNSP_FunctionOid);
SPI_connect(); SPI_connect();
if (!plan_name2id) sprintf(buf, "select oid from %s.pg_ts_cfg where ts_name = $1", nsp);
{ plan= SPI_prepare(buf, 1, arg);
plan_name2id = SPI_saveplan(SPI_prepare("select oid from pg_ts_cfg where ts_name = $1", 1, arg)); if (!plan)
if (!plan_name2id) /* internal error */
/* internal error */ elog(ERROR, "SPI_prepare() failed");
elog(ERROR, "SPI_prepare() failed");
}
stat = SPI_execp(plan_name2id, pars, " ", 1); stat = SPI_execp(plan, pars, " ", 1);
if (stat < 0) if (stat < 0)
/* internal error */ /* internal error */
elog(ERROR, "SPI_execp return %d", stat); elog(ERROR, "SPI_execp return %d", stat);
...@@ -268,6 +270,7 @@ name2id_cfg(text *name) ...@@ -268,6 +270,7 @@ name2id_cfg(text *name)
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("no tsearch config"))); errmsg("no tsearch config")));
SPI_freeplan(plan);
SPI_finish(); SPI_finish();
addSNMap_t(&(CList.name2id_map), name, id); addSNMap_t(&(CList.name2id_map), name, id);
return id; return id;
...@@ -549,22 +552,25 @@ get_currcfg(void) ...@@ -549,22 +552,25 @@ get_currcfg(void)
Datum pars[1]; Datum pars[1];
bool isnull; bool isnull;
int stat; int stat;
char buf[1024];
char *nsp;
void *plan;
if (current_cfg_id > 0) if (current_cfg_id > 0)
return current_cfg_id; return current_cfg_id;
nsp=get_namespace(TSNSP_FunctionOid);
SPI_connect(); SPI_connect();
if (!plan_getcfg_bylocale) sprintf(buf, "select oid from %s.pg_ts_cfg where locale = $1 ", nsp);
{ pfree(nsp);
plan_getcfg_bylocale = SPI_saveplan(SPI_prepare("select oid from pg_ts_cfg where locale = $1 ", 1, arg)); plan = SPI_prepare(buf, 1, arg);
if (!plan_getcfg_bylocale) if (!plan)
/* internal error */ /* internal error */
elog(ERROR, "SPI_prepare() failed"); elog(ERROR, "SPI_prepare() failed");
}
curlocale = setlocale(LC_CTYPE, NULL); curlocale = setlocale(LC_CTYPE, NULL);
pars[0] = PointerGetDatum(char2text((char *) curlocale)); pars[0] = PointerGetDatum(char2text((char *) curlocale));
stat = SPI_execp(plan_getcfg_bylocale, pars, " ", 1); stat = SPI_execp(plan, pars, " ", 1);
if (stat < 0) if (stat < 0)
/* internal error */ /* internal error */
...@@ -577,6 +583,7 @@ get_currcfg(void) ...@@ -577,6 +583,7 @@ get_currcfg(void)
errmsg("could not find tsearch config by locale"))); errmsg("could not find tsearch config by locale")));
pfree(DatumGetPointer(pars[0])); pfree(DatumGetPointer(pars[0]));
SPI_freeplan(plan);
SPI_finish(); SPI_finish();
return current_cfg_id; return current_cfg_id;
} }
...@@ -586,6 +593,7 @@ Datum set_curcfg(PG_FUNCTION_ARGS); ...@@ -586,6 +593,7 @@ Datum set_curcfg(PG_FUNCTION_ARGS);
Datum Datum
set_curcfg(PG_FUNCTION_ARGS) set_curcfg(PG_FUNCTION_ARGS)
{ {
SET_FUNCOID();
findcfg(PG_GETARG_OID(0)); findcfg(PG_GETARG_OID(0));
current_cfg_id = PG_GETARG_OID(0); current_cfg_id = PG_GETARG_OID(0);
PG_RETURN_VOID(); PG_RETURN_VOID();
...@@ -597,7 +605,7 @@ Datum ...@@ -597,7 +605,7 @@ Datum
set_curcfg_byname(PG_FUNCTION_ARGS) set_curcfg_byname(PG_FUNCTION_ARGS)
{ {
text *name = PG_GETARG_TEXT_P(0); text *name = PG_GETARG_TEXT_P(0);
SET_FUNCOID();
DirectFunctionCall1( DirectFunctionCall1(
set_curcfg, set_curcfg,
ObjectIdGetDatum(name2id_cfg(name)) ObjectIdGetDatum(name2id_cfg(name))
...@@ -611,6 +619,7 @@ Datum show_curcfg(PG_FUNCTION_ARGS); ...@@ -611,6 +619,7 @@ Datum show_curcfg(PG_FUNCTION_ARGS);
Datum Datum
show_curcfg(PG_FUNCTION_ARGS) show_curcfg(PG_FUNCTION_ARGS)
{ {
SET_FUNCOID();
PG_RETURN_OID(get_currcfg()); PG_RETURN_OID(get_currcfg());
} }
...@@ -619,6 +628,8 @@ Datum reset_tsearch(PG_FUNCTION_ARGS); ...@@ -619,6 +628,8 @@ Datum reset_tsearch(PG_FUNCTION_ARGS);
Datum Datum
reset_tsearch(PG_FUNCTION_ARGS) reset_tsearch(PG_FUNCTION_ARGS)
{ {
SET_FUNCOID();
ts_error(NOTICE, "TSearch cache cleaned"); ts_error(NOTICE, "TSearch cache cleaned");
PG_RETURN_VOID(); PG_RETURN_VOID();
} }
...@@ -57,9 +57,9 @@ CREATE FUNCTION dex_lexize(internal,internal,int4) ...@@ -57,9 +57,9 @@ CREATE FUNCTION dex_lexize(internal,internal,int4)
insert into pg_ts_dict select insert into pg_ts_dict select
'simple', 'simple',
(select oid from pg_proc where proname='dex_init'), 'dex_init(text)',
null, null,
(select oid from pg_proc where proname='dex_lexize'), 'dex_lexize(internal,internal,int4)',
'Simple example of dictionary.' 'Simple example of dictionary.'
; ;
...@@ -76,9 +76,9 @@ CREATE FUNCTION snb_lexize(internal,internal,int4) ...@@ -76,9 +76,9 @@ CREATE FUNCTION snb_lexize(internal,internal,int4)
insert into pg_ts_dict select insert into pg_ts_dict select
'en_stem', 'en_stem',
(select oid from pg_proc where proname='snb_en_init'), 'snb_en_init(text)',
'DATA_PATH/english.stop', 'DATA_PATH/english.stop',
(select oid from pg_proc where proname='snb_lexize'), 'snb_lexize(internal,internal,int4)',
'English Stemmer. Snowball.' 'English Stemmer. Snowball.'
; ;
...@@ -89,9 +89,9 @@ CREATE FUNCTION snb_ru_init(text) ...@@ -89,9 +89,9 @@ CREATE FUNCTION snb_ru_init(text)
insert into pg_ts_dict select insert into pg_ts_dict select
'ru_stem', 'ru_stem',
(select oid from pg_proc where proname='snb_ru_init'), 'snb_ru_init(text)',
'DATA_PATH/russian.stop', 'DATA_PATH/russian.stop',
(select oid from pg_proc where proname='snb_lexize'), 'snb_lexize(internal,internal,int4)',
'Russian Stemmer. Snowball.' 'Russian Stemmer. Snowball.'
; ;
...@@ -108,9 +108,9 @@ CREATE FUNCTION spell_lexize(internal,internal,int4) ...@@ -108,9 +108,9 @@ CREATE FUNCTION spell_lexize(internal,internal,int4)
insert into pg_ts_dict select insert into pg_ts_dict select
'ispell_template', 'ispell_template',
(select oid from pg_proc where proname='spell_init'), 'spell_init(text)',
null, null,
(select oid from pg_proc where proname='spell_lexize'), 'spell_lexize(internal,internal,int4)',
'ISpell interface. Must have .dict and .aff files' 'ISpell interface. Must have .dict and .aff files'
; ;
...@@ -127,9 +127,9 @@ CREATE FUNCTION syn_lexize(internal,internal,int4) ...@@ -127,9 +127,9 @@ CREATE FUNCTION syn_lexize(internal,internal,int4)
insert into pg_ts_dict select insert into pg_ts_dict select
'synonym', 'synonym',
(select oid from pg_proc where proname='syn_init'), 'syn_init(text)',
null, null,
(select oid from pg_proc where proname='syn_lexize'), 'syn_lexize(internal,internal,int4)',
'Example of synonym dictionary' 'Example of synonym dictionary'
; ;
...@@ -227,11 +227,11 @@ CREATE FUNCTION prsd_headline(internal,internal,internal) ...@@ -227,11 +227,11 @@ CREATE FUNCTION prsd_headline(internal,internal,internal)
insert into pg_ts_parser select insert into pg_ts_parser select
'default', 'default',
(select oid from pg_proc where proname='prsd_start'), 'prsd_start(internal,int4)',
(select oid from pg_proc where proname='prsd_getlexeme'), 'prsd_getlexeme(internal,internal,internal)',
(select oid from pg_proc where proname='prsd_end'), 'prsd_end(internal)',
(select oid from pg_proc where proname='prsd_headline'), 'prsd_headline(internal,internal,internal)',
(select oid from pg_proc where proname='prsd_lextype'), 'prsd_lextype(internal)',
'Parser from OpenFTS v0.34' 'Parser from OpenFTS v0.34'
; ;
......
...@@ -404,7 +404,7 @@ tsvector_in(PG_FUNCTION_ARGS) ...@@ -404,7 +404,7 @@ tsvector_in(PG_FUNCTION_ARGS)
*cur; *cur;
int4 i, int4 i,
buflen = 256; buflen = 256;
SET_FUNCOID();
state.prsbuf = buf; state.prsbuf = buf;
state.len = 32; state.len = 32;
state.word = (char *) palloc(state.len); state.word = (char *) palloc(state.len);
...@@ -723,7 +723,10 @@ to_tsvector(PG_FUNCTION_ARGS) ...@@ -723,7 +723,10 @@ to_tsvector(PG_FUNCTION_ARGS)
text *in = PG_GETARG_TEXT_P(1); text *in = PG_GETARG_TEXT_P(1);
PRSTEXT prs; PRSTEXT prs;
tsvector *out = NULL; tsvector *out = NULL;
TSCfgInfo *cfg = findcfg(PG_GETARG_INT32(0)); TSCfgInfo *cfg;
SET_FUNCOID();
cfg = findcfg(PG_GETARG_INT32(0));
prs.lenwords = 32; prs.lenwords = 32;
prs.curwords = 0; prs.curwords = 0;
...@@ -749,7 +752,9 @@ Datum ...@@ -749,7 +752,9 @@ Datum
to_tsvector_name(PG_FUNCTION_ARGS) to_tsvector_name(PG_FUNCTION_ARGS)
{ {
text *cfg = PG_GETARG_TEXT_P(0); text *cfg = PG_GETARG_TEXT_P(0);
Datum res = DirectFunctionCall3( Datum res;
SET_FUNCOID();
res = DirectFunctionCall3(
to_tsvector, to_tsvector,
Int32GetDatum(name2id_cfg(cfg)), Int32GetDatum(name2id_cfg(cfg)),
PG_GETARG_DATUM(1), PG_GETARG_DATUM(1),
...@@ -763,7 +768,9 @@ to_tsvector_name(PG_FUNCTION_ARGS) ...@@ -763,7 +768,9 @@ to_tsvector_name(PG_FUNCTION_ARGS)
Datum Datum
to_tsvector_current(PG_FUNCTION_ARGS) to_tsvector_current(PG_FUNCTION_ARGS)
{ {
Datum res = DirectFunctionCall3( Datum res;
SET_FUNCOID();
res = DirectFunctionCall3(
to_tsvector, to_tsvector,
Int32GetDatum(get_currcfg()), Int32GetDatum(get_currcfg()),
PG_GETARG_DATUM(0), PG_GETARG_DATUM(0),
...@@ -809,12 +816,15 @@ tsearch2(PG_FUNCTION_ARGS) ...@@ -809,12 +816,15 @@ tsearch2(PG_FUNCTION_ARGS)
Trigger *trigger; Trigger *trigger;
Relation rel; Relation rel;
HeapTuple rettuple = NULL; HeapTuple rettuple = NULL;
TSCfgInfo *cfg = findcfg(get_currcfg());
int numidxattr, int numidxattr,
i; i;
PRSTEXT prs; PRSTEXT prs;
Datum datum = (Datum) 0; Datum datum = (Datum) 0;
Oid funcoid = InvalidOid; Oid funcoid = InvalidOid;
TSCfgInfo *cfg;
SET_FUNCOID();
cfg = findcfg(get_currcfg());
if (!CALLED_AS_TRIGGER(fcinfo)) if (!CALLED_AS_TRIGGER(fcinfo))
/* internal error */ /* internal error */
......
...@@ -12,7 +12,7 @@ DROP OPERATOR || (tsvector, tsvector); ...@@ -12,7 +12,7 @@ DROP OPERATOR || (tsvector, tsvector);
DROP OPERATOR @@ (tsvector, tsquery); DROP OPERATOR @@ (tsvector, tsquery);
DROP OPERATOR @@ (tsquery, tsvector); DROP OPERATOR @@ (tsquery, tsvector);
DROP AGGREGATE stat(tsvector); --DROP AGGREGATE stat(tsvector);
DROP TABLE pg_ts_dict; DROP TABLE pg_ts_dict;
DROP TABLE pg_ts_parser; DROP TABLE pg_ts_parser;
...@@ -24,7 +24,7 @@ DROP TYPE tokenout CASCADE; ...@@ -24,7 +24,7 @@ DROP TYPE tokenout CASCADE;
DROP TYPE tsvector CASCADE; DROP TYPE tsvector CASCADE;
DROP TYPE tsquery CASCADE; DROP TYPE tsquery CASCADE;
DROP TYPE gtsvector CASCADE; DROP TYPE gtsvector CASCADE;
DROP TYPE tsstat CASCADE; --DROP TYPE tsstat CASCADE;
DROP TYPE statinfo CASCADE; DROP TYPE statinfo CASCADE;
DROP TYPE tsdebug CASCADE; DROP TYPE tsdebug CASCADE;
...@@ -59,8 +59,6 @@ DROP FUNCTION gtsvector_penalty(internal,internal,internal); ...@@ -59,8 +59,6 @@ DROP FUNCTION gtsvector_penalty(internal,internal,internal);
DROP FUNCTION gtsvector_picksplit(internal, internal); DROP FUNCTION gtsvector_picksplit(internal, internal);
DROP FUNCTION gtsvector_union(internal, internal); DROP FUNCTION gtsvector_union(internal, internal);
DROP FUNCTION reset_tsearch(); DROP FUNCTION reset_tsearch();
DROP FUNCTION stat(text);
DROP FUNCTION stat(text,stat);
DROP FUNCTION tsearch2() CASCADE; DROP FUNCTION tsearch2() CASCADE;
DROP FUNCTION _get_parser_from_curcfg(); DROP FUNCTION _get_parser_from_curcfg();
......
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
/*********top interface**********/ /*********top interface**********/
static void *plan_getparser = NULL;
static Oid current_parser_id = InvalidOid; static Oid current_parser_id = InvalidOid;
void void
...@@ -31,20 +30,22 @@ init_prs(Oid id, WParserInfo * prs) ...@@ -31,20 +30,22 @@ init_prs(Oid id, WParserInfo * prs)
bool isnull; bool isnull;
Datum pars[1]; Datum pars[1];
int stat; int stat;
void *plan;
char buf[1024], *nsp;
arg[0] = OIDOID; arg[0] = OIDOID;
pars[0] = ObjectIdGetDatum(id); pars[0] = ObjectIdGetDatum(id);
memset(prs, 0, sizeof(WParserInfo)); memset(prs, 0, sizeof(WParserInfo));
SPI_connect(); SPI_connect();
if (!plan_getparser) nsp=get_namespace(TSNSP_FunctionOid);
{ sprintf(buf, "select prs_start, prs_nexttoken, prs_end, prs_lextype, prs_headline from %s.pg_ts_parser where oid = $1", nsp);
plan_getparser = SPI_saveplan(SPI_prepare("select prs_start, prs_nexttoken, prs_end, prs_lextype, prs_headline from pg_ts_parser where oid = $1", 1, arg)); pfree(nsp);
if (!plan_getparser) plan= SPI_prepare(buf, 1, arg);
ts_error(ERROR, "SPI_prepare() failed"); if (!plan)
} ts_error(ERROR, "SPI_prepare() failed");
stat = SPI_execp(plan_getparser, pars, " ", 1); stat = SPI_execp(plan, pars, " ", 1);
if (stat < 0) if (stat < 0)
ts_error(ERROR, "SPI_execp return %d", stat); ts_error(ERROR, "SPI_execp return %d", stat);
if (SPI_processed > 0) if (SPI_processed > 0)
...@@ -64,6 +65,7 @@ init_prs(Oid id, WParserInfo * prs) ...@@ -64,6 +65,7 @@ init_prs(Oid id, WParserInfo * prs)
} }
else else
ts_error(ERROR, "No parser with id %d", id); ts_error(ERROR, "No parser with id %d", id);
SPI_freeplan(plan);
SPI_finish(); SPI_finish();
} }
...@@ -130,8 +132,6 @@ findprs(Oid id) ...@@ -130,8 +132,6 @@ findprs(Oid id)
return findprs(id); /* qsort changed order!! */ ; return findprs(id); /* qsort changed order!! */ ;
} }
static void *plan_name2id = NULL;
Oid Oid
name2id_prs(text *name) name2id_prs(text *name)
{ {
...@@ -140,6 +140,8 @@ name2id_prs(text *name) ...@@ -140,6 +140,8 @@ name2id_prs(text *name)
Datum pars[1]; Datum pars[1];
int stat; int stat;
Oid id = findSNMap_t(&(PList.name2id_map), name); Oid id = findSNMap_t(&(PList.name2id_map), name);
char buf[1024], *nsp;
void *plan;
arg[0] = TEXTOID; arg[0] = TEXTOID;
pars[0] = PointerGetDatum(name); pars[0] = PointerGetDatum(name);
...@@ -148,20 +150,21 @@ name2id_prs(text *name) ...@@ -148,20 +150,21 @@ name2id_prs(text *name)
return id; return id;
SPI_connect(); SPI_connect();
if (!plan_name2id) nsp = get_namespace(TSNSP_FunctionOid);
{ sprintf(buf, "select oid from %s.pg_ts_parser where prs_name = $1", nsp);
plan_name2id = SPI_saveplan(SPI_prepare("select oid from pg_ts_parser where prs_name = $1", 1, arg)); pfree(nsp);
if (!plan_name2id) plan= SPI_prepare(buf, 1, arg);
ts_error(ERROR, "SPI_prepare() failed"); if (!plan)
} ts_error(ERROR, "SPI_prepare() failed");
stat = SPI_execp(plan_name2id, pars, " ", 1); stat = SPI_execp(plan, pars, " ", 1);
if (stat < 0) if (stat < 0)
ts_error(ERROR, "SPI_execp return %d", stat); ts_error(ERROR, "SPI_execp return %d", stat);
if (SPI_processed > 0) if (SPI_processed > 0)
id = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); id = DatumGetObjectId(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull));
else else
ts_error(ERROR, "No parser '%s'", text2char(name)); ts_error(ERROR, "No parser '%s'", text2char(name));
SPI_freeplan(plan);
SPI_finish(); SPI_finish();
addSNMap_t(&(PList.name2id_map), name, id); addSNMap_t(&(PList.name2id_map), name, id);
return id; return id;
...@@ -239,7 +242,7 @@ token_type(PG_FUNCTION_ARGS) ...@@ -239,7 +242,7 @@ token_type(PG_FUNCTION_ARGS)
{ {
FuncCallContext *funcctx; FuncCallContext *funcctx;
Datum result; Datum result;
SET_FUNCOID();
if (SRF_IS_FIRSTCALL()) if (SRF_IS_FIRSTCALL())
{ {
funcctx = SRF_FIRSTCALL_INIT(); funcctx = SRF_FIRSTCALL_INIT();
...@@ -260,7 +263,7 @@ token_type_byname(PG_FUNCTION_ARGS) ...@@ -260,7 +263,7 @@ token_type_byname(PG_FUNCTION_ARGS)
{ {
FuncCallContext *funcctx; FuncCallContext *funcctx;
Datum result; Datum result;
SET_FUNCOID();
if (SRF_IS_FIRSTCALL()) if (SRF_IS_FIRSTCALL())
{ {
text *name = PG_GETARG_TEXT_P(0); text *name = PG_GETARG_TEXT_P(0);
...@@ -284,7 +287,7 @@ token_type_current(PG_FUNCTION_ARGS) ...@@ -284,7 +287,7 @@ token_type_current(PG_FUNCTION_ARGS)
{ {
FuncCallContext *funcctx; FuncCallContext *funcctx;
Datum result; Datum result;
SET_FUNCOID();
if (SRF_IS_FIRSTCALL()) if (SRF_IS_FIRSTCALL())
{ {
funcctx = SRF_FIRSTCALL_INIT(); funcctx = SRF_FIRSTCALL_INIT();
...@@ -306,6 +309,7 @@ Datum set_curprs(PG_FUNCTION_ARGS); ...@@ -306,6 +309,7 @@ Datum set_curprs(PG_FUNCTION_ARGS);
Datum Datum
set_curprs(PG_FUNCTION_ARGS) set_curprs(PG_FUNCTION_ARGS)
{ {
SET_FUNCOID();
findprs(PG_GETARG_OID(0)); findprs(PG_GETARG_OID(0));
current_parser_id = PG_GETARG_OID(0); current_parser_id = PG_GETARG_OID(0);
PG_RETURN_VOID(); PG_RETURN_VOID();
...@@ -317,7 +321,7 @@ Datum ...@@ -317,7 +321,7 @@ Datum
set_curprs_byname(PG_FUNCTION_ARGS) set_curprs_byname(PG_FUNCTION_ARGS)
{ {
text *name = PG_GETARG_TEXT_P(0); text *name = PG_GETARG_TEXT_P(0);
SET_FUNCOID();
DirectFunctionCall1( DirectFunctionCall1(
set_curprs, set_curprs,
ObjectIdGetDatum(name2id_prs(name)) ObjectIdGetDatum(name2id_prs(name))
...@@ -440,7 +444,7 @@ parse(PG_FUNCTION_ARGS) ...@@ -440,7 +444,7 @@ parse(PG_FUNCTION_ARGS)
{ {
FuncCallContext *funcctx; FuncCallContext *funcctx;
Datum result; Datum result;
SET_FUNCOID();
if (SRF_IS_FIRSTCALL()) if (SRF_IS_FIRSTCALL())
{ {
text *txt = PG_GETARG_TEXT_P(1); text *txt = PG_GETARG_TEXT_P(1);
...@@ -464,7 +468,7 @@ parse_byname(PG_FUNCTION_ARGS) ...@@ -464,7 +468,7 @@ parse_byname(PG_FUNCTION_ARGS)
{ {
FuncCallContext *funcctx; FuncCallContext *funcctx;
Datum result; Datum result;
SET_FUNCOID();
if (SRF_IS_FIRSTCALL()) if (SRF_IS_FIRSTCALL())
{ {
text *name = PG_GETARG_TEXT_P(0); text *name = PG_GETARG_TEXT_P(0);
...@@ -491,7 +495,7 @@ parse_current(PG_FUNCTION_ARGS) ...@@ -491,7 +495,7 @@ parse_current(PG_FUNCTION_ARGS)
{ {
FuncCallContext *funcctx; FuncCallContext *funcctx;
Datum result; Datum result;
SET_FUNCOID();
if (SRF_IS_FIRSTCALL()) if (SRF_IS_FIRSTCALL())
{ {
text *txt = PG_GETARG_TEXT_P(0); text *txt = PG_GETARG_TEXT_P(0);
...@@ -515,13 +519,17 @@ Datum headline(PG_FUNCTION_ARGS); ...@@ -515,13 +519,17 @@ Datum headline(PG_FUNCTION_ARGS);
Datum Datum
headline(PG_FUNCTION_ARGS) headline(PG_FUNCTION_ARGS)
{ {
TSCfgInfo *cfg = findcfg(PG_GETARG_OID(0));
text *in = PG_GETARG_TEXT_P(1); text *in = PG_GETARG_TEXT_P(1);
QUERYTYPE *query = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(2))); QUERYTYPE *query = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(2)));
text *opt = (PG_NARGS() > 3 && PG_GETARG_POINTER(3)) ? PG_GETARG_TEXT_P(3) : NULL; text *opt = (PG_NARGS() > 3 && PG_GETARG_POINTER(3)) ? PG_GETARG_TEXT_P(3) : NULL;
HLPRSTEXT prs; HLPRSTEXT prs;
text *out; text *out;
WParserInfo *prsobj = findprs(cfg->prs_id); TSCfgInfo *cfg;
WParserInfo *prsobj;
SET_FUNCOID();
cfg = findcfg(PG_GETARG_OID(0));
prsobj = findprs(cfg->prs_id);
memset(&prs, 0, sizeof(HLPRSTEXT)); memset(&prs, 0, sizeof(HLPRSTEXT));
prs.lenwords = 32; prs.lenwords = 32;
...@@ -557,7 +565,9 @@ headline_byname(PG_FUNCTION_ARGS) ...@@ -557,7 +565,9 @@ headline_byname(PG_FUNCTION_ARGS)
{ {
text *cfg = PG_GETARG_TEXT_P(0); text *cfg = PG_GETARG_TEXT_P(0);
Datum out = DirectFunctionCall4( Datum out;
SET_FUNCOID();
out = DirectFunctionCall4(
headline, headline,
ObjectIdGetDatum(name2id_cfg(cfg)), ObjectIdGetDatum(name2id_cfg(cfg)),
PG_GETARG_DATUM(1), PG_GETARG_DATUM(1),
...@@ -574,6 +584,7 @@ Datum headline_current(PG_FUNCTION_ARGS); ...@@ -574,6 +584,7 @@ Datum headline_current(PG_FUNCTION_ARGS);
Datum Datum
headline_current(PG_FUNCTION_ARGS) headline_current(PG_FUNCTION_ARGS)
{ {
SET_FUNCOID();
PG_RETURN_DATUM(DirectFunctionCall4( PG_RETURN_DATUM(DirectFunctionCall4(
headline, headline,
ObjectIdGetDatum(get_currcfg()), ObjectIdGetDatum(get_currcfg()),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment