diff options
Diffstat (limited to 'contrib/tsearch2/tsearch2.c')
-rw-r--r-- | contrib/tsearch2/tsearch2.c | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/contrib/tsearch2/tsearch2.c b/contrib/tsearch2/tsearch2.c new file mode 100644 index 0000000000..d51ba7ede7 --- /dev/null +++ b/contrib/tsearch2/tsearch2.c @@ -0,0 +1,441 @@ +/*------------------------------------------------------------------------- + * + * tsearch2.c + * Backwards-compatibility package for old contrib/tsearch2 API + * + * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * $PostgreSQL: pgsql/contrib/tsearch2/tsearch2.c,v 1.1 2007/11/13 21:02:29 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "catalog/namespace.h" +#include "commands/trigger.h" +#include "fmgr.h" +#include "tsearch/ts_utils.h" +#include "utils/builtins.h" +#include "utils/guc.h" +#include "utils/syscache.h" + +PG_MODULE_MAGIC; + +static Oid current_dictionary_oid = InvalidOid; +static Oid current_parser_oid = InvalidOid; + +/* insert given value at argument position 0 */ +#define INSERT_ARGUMENT0(argument, isnull) \ + do { \ + int i; \ + for (i = fcinfo->nargs; i > 0; i--) \ + { \ + fcinfo->arg[i] = fcinfo->arg[i-1]; \ + fcinfo->argnull[i] = fcinfo->argnull[i-1]; \ + } \ + fcinfo->arg[0] = (argument); \ + fcinfo->argnull[0] = (isnull); \ + fcinfo->nargs++; \ + } while (0) + +#define TextPGetCString(t) \ + DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(t))) +#define CStringGetTextP(c) \ + DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(c))) + +#define TextGetObjectId(infunction, text) \ + DatumGetObjectId(DirectFunctionCall1(infunction, \ + DirectFunctionCall1(textout, PointerGetDatum(text)))) + +#define UNSUPPORTED_FUNCTION(name) \ + Datum name(PG_FUNCTION_ARGS); \ + Datum \ + name(PG_FUNCTION_ARGS) \ + { \ + ereport(ERROR, \ + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\ + errmsg("function %s is no longer supported", \ + format_procedure(fcinfo->flinfo->fn_oid)), \ + errhint("Switch to new tsearch functionality."))); \ + /* keep compiler quiet */ \ + PG_RETURN_NULL(); \ + } \ + PG_FUNCTION_INFO_V1(name) + +static Oid GetCurrentDict(void); +static Oid GetCurrentParser(void); + +Datum tsa_lexize_byname(PG_FUNCTION_ARGS); +Datum tsa_lexize_bycurrent(PG_FUNCTION_ARGS); +Datum tsa_set_curdict(PG_FUNCTION_ARGS); +Datum tsa_set_curdict_byname(PG_FUNCTION_ARGS); +Datum tsa_token_type_current(PG_FUNCTION_ARGS); +Datum tsa_set_curprs(PG_FUNCTION_ARGS); +Datum tsa_set_curprs_byname(PG_FUNCTION_ARGS); +Datum tsa_parse_current(PG_FUNCTION_ARGS); +Datum tsa_set_curcfg(PG_FUNCTION_ARGS); +Datum tsa_set_curcfg_byname(PG_FUNCTION_ARGS); +Datum tsa_show_curcfg(PG_FUNCTION_ARGS); +Datum tsa_to_tsvector_name(PG_FUNCTION_ARGS); +Datum tsa_to_tsquery_name(PG_FUNCTION_ARGS); +Datum tsa_plainto_tsquery_name(PG_FUNCTION_ARGS); +Datum tsa_headline_byname(PG_FUNCTION_ARGS); +Datum tsa_ts_stat(PG_FUNCTION_ARGS); +Datum tsa_tsearch2(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(tsa_lexize_byname); +PG_FUNCTION_INFO_V1(tsa_lexize_bycurrent); +PG_FUNCTION_INFO_V1(tsa_set_curdict); +PG_FUNCTION_INFO_V1(tsa_set_curdict_byname); +PG_FUNCTION_INFO_V1(tsa_token_type_current); +PG_FUNCTION_INFO_V1(tsa_set_curprs); +PG_FUNCTION_INFO_V1(tsa_set_curprs_byname); +PG_FUNCTION_INFO_V1(tsa_parse_current); +PG_FUNCTION_INFO_V1(tsa_set_curcfg); +PG_FUNCTION_INFO_V1(tsa_set_curcfg_byname); +PG_FUNCTION_INFO_V1(tsa_show_curcfg); +PG_FUNCTION_INFO_V1(tsa_to_tsvector_name); +PG_FUNCTION_INFO_V1(tsa_to_tsquery_name); +PG_FUNCTION_INFO_V1(tsa_plainto_tsquery_name); +PG_FUNCTION_INFO_V1(tsa_headline_byname); +PG_FUNCTION_INFO_V1(tsa_ts_stat); +PG_FUNCTION_INFO_V1(tsa_tsearch2); + + +/* + * List of unsupported functions + * + * The parser and dictionary functions are defined only so that the former + * contents of pg_ts_parser and pg_ts_dict can be loaded into the system, + * for ease of reference while creating the new tsearch configuration. + */ + +UNSUPPORTED_FUNCTION(tsa_dex_init); +UNSUPPORTED_FUNCTION(tsa_dex_lexize); + +UNSUPPORTED_FUNCTION(tsa_snb_en_init); +UNSUPPORTED_FUNCTION(tsa_snb_lexize); +UNSUPPORTED_FUNCTION(tsa_snb_ru_init_koi8); +UNSUPPORTED_FUNCTION(tsa_snb_ru_init_utf8); + +UNSUPPORTED_FUNCTION(tsa_spell_init); +UNSUPPORTED_FUNCTION(tsa_spell_lexize); + +UNSUPPORTED_FUNCTION(tsa_syn_init); +UNSUPPORTED_FUNCTION(tsa_syn_lexize); + +UNSUPPORTED_FUNCTION(tsa_thesaurus_init); +UNSUPPORTED_FUNCTION(tsa_thesaurus_lexize); + +UNSUPPORTED_FUNCTION(tsa_prsd_start); +UNSUPPORTED_FUNCTION(tsa_prsd_getlexeme); +UNSUPPORTED_FUNCTION(tsa_prsd_end); +UNSUPPORTED_FUNCTION(tsa_prsd_lextype); +UNSUPPORTED_FUNCTION(tsa_prsd_headline); + +UNSUPPORTED_FUNCTION(tsa_reset_tsearch); +UNSUPPORTED_FUNCTION(tsa_get_covers); + +UNSUPPORTED_FUNCTION(tsa_rewrite_accum); +UNSUPPORTED_FUNCTION(tsa_rewrite_finish); + + +/* + * list of redefined functions + */ + +/* lexize(text, text) */ +Datum +tsa_lexize_byname(PG_FUNCTION_ARGS) +{ + text *dictname = PG_GETARG_TEXT_P(0); + Datum arg1 = PG_GETARG_DATUM(1); + + return DirectFunctionCall2(ts_lexize, + ObjectIdGetDatum(TextGetObjectId(regdictionaryin, dictname)), + arg1); +} + +/* lexize(text) */ +Datum +tsa_lexize_bycurrent(PG_FUNCTION_ARGS) +{ + Datum arg0 = PG_GETARG_DATUM(0); + Oid id = GetCurrentDict(); + + return DirectFunctionCall2(ts_lexize, + ObjectIdGetDatum(id), + arg0); +} + +/* set_curdict(int) */ +Datum +tsa_set_curdict(PG_FUNCTION_ARGS) +{ + Oid dict_oid = PG_GETARG_OID(0); + + if (!SearchSysCacheExists(TSDICTOID, + ObjectIdGetDatum(dict_oid), + 0, 0, 0)) + elog(ERROR, "cache lookup failed for text search dictionary %u", + dict_oid); + + current_dictionary_oid = dict_oid; + + PG_RETURN_VOID(); +} + +/* set_curdict(text) */ +Datum +tsa_set_curdict_byname(PG_FUNCTION_ARGS) +{ + text *name = PG_GETARG_TEXT_P(0); + Oid dict_oid; + + dict_oid = TSDictionaryGetDictid(stringToQualifiedNameList(TextPGetCString(name)), false); + + current_dictionary_oid = dict_oid; + + PG_RETURN_VOID(); +} + +/* token_type() */ +Datum +tsa_token_type_current(PG_FUNCTION_ARGS) +{ + INSERT_ARGUMENT0(ObjectIdGetDatum(GetCurrentParser()), false); + return ts_token_type_byid(fcinfo); +} + +/* set_curprs(int) */ +Datum +tsa_set_curprs(PG_FUNCTION_ARGS) +{ + Oid parser_oid = PG_GETARG_OID(0); + + if (!SearchSysCacheExists(TSPARSEROID, + ObjectIdGetDatum(parser_oid), + 0, 0, 0)) + elog(ERROR, "cache lookup failed for text search parser %u", + parser_oid); + + current_parser_oid = parser_oid; + + PG_RETURN_VOID(); +} + +/* set_curprs(text) */ +Datum +tsa_set_curprs_byname(PG_FUNCTION_ARGS) +{ + text *name = PG_GETARG_TEXT_P(0); + Oid parser_oid; + + parser_oid = TSParserGetPrsid(stringToQualifiedNameList(TextPGetCString(name)), false); + + current_parser_oid = parser_oid; + + PG_RETURN_VOID(); +} + +/* parse(text) */ +Datum +tsa_parse_current(PG_FUNCTION_ARGS) +{ + INSERT_ARGUMENT0(ObjectIdGetDatum(GetCurrentParser()), false); + return ts_parse_byid(fcinfo); +} + +/* set_curcfg(int) */ +Datum +tsa_set_curcfg(PG_FUNCTION_ARGS) +{ + Oid arg0 = PG_GETARG_OID(0); + char *name; + + name = DatumGetCString(DirectFunctionCall1(regconfigout, + ObjectIdGetDatum(arg0))); + + set_config_option("default_text_search_config", name, + PGC_USERSET, + PGC_S_SESSION, + GUC_ACTION_SET, + true); + + PG_RETURN_VOID(); +} + +/* set_curcfg(text) */ +Datum +tsa_set_curcfg_byname(PG_FUNCTION_ARGS) +{ + text *arg0 = PG_GETARG_TEXT_P(0); + char *name; + + name = TextPGetCString(arg0); + set_config_option("default_text_search_config", name, + PGC_USERSET, + PGC_S_SESSION, + GUC_ACTION_SET, + true); + + PG_RETURN_VOID(); +} + +/* show_curcfg() */ +Datum +tsa_show_curcfg(PG_FUNCTION_ARGS) +{ + char *cfgname; + Oid config_oid; + + cfgname = GetConfigOptionByName("default_text_search_config", NULL); + config_oid = DatumGetObjectId(DirectFunctionCall1(regconfigin, + CStringGetDatum(cfgname))); + + PG_RETURN_OID(config_oid); +} + +/* to_tsvector(text, text) */ +Datum +tsa_to_tsvector_name(PG_FUNCTION_ARGS) +{ + text *cfgname = PG_GETARG_TEXT_P(0); + Datum arg1 = PG_GETARG_DATUM(1); + Oid config_oid; + + config_oid = TextGetObjectId(regconfigin, cfgname); + + return DirectFunctionCall2(to_tsvector_byid, + ObjectIdGetDatum(config_oid), arg1); +} + +/* to_tsquery(text, text) */ +Datum +tsa_to_tsquery_name(PG_FUNCTION_ARGS) +{ + text *cfgname = PG_GETARG_TEXT_P(0); + Datum arg1 = PG_GETARG_DATUM(1); + Oid config_oid; + + config_oid = TextGetObjectId(regconfigin, cfgname); + + return DirectFunctionCall2(to_tsquery_byid, + ObjectIdGetDatum(config_oid), arg1); +} + + +/* plainto_tsquery(text, text) */ +Datum +tsa_plainto_tsquery_name(PG_FUNCTION_ARGS) +{ + text *cfgname = PG_GETARG_TEXT_P(0); + Datum arg1 = PG_GETARG_DATUM(1); + Oid config_oid; + + config_oid = TextGetObjectId(regconfigin, cfgname); + + return DirectFunctionCall2(plainto_tsquery_byid, + ObjectIdGetDatum(config_oid), arg1); +} + +/* headline(text, text, tsquery [,text]) */ +Datum +tsa_headline_byname(PG_FUNCTION_ARGS) +{ + Datum arg0 = PG_GETARG_DATUM(0); + Datum arg1 = PG_GETARG_DATUM(1); + Datum arg2 = PG_GETARG_DATUM(2); + Datum result; + Oid config_oid; + + /* first parameter has to be converted to oid */ + config_oid = DatumGetObjectId(DirectFunctionCall1(regconfigin, + DirectFunctionCall1(textout, arg0))); + + if (PG_NARGS() == 3) + result = DirectFunctionCall3(ts_headline_byid, + ObjectIdGetDatum(config_oid), arg1, arg2); + else + { + Datum arg3 = PG_GETARG_DATUM(3); + + result = DirectFunctionCall4(ts_headline_byid_opt, + ObjectIdGetDatum(config_oid), + arg1, arg2, arg3); + } + + return result; +} + +/* + * tsearch2 version of update trigger + * + * We pass this on to the core trigger after inserting the default text + * search configuration name as the second argument. Note that this isn't + * a complete implementation of the original functionality; tsearch2 allowed + * transformation function names to be included in the list. However, that + * is deliberately removed as being a security risk. + */ +Datum +tsa_tsearch2(PG_FUNCTION_ARGS) +{ + TriggerData *trigdata; + Trigger *trigger; + char **tgargs; + int i; + + /* Check call context */ + if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */ + elog(ERROR, "tsvector_update_trigger: not fired by trigger manager"); + + trigdata = (TriggerData *) fcinfo->context; + trigger = trigdata->tg_trigger; + + if (trigger->tgnargs < 2) + elog(ERROR, "TSearch: format tsearch2(tsvector_field, text_field1,...)"); + + /* create space for configuration name */ + tgargs = (char **) palloc((trigger->tgnargs + 1) * sizeof(char *)); + tgargs[0] = trigger->tgargs[0]; + for (i = 1; i < trigger->tgnargs; i++) + tgargs[i+1] = trigger->tgargs[i]; + + tgargs[1] = pstrdup(GetConfigOptionByName("default_text_search_config", + NULL)); + trigger->tgargs = tgargs; + trigger->tgnargs++; + + return tsvector_update_trigger_byid(fcinfo); +} + +/* + * Get Oid of current dictionary + */ +static Oid +GetCurrentDict(void) +{ + if (current_dictionary_oid == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("no current dictionary"), + errhint("Execute SELECT set_curdict(...)."))); + + return current_dictionary_oid; +} + +/* + * Get Oid of current parser + * + * Here, it seems reasonable to select the "default" parser if none has been + * set. + */ +static Oid +GetCurrentParser(void) +{ + if (current_parser_oid == InvalidOid) + current_parser_oid = TSParserGetPrsid(stringToQualifiedNameList("pg_catalog.default"), false); + return current_parser_oid; +} |