Header And Logo

PostgreSQL
| The world's most advanced open source database.

tsearch2.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * tsearch2.c
00004  *      Backwards-compatibility package for old contrib/tsearch2 API
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  *
00008  *
00009  * IDENTIFICATION
00010  *    contrib/tsearch2/tsearch2.c
00011  *
00012  *-------------------------------------------------------------------------
00013  */
00014 #include "postgres.h"
00015 
00016 #include "catalog/namespace.h"
00017 #include "catalog/pg_type.h"
00018 #include "commands/trigger.h"
00019 #include "tsearch/ts_utils.h"
00020 #include "utils/builtins.h"
00021 #include "utils/guc.h"
00022 #include "utils/syscache.h"
00023 
00024 PG_MODULE_MAGIC;
00025 
00026 static Oid  current_dictionary_oid = InvalidOid;
00027 static Oid  current_parser_oid = InvalidOid;
00028 
00029 /* insert given value at argument position 0 */
00030 #define INSERT_ARGUMENT0(argument, isnull)              \
00031     do {                                                \
00032         int i;                                          \
00033         for (i = fcinfo->nargs; i > 0; i--)             \
00034         {                                               \
00035             fcinfo->arg[i] = fcinfo->arg[i-1];          \
00036             fcinfo->argnull[i] = fcinfo->argnull[i-1];  \
00037         }                                               \
00038         fcinfo->arg[0] = (argument);                    \
00039         fcinfo->argnull[0] = (isnull);                  \
00040         fcinfo->nargs++;                                \
00041     } while (0)
00042 
00043 #define TextGetObjectId(infunction, text) \
00044     DatumGetObjectId(DirectFunctionCall1(infunction, \
00045                      CStringGetDatum(text_to_cstring(text))))
00046 
00047 #define UNSUPPORTED_FUNCTION(name)                      \
00048     Datum name(PG_FUNCTION_ARGS);                       \
00049     Datum                                               \
00050     name(PG_FUNCTION_ARGS)                              \
00051     {                                                   \
00052         ereport(ERROR,                                  \
00053                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\
00054                  errmsg("function %s is no longer supported", \
00055                         format_procedure(fcinfo->flinfo->fn_oid)), \
00056                  errhint("Switch to new tsearch functionality."))); \
00057         /* keep compiler quiet */                       \
00058         PG_RETURN_NULL();                               \
00059     }                                                   \
00060     PG_FUNCTION_INFO_V1(name)
00061 
00062 static Oid  GetCurrentDict(void);
00063 static Oid  GetCurrentParser(void);
00064 
00065 Datum       tsa_lexize_byname(PG_FUNCTION_ARGS);
00066 Datum       tsa_lexize_bycurrent(PG_FUNCTION_ARGS);
00067 Datum       tsa_set_curdict(PG_FUNCTION_ARGS);
00068 Datum       tsa_set_curdict_byname(PG_FUNCTION_ARGS);
00069 Datum       tsa_token_type_current(PG_FUNCTION_ARGS);
00070 Datum       tsa_set_curprs(PG_FUNCTION_ARGS);
00071 Datum       tsa_set_curprs_byname(PG_FUNCTION_ARGS);
00072 Datum       tsa_parse_current(PG_FUNCTION_ARGS);
00073 Datum       tsa_set_curcfg(PG_FUNCTION_ARGS);
00074 Datum       tsa_set_curcfg_byname(PG_FUNCTION_ARGS);
00075 Datum       tsa_to_tsvector_name(PG_FUNCTION_ARGS);
00076 Datum       tsa_to_tsquery_name(PG_FUNCTION_ARGS);
00077 Datum       tsa_plainto_tsquery_name(PG_FUNCTION_ARGS);
00078 Datum       tsa_headline_byname(PG_FUNCTION_ARGS);
00079 Datum       tsa_ts_stat(PG_FUNCTION_ARGS);
00080 Datum       tsa_tsearch2(PG_FUNCTION_ARGS);
00081 Datum       tsa_rewrite_accum(PG_FUNCTION_ARGS);
00082 Datum       tsa_rewrite_finish(PG_FUNCTION_ARGS);
00083 
00084 PG_FUNCTION_INFO_V1(tsa_lexize_byname);
00085 PG_FUNCTION_INFO_V1(tsa_lexize_bycurrent);
00086 PG_FUNCTION_INFO_V1(tsa_set_curdict);
00087 PG_FUNCTION_INFO_V1(tsa_set_curdict_byname);
00088 PG_FUNCTION_INFO_V1(tsa_token_type_current);
00089 PG_FUNCTION_INFO_V1(tsa_set_curprs);
00090 PG_FUNCTION_INFO_V1(tsa_set_curprs_byname);
00091 PG_FUNCTION_INFO_V1(tsa_parse_current);
00092 PG_FUNCTION_INFO_V1(tsa_set_curcfg);
00093 PG_FUNCTION_INFO_V1(tsa_set_curcfg_byname);
00094 PG_FUNCTION_INFO_V1(tsa_to_tsvector_name);
00095 PG_FUNCTION_INFO_V1(tsa_to_tsquery_name);
00096 PG_FUNCTION_INFO_V1(tsa_plainto_tsquery_name);
00097 PG_FUNCTION_INFO_V1(tsa_headline_byname);
00098 PG_FUNCTION_INFO_V1(tsa_ts_stat);
00099 PG_FUNCTION_INFO_V1(tsa_tsearch2);
00100 PG_FUNCTION_INFO_V1(tsa_rewrite_accum);
00101 PG_FUNCTION_INFO_V1(tsa_rewrite_finish);
00102 
00103 
00104 /*
00105  * List of unsupported functions
00106  *
00107  * The parser and dictionary functions are defined only so that the former
00108  * contents of pg_ts_parser and pg_ts_dict can be loaded into the system,
00109  * for ease of reference while creating the new tsearch configuration.
00110  */
00111 
00112 UNSUPPORTED_FUNCTION(tsa_dex_init);
00113 UNSUPPORTED_FUNCTION(tsa_dex_lexize);
00114 
00115 UNSUPPORTED_FUNCTION(tsa_snb_en_init);
00116 UNSUPPORTED_FUNCTION(tsa_snb_lexize);
00117 UNSUPPORTED_FUNCTION(tsa_snb_ru_init_koi8);
00118 UNSUPPORTED_FUNCTION(tsa_snb_ru_init_utf8);
00119 UNSUPPORTED_FUNCTION(tsa_snb_ru_init);
00120 
00121 UNSUPPORTED_FUNCTION(tsa_spell_init);
00122 UNSUPPORTED_FUNCTION(tsa_spell_lexize);
00123 
00124 UNSUPPORTED_FUNCTION(tsa_syn_init);
00125 UNSUPPORTED_FUNCTION(tsa_syn_lexize);
00126 
00127 UNSUPPORTED_FUNCTION(tsa_thesaurus_init);
00128 UNSUPPORTED_FUNCTION(tsa_thesaurus_lexize);
00129 
00130 UNSUPPORTED_FUNCTION(tsa_prsd_start);
00131 UNSUPPORTED_FUNCTION(tsa_prsd_getlexeme);
00132 UNSUPPORTED_FUNCTION(tsa_prsd_end);
00133 UNSUPPORTED_FUNCTION(tsa_prsd_lextype);
00134 UNSUPPORTED_FUNCTION(tsa_prsd_headline);
00135 
00136 UNSUPPORTED_FUNCTION(tsa_reset_tsearch);
00137 UNSUPPORTED_FUNCTION(tsa_get_covers);
00138 
00139 
00140 /*
00141  * list of redefined functions
00142  */
00143 
00144 /* lexize(text, text) */
00145 Datum
00146 tsa_lexize_byname(PG_FUNCTION_ARGS)
00147 {
00148     text       *dictname = PG_GETARG_TEXT_PP(0);
00149     Datum       arg1 = PG_GETARG_DATUM(1);
00150 
00151     return DirectFunctionCall2(ts_lexize,
00152                 ObjectIdGetDatum(TextGetObjectId(regdictionaryin, dictname)),
00153                                arg1);
00154 }
00155 
00156 /* lexize(text) */
00157 Datum
00158 tsa_lexize_bycurrent(PG_FUNCTION_ARGS)
00159 {
00160     Datum       arg0 = PG_GETARG_DATUM(0);
00161     Oid         id = GetCurrentDict();
00162 
00163     return DirectFunctionCall2(ts_lexize,
00164                                ObjectIdGetDatum(id),
00165                                arg0);
00166 }
00167 
00168 /* set_curdict(int) */
00169 Datum
00170 tsa_set_curdict(PG_FUNCTION_ARGS)
00171 {
00172     Oid         dict_oid = PG_GETARG_OID(0);
00173 
00174     if (!SearchSysCacheExists(TSDICTOID,
00175                               ObjectIdGetDatum(dict_oid),
00176                               0, 0, 0))
00177         elog(ERROR, "cache lookup failed for text search dictionary %u",
00178              dict_oid);
00179 
00180     current_dictionary_oid = dict_oid;
00181 
00182     PG_RETURN_VOID();
00183 }
00184 
00185 /* set_curdict(text) */
00186 Datum
00187 tsa_set_curdict_byname(PG_FUNCTION_ARGS)
00188 {
00189     text       *name = PG_GETARG_TEXT_PP(0);
00190     Oid         dict_oid;
00191 
00192     dict_oid = get_ts_dict_oid(stringToQualifiedNameList(text_to_cstring(name)), false);
00193 
00194     current_dictionary_oid = dict_oid;
00195 
00196     PG_RETURN_VOID();
00197 }
00198 
00199 /* token_type() */
00200 Datum
00201 tsa_token_type_current(PG_FUNCTION_ARGS)
00202 {
00203     INSERT_ARGUMENT0(ObjectIdGetDatum(GetCurrentParser()), false);
00204     return ts_token_type_byid(fcinfo);
00205 }
00206 
00207 /* set_curprs(int) */
00208 Datum
00209 tsa_set_curprs(PG_FUNCTION_ARGS)
00210 {
00211     Oid         parser_oid = PG_GETARG_OID(0);
00212 
00213     if (!SearchSysCacheExists(TSPARSEROID,
00214                               ObjectIdGetDatum(parser_oid),
00215                               0, 0, 0))
00216         elog(ERROR, "cache lookup failed for text search parser %u",
00217              parser_oid);
00218 
00219     current_parser_oid = parser_oid;
00220 
00221     PG_RETURN_VOID();
00222 }
00223 
00224 /* set_curprs(text) */
00225 Datum
00226 tsa_set_curprs_byname(PG_FUNCTION_ARGS)
00227 {
00228     text       *name = PG_GETARG_TEXT_PP(0);
00229     Oid         parser_oid;
00230 
00231     parser_oid = get_ts_parser_oid(stringToQualifiedNameList(text_to_cstring(name)), false);
00232 
00233     current_parser_oid = parser_oid;
00234 
00235     PG_RETURN_VOID();
00236 }
00237 
00238 /* parse(text) */
00239 Datum
00240 tsa_parse_current(PG_FUNCTION_ARGS)
00241 {
00242     INSERT_ARGUMENT0(ObjectIdGetDatum(GetCurrentParser()), false);
00243     return ts_parse_byid(fcinfo);
00244 }
00245 
00246 /* set_curcfg(int) */
00247 Datum
00248 tsa_set_curcfg(PG_FUNCTION_ARGS)
00249 {
00250     Oid         arg0 = PG_GETARG_OID(0);
00251     char       *name;
00252 
00253     name = DatumGetCString(DirectFunctionCall1(regconfigout,
00254                                                ObjectIdGetDatum(arg0)));
00255 
00256     SetConfigOption("default_text_search_config", name,
00257                     PGC_USERSET, PGC_S_SESSION);
00258 
00259     PG_RETURN_VOID();
00260 }
00261 
00262 /* set_curcfg(text) */
00263 Datum
00264 tsa_set_curcfg_byname(PG_FUNCTION_ARGS)
00265 {
00266     text       *arg0 = PG_GETARG_TEXT_PP(0);
00267     char       *name;
00268 
00269     name = text_to_cstring(arg0);
00270 
00271     SetConfigOption("default_text_search_config", name,
00272                     PGC_USERSET, PGC_S_SESSION);
00273 
00274     PG_RETURN_VOID();
00275 }
00276 
00277 /* to_tsvector(text, text) */
00278 Datum
00279 tsa_to_tsvector_name(PG_FUNCTION_ARGS)
00280 {
00281     text       *cfgname = PG_GETARG_TEXT_PP(0);
00282     Datum       arg1 = PG_GETARG_DATUM(1);
00283     Oid         config_oid;
00284 
00285     config_oid = TextGetObjectId(regconfigin, cfgname);
00286 
00287     return DirectFunctionCall2(to_tsvector_byid,
00288                                ObjectIdGetDatum(config_oid), arg1);
00289 }
00290 
00291 /* to_tsquery(text, text) */
00292 Datum
00293 tsa_to_tsquery_name(PG_FUNCTION_ARGS)
00294 {
00295     text       *cfgname = PG_GETARG_TEXT_PP(0);
00296     Datum       arg1 = PG_GETARG_DATUM(1);
00297     Oid         config_oid;
00298 
00299     config_oid = TextGetObjectId(regconfigin, cfgname);
00300 
00301     return DirectFunctionCall2(to_tsquery_byid,
00302                                ObjectIdGetDatum(config_oid), arg1);
00303 }
00304 
00305 
00306 /* plainto_tsquery(text, text) */
00307 Datum
00308 tsa_plainto_tsquery_name(PG_FUNCTION_ARGS)
00309 {
00310     text       *cfgname = PG_GETARG_TEXT_PP(0);
00311     Datum       arg1 = PG_GETARG_DATUM(1);
00312     Oid         config_oid;
00313 
00314     config_oid = TextGetObjectId(regconfigin, cfgname);
00315 
00316     return DirectFunctionCall2(plainto_tsquery_byid,
00317                                ObjectIdGetDatum(config_oid), arg1);
00318 }
00319 
00320 /* headline(text, text, tsquery [,text]) */
00321 Datum
00322 tsa_headline_byname(PG_FUNCTION_ARGS)
00323 {
00324     Datum       arg0 = PG_GETARG_DATUM(0);
00325     Datum       arg1 = PG_GETARG_DATUM(1);
00326     Datum       arg2 = PG_GETARG_DATUM(2);
00327     Datum       result;
00328     Oid         config_oid;
00329 
00330     /* first parameter has to be converted to oid */
00331     config_oid = DatumGetObjectId(DirectFunctionCall1(regconfigin,
00332                                 CStringGetDatum(TextDatumGetCString(arg0))));
00333 
00334     if (PG_NARGS() == 3)
00335         result = DirectFunctionCall3(ts_headline_byid,
00336                                    ObjectIdGetDatum(config_oid), arg1, arg2);
00337     else
00338     {
00339         Datum       arg3 = PG_GETARG_DATUM(3);
00340 
00341         result = DirectFunctionCall4(ts_headline_byid_opt,
00342                                      ObjectIdGetDatum(config_oid),
00343                                      arg1, arg2, arg3);
00344     }
00345 
00346     return result;
00347 }
00348 
00349 /*
00350  * tsearch2 version of update trigger
00351  *
00352  * We pass this on to the core trigger after inserting the default text
00353  * search configuration name as the second argument.  Note that this isn't
00354  * a complete implementation of the original functionality; tsearch2 allowed
00355  * transformation function names to be included in the list.  However, that
00356  * is deliberately removed as being a security risk.
00357  */
00358 Datum
00359 tsa_tsearch2(PG_FUNCTION_ARGS)
00360 {
00361     TriggerData *trigdata;
00362     Trigger    *trigger;
00363     char      **tgargs,
00364               **tgargs_old;
00365     int         i;
00366     Datum       res;
00367 
00368     /* Check call context */
00369     if (!CALLED_AS_TRIGGER(fcinfo))     /* internal error */
00370         elog(ERROR, "tsvector_update_trigger: not fired by trigger manager");
00371 
00372     trigdata = (TriggerData *) fcinfo->context;
00373     trigger = trigdata->tg_trigger;
00374 
00375     if (trigger->tgnargs < 2)
00376         elog(ERROR, "TSearch: format tsearch2(tsvector_field, text_field1,...)");
00377 
00378     /* create space for configuration name */
00379     tgargs = (char **) palloc((trigger->tgnargs + 1) * sizeof(char *));
00380     tgargs[0] = trigger->tgargs[0];
00381     for (i = 1; i < trigger->tgnargs; i++)
00382         tgargs[i + 1] = trigger->tgargs[i];
00383 
00384     tgargs[1] = pstrdup(GetConfigOptionByName("default_text_search_config",
00385                                               NULL));
00386     tgargs_old = trigger->tgargs;
00387     trigger->tgargs = tgargs;
00388     trigger->tgnargs++;
00389 
00390     res = tsvector_update_trigger_byid(fcinfo);
00391 
00392     /* restore old trigger data */
00393     trigger->tgargs = tgargs_old;
00394     trigger->tgnargs--;
00395 
00396     pfree(tgargs[1]);
00397     pfree(tgargs);
00398 
00399     return res;
00400 }
00401 
00402 
00403 Datum
00404 tsa_rewrite_accum(PG_FUNCTION_ARGS)
00405 {
00406     TSQuery     acc;
00407     ArrayType  *qa;
00408     TSQuery     q;
00409     QTNode     *qex = NULL,
00410                *subs = NULL,
00411                *acctree = NULL;
00412     bool        isfind = false;
00413     Datum      *elemsp;
00414     int         nelemsp;
00415     MemoryContext aggcontext;
00416     MemoryContext oldcontext;
00417 
00418     if (!AggCheckCallContext(fcinfo, &aggcontext))
00419         elog(ERROR, "tsa_rewrite_accum called in non-aggregate context");
00420 
00421     if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL)
00422     {
00423         acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ);
00424         SET_VARSIZE(acc, HDRSIZETQ);
00425         acc->size = 0;
00426     }
00427     else
00428         acc = PG_GETARG_TSQUERY(0);
00429 
00430     if (PG_ARGISNULL(1) || PG_GETARG_POINTER(1) == NULL)
00431         PG_RETURN_TSQUERY(acc);
00432     else
00433         qa = PG_GETARG_ARRAYTYPE_P_COPY(1);
00434 
00435     if (ARR_NDIM(qa) != 1)
00436         elog(ERROR, "array must be one-dimensional, not %d dimensions",
00437              ARR_NDIM(qa));
00438     if (ArrayGetNItems(ARR_NDIM(qa), ARR_DIMS(qa)) != 3)
00439         elog(ERROR, "array must have three elements");
00440     if (ARR_ELEMTYPE(qa) != TSQUERYOID)
00441         elog(ERROR, "array must contain tsquery elements");
00442 
00443     deconstruct_array(qa, TSQUERYOID, -1, false, 'i', &elemsp, NULL, &nelemsp);
00444 
00445     q = DatumGetTSQuery(elemsp[0]);
00446     if (q->size == 0)
00447     {
00448         pfree(elemsp);
00449         PG_RETURN_POINTER(acc);
00450     }
00451 
00452     if (!acc->size)
00453     {
00454         if (VARSIZE(acc) > HDRSIZETQ)
00455         {
00456             pfree(elemsp);
00457             PG_RETURN_POINTER(acc);
00458         }
00459         else
00460             acctree = QT2QTN(GETQUERY(q), GETOPERAND(q));
00461     }
00462     else
00463         acctree = QT2QTN(GETQUERY(acc), GETOPERAND(acc));
00464 
00465     QTNTernary(acctree);
00466     QTNSort(acctree);
00467 
00468     q = DatumGetTSQuery(elemsp[1]);
00469     if (q->size == 0)
00470     {
00471         pfree(elemsp);
00472         PG_RETURN_POINTER(acc);
00473     }
00474     qex = QT2QTN(GETQUERY(q), GETOPERAND(q));
00475     QTNTernary(qex);
00476     QTNSort(qex);
00477 
00478     q = DatumGetTSQuery(elemsp[2]);
00479     if (q->size)
00480         subs = QT2QTN(GETQUERY(q), GETOPERAND(q));
00481 
00482     acctree = findsubquery(acctree, qex, subs, &isfind);
00483 
00484     if (isfind || !acc->size)
00485     {
00486         /* pfree( acc ); do not pfree(p), because nodeAgg.c will */
00487         if (acctree)
00488         {
00489             QTNBinary(acctree);
00490             oldcontext = MemoryContextSwitchTo(aggcontext);
00491             acc = QTN2QT(acctree);
00492             MemoryContextSwitchTo(oldcontext);
00493         }
00494         else
00495         {
00496             acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ);
00497             SET_VARSIZE(acc, HDRSIZETQ);
00498             acc->size = 0;
00499         }
00500     }
00501 
00502     pfree(elemsp);
00503     QTNFree(qex);
00504     QTNFree(subs);
00505     QTNFree(acctree);
00506 
00507     PG_RETURN_TSQUERY(acc);
00508 }
00509 
00510 Datum
00511 tsa_rewrite_finish(PG_FUNCTION_ARGS)
00512 {
00513     TSQuery     acc = PG_GETARG_TSQUERY(0);
00514     TSQuery     rewrited;
00515 
00516     if (acc == NULL || PG_ARGISNULL(0) || acc->size == 0)
00517     {
00518         rewrited = (TSQuery) palloc(HDRSIZETQ);
00519         SET_VARSIZE(rewrited, HDRSIZETQ);
00520         rewrited->size = 0;
00521     }
00522     else
00523     {
00524         rewrited = (TSQuery) palloc(VARSIZE(acc));
00525         memcpy(rewrited, acc, VARSIZE(acc));
00526         pfree(acc);
00527     }
00528 
00529     PG_RETURN_POINTER(rewrited);
00530 }
00531 
00532 
00533 /*
00534  * Get Oid of current dictionary
00535  */
00536 static Oid
00537 GetCurrentDict(void)
00538 {
00539     if (current_dictionary_oid == InvalidOid)
00540         ereport(ERROR,
00541                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00542                  errmsg("no current dictionary"),
00543                  errhint("Execute SELECT set_curdict(...).")));
00544 
00545     return current_dictionary_oid;
00546 }
00547 
00548 /*
00549  * Get Oid of current parser
00550  *
00551  * Here, it seems reasonable to select the "default" parser if none has been
00552  * set.
00553  */
00554 static Oid
00555 GetCurrentParser(void)
00556 {
00557     if (current_parser_oid == InvalidOid)
00558         current_parser_oid = get_ts_parser_oid(stringToQualifiedNameList("pg_catalog.default"), false);
00559     return current_parser_oid;
00560 }