Fix xslt_process() to ensure that it inserts a NULL terminator after the
[PostgreSQL.git] / contrib / tsearch2 / tsearch2.c
blobd786fde6d9e260e2863a5c1c41af6b8d3c019c38
1 /*-------------------------------------------------------------------------
3 * tsearch2.c
4 * Backwards-compatibility package for old contrib/tsearch2 API
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
9 * IDENTIFICATION
10 * $PostgreSQL$
12 *-------------------------------------------------------------------------
14 #include "postgres.h"
16 #include "catalog/namespace.h"
17 #include "catalog/pg_type.h"
18 #include "commands/trigger.h"
19 #include "fmgr.h"
20 #include "tsearch/ts_utils.h"
21 #include "utils/builtins.h"
22 #include "utils/guc.h"
23 #include "utils/syscache.h"
25 PG_MODULE_MAGIC;
27 static Oid current_dictionary_oid = InvalidOid;
28 static Oid current_parser_oid = InvalidOid;
30 /* insert given value at argument position 0 */
31 #define INSERT_ARGUMENT0(argument, isnull) \
32 do { \
33 int i; \
34 for (i = fcinfo->nargs; i > 0; i--) \
35 { \
36 fcinfo->arg[i] = fcinfo->arg[i-1]; \
37 fcinfo->argnull[i] = fcinfo->argnull[i-1]; \
38 } \
39 fcinfo->arg[0] = (argument); \
40 fcinfo->argnull[0] = (isnull); \
41 fcinfo->nargs++; \
42 } while (0)
44 #define TextGetObjectId(infunction, text) \
45 DatumGetObjectId(DirectFunctionCall1(infunction, \
46 CStringGetDatum(text_to_cstring(text))))
48 #define UNSUPPORTED_FUNCTION(name) \
49 Datum name(PG_FUNCTION_ARGS); \
50 Datum \
51 name(PG_FUNCTION_ARGS) \
52 { \
53 ereport(ERROR, \
54 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\
55 errmsg("function %s is no longer supported", \
56 format_procedure(fcinfo->flinfo->fn_oid)), \
57 errhint("Switch to new tsearch functionality."))); \
58 /* keep compiler quiet */ \
59 PG_RETURN_NULL(); \
60 } \
61 PG_FUNCTION_INFO_V1(name)
63 static Oid GetCurrentDict(void);
64 static Oid GetCurrentParser(void);
66 Datum tsa_lexize_byname(PG_FUNCTION_ARGS);
67 Datum tsa_lexize_bycurrent(PG_FUNCTION_ARGS);
68 Datum tsa_set_curdict(PG_FUNCTION_ARGS);
69 Datum tsa_set_curdict_byname(PG_FUNCTION_ARGS);
70 Datum tsa_token_type_current(PG_FUNCTION_ARGS);
71 Datum tsa_set_curprs(PG_FUNCTION_ARGS);
72 Datum tsa_set_curprs_byname(PG_FUNCTION_ARGS);
73 Datum tsa_parse_current(PG_FUNCTION_ARGS);
74 Datum tsa_set_curcfg(PG_FUNCTION_ARGS);
75 Datum tsa_set_curcfg_byname(PG_FUNCTION_ARGS);
76 Datum tsa_to_tsvector_name(PG_FUNCTION_ARGS);
77 Datum tsa_to_tsquery_name(PG_FUNCTION_ARGS);
78 Datum tsa_plainto_tsquery_name(PG_FUNCTION_ARGS);
79 Datum tsa_headline_byname(PG_FUNCTION_ARGS);
80 Datum tsa_ts_stat(PG_FUNCTION_ARGS);
81 Datum tsa_tsearch2(PG_FUNCTION_ARGS);
82 Datum tsa_rewrite_accum(PG_FUNCTION_ARGS);
83 Datum tsa_rewrite_finish(PG_FUNCTION_ARGS);
85 PG_FUNCTION_INFO_V1(tsa_lexize_byname);
86 PG_FUNCTION_INFO_V1(tsa_lexize_bycurrent);
87 PG_FUNCTION_INFO_V1(tsa_set_curdict);
88 PG_FUNCTION_INFO_V1(tsa_set_curdict_byname);
89 PG_FUNCTION_INFO_V1(tsa_token_type_current);
90 PG_FUNCTION_INFO_V1(tsa_set_curprs);
91 PG_FUNCTION_INFO_V1(tsa_set_curprs_byname);
92 PG_FUNCTION_INFO_V1(tsa_parse_current);
93 PG_FUNCTION_INFO_V1(tsa_set_curcfg);
94 PG_FUNCTION_INFO_V1(tsa_set_curcfg_byname);
95 PG_FUNCTION_INFO_V1(tsa_to_tsvector_name);
96 PG_FUNCTION_INFO_V1(tsa_to_tsquery_name);
97 PG_FUNCTION_INFO_V1(tsa_plainto_tsquery_name);
98 PG_FUNCTION_INFO_V1(tsa_headline_byname);
99 PG_FUNCTION_INFO_V1(tsa_ts_stat);
100 PG_FUNCTION_INFO_V1(tsa_tsearch2);
101 PG_FUNCTION_INFO_V1(tsa_rewrite_accum);
102 PG_FUNCTION_INFO_V1(tsa_rewrite_finish);
106 * List of unsupported functions
108 * The parser and dictionary functions are defined only so that the former
109 * contents of pg_ts_parser and pg_ts_dict can be loaded into the system,
110 * for ease of reference while creating the new tsearch configuration.
113 UNSUPPORTED_FUNCTION(tsa_dex_init);
114 UNSUPPORTED_FUNCTION(tsa_dex_lexize);
116 UNSUPPORTED_FUNCTION(tsa_snb_en_init);
117 UNSUPPORTED_FUNCTION(tsa_snb_lexize);
118 UNSUPPORTED_FUNCTION(tsa_snb_ru_init_koi8);
119 UNSUPPORTED_FUNCTION(tsa_snb_ru_init_utf8);
120 UNSUPPORTED_FUNCTION(tsa_snb_ru_init);
122 UNSUPPORTED_FUNCTION(tsa_spell_init);
123 UNSUPPORTED_FUNCTION(tsa_spell_lexize);
125 UNSUPPORTED_FUNCTION(tsa_syn_init);
126 UNSUPPORTED_FUNCTION(tsa_syn_lexize);
128 UNSUPPORTED_FUNCTION(tsa_thesaurus_init);
129 UNSUPPORTED_FUNCTION(tsa_thesaurus_lexize);
131 UNSUPPORTED_FUNCTION(tsa_prsd_start);
132 UNSUPPORTED_FUNCTION(tsa_prsd_getlexeme);
133 UNSUPPORTED_FUNCTION(tsa_prsd_end);
134 UNSUPPORTED_FUNCTION(tsa_prsd_lextype);
135 UNSUPPORTED_FUNCTION(tsa_prsd_headline);
137 UNSUPPORTED_FUNCTION(tsa_reset_tsearch);
138 UNSUPPORTED_FUNCTION(tsa_get_covers);
142 * list of redefined functions
145 /* lexize(text, text) */
146 Datum
147 tsa_lexize_byname(PG_FUNCTION_ARGS)
149 text *dictname = PG_GETARG_TEXT_PP(0);
150 Datum arg1 = PG_GETARG_DATUM(1);
152 return DirectFunctionCall2(ts_lexize,
153 ObjectIdGetDatum(TextGetObjectId(regdictionaryin, dictname)),
154 arg1);
157 /* lexize(text) */
158 Datum
159 tsa_lexize_bycurrent(PG_FUNCTION_ARGS)
161 Datum arg0 = PG_GETARG_DATUM(0);
162 Oid id = GetCurrentDict();
164 return DirectFunctionCall2(ts_lexize,
165 ObjectIdGetDatum(id),
166 arg0);
169 /* set_curdict(int) */
170 Datum
171 tsa_set_curdict(PG_FUNCTION_ARGS)
173 Oid dict_oid = PG_GETARG_OID(0);
175 if (!SearchSysCacheExists(TSDICTOID,
176 ObjectIdGetDatum(dict_oid),
177 0, 0, 0))
178 elog(ERROR, "cache lookup failed for text search dictionary %u",
179 dict_oid);
181 current_dictionary_oid = dict_oid;
183 PG_RETURN_VOID();
186 /* set_curdict(text) */
187 Datum
188 tsa_set_curdict_byname(PG_FUNCTION_ARGS)
190 text *name = PG_GETARG_TEXT_PP(0);
191 Oid dict_oid;
193 dict_oid = TSDictionaryGetDictid(stringToQualifiedNameList(text_to_cstring(name)), false);
195 current_dictionary_oid = dict_oid;
197 PG_RETURN_VOID();
200 /* token_type() */
201 Datum
202 tsa_token_type_current(PG_FUNCTION_ARGS)
204 INSERT_ARGUMENT0(ObjectIdGetDatum(GetCurrentParser()), false);
205 return ts_token_type_byid(fcinfo);
208 /* set_curprs(int) */
209 Datum
210 tsa_set_curprs(PG_FUNCTION_ARGS)
212 Oid parser_oid = PG_GETARG_OID(0);
214 if (!SearchSysCacheExists(TSPARSEROID,
215 ObjectIdGetDatum(parser_oid),
216 0, 0, 0))
217 elog(ERROR, "cache lookup failed for text search parser %u",
218 parser_oid);
220 current_parser_oid = parser_oid;
222 PG_RETURN_VOID();
225 /* set_curprs(text) */
226 Datum
227 tsa_set_curprs_byname(PG_FUNCTION_ARGS)
229 text *name = PG_GETARG_TEXT_PP(0);
230 Oid parser_oid;
232 parser_oid = TSParserGetPrsid(stringToQualifiedNameList(text_to_cstring(name)), false);
234 current_parser_oid = parser_oid;
236 PG_RETURN_VOID();
239 /* parse(text) */
240 Datum
241 tsa_parse_current(PG_FUNCTION_ARGS)
243 INSERT_ARGUMENT0(ObjectIdGetDatum(GetCurrentParser()), false);
244 return ts_parse_byid(fcinfo);
247 /* set_curcfg(int) */
248 Datum
249 tsa_set_curcfg(PG_FUNCTION_ARGS)
251 Oid arg0 = PG_GETARG_OID(0);
252 char *name;
254 name = DatumGetCString(DirectFunctionCall1(regconfigout,
255 ObjectIdGetDatum(arg0)));
257 set_config_option("default_text_search_config", name,
258 PGC_USERSET,
259 PGC_S_SESSION,
260 GUC_ACTION_SET,
261 true);
263 PG_RETURN_VOID();
266 /* set_curcfg(text) */
267 Datum
268 tsa_set_curcfg_byname(PG_FUNCTION_ARGS)
270 text *arg0 = PG_GETARG_TEXT_PP(0);
271 char *name;
273 name = text_to_cstring(arg0);
275 set_config_option("default_text_search_config", name,
276 PGC_USERSET,
277 PGC_S_SESSION,
278 GUC_ACTION_SET,
279 true);
281 PG_RETURN_VOID();
284 /* to_tsvector(text, text) */
285 Datum
286 tsa_to_tsvector_name(PG_FUNCTION_ARGS)
288 text *cfgname = PG_GETARG_TEXT_PP(0);
289 Datum arg1 = PG_GETARG_DATUM(1);
290 Oid config_oid;
292 config_oid = TextGetObjectId(regconfigin, cfgname);
294 return DirectFunctionCall2(to_tsvector_byid,
295 ObjectIdGetDatum(config_oid), arg1);
298 /* to_tsquery(text, text) */
299 Datum
300 tsa_to_tsquery_name(PG_FUNCTION_ARGS)
302 text *cfgname = PG_GETARG_TEXT_PP(0);
303 Datum arg1 = PG_GETARG_DATUM(1);
304 Oid config_oid;
306 config_oid = TextGetObjectId(regconfigin, cfgname);
308 return DirectFunctionCall2(to_tsquery_byid,
309 ObjectIdGetDatum(config_oid), arg1);
313 /* plainto_tsquery(text, text) */
314 Datum
315 tsa_plainto_tsquery_name(PG_FUNCTION_ARGS)
317 text *cfgname = PG_GETARG_TEXT_PP(0);
318 Datum arg1 = PG_GETARG_DATUM(1);
319 Oid config_oid;
321 config_oid = TextGetObjectId(regconfigin, cfgname);
323 return DirectFunctionCall2(plainto_tsquery_byid,
324 ObjectIdGetDatum(config_oid), arg1);
327 /* headline(text, text, tsquery [,text]) */
328 Datum
329 tsa_headline_byname(PG_FUNCTION_ARGS)
331 Datum arg0 = PG_GETARG_DATUM(0);
332 Datum arg1 = PG_GETARG_DATUM(1);
333 Datum arg2 = PG_GETARG_DATUM(2);
334 Datum result;
335 Oid config_oid;
337 /* first parameter has to be converted to oid */
338 config_oid = DatumGetObjectId(DirectFunctionCall1(regconfigin,
339 CStringGetDatum(TextDatumGetCString(arg0))));
341 if (PG_NARGS() == 3)
342 result = DirectFunctionCall3(ts_headline_byid,
343 ObjectIdGetDatum(config_oid), arg1, arg2);
344 else
346 Datum arg3 = PG_GETARG_DATUM(3);
348 result = DirectFunctionCall4(ts_headline_byid_opt,
349 ObjectIdGetDatum(config_oid),
350 arg1, arg2, arg3);
353 return result;
357 * tsearch2 version of update trigger
359 * We pass this on to the core trigger after inserting the default text
360 * search configuration name as the second argument. Note that this isn't
361 * a complete implementation of the original functionality; tsearch2 allowed
362 * transformation function names to be included in the list. However, that
363 * is deliberately removed as being a security risk.
365 Datum
366 tsa_tsearch2(PG_FUNCTION_ARGS)
368 TriggerData *trigdata;
369 Trigger *trigger;
370 char **tgargs,
371 **tgargs_old;
372 int i;
373 Datum res;
375 /* Check call context */
376 if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */
377 elog(ERROR, "tsvector_update_trigger: not fired by trigger manager");
379 trigdata = (TriggerData *) fcinfo->context;
380 trigger = trigdata->tg_trigger;
382 if (trigger->tgnargs < 2)
383 elog(ERROR, "TSearch: format tsearch2(tsvector_field, text_field1,...)");
385 /* create space for configuration name */
386 tgargs = (char **) palloc((trigger->tgnargs + 1) * sizeof(char *));
387 tgargs[0] = trigger->tgargs[0];
388 for (i = 1; i < trigger->tgnargs; i++)
389 tgargs[i + 1] = trigger->tgargs[i];
391 tgargs[1] = pstrdup(GetConfigOptionByName("default_text_search_config",
392 NULL));
393 tgargs_old = trigger->tgargs;
394 trigger->tgargs = tgargs;
395 trigger->tgnargs++;
397 res = tsvector_update_trigger_byid(fcinfo);
399 /* restore old trigger data */
400 trigger->tgargs = tgargs_old;
401 trigger->tgnargs--;
403 pfree(tgargs[1]);
404 pfree(tgargs);
406 return res;
410 Datum
411 tsa_rewrite_accum(PG_FUNCTION_ARGS)
413 TSQuery acc;
414 ArrayType *qa;
415 TSQuery q;
416 QTNode *qex = NULL,
417 *subs = NULL,
418 *acctree = NULL;
419 bool isfind = false;
420 Datum *elemsp;
421 int nelemsp;
422 MemoryContext aggcontext;
423 MemoryContext oldcontext;
425 if (fcinfo->context && IsA(fcinfo->context, AggState))
426 aggcontext = ((AggState *) fcinfo->context)->aggcontext;
427 else if (fcinfo->context && IsA(fcinfo->context, WindowAggState))
428 aggcontext = ((WindowAggState *) fcinfo->context)->wincontext;
429 else
431 elog(ERROR, "tsa_rewrite_accum called in non-aggregate context");
432 aggcontext = NULL; /* keep compiler quiet */
435 if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL)
437 acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ);
438 SET_VARSIZE(acc, HDRSIZETQ);
439 acc->size = 0;
441 else
442 acc = PG_GETARG_TSQUERY(0);
444 if (PG_ARGISNULL(1) || PG_GETARG_POINTER(1) == NULL)
445 PG_RETURN_TSQUERY(acc);
446 else
447 qa = PG_GETARG_ARRAYTYPE_P_COPY(1);
449 if (ARR_NDIM(qa) != 1)
450 elog(ERROR, "array must be one-dimensional, not %d dimensions",
451 ARR_NDIM(qa));
452 if (ArrayGetNItems(ARR_NDIM(qa), ARR_DIMS(qa)) != 3)
453 elog(ERROR, "array must have three elements");
454 if (ARR_ELEMTYPE(qa) != TSQUERYOID)
455 elog(ERROR, "array must contain tsquery elements");
457 deconstruct_array(qa, TSQUERYOID, -1, false, 'i', &elemsp, NULL, &nelemsp);
459 q = DatumGetTSQuery(elemsp[0]);
460 if (q->size == 0)
462 pfree(elemsp);
463 PG_RETURN_POINTER(acc);
466 if (!acc->size)
468 if (VARSIZE(acc) > HDRSIZETQ)
470 pfree(elemsp);
471 PG_RETURN_POINTER(acc);
473 else
474 acctree = QT2QTN(GETQUERY(q), GETOPERAND(q));
476 else
477 acctree = QT2QTN(GETQUERY(acc), GETOPERAND(acc));
479 QTNTernary(acctree);
480 QTNSort(acctree);
482 q = DatumGetTSQuery(elemsp[1]);
483 if (q->size == 0)
485 pfree(elemsp);
486 PG_RETURN_POINTER(acc);
488 qex = QT2QTN(GETQUERY(q), GETOPERAND(q));
489 QTNTernary(qex);
490 QTNSort(qex);
492 q = DatumGetTSQuery(elemsp[2]);
493 if (q->size)
494 subs = QT2QTN(GETQUERY(q), GETOPERAND(q));
496 acctree = findsubquery(acctree, qex, subs, &isfind);
498 if (isfind || !acc->size)
500 /* pfree( acc ); do not pfree(p), because nodeAgg.c will */
501 if (acctree)
503 QTNBinary(acctree);
504 oldcontext = MemoryContextSwitchTo(aggcontext);
505 acc = QTN2QT(acctree);
506 MemoryContextSwitchTo(oldcontext);
508 else
510 acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ);
511 SET_VARSIZE(acc, HDRSIZETQ);
512 acc->size = 0;
516 pfree(elemsp);
517 QTNFree(qex);
518 QTNFree(subs);
519 QTNFree(acctree);
521 PG_RETURN_TSQUERY(acc);
524 Datum
525 tsa_rewrite_finish(PG_FUNCTION_ARGS)
527 TSQuery acc = PG_GETARG_TSQUERY(0);
528 TSQuery rewrited;
530 if (acc == NULL || PG_ARGISNULL(0) || acc->size == 0)
532 rewrited = (TSQuery) palloc(HDRSIZETQ);
533 SET_VARSIZE(rewrited, HDRSIZETQ);
534 rewrited->size = 0;
536 else
538 rewrited = (TSQuery) palloc(VARSIZE(acc));
539 memcpy(rewrited, acc, VARSIZE(acc));
540 pfree(acc);
543 PG_RETURN_POINTER(rewrited);
548 * Get Oid of current dictionary
550 static Oid
551 GetCurrentDict(void)
553 if (current_dictionary_oid == InvalidOid)
554 ereport(ERROR,
555 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
556 errmsg("no current dictionary"),
557 errhint("Execute SELECT set_curdict(...).")));
559 return current_dictionary_oid;
563 * Get Oid of current parser
565 * Here, it seems reasonable to select the "default" parser if none has been
566 * set.
568 static Oid
569 GetCurrentParser(void)
571 if (current_parser_oid == InvalidOid)
572 current_parser_oid = TSParserGetPrsid(stringToQualifiedNameList("pg_catalog.default"), false);
573 return current_parser_oid;