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