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