Only skip pages marked as clean in the visibility map, if the last 32
[PostgreSQL.git] / contrib / tsearch2 / tsearch2.c
blob4e127264eb9e7d6a32a5c9da5d5098ab74ec3418
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 int i;
373 /* Check call context */
374 if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */
375 elog(ERROR, "tsvector_update_trigger: not fired by trigger manager");
377 trigdata = (TriggerData *) fcinfo->context;
378 trigger = trigdata->tg_trigger;
380 if (trigger->tgnargs < 2)
381 elog(ERROR, "TSearch: format tsearch2(tsvector_field, text_field1,...)");
383 /* create space for configuration name */
384 tgargs = (char **) palloc((trigger->tgnargs + 1) * sizeof(char *));
385 tgargs[0] = trigger->tgargs[0];
386 for (i = 1; i < trigger->tgnargs; i++)
387 tgargs[i + 1] = trigger->tgargs[i];
389 tgargs[1] = pstrdup(GetConfigOptionByName("default_text_search_config",
390 NULL));
391 trigger->tgargs = tgargs;
392 trigger->tgnargs++;
394 return tsvector_update_trigger_byid(fcinfo);
398 Datum
399 tsa_rewrite_accum(PG_FUNCTION_ARGS)
401 TSQuery acc;
402 ArrayType *qa;
403 TSQuery q;
404 QTNode *qex = NULL,
405 *subs = NULL,
406 *acctree = NULL;
407 bool isfind = false;
408 Datum *elemsp;
409 int nelemsp;
410 MemoryContext aggcontext;
411 MemoryContext oldcontext;
413 if (fcinfo->context && IsA(fcinfo->context, AggState))
414 aggcontext = ((AggState *) fcinfo->context)->aggcontext;
415 else if (fcinfo->context && IsA(fcinfo->context, WindowAggState))
416 aggcontext = ((WindowAggState *) fcinfo->context)->wincontext;
417 else
419 elog(ERROR, "tsa_rewrite_accum called in non-aggregate context");
420 aggcontext = NULL; /* keep compiler quiet */
423 if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL)
425 acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ);
426 SET_VARSIZE(acc, HDRSIZETQ);
427 acc->size = 0;
429 else
430 acc = PG_GETARG_TSQUERY(0);
432 if (PG_ARGISNULL(1) || PG_GETARG_POINTER(1) == NULL)
433 PG_RETURN_TSQUERY(acc);
434 else
435 qa = PG_GETARG_ARRAYTYPE_P_COPY(1);
437 if (ARR_NDIM(qa) != 1)
438 elog(ERROR, "array must be one-dimensional, not %d dimensions",
439 ARR_NDIM(qa));
440 if (ArrayGetNItems(ARR_NDIM(qa), ARR_DIMS(qa)) != 3)
441 elog(ERROR, "array must have three elements");
442 if (ARR_ELEMTYPE(qa) != TSQUERYOID)
443 elog(ERROR, "array must contain tsquery elements");
445 deconstruct_array(qa, TSQUERYOID, -1, false, 'i', &elemsp, NULL, &nelemsp);
447 q = DatumGetTSQuery(elemsp[0]);
448 if (q->size == 0)
450 pfree(elemsp);
451 PG_RETURN_POINTER(acc);
454 if (!acc->size)
456 if (VARSIZE(acc) > HDRSIZETQ)
458 pfree(elemsp);
459 PG_RETURN_POINTER(acc);
461 else
462 acctree = QT2QTN(GETQUERY(q), GETOPERAND(q));
464 else
465 acctree = QT2QTN(GETQUERY(acc), GETOPERAND(acc));
467 QTNTernary(acctree);
468 QTNSort(acctree);
470 q = DatumGetTSQuery(elemsp[1]);
471 if (q->size == 0)
473 pfree(elemsp);
474 PG_RETURN_POINTER(acc);
476 qex = QT2QTN(GETQUERY(q), GETOPERAND(q));
477 QTNTernary(qex);
478 QTNSort(qex);
480 q = DatumGetTSQuery(elemsp[2]);
481 if (q->size)
482 subs = QT2QTN(GETQUERY(q), GETOPERAND(q));
484 acctree = findsubquery(acctree, qex, subs, &isfind);
486 if (isfind || !acc->size)
488 /* pfree( acc ); do not pfree(p), because nodeAgg.c will */
489 if (acctree)
491 QTNBinary(acctree);
492 oldcontext = MemoryContextSwitchTo(aggcontext);
493 acc = QTN2QT(acctree);
494 MemoryContextSwitchTo(oldcontext);
496 else
498 acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ);
499 SET_VARSIZE(acc, HDRSIZETQ);
500 acc->size = 0;
504 pfree(elemsp);
505 QTNFree(qex);
506 QTNFree(subs);
507 QTNFree(acctree);
509 PG_RETURN_TSQUERY(acc);
512 Datum
513 tsa_rewrite_finish(PG_FUNCTION_ARGS)
515 TSQuery acc = PG_GETARG_TSQUERY(0);
516 TSQuery rewrited;
518 if (acc == NULL || PG_ARGISNULL(0) || acc->size == 0)
520 rewrited = (TSQuery) palloc(HDRSIZETQ);
521 SET_VARSIZE(rewrited, HDRSIZETQ);
522 rewrited->size = 0;
524 else
526 rewrited = (TSQuery) palloc(VARSIZE(acc));
527 memcpy(rewrited, acc, VARSIZE(acc));
528 pfree(acc);
531 PG_RETURN_POINTER(rewrited);
536 * Get Oid of current dictionary
538 static Oid
539 GetCurrentDict(void)
541 if (current_dictionary_oid == InvalidOid)
542 ereport(ERROR,
543 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
544 errmsg("no current dictionary"),
545 errhint("Execute SELECT set_curdict(...).")));
547 return current_dictionary_oid;
551 * Get Oid of current parser
553 * Here, it seems reasonable to select the "default" parser if none has been
554 * set.
556 static Oid
557 GetCurrentParser(void)
559 if (current_parser_oid == InvalidOid)
560 current_parser_oid = TSParserGetPrsid(stringToQualifiedNameList("pg_catalog.default"), false);
561 return current_parser_oid;