/*------------------------------------------------------------------------- * * 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.3 2007/11/15 21:14:31 momjian Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "catalog/namespace.h" #include "catalog/pg_type.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_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); Datum tsa_rewrite_accum(PG_FUNCTION_ARGS); Datum tsa_rewrite_finish(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_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); PG_FUNCTION_INFO_V1(tsa_rewrite_accum); PG_FUNCTION_INFO_V1(tsa_rewrite_finish); /* * 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); /* * 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(); } /* 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); } Datum tsa_rewrite_accum(PG_FUNCTION_ARGS) { TSQuery acc; ArrayType *qa; TSQuery q; QTNode *qex = NULL, *subs = NULL, *acctree = NULL; bool isfind = false; Datum *elemsp; int nelemsp; MemoryContext aggcontext; MemoryContext oldcontext; aggcontext = ((AggState *) fcinfo->context)->aggcontext; if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL) { acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ); SET_VARSIZE(acc, HDRSIZETQ); acc->size = 0; } else acc = PG_GETARG_TSQUERY(0); if (PG_ARGISNULL(1) || PG_GETARG_POINTER(1) == NULL) PG_RETURN_TSQUERY(acc); else qa = PG_GETARG_ARRAYTYPE_P_COPY(1); if (ARR_NDIM(qa) != 1) elog(ERROR, "array must be one-dimensional, not %d dimensions", ARR_NDIM(qa)); if (ArrayGetNItems(ARR_NDIM(qa), ARR_DIMS(qa)) != 3) elog(ERROR, "array must have three elements"); if (ARR_ELEMTYPE(qa) != TSQUERYOID) elog(ERROR, "array must contain tsquery elements"); deconstruct_array(qa, TSQUERYOID, -1, false, 'i', &elemsp, NULL, &nelemsp); q = DatumGetTSQuery(elemsp[0]); if (q->size == 0) { pfree(elemsp); PG_RETURN_POINTER(acc); } if (!acc->size) { if (VARSIZE(acc) > HDRSIZETQ) { pfree(elemsp); PG_RETURN_POINTER(acc); } else acctree = QT2QTN(GETQUERY(q), GETOPERAND(q)); } else acctree = QT2QTN(GETQUERY(acc), GETOPERAND(acc)); QTNTernary(acctree); QTNSort(acctree); q = DatumGetTSQuery(elemsp[1]); if (q->size == 0) { pfree(elemsp); PG_RETURN_POINTER(acc); } qex = QT2QTN(GETQUERY(q), GETOPERAND(q)); QTNTernary(qex); QTNSort(qex); q = DatumGetTSQuery(elemsp[2]); if (q->size) subs = QT2QTN(GETQUERY(q), GETOPERAND(q)); acctree = findsubquery(acctree, qex, subs, &isfind); if (isfind || !acc->size) { /* pfree( acc ); do not pfree(p), because nodeAgg.c will */ if (acctree) { QTNBinary(acctree); oldcontext = MemoryContextSwitchTo(aggcontext); acc = QTN2QT(acctree); MemoryContextSwitchTo(oldcontext); } else { acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ); SET_VARSIZE(acc, HDRSIZETQ); acc->size = 0; } } pfree(elemsp); QTNFree(qex); QTNFree(subs); QTNFree(acctree); PG_RETURN_TSQUERY(acc); } Datum tsa_rewrite_finish(PG_FUNCTION_ARGS) { TSQuery acc = PG_GETARG_TSQUERY(0); TSQuery rewrited; if (acc == NULL || PG_ARGISNULL(0) || acc->size == 0) { rewrited = (TSQuery) palloc(HDRSIZETQ); SET_VARSIZE(rewrited, HDRSIZETQ); rewrited->size = 0; } else { rewrited = (TSQuery) palloc(VARSIZE(acc)); memcpy(rewrited, acc, VARSIZE(acc)); pfree(acc); } PG_RETURN_POINTER(rewrited); } /* * 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; }