Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * to_tsany.c
4 : * to_ts* function definitions
5 : *
6 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7 : *
8 : *
9 : * IDENTIFICATION
10 : * src/backend/tsearch/to_tsany.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #include "common/jsonapi.h"
17 : #include "tsearch/ts_cache.h"
18 : #include "tsearch/ts_utils.h"
19 : #include "utils/builtins.h"
20 : #include "utils/jsonfuncs.h"
21 :
22 :
23 : /*
24 : * Opaque data structure, which is passed by parse_tsquery() to pushval_morph().
25 : */
26 : typedef struct MorphOpaque
27 : {
28 : Oid cfg_id;
29 :
30 : /*
31 : * Single tsquery morph could be parsed into multiple words. When these
32 : * words reside in adjacent positions, they are connected using this
33 : * operator. Usually, that is OP_PHRASE, which requires word positions of
34 : * a complex morph to exactly match the tsvector.
35 : */
36 : int qoperator;
37 : } MorphOpaque;
38 :
39 : typedef struct TSVectorBuildState
40 : {
41 : ParsedText *prs;
42 : Oid cfgId;
43 : } TSVectorBuildState;
44 :
45 : static void add_to_tsvector(void *_state, char *elem_value, int elem_len);
46 :
47 :
48 : Datum
5710 tgl 49 UBC 0 : get_current_ts_config(PG_FUNCTION_ARGS)
50 : {
51 0 : PG_RETURN_OID(getTSCurrentConfig(true));
52 : }
53 :
54 : /*
55 : * to_tsvector
56 : */
57 : static int
5710 tgl 58 CBC 6229 : compareWORD(const void *a, const void *b)
59 : {
60 : int res;
61 :
1165 alvherre 62 6229 : res = tsCompareString(((const ParsedWord *) a)->word, ((const ParsedWord *) a)->len,
2118 tgl 63 6229 : ((const ParsedWord *) b)->word, ((const ParsedWord *) b)->len,
64 : false);
65 :
5441 66 6229 : if (res == 0)
67 : {
4228 peter_e 68 525 : if (((const ParsedWord *) a)->pos.pos == ((const ParsedWord *) b)->pos.pos)
5441 tgl 69 12 : return 0;
70 :
4228 peter_e 71 513 : res = (((const ParsedWord *) a)->pos.pos > ((const ParsedWord *) b)->pos.pos) ? 1 : -1;
72 : }
73 :
5441 tgl 74 6217 : return res;
75 : }
76 :
77 : static int
3940 peter_e 78 338 : uniqueWORD(ParsedWord *a, int32 l)
79 : {
80 : ParsedWord *ptr,
81 : *res;
82 : int tmppos;
83 :
5710 tgl 84 338 : if (l == 1)
85 : {
86 13 : tmppos = LIMITPOS(a->pos.pos);
87 13 : a->alen = 2;
88 13 : a->pos.apos = (uint16 *) palloc(sizeof(uint16) * a->alen);
89 13 : a->pos.apos[0] = 1;
90 13 : a->pos.apos[1] = tmppos;
91 13 : return l;
92 : }
93 :
94 325 : res = a;
95 325 : ptr = a + 1;
96 :
97 : /*
98 : * Sort words with its positions
99 : */
61 peter 100 GNC 325 : qsort(a, l, sizeof(ParsedWord), compareWORD);
101 :
102 : /*
103 : * Initialize first word and its first position
104 : */
5710 tgl 105 CBC 325 : tmppos = LIMITPOS(a->pos.pos);
106 325 : a->alen = 2;
107 325 : a->pos.apos = (uint16 *) palloc(sizeof(uint16) * a->alen);
108 325 : a->pos.apos[0] = 1;
109 325 : a->pos.apos[1] = tmppos;
110 :
111 : /*
112 : * Summarize position information for each word
113 : */
114 2096 : while (ptr - a < l)
115 : {
116 1771 : if (!(ptr->len == res->len &&
117 1015 : strncmp(ptr->word, res->word, res->len) == 0))
118 : {
119 : /*
120 : * Got a new word, so put it in result
121 : */
122 1444 : res++;
123 1444 : res->len = ptr->len;
124 1444 : res->word = ptr->word;
125 1444 : tmppos = LIMITPOS(ptr->pos.pos);
126 1444 : res->alen = 2;
127 1444 : res->pos.apos = (uint16 *) palloc(sizeof(uint16) * res->alen);
128 1444 : res->pos.apos[0] = 1;
129 1444 : res->pos.apos[1] = tmppos;
130 : }
131 : else
132 : {
133 : /*
134 : * The word already exists, so adjust position information. But
135 : * before we should check size of position's array, max allowed
136 : * value for position and uniqueness of position
137 : */
138 327 : pfree(ptr->word);
5674 teodor 139 327 : if (res->pos.apos[0] < MAXNUMPOS - 1 && res->pos.apos[res->pos.apos[0]] != MAXENTRYPOS - 1 &&
5624 bruce 140 327 : res->pos.apos[res->pos.apos[0]] != LIMITPOS(ptr->pos.pos))
141 : {
5710 tgl 142 315 : if (res->pos.apos[0] + 1 >= res->alen)
143 : {
144 252 : res->alen *= 2;
145 252 : res->pos.apos = (uint16 *) repalloc(res->pos.apos, sizeof(uint16) * res->alen);
146 : }
147 315 : if (res->pos.apos[0] == 0 || res->pos.apos[res->pos.apos[0]] != LIMITPOS(ptr->pos.pos))
148 : {
149 315 : res->pos.apos[res->pos.apos[0] + 1] = LIMITPOS(ptr->pos.pos);
150 315 : res->pos.apos[0]++;
151 : }
152 : }
153 : }
154 1771 : ptr++;
155 : }
156 :
157 325 : return res + 1 - a;
158 : }
159 :
160 : /*
161 : * make value of tsvector, given parsed text
162 : *
163 : * Note: frees prs->words and subsidiary data.
164 : */
165 : TSVector
5624 bruce 166 398 : make_tsvector(ParsedText *prs)
167 : {
168 : int i,
169 : j,
5710 tgl 170 398 : lenstr = 0,
171 : totallen;
172 : TSVector in;
173 : WordEntry *ptr;
174 : char *str;
175 : int stroff;
176 :
177 : /* Merge duplicate words */
2091 178 398 : if (prs->curwords > 0)
179 338 : prs->curwords = uniqueWORD(prs->words, prs->curwords);
180 :
181 : /* Determine space needed */
5710 182 2180 : for (i = 0; i < prs->curwords; i++)
183 : {
5647 184 1782 : lenstr += prs->words[i].len;
5710 185 1782 : if (prs->words[i].alen)
186 : {
5647 187 1782 : lenstr = SHORTALIGN(lenstr);
5710 188 1782 : lenstr += sizeof(uint16) + prs->words[i].pos.apos[0] * sizeof(WordEntryPos);
189 : }
190 : }
191 :
5647 192 398 : if (lenstr > MAXSTRPOS)
5647 tgl 193 UBC 0 : ereport(ERROR,
194 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
195 : errmsg("string is too long for tsvector (%d bytes, max %d bytes)", lenstr, MAXSTRPOS)));
196 :
5710 tgl 197 CBC 398 : totallen = CALCDATASIZE(prs->curwords, lenstr);
198 398 : in = (TSVector) palloc0(totallen);
199 398 : SET_VARSIZE(in, totallen);
200 398 : in->size = prs->curwords;
201 :
202 398 : ptr = ARRPTR(in);
5647 203 398 : str = STRPTR(in);
204 398 : stroff = 0;
5710 205 2180 : for (i = 0; i < prs->curwords; i++)
206 : {
207 1782 : ptr->len = prs->words[i].len;
5647 208 1782 : ptr->pos = stroff;
209 1782 : memcpy(str + stroff, prs->words[i].word, prs->words[i].len);
210 1782 : stroff += prs->words[i].len;
5710 211 1782 : pfree(prs->words[i].word);
212 1782 : if (prs->words[i].alen)
213 : {
5624 bruce 214 1782 : int k = prs->words[i].pos.apos[0];
215 : WordEntryPos *wptr;
216 :
5647 tgl 217 1782 : if (k > 0xFFFF)
5647 tgl 218 UBC 0 : elog(ERROR, "positions array too long");
219 :
5710 tgl 220 CBC 1782 : ptr->haspos = 1;
5647 221 1782 : stroff = SHORTALIGN(stroff);
222 1782 : *(uint16 *) (str + stroff) = (uint16) k;
5710 223 1782 : wptr = POSDATAPTR(in, ptr);
5647 224 3879 : for (j = 0; j < k; j++)
225 : {
5710 226 2097 : WEP_SETWEIGHT(wptr[j], 0);
227 2097 : WEP_SETPOS(wptr[j], prs->words[i].pos.apos[j + 1]);
228 : }
5647 229 1782 : stroff += sizeof(uint16) + k * sizeof(WordEntryPos);
5710 230 1782 : pfree(prs->words[i].pos.apos);
231 : }
232 : else
5710 tgl 233 UBC 0 : ptr->haspos = 0;
5710 tgl 234 CBC 1782 : ptr++;
235 : }
236 :
2091 237 398 : if (prs->words)
238 356 : pfree(prs->words);
239 :
5710 240 398 : return in;
241 : }
242 :
243 : Datum
244 239 : to_tsvector_byid(PG_FUNCTION_ARGS)
245 : {
246 239 : Oid cfgId = PG_GETARG_OID(0);
2219 noah 247 239 : text *in = PG_GETARG_TEXT_PP(1);
248 : ParsedText prs;
249 : TSVector out;
250 :
251 239 : prs.lenwords = VARSIZE_ANY_EXHDR(in) / 6; /* just estimation of word's
252 : * number */
2091 tgl 253 239 : if (prs.lenwords < 2)
5710 254 169 : prs.lenwords = 2;
255 239 : prs.curwords = 0;
256 239 : prs.pos = 0;
257 239 : prs.words = (ParsedWord *) palloc(sizeof(ParsedWord) * prs.lenwords);
258 :
2219 noah 259 239 : parsetext(cfgId, &prs, VARDATA_ANY(in), VARSIZE_ANY_EXHDR(in));
260 :
5710 tgl 261 239 : PG_FREE_IF_COPY(in, 1);
262 :
2091 263 239 : out = make_tsvector(&prs);
264 :
265 239 : PG_RETURN_TSVECTOR(out);
266 : }
267 :
268 : Datum
5710 269 27 : to_tsvector(PG_FUNCTION_ARGS)
270 : {
2219 noah 271 27 : text *in = PG_GETARG_TEXT_PP(0);
272 : Oid cfgId;
273 :
5710 tgl 274 27 : cfgId = getTSCurrentConfig(true);
275 27 : PG_RETURN_DATUM(DirectFunctionCall2(to_tsvector_byid,
276 : ObjectIdGetDatum(cfgId),
277 : PointerGetDatum(in)));
278 : }
279 :
280 : /*
281 : * Worker function for jsonb(_string)_to_tsvector(_byid)
282 : */
283 : static TSVector
1828 teodor 284 75 : jsonb_to_tsvector_worker(Oid cfgId, Jsonb *jb, uint32 flags)
285 : {
286 : TSVectorBuildState state;
287 : ParsedText prs;
288 :
2091 tgl 289 75 : prs.words = NULL;
290 75 : prs.curwords = 0;
291 75 : state.prs = &prs;
2200 andrew 292 75 : state.cfgId = cfgId;
293 :
1828 teodor 294 75 : iterate_jsonb_values(jb, flags, &state, add_to_tsvector);
295 :
296 75 : return make_tsvector(&prs);
297 : }
298 :
299 : Datum
300 9 : jsonb_string_to_tsvector_byid(PG_FUNCTION_ARGS)
301 : {
302 9 : Oid cfgId = PG_GETARG_OID(0);
303 9 : Jsonb *jb = PG_GETARG_JSONB_P(1);
304 : TSVector result;
305 :
306 9 : result = jsonb_to_tsvector_worker(cfgId, jb, jtiString);
307 9 : PG_FREE_IF_COPY(jb, 1);
308 :
2091 tgl 309 9 : PG_RETURN_TSVECTOR(result);
310 : }
311 :
312 : Datum
1828 teodor 313 15 : jsonb_string_to_tsvector(PG_FUNCTION_ARGS)
314 : {
2029 tgl 315 15 : Jsonb *jb = PG_GETARG_JSONB_P(0);
316 : Oid cfgId;
317 : TSVector result;
318 :
2200 andrew 319 15 : cfgId = getTSCurrentConfig(true);
1828 teodor 320 15 : result = jsonb_to_tsvector_worker(cfgId, jb, jtiString);
321 15 : PG_FREE_IF_COPY(jb, 0);
322 :
323 15 : PG_RETURN_TSVECTOR(result);
324 : }
325 :
326 : Datum
327 51 : jsonb_to_tsvector_byid(PG_FUNCTION_ARGS)
328 : {
2153 bruce 329 51 : Oid cfgId = PG_GETARG_OID(0);
1828 teodor 330 51 : Jsonb *jb = PG_GETARG_JSONB_P(1);
331 51 : Jsonb *jbFlags = PG_GETARG_JSONB_P(2);
332 : TSVector result;
333 51 : uint32 flags = parse_jsonb_index_flags(jbFlags);
334 :
335 39 : result = jsonb_to_tsvector_worker(cfgId, jb, flags);
336 39 : PG_FREE_IF_COPY(jb, 1);
337 39 : PG_FREE_IF_COPY(jbFlags, 2);
338 :
339 39 : PG_RETURN_TSVECTOR(result);
340 : }
341 :
342 : Datum
343 12 : jsonb_to_tsvector(PG_FUNCTION_ARGS)
344 : {
345 12 : Jsonb *jb = PG_GETARG_JSONB_P(0);
346 12 : Jsonb *jbFlags = PG_GETARG_JSONB_P(1);
347 : Oid cfgId;
348 : TSVector result;
349 12 : uint32 flags = parse_jsonb_index_flags(jbFlags);
350 :
351 12 : cfgId = getTSCurrentConfig(true);
352 12 : result = jsonb_to_tsvector_worker(cfgId, jb, flags);
353 12 : PG_FREE_IF_COPY(jb, 0);
354 12 : PG_FREE_IF_COPY(jbFlags, 1);
355 :
356 12 : PG_RETURN_TSVECTOR(result);
357 : }
358 :
359 : /*
360 : * Worker function for json(_string)_to_tsvector(_byid)
361 : */
362 : static TSVector
363 75 : json_to_tsvector_worker(Oid cfgId, text *json, uint32 flags)
364 : {
365 : TSVectorBuildState state;
366 : ParsedText prs;
367 :
2091 tgl 368 75 : prs.words = NULL;
369 75 : prs.curwords = 0;
370 75 : state.prs = &prs;
2200 andrew 371 75 : state.cfgId = cfgId;
372 :
1828 teodor 373 75 : iterate_json_values(json, flags, &state, add_to_tsvector);
374 :
375 75 : return make_tsvector(&prs);
376 : }
377 :
378 : Datum
379 9 : json_string_to_tsvector_byid(PG_FUNCTION_ARGS)
380 : {
381 9 : Oid cfgId = PG_GETARG_OID(0);
382 9 : text *json = PG_GETARG_TEXT_P(1);
383 : TSVector result;
384 :
385 9 : result = json_to_tsvector_worker(cfgId, json, jtiString);
2091 tgl 386 9 : PG_FREE_IF_COPY(json, 1);
387 :
1828 teodor 388 9 : PG_RETURN_TSVECTOR(result);
389 : }
390 :
391 : Datum
392 15 : json_string_to_tsvector(PG_FUNCTION_ARGS)
393 : {
394 15 : text *json = PG_GETARG_TEXT_P(0);
395 : Oid cfgId;
396 : TSVector result;
397 :
398 15 : cfgId = getTSCurrentConfig(true);
399 15 : result = json_to_tsvector_worker(cfgId, json, jtiString);
400 15 : PG_FREE_IF_COPY(json, 0);
401 :
402 15 : PG_RETURN_TSVECTOR(result);
403 : }
404 :
405 : Datum
406 51 : json_to_tsvector_byid(PG_FUNCTION_ARGS)
407 : {
408 51 : Oid cfgId = PG_GETARG_OID(0);
409 51 : text *json = PG_GETARG_TEXT_P(1);
410 51 : Jsonb *jbFlags = PG_GETARG_JSONB_P(2);
411 : TSVector result;
412 51 : uint32 flags = parse_jsonb_index_flags(jbFlags);
413 :
414 39 : result = json_to_tsvector_worker(cfgId, json, flags);
415 39 : PG_FREE_IF_COPY(json, 1);
416 39 : PG_FREE_IF_COPY(jbFlags, 2);
417 :
2091 tgl 418 39 : PG_RETURN_TSVECTOR(result);
419 : }
420 :
421 : Datum
2200 andrew 422 12 : json_to_tsvector(PG_FUNCTION_ARGS)
423 : {
2153 bruce 424 12 : text *json = PG_GETARG_TEXT_P(0);
1828 teodor 425 12 : Jsonb *jbFlags = PG_GETARG_JSONB_P(1);
426 : Oid cfgId;
427 : TSVector result;
428 12 : uint32 flags = parse_jsonb_index_flags(jbFlags);
429 :
2200 andrew 430 12 : cfgId = getTSCurrentConfig(true);
1828 teodor 431 12 : result = json_to_tsvector_worker(cfgId, json, flags);
432 12 : PG_FREE_IF_COPY(json, 0);
433 12 : PG_FREE_IF_COPY(jbFlags, 1);
434 :
435 12 : PG_RETURN_TSVECTOR(result);
436 : }
437 :
438 : /*
439 : * Parse lexemes in an element of a json(b) value, add to TSVectorBuildState.
440 : */
441 : static void
2200 andrew 442 372 : add_to_tsvector(void *_state, char *elem_value, int elem_len)
443 : {
444 372 : TSVectorBuildState *state = (TSVectorBuildState *) _state;
2153 bruce 445 372 : ParsedText *prs = state->prs;
446 : int32 prevwords;
447 :
2091 tgl 448 372 : if (prs->words == NULL)
449 : {
450 : /*
451 : * First time through: initialize words array to a reasonable size.
452 : * (parsetext() will realloc it bigger as needed.)
453 : */
1828 teodor 454 108 : prs->lenwords = 16;
2091 tgl 455 108 : prs->words = (ParsedWord *) palloc(sizeof(ParsedWord) * prs->lenwords);
456 108 : prs->curwords = 0;
457 108 : prs->pos = 0;
458 : }
459 :
460 372 : prevwords = prs->curwords;
461 :
2200 andrew 462 372 : parsetext(state->cfgId, prs, elem_value, elem_len);
463 :
464 : /*
465 : * If we extracted any words from this JSON element, advance pos to create
466 : * an artificial break between elements. This is because we don't want
467 : * phrase searches to think that the last word in this element is adjacent
468 : * to the first word in the next one.
469 : */
2091 tgl 470 372 : if (prs->curwords > prevwords)
471 336 : prs->pos += 1;
2200 andrew 472 372 : }
473 :
474 :
475 : /*
476 : * to_tsquery
477 : */
478 :
479 :
480 : /*
481 : * This function is used for morph parsing.
482 : *
483 : * The value is passed to parsetext which will call the right dictionary to
484 : * lexize the word. If it turns out to be a stopword, we push a QI_VALSTOP
485 : * to the stack.
486 : *
487 : * All words belonging to the same variant are pushed as an ANDed list,
488 : * and different variants are ORed together.
489 : */
490 : static void
3940 peter_e 491 1544 : pushval_morph(Datum opaque, TSQueryParserState state, char *strval, int lenval, int16 weight, bool prefix)
492 : {
2495 rhaas 493 1544 : int32 count = 0;
494 : ParsedText prs;
495 : uint32 variant,
496 1544 : pos = 0,
497 1544 : cntvar = 0,
498 1544 : cntpos = 0,
499 1544 : cnt = 0;
500 1544 : MorphOpaque *data = (MorphOpaque *) DatumGetPointer(opaque);
501 :
5710 tgl 502 1544 : prs.lenwords = 4;
503 1544 : prs.curwords = 0;
504 1544 : prs.pos = 0;
505 1544 : prs.words = (ParsedWord *) palloc(sizeof(ParsedWord) * prs.lenwords);
506 :
2558 teodor 507 1544 : parsetext(data->cfg_id, &prs, strval, lenval);
508 :
5710 tgl 509 1544 : if (prs.curwords > 0)
510 : {
511 2674 : while (count < prs.curwords)
512 : {
513 : /*
514 : * Were any stop words removed? If so, fill empty positions with
515 : * placeholders linked by an appropriate operator.
516 : */
2558 teodor 517 1439 : if (pos > 0 && pos + 1 < prs.words[count].pos.pos)
518 : {
519 39 : while (pos + 1 < prs.words[count].pos.pos)
520 : {
521 : /* put placeholders for each missing stop word */
522 24 : pushStop(state);
523 24 : if (cntpos)
524 24 : pushOperator(state, data->qoperator, 1);
525 24 : cntpos++;
526 24 : pos++;
527 : }
528 : }
529 :
530 : /* save current word's position */
2532 rhaas 531 1439 : pos = prs.words[count].pos.pos;
532 :
533 : /* Go through all variants obtained from this token */
5710 tgl 534 1439 : cntvar = 0;
535 2914 : while (count < prs.curwords && pos == prs.words[count].pos.pos)
536 : {
537 1475 : variant = prs.words[count].nvariant;
538 :
539 : /* Push all words belonging to the same variant */
540 1475 : cnt = 0;
2558 teodor 541 1475 : while (count < prs.curwords &&
542 3022 : pos == prs.words[count].pos.pos &&
543 1583 : variant == prs.words[count].nvariant)
544 : {
545 1547 : pushValue(state,
546 1547 : prs.words[count].word,
547 1547 : prs.words[count].len,
548 : weight,
2118 tgl 549 1547 : ((prs.words[count].flags & TSL_PREFIX) || prefix));
5710 550 1547 : pfree(prs.words[count].word);
551 1547 : if (cnt)
2558 teodor 552 72 : pushOperator(state, OP_AND, 0);
5710 tgl 553 1547 : cnt++;
554 1547 : count++;
555 : }
556 :
557 1475 : if (cntvar)
2558 teodor 558 36 : pushOperator(state, OP_OR, 0);
5710 tgl 559 1475 : cntvar++;
560 : }
561 :
562 1439 : if (cntpos)
563 : {
564 : /* distance may be useful */
2532 rhaas 565 204 : pushOperator(state, data->qoperator, 1);
566 : }
567 :
5710 tgl 568 1439 : cntpos++;
569 : }
570 :
571 1235 : pfree(prs.words);
572 : }
573 : else
5693 teodor 574 309 : pushStop(state);
5710 tgl 575 1544 : }
576 :
577 : Datum
578 377 : to_tsquery_byid(PG_FUNCTION_ARGS)
579 : {
2219 noah 580 377 : text *in = PG_GETARG_TEXT_PP(1);
581 : TSQuery query;
582 : MorphOpaque data;
583 :
2558 teodor 584 377 : data.cfg_id = PG_GETARG_OID(0);
585 :
586 : /*
587 : * Passing OP_PHRASE as a qoperator makes tsquery require matching of word
588 : * positions of a complex morph exactly match the tsvector. Also, when
589 : * the complex morphs are connected with OP_PHRASE operator, we connect
590 : * all their words into the OP_PHRASE sequence.
591 : */
798 akorotkov 592 377 : data.qoperator = OP_PHRASE;
593 :
2558 teodor 594 377 : query = parse_tsquery(text_to_cstring(in),
595 : pushval_morph,
596 : PointerGetDatum(&data),
597 : 0,
598 : NULL);
599 :
5710 tgl 600 GIC 377 : PG_RETURN_TSQUERY(query);
5710 tgl 601 ECB : }
602 :
603 : Datum
5710 tgl 604 GIC 66 : to_tsquery(PG_FUNCTION_ARGS)
5710 tgl 605 ECB : {
2219 noah 606 GIC 66 : text *in = PG_GETARG_TEXT_PP(0);
5710 tgl 607 ECB : Oid cfgId;
608 :
5710 tgl 609 GIC 66 : cfgId = getTSCurrentConfig(true);
5710 tgl 610 CBC 66 : PG_RETURN_DATUM(DirectFunctionCall2(to_tsquery_byid,
5710 tgl 611 ECB : ObjectIdGetDatum(cfgId),
612 : PointerGetDatum(in)));
613 : }
614 :
615 : Datum
5710 tgl 616 GIC 30 : plainto_tsquery_byid(PG_FUNCTION_ARGS)
5710 tgl 617 ECB : {
2219 noah 618 GIC 30 : text *in = PG_GETARG_TEXT_PP(1);
2495 rhaas 619 ECB : TSQuery query;
620 : MorphOpaque data;
621 :
2558 teodor 622 GIC 30 : data.cfg_id = PG_GETARG_OID(0);
798 akorotkov 623 ECB :
624 : /*
625 : * parse_tsquery() with P_TSQ_PLAIN flag takes the whole input text as a
626 : * single morph. Passing OP_PHRASE as a qoperator makes tsquery require
627 : * matching of all words independently on their positions.
628 : */
2558 teodor 629 GIC 30 : data.qoperator = OP_AND;
5710 tgl 630 ECB :
2558 teodor 631 GIC 30 : query = parse_tsquery(text_to_cstring(in),
2558 teodor 632 ECB : pushval_morph,
633 : PointerGetDatum(&data),
634 : P_TSQ_PLAIN,
635 : NULL);
636 :
2558 teodor 637 GIC 30 : PG_RETURN_POINTER(query);
638 : }
5511 teodor 639 ECB :
640 : Datum
2558 teodor 641 GIC 6 : plainto_tsquery(PG_FUNCTION_ARGS)
642 : {
2219 noah 643 CBC 6 : text *in = PG_GETARG_TEXT_PP(0);
644 : Oid cfgId;
2558 teodor 645 ECB :
2558 teodor 646 GIC 6 : cfgId = getTSCurrentConfig(true);
647 6 : PG_RETURN_DATUM(DirectFunctionCall2(plainto_tsquery_byid,
2558 teodor 648 ECB : ObjectIdGetDatum(cfgId),
649 : PointerGetDatum(in)));
650 : }
651 :
652 :
653 : Datum
2558 teodor 654 GIC 24 : phraseto_tsquery_byid(PG_FUNCTION_ARGS)
655 : {
2219 noah 656 CBC 24 : text *in = PG_GETARG_TEXT_PP(1);
657 : TSQuery query;
2495 rhaas 658 ECB : MorphOpaque data;
659 :
2558 teodor 660 GIC 24 : data.cfg_id = PG_GETARG_OID(0);
661 :
798 akorotkov 662 ECB : /*
663 : * parse_tsquery() with P_TSQ_PLAIN flag takes the whole input text as a
664 : * single morph. Passing OP_PHRASE as a qoperator makes tsquery require
665 : * matching of word positions.
666 : */
2558 teodor 667 GIC 24 : data.qoperator = OP_PHRASE;
668 :
2558 teodor 669 CBC 24 : query = parse_tsquery(text_to_cstring(in),
670 : pushval_morph,
2558 teodor 671 ECB : PointerGetDatum(&data),
672 : P_TSQ_PLAIN,
673 : NULL);
674 :
2558 teodor 675 GIC 24 : PG_RETURN_TSQUERY(query);
676 : }
677 :
5710 tgl 678 ECB : Datum
2558 teodor 679 UIC 0 : phraseto_tsquery(PG_FUNCTION_ARGS)
680 : {
2219 noah 681 0 : text *in = PG_GETARG_TEXT_PP(0);
5710 tgl 682 EUB : Oid cfgId;
683 :
5710 tgl 684 UBC 0 : cfgId = getTSCurrentConfig(true);
2558 teodor 685 UIC 0 : PG_RETURN_DATUM(DirectFunctionCall2(phraseto_tsquery_byid,
686 : ObjectIdGetDatum(cfgId),
5710 tgl 687 EUB : PointerGetDatum(in)));
688 : }
689 :
690 : Datum
1830 teodor 691 GIC 204 : websearch_to_tsquery_byid(PG_FUNCTION_ARGS)
692 : {
693 204 : text *in = PG_GETARG_TEXT_PP(1);
1809 tgl 694 ECB : MorphOpaque data;
1830 teodor 695 GIC 204 : TSQuery query = NULL;
1830 teodor 696 ECB :
1830 teodor 697 GIC 204 : data.cfg_id = PG_GETARG_OID(0);
1830 teodor 698 ECB :
699 : /*
798 akorotkov 700 : * Passing OP_PHRASE as a qoperator makes tsquery require matching of word
701 : * positions of a complex morph exactly match the tsvector. Also, when
702 : * the complex morphs are given in quotes, we connect all their words into
703 : * the OP_PHRASE sequence.
704 : */
798 akorotkov 705 GIC 204 : data.qoperator = OP_PHRASE;
706 :
1830 teodor 707 204 : query = parse_tsquery(text_to_cstring(in),
1830 teodor 708 ECB : pushval_morph,
709 : PointerGetDatum(&data),
710 : P_TSQ_WEB,
711 : NULL);
712 :
1830 teodor 713 GIC 204 : PG_RETURN_TSQUERY(query);
714 : }
715 :
716 : Datum
1830 teodor 717 CBC 12 : websearch_to_tsquery(PG_FUNCTION_ARGS)
718 : {
1830 teodor 719 GIC 12 : text *in = PG_GETARG_TEXT_PP(0);
720 : Oid cfgId;
1830 teodor 721 ECB :
1830 teodor 722 GIC 12 : cfgId = getTSCurrentConfig(true);
1830 teodor 723 CBC 12 : PG_RETURN_DATUM(DirectFunctionCall2(websearch_to_tsquery_byid,
724 : ObjectIdGetDatum(cfgId),
725 : PointerGetDatum(in)));
1830 teodor 726 ECB : }
|