Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * wparser.c
4 : * Standard interface to word parser
5 : *
6 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : *
8 : *
9 : * IDENTIFICATION
10 : * src/backend/tsearch/wparser.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #include "catalog/namespace.h"
17 : #include "commands/defrem.h"
18 : #include "funcapi.h"
19 : #include "tsearch/ts_cache.h"
20 : #include "tsearch/ts_utils.h"
21 : #include "utils/fmgrprotos.h"
22 : #include "utils/jsonfuncs.h"
23 : #include "utils/varlena.h"
24 :
25 : /******sql-level interface******/
26 :
27 : typedef struct
28 : {
29 : int cur;
30 : LexDescr *list;
31 : } TSTokenTypeStorage;
32 :
33 : /* state for ts_headline_json_* */
34 : typedef struct HeadlineJsonState
35 : {
36 : HeadlineParsedText *prs;
37 : TSConfigCacheEntry *cfg;
38 : TSParserCacheEntry *prsobj;
39 : TSQuery query;
40 : List *prsoptions;
41 : bool transformed;
42 : } HeadlineJsonState;
43 :
44 : static text *headline_json_value(void *_state, char *elem_value, int elem_len);
45 :
46 : static void
47 496 : tt_setup_firstcall(FuncCallContext *funcctx, FunctionCallInfo fcinfo,
48 : Oid prsid)
49 : {
50 : TupleDesc tupdesc;
51 : MemoryContext oldcontext;
52 : TSTokenTypeStorage *st;
53 496 : TSParserCacheEntry *prs = lookup_ts_parser_cache(prsid);
54 :
55 496 : if (!OidIsValid(prs->lextypeOid))
56 0 : elog(ERROR, "method lextype isn't defined for text search parser %u",
57 : prsid);
58 :
59 496 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
60 :
61 496 : st = (TSTokenTypeStorage *) palloc(sizeof(TSTokenTypeStorage));
62 496 : st->cur = 0;
63 : /* lextype takes one dummy argument */
64 496 : st->list = (LexDescr *) DatumGetPointer(OidFunctionCall1(prs->lextypeOid,
65 : (Datum) 0));
66 496 : funcctx->user_fctx = (void *) st;
67 :
68 496 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
69 0 : elog(ERROR, "return type must be a row type");
70 496 : funcctx->tuple_desc = tupdesc;
71 496 : funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
72 :
73 496 : MemoryContextSwitchTo(oldcontext);
74 496 : }
75 :
76 : static Datum
77 11904 : tt_process_call(FuncCallContext *funcctx)
78 : {
79 : TSTokenTypeStorage *st;
80 :
81 11904 : st = (TSTokenTypeStorage *) funcctx->user_fctx;
82 11904 : if (st->list && st->list[st->cur].lexid)
83 : {
84 : Datum result;
85 : char *values[3];
86 : char txtid[16];
87 : HeapTuple tuple;
88 :
89 11408 : sprintf(txtid, "%d", st->list[st->cur].lexid);
90 11408 : values[0] = txtid;
91 11408 : values[1] = st->list[st->cur].alias;
92 11408 : values[2] = st->list[st->cur].descr;
93 :
94 11408 : tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
95 11408 : result = HeapTupleGetDatum(tuple);
96 :
97 11408 : pfree(values[1]);
98 11408 : pfree(values[2]);
99 11408 : st->cur++;
100 11408 : return result;
101 : }
102 496 : return (Datum) 0;
103 : }
104 :
105 : Datum
106 11760 : ts_token_type_byid(PG_FUNCTION_ARGS)
107 : {
108 : FuncCallContext *funcctx;
109 : Datum result;
110 :
111 11760 : if (SRF_IS_FIRSTCALL())
112 : {
113 490 : funcctx = SRF_FIRSTCALL_INIT();
114 490 : tt_setup_firstcall(funcctx, fcinfo, PG_GETARG_OID(0));
115 : }
116 :
117 11760 : funcctx = SRF_PERCALL_SETUP();
118 :
119 11760 : if ((result = tt_process_call(funcctx)) != (Datum) 0)
120 11270 : SRF_RETURN_NEXT(funcctx, result);
121 490 : SRF_RETURN_DONE(funcctx);
122 : }
123 :
124 : Datum
125 144 : ts_token_type_byname(PG_FUNCTION_ARGS)
126 : {
127 : FuncCallContext *funcctx;
128 : Datum result;
129 :
130 144 : if (SRF_IS_FIRSTCALL())
131 : {
132 6 : text *prsname = PG_GETARG_TEXT_PP(0);
133 : Oid prsId;
134 :
135 6 : funcctx = SRF_FIRSTCALL_INIT();
136 6 : prsId = get_ts_parser_oid(textToQualifiedNameList(prsname), false);
137 6 : tt_setup_firstcall(funcctx, fcinfo, prsId);
138 : }
139 :
140 144 : funcctx = SRF_PERCALL_SETUP();
141 :
142 144 : if ((result = tt_process_call(funcctx)) != (Datum) 0)
143 138 : SRF_RETURN_NEXT(funcctx, result);
144 6 : SRF_RETURN_DONE(funcctx);
145 : }
146 :
147 : typedef struct
148 : {
149 : int type;
150 : char *lexeme;
151 : } LexemeEntry;
152 :
153 : typedef struct
154 : {
155 : int cur;
156 : int len;
157 : LexemeEntry *list;
158 : } PrsStorage;
159 :
160 :
161 : static void
162 44 : prs_setup_firstcall(FuncCallContext *funcctx, FunctionCallInfo fcinfo,
163 : Oid prsid, text *txt)
164 : {
165 : TupleDesc tupdesc;
166 : MemoryContext oldcontext;
167 : PrsStorage *st;
168 44 : TSParserCacheEntry *prs = lookup_ts_parser_cache(prsid);
169 44 : char *lex = NULL;
170 44 : int llen = 0,
171 44 : type = 0;
172 : void *prsdata;
173 :
174 44 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
175 :
176 44 : st = (PrsStorage *) palloc(sizeof(PrsStorage));
177 44 : st->cur = 0;
178 44 : st->len = 16;
179 44 : st->list = (LexemeEntry *) palloc(sizeof(LexemeEntry) * st->len);
180 :
181 44 : prsdata = (void *) DatumGetPointer(FunctionCall2(&prs->prsstart,
182 : PointerGetDatum(VARDATA_ANY(txt)),
183 : Int32GetDatum(VARSIZE_ANY_EXHDR(txt))));
184 :
185 1082 : while ((type = DatumGetInt32(FunctionCall3(&prs->prstoken,
186 : PointerGetDatum(prsdata),
187 : PointerGetDatum(&lex),
188 : PointerGetDatum(&llen)))) != 0)
189 : {
190 1038 : if (st->cur >= st->len)
191 : {
192 24 : st->len = 2 * st->len;
193 24 : st->list = (LexemeEntry *) repalloc(st->list, sizeof(LexemeEntry) * st->len);
194 : }
195 1038 : st->list[st->cur].lexeme = palloc(llen + 1);
196 1038 : memcpy(st->list[st->cur].lexeme, lex, llen);
197 1038 : st->list[st->cur].lexeme[llen] = '\0';
198 1038 : st->list[st->cur].type = type;
199 1038 : st->cur++;
200 : }
201 :
202 44 : FunctionCall1(&prs->prsend, PointerGetDatum(prsdata));
203 :
204 44 : st->len = st->cur;
205 44 : st->cur = 0;
206 :
207 44 : funcctx->user_fctx = (void *) st;
208 44 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
209 0 : elog(ERROR, "return type must be a row type");
210 44 : funcctx->tuple_desc = tupdesc;
211 44 : funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
212 44 : MemoryContextSwitchTo(oldcontext);
213 44 : }
214 :
215 : static Datum
216 1082 : prs_process_call(FuncCallContext *funcctx)
217 : {
218 : PrsStorage *st;
219 :
220 1082 : st = (PrsStorage *) funcctx->user_fctx;
221 1082 : if (st->cur < st->len)
222 : {
223 : Datum result;
224 : char *values[2];
225 : char tid[16];
226 : HeapTuple tuple;
227 :
228 1038 : values[0] = tid;
229 1038 : sprintf(tid, "%d", st->list[st->cur].type);
230 1038 : values[1] = st->list[st->cur].lexeme;
231 1038 : tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
232 1038 : result = HeapTupleGetDatum(tuple);
233 :
234 1038 : pfree(values[1]);
235 1038 : st->cur++;
236 1038 : return result;
237 : }
238 44 : return (Datum) 0;
239 : }
240 :
241 : Datum
242 210 : ts_parse_byid(PG_FUNCTION_ARGS)
243 : {
244 : FuncCallContext *funcctx;
245 : Datum result;
246 :
247 210 : if (SRF_IS_FIRSTCALL())
248 : {
249 36 : text *txt = PG_GETARG_TEXT_PP(1);
250 :
251 36 : funcctx = SRF_FIRSTCALL_INIT();
252 36 : prs_setup_firstcall(funcctx, fcinfo, PG_GETARG_OID(0), txt);
253 36 : PG_FREE_IF_COPY(txt, 1);
254 : }
255 :
256 210 : funcctx = SRF_PERCALL_SETUP();
257 :
258 210 : if ((result = prs_process_call(funcctx)) != (Datum) 0)
259 174 : SRF_RETURN_NEXT(funcctx, result);
260 36 : SRF_RETURN_DONE(funcctx);
261 : }
262 :
263 : Datum
264 872 : ts_parse_byname(PG_FUNCTION_ARGS)
265 : {
266 : FuncCallContext *funcctx;
267 : Datum result;
268 :
269 872 : if (SRF_IS_FIRSTCALL())
270 : {
271 8 : text *prsname = PG_GETARG_TEXT_PP(0);
272 8 : text *txt = PG_GETARG_TEXT_PP(1);
273 : Oid prsId;
274 :
275 8 : funcctx = SRF_FIRSTCALL_INIT();
276 8 : prsId = get_ts_parser_oid(textToQualifiedNameList(prsname), false);
277 8 : prs_setup_firstcall(funcctx, fcinfo, prsId, txt);
278 : }
279 :
280 872 : funcctx = SRF_PERCALL_SETUP();
281 :
282 872 : if ((result = prs_process_call(funcctx)) != (Datum) 0)
283 864 : SRF_RETURN_NEXT(funcctx, result);
284 8 : SRF_RETURN_DONE(funcctx);
285 : }
286 :
287 : Datum
288 146 : ts_headline_byid_opt(PG_FUNCTION_ARGS)
289 : {
290 146 : Oid tsconfig = PG_GETARG_OID(0);
291 146 : text *in = PG_GETARG_TEXT_PP(1);
292 146 : TSQuery query = PG_GETARG_TSQUERY(2);
293 146 : text *opt = (PG_NARGS() > 3 && PG_GETARG_POINTER(3)) ? PG_GETARG_TEXT_PP(3) : NULL;
294 : HeadlineParsedText prs;
295 : List *prsoptions;
296 : text *out;
297 : TSConfigCacheEntry *cfg;
298 : TSParserCacheEntry *prsobj;
299 :
300 146 : cfg = lookup_ts_config_cache(tsconfig);
301 146 : prsobj = lookup_ts_parser_cache(cfg->prsId);
302 :
303 146 : if (!OidIsValid(prsobj->headlineOid))
304 0 : ereport(ERROR,
305 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
306 : errmsg("text search parser does not support headline creation")));
307 :
308 146 : memset(&prs, 0, sizeof(HeadlineParsedText));
309 146 : prs.lenwords = 32;
310 146 : prs.words = (HeadlineWordEntry *) palloc(sizeof(HeadlineWordEntry) * prs.lenwords);
311 :
312 292 : hlparsetext(cfg->cfgId, &prs, query,
313 292 : VARDATA_ANY(in), VARSIZE_ANY_EXHDR(in));
314 :
315 146 : if (opt)
316 66 : prsoptions = deserialize_deflist(PointerGetDatum(opt));
317 : else
318 80 : prsoptions = NIL;
319 :
320 146 : FunctionCall3(&(prsobj->prsheadline),
321 : PointerGetDatum(&prs),
322 : PointerGetDatum(prsoptions),
323 : PointerGetDatum(query));
324 :
325 146 : out = generateHeadline(&prs);
326 :
327 146 : PG_FREE_IF_COPY(in, 1);
328 146 : PG_FREE_IF_COPY(query, 2);
329 146 : if (opt)
330 66 : PG_FREE_IF_COPY(opt, 3);
331 146 : pfree(prs.words);
332 146 : pfree(prs.startsel);
333 146 : pfree(prs.stopsel);
334 :
335 146 : PG_RETURN_POINTER(out);
336 : }
337 :
338 : Datum
339 80 : ts_headline_byid(PG_FUNCTION_ARGS)
340 : {
341 80 : PG_RETURN_DATUM(DirectFunctionCall3(ts_headline_byid_opt,
342 : PG_GETARG_DATUM(0),
343 : PG_GETARG_DATUM(1),
344 : PG_GETARG_DATUM(2)));
345 : }
346 :
347 : Datum
348 0 : ts_headline(PG_FUNCTION_ARGS)
349 : {
350 0 : PG_RETURN_DATUM(DirectFunctionCall3(ts_headline_byid_opt,
351 : ObjectIdGetDatum(getTSCurrentConfig(true)),
352 : PG_GETARG_DATUM(0),
353 : PG_GETARG_DATUM(1)));
354 : }
355 :
356 : Datum
357 0 : ts_headline_opt(PG_FUNCTION_ARGS)
358 : {
359 0 : PG_RETURN_DATUM(DirectFunctionCall4(ts_headline_byid_opt,
360 : ObjectIdGetDatum(getTSCurrentConfig(true)),
361 : PG_GETARG_DATUM(0),
362 : PG_GETARG_DATUM(1),
363 : PG_GETARG_DATUM(2)));
364 : }
365 :
366 : Datum
367 42 : ts_headline_jsonb_byid_opt(PG_FUNCTION_ARGS)
368 : {
369 42 : Oid tsconfig = PG_GETARG_OID(0);
370 42 : Jsonb *jb = PG_GETARG_JSONB_P(1);
371 42 : TSQuery query = PG_GETARG_TSQUERY(2);
372 42 : text *opt = (PG_NARGS() > 3 && PG_GETARG_POINTER(3)) ? PG_GETARG_TEXT_P(3) : NULL;
373 : Jsonb *out;
374 42 : JsonTransformStringValuesAction action = (JsonTransformStringValuesAction) headline_json_value;
375 : HeadlineParsedText prs;
376 42 : HeadlineJsonState *state = palloc0(sizeof(HeadlineJsonState));
377 :
378 42 : memset(&prs, 0, sizeof(HeadlineParsedText));
379 42 : prs.lenwords = 32;
380 42 : prs.words = (HeadlineWordEntry *) palloc(sizeof(HeadlineWordEntry) * prs.lenwords);
381 :
382 42 : state->prs = &prs;
383 42 : state->cfg = lookup_ts_config_cache(tsconfig);
384 42 : state->prsobj = lookup_ts_parser_cache(state->cfg->prsId);
385 42 : state->query = query;
386 42 : if (opt)
387 12 : state->prsoptions = deserialize_deflist(PointerGetDatum(opt));
388 : else
389 30 : state->prsoptions = NIL;
390 :
391 42 : if (!OidIsValid(state->prsobj->headlineOid))
392 0 : ereport(ERROR,
393 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
394 : errmsg("text search parser does not support headline creation")));
395 :
396 42 : out = transform_jsonb_string_values(jb, state, action);
397 :
398 42 : PG_FREE_IF_COPY(jb, 1);
399 42 : PG_FREE_IF_COPY(query, 2);
400 42 : if (opt)
401 12 : PG_FREE_IF_COPY(opt, 3);
402 :
403 42 : pfree(prs.words);
404 :
405 42 : if (state->transformed)
406 : {
407 24 : pfree(prs.startsel);
408 24 : pfree(prs.stopsel);
409 : }
410 :
411 42 : PG_RETURN_JSONB_P(out);
412 : }
413 :
414 : Datum
415 24 : ts_headline_jsonb(PG_FUNCTION_ARGS)
416 : {
417 24 : PG_RETURN_DATUM(DirectFunctionCall3(ts_headline_jsonb_byid_opt,
418 : ObjectIdGetDatum(getTSCurrentConfig(true)),
419 : PG_GETARG_DATUM(0),
420 : PG_GETARG_DATUM(1)));
421 : }
422 :
423 : Datum
424 6 : ts_headline_jsonb_byid(PG_FUNCTION_ARGS)
425 : {
426 6 : PG_RETURN_DATUM(DirectFunctionCall3(ts_headline_jsonb_byid_opt,
427 : PG_GETARG_DATUM(0),
428 : PG_GETARG_DATUM(1),
429 : PG_GETARG_DATUM(2)));
430 : }
431 :
432 : Datum
433 6 : ts_headline_jsonb_opt(PG_FUNCTION_ARGS)
434 : {
435 6 : PG_RETURN_DATUM(DirectFunctionCall4(ts_headline_jsonb_byid_opt,
436 : ObjectIdGetDatum(getTSCurrentConfig(true)),
437 : PG_GETARG_DATUM(0),
438 : PG_GETARG_DATUM(1),
439 : PG_GETARG_DATUM(2)));
440 : }
441 :
442 : Datum
443 42 : ts_headline_json_byid_opt(PG_FUNCTION_ARGS)
444 : {
445 42 : Oid tsconfig = PG_GETARG_OID(0);
446 42 : text *json = PG_GETARG_TEXT_P(1);
447 42 : TSQuery query = PG_GETARG_TSQUERY(2);
448 42 : text *opt = (PG_NARGS() > 3 && PG_GETARG_POINTER(3)) ? PG_GETARG_TEXT_P(3) : NULL;
449 : text *out;
450 42 : JsonTransformStringValuesAction action = (JsonTransformStringValuesAction) headline_json_value;
451 :
452 : HeadlineParsedText prs;
453 42 : HeadlineJsonState *state = palloc0(sizeof(HeadlineJsonState));
454 :
455 42 : memset(&prs, 0, sizeof(HeadlineParsedText));
456 42 : prs.lenwords = 32;
457 42 : prs.words = (HeadlineWordEntry *) palloc(sizeof(HeadlineWordEntry) * prs.lenwords);
458 :
459 42 : state->prs = &prs;
460 42 : state->cfg = lookup_ts_config_cache(tsconfig);
461 42 : state->prsobj = lookup_ts_parser_cache(state->cfg->prsId);
462 42 : state->query = query;
463 42 : if (opt)
464 12 : state->prsoptions = deserialize_deflist(PointerGetDatum(opt));
465 : else
466 30 : state->prsoptions = NIL;
467 :
468 42 : if (!OidIsValid(state->prsobj->headlineOid))
469 0 : ereport(ERROR,
470 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
471 : errmsg("text search parser does not support headline creation")));
472 :
473 42 : out = transform_json_string_values(json, state, action);
474 :
475 42 : PG_FREE_IF_COPY(json, 1);
476 42 : PG_FREE_IF_COPY(query, 2);
477 42 : if (opt)
478 12 : PG_FREE_IF_COPY(opt, 3);
479 42 : pfree(prs.words);
480 :
481 42 : if (state->transformed)
482 : {
483 24 : pfree(prs.startsel);
484 24 : pfree(prs.stopsel);
485 : }
486 :
487 42 : PG_RETURN_TEXT_P(out);
488 : }
489 :
490 : Datum
491 24 : ts_headline_json(PG_FUNCTION_ARGS)
492 : {
493 24 : PG_RETURN_DATUM(DirectFunctionCall3(ts_headline_json_byid_opt,
494 : ObjectIdGetDatum(getTSCurrentConfig(true)),
495 : PG_GETARG_DATUM(0),
496 : PG_GETARG_DATUM(1)));
497 : }
498 :
499 : Datum
500 6 : ts_headline_json_byid(PG_FUNCTION_ARGS)
501 : {
502 6 : PG_RETURN_DATUM(DirectFunctionCall3(ts_headline_json_byid_opt,
503 : PG_GETARG_DATUM(0),
504 : PG_GETARG_DATUM(1),
505 : PG_GETARG_DATUM(2)));
506 : }
507 :
508 : Datum
509 6 : ts_headline_json_opt(PG_FUNCTION_ARGS)
510 : {
511 6 : PG_RETURN_DATUM(DirectFunctionCall4(ts_headline_json_byid_opt,
512 : ObjectIdGetDatum(getTSCurrentConfig(true)),
513 : PG_GETARG_DATUM(0),
514 : PG_GETARG_DATUM(1),
515 : PG_GETARG_DATUM(2)));
516 : }
517 :
518 :
519 : /*
520 : * Return headline in text from, generated from a json(b) element
521 : */
522 : static text *
523 228 : headline_json_value(void *_state, char *elem_value, int elem_len)
524 : {
525 228 : HeadlineJsonState *state = (HeadlineJsonState *) _state;
526 :
527 228 : HeadlineParsedText *prs = state->prs;
528 228 : TSConfigCacheEntry *cfg = state->cfg;
529 228 : TSParserCacheEntry *prsobj = state->prsobj;
530 228 : TSQuery query = state->query;
531 228 : List *prsoptions = state->prsoptions;
532 :
533 228 : prs->curwords = 0;
534 228 : hlparsetext(cfg->cfgId, prs, query, elem_value, elem_len);
535 228 : FunctionCall3(&(prsobj->prsheadline),
536 : PointerGetDatum(prs),
537 : PointerGetDatum(prsoptions),
538 : PointerGetDatum(query));
539 :
540 228 : state->transformed = true;
541 228 : return generateHeadline(prs);
542 : }
|