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