Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * tsquery.c
4 : : * I/O functions for tsquery
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : *
8 : : *
9 : : * IDENTIFICATION
10 : : * src/backend/utils/adt/tsquery.c
11 : : *
12 : : *-------------------------------------------------------------------------
13 : : */
14 : :
15 : : #include "postgres.h"
16 : :
17 : : #include "libpq/pqformat.h"
18 : : #include "miscadmin.h"
19 : : #include "nodes/miscnodes.h"
20 : : #include "tsearch/ts_locale.h"
21 : : #include "tsearch/ts_type.h"
22 : : #include "tsearch/ts_utils.h"
23 : : #include "utils/builtins.h"
24 : : #include "utils/memutils.h"
25 : : #include "utils/pg_crc.h"
26 : : #include "varatt.h"
27 : :
28 : : /* FTS operator priorities, see ts_type.h */
29 : : const int tsearch_op_priority[OP_COUNT] =
30 : : {
31 : : 4, /* OP_NOT */
32 : : 2, /* OP_AND */
33 : : 1, /* OP_OR */
34 : : 3 /* OP_PHRASE */
35 : : };
36 : :
37 : : /*
38 : : * parser's states
39 : : */
40 : : typedef enum
41 : : {
42 : : WAITOPERAND = 1,
43 : : WAITOPERATOR = 2,
44 : : WAITFIRSTOPERAND = 3,
45 : : } ts_parserstate;
46 : :
47 : : /*
48 : : * token types for parsing
49 : : */
50 : : typedef enum
51 : : {
52 : : PT_END = 0,
53 : : PT_ERR = 1,
54 : : PT_VAL = 2,
55 : : PT_OPR = 3,
56 : : PT_OPEN = 4,
57 : : PT_CLOSE = 5,
58 : : } ts_tokentype;
59 : :
60 : : /*
61 : : * get token from query string
62 : : *
63 : : * All arguments except "state" are output arguments.
64 : : *
65 : : * If return value is PT_OPR, then *operator is filled with an OP_* code
66 : : * and *weight will contain a distance value in case of phrase operator.
67 : : *
68 : : * If return value is PT_VAL, then *lenval, *strval, *weight, and *prefix
69 : : * are filled.
70 : : *
71 : : * If PT_ERR is returned then a soft error has occurred. If state->escontext
72 : : * isn't already filled then this should be reported as a generic parse error.
73 : : */
74 : : typedef ts_tokentype (*ts_tokenizer) (TSQueryParserState state, int8 *operator,
75 : : int *lenval, char **strval,
76 : : int16 *weight, bool *prefix);
77 : :
78 : : struct TSQueryParserStateData
79 : : {
80 : : /* Tokenizer used for parsing tsquery */
81 : : ts_tokenizer gettoken;
82 : :
83 : : /* State of tokenizer function */
84 : : char *buffer; /* entire string we are scanning */
85 : : char *buf; /* current scan point */
86 : : int count; /* nesting count, incremented by (,
87 : : * decremented by ) */
88 : : ts_parserstate state;
89 : :
90 : : /* polish (prefix) notation in list, filled in by push* functions */
91 : : List *polstr;
92 : :
93 : : /*
94 : : * Strings from operands are collected in op. curop is a pointer to the
95 : : * end of used space of op.
96 : : */
97 : : char *op;
98 : : char *curop;
99 : : int lenop; /* allocated size of op */
100 : : int sumlen; /* used size of op */
101 : :
102 : : /* state for value's parser */
103 : : TSVectorParseState valstate;
104 : :
105 : : /* context object for soft errors - must match valstate's escontext */
106 : : Node *escontext;
107 : : };
108 : :
109 : : /*
110 : : * subroutine to parse the modifiers (weight and prefix flag currently)
111 : : * part, like ':AB*' of a query.
112 : : */
113 : : static char *
114 : 1197 : get_modifiers(char *buf, int16 *weight, bool *prefix)
115 : : {
116 : 1197 : *weight = 0;
117 : 1197 : *prefix = false;
118 : :
119 [ + + ]: 1197 : if (!t_iseq(buf, ':'))
120 : 1091 : return buf;
121 : :
122 : 106 : buf++;
123 [ + + + + ]: 248 : while (*buf && pg_mblen(buf) == 1)
124 : : {
125 [ + + + + : 178 : switch (*buf)
+ + ]
126 : : {
127 : : case 'a':
128 : : case 'A':
129 : 39 : *weight |= 1 << 3;
130 : 39 : break;
131 : : case 'b':
132 : : case 'B':
133 : 11 : *weight |= 1 << 2;
134 : 11 : break;
135 : : case 'c':
136 : : case 'C':
137 : 19 : *weight |= 1 << 1;
138 : 19 : break;
139 : : case 'd':
140 : : case 'D':
141 : 20 : *weight |= 1;
142 : 20 : break;
143 : : case '*':
144 : 53 : *prefix = true;
145 : 53 : break;
146 : : default:
147 : 36 : return buf;
148 : : }
149 : 142 : buf++;
150 : : }
151 : :
152 : 70 : return buf;
153 : 1197 : }
154 : :
155 : : /*
156 : : * Parse phrase operator. The operator
157 : : * may take the following forms:
158 : : *
159 : : * a <N> b (distance is exactly N lexemes)
160 : : * a <-> b (default distance = 1)
161 : : *
162 : : * The buffer should begin with '<' char
163 : : */
164 : : static bool
165 : 1509 : parse_phrase_operator(TSQueryParserState pstate, int16 *distance)
166 : : {
167 : 1509 : enum
168 : : {
169 : : PHRASE_OPEN = 0,
170 : : PHRASE_DIST,
171 : : PHRASE_CLOSE,
172 : : PHRASE_FINISH
173 : 1509 : } state = PHRASE_OPEN;
174 : 1509 : char *ptr = pstate->buf;
175 : 1509 : char *endptr;
176 : 1509 : long l = 1; /* default distance */
177 : :
178 [ + + ]: 2377 : while (*ptr)
179 : : {
180 [ + - + + : 1829 : switch (state)
+ ]
181 : : {
182 : : case PHRASE_OPEN:
183 [ + + ]: 961 : if (t_iseq(ptr, '<'))
184 : : {
185 : 290 : state = PHRASE_DIST;
186 : 290 : ptr++;
187 : 290 : }
188 : : else
189 : 671 : return false;
190 : 290 : break;
191 : :
192 : : case PHRASE_DIST:
193 [ + + ]: 290 : if (t_iseq(ptr, '-'))
194 : : {
195 : 241 : state = PHRASE_CLOSE;
196 : 241 : ptr++;
197 : 241 : continue;
198 : : }
199 : :
200 [ + - ]: 49 : if (!isdigit((unsigned char) *ptr))
201 : 0 : return false;
202 : :
203 : 49 : errno = 0;
204 : 49 : l = strtol(ptr, &endptr, 10);
205 [ + - ]: 49 : if (ptr == endptr)
206 : 0 : return false;
207 [ + - + - : 49 : else if (errno == ERANGE || l < 0 || l > MAXENTRYPOS)
+ + ]
208 [ - + ]: 1 : ereturn(pstate->escontext, false,
209 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
210 : : errmsg("distance in phrase operator must be an integer value between zero and %d inclusive",
211 : : MAXENTRYPOS)));
212 : : else
213 : : {
214 : 48 : state = PHRASE_CLOSE;
215 : 48 : ptr = endptr;
216 : : }
217 : 48 : break;
218 : :
219 : : case PHRASE_CLOSE:
220 [ - + ]: 289 : if (t_iseq(ptr, '>'))
221 : : {
222 : 289 : state = PHRASE_FINISH;
223 : 289 : ptr++;
224 : 289 : }
225 : : else
226 : 0 : return false;
227 : 289 : break;
228 : :
229 : : case PHRASE_FINISH:
230 : 289 : *distance = (int16) l;
231 : 289 : pstate->buf = ptr;
232 : 289 : return true;
233 : : }
234 : : }
235 : :
236 : 548 : return false;
237 : 1509 : }
238 : :
239 : : /*
240 : : * Parse OR operator used in websearch_to_tsquery(), returns true if we
241 : : * believe that "OR" literal could be an operator OR
242 : : */
243 : : static bool
244 : 233 : parse_or_operator(TSQueryParserState pstate)
245 : : {
246 : 233 : char *ptr = pstate->buf;
247 : :
248 : : /* it should begin with "OR" literal */
249 [ + + ]: 233 : if (pg_strncasecmp(ptr, "or", 2) != 0)
250 : 208 : return false;
251 : :
252 : 25 : ptr += 2;
253 : :
254 : : /*
255 : : * it shouldn't be a part of any word but somewhere later it should be
256 : : * some operand
257 : : */
258 [ + + ]: 25 : if (*ptr == '\0') /* no operand */
259 : 1 : return false;
260 : :
261 : : /* it shouldn't be a part of any word */
262 [ + + + + : 24 : if (t_iseq(ptr, '-') || t_iseq(ptr, '_') || t_isalnum(ptr))
+ + ]
263 : 4 : return false;
264 : :
265 : 20 : for (;;)
266 : : {
267 : 20 : ptr += pg_mblen(ptr);
268 : :
269 [ + + ]: 20 : if (*ptr == '\0') /* got end of string without operand */
270 : 2 : return false;
271 : :
272 : : /*
273 : : * Suppose, we found an operand, but could be a not correct operand.
274 : : * So we still treat OR literal as operation with possibly incorrect
275 : : * operand and will not search it as lexeme
276 : : */
277 [ + - ]: 18 : if (!isspace((unsigned char) *ptr))
278 : 18 : break;
279 : : }
280 : :
281 : 18 : pstate->buf += 2;
282 : 18 : return true;
283 : 233 : }
284 : :
285 : : static ts_tokentype
286 : 2907 : gettoken_query_standard(TSQueryParserState state, int8 *operator,
287 : : int *lenval, char **strval,
288 : : int16 *weight, bool *prefix)
289 : : {
290 : 2907 : *weight = 0;
291 : 2907 : *prefix = false;
292 : :
293 : 3886 : while (true)
294 : : {
295 [ + - + ]: 3886 : switch (state->state)
296 : : {
297 : : case WAITFIRSTOPERAND:
298 : : case WAITOPERAND:
299 [ + + ]: 2020 : if (t_iseq(state->buf, '!'))
300 : : {
301 : 155 : state->buf++;
302 : 155 : state->state = WAITOPERAND;
303 : 155 : *operator = OP_NOT;
304 : 155 : return PT_OPR;
305 : : }
306 [ + + ]: 1865 : else if (t_iseq(state->buf, '('))
307 : : {
308 : 177 : state->buf++;
309 : 177 : state->state = WAITOPERAND;
310 : 177 : state->count++;
311 : 177 : return PT_OPEN;
312 : : }
313 [ - + ]: 1688 : else if (t_iseq(state->buf, ':'))
314 : : {
315 : : /* generic syntax error message is fine */
316 : 0 : return PT_ERR;
317 : : }
318 [ + + ]: 1688 : else if (!isspace((unsigned char) *state->buf))
319 : : {
320 : : /*
321 : : * We rely on the tsvector parser to parse the value for
322 : : * us
323 : : */
324 : 1201 : reset_tsvector_parser(state->valstate, state->buf);
325 [ + + + + ]: 2402 : if (gettoken_tsvector(state->valstate, strval, lenval,
326 : 1201 : NULL, NULL, &state->buf))
327 : : {
328 : 1197 : state->buf = get_modifiers(state->buf, weight, prefix);
329 : 1197 : state->state = WAITOPERATOR;
330 : 1197 : return PT_VAL;
331 : : }
332 [ - + # # : 4 : else if (SOFT_ERROR_OCCURRED(state->escontext))
# # ]
333 : : {
334 : : /* gettoken_tsvector reported a soft error */
335 : 0 : return PT_ERR;
336 : : }
337 [ + - ]: 4 : else if (state->state == WAITFIRSTOPERAND)
338 : : {
339 : 4 : return PT_END;
340 : : }
341 : : else
342 [ # # ]: 0 : ereturn(state->escontext, PT_ERR,
343 : : (errcode(ERRCODE_SYNTAX_ERROR),
344 : : errmsg("no operand in tsquery: \"%s\"",
345 : : state->buffer)));
346 : 0 : }
347 : 487 : break;
348 : :
349 : : case WAITOPERATOR:
350 [ + + ]: 1866 : if (t_iseq(state->buf, '&'))
351 : : {
352 : 220 : state->buf++;
353 : 220 : state->state = WAITOPERAND;
354 : 220 : *operator = OP_AND;
355 : 220 : return PT_OPR;
356 : : }
357 [ + + ]: 1646 : else if (t_iseq(state->buf, '|'))
358 : : {
359 : 137 : state->buf++;
360 : 137 : state->state = WAITOPERAND;
361 : 137 : *operator = OP_OR;
362 : 137 : return PT_OPR;
363 : : }
364 [ + + ]: 1509 : else if (parse_phrase_operator(state, weight))
365 : : {
366 : : /* weight var is used as storage for distance */
367 : 289 : state->state = WAITOPERAND;
368 : 289 : *operator = OP_PHRASE;
369 : 289 : return PT_OPR;
370 : : }
371 [ + + + - : 1220 : else if (SOFT_ERROR_OCCURRED(state->escontext))
+ + ]
372 : : {
373 : : /* parse_phrase_operator reported a soft error */
374 : 1 : return PT_ERR;
375 : : }
376 [ + + ]: 1219 : else if (t_iseq(state->buf, ')'))
377 : : {
378 : 177 : state->buf++;
379 : 177 : state->count--;
380 : 177 : return (state->count < 0) ? PT_ERR : PT_CLOSE;
381 : : }
382 [ + + ]: 1042 : else if (*state->buf == '\0')
383 : : {
384 : 548 : return (state->count) ? PT_ERR : PT_END;
385 : : }
386 [ + + ]: 494 : else if (!isspace((unsigned char) *state->buf))
387 : : {
388 : 2 : return PT_ERR;
389 : : }
390 : 492 : break;
391 : : }
392 : :
393 : 979 : state->buf += pg_mblen(state->buf);
394 : : }
395 : 2907 : }
396 : :
397 : : static ts_tokentype
398 : 377 : gettoken_query_websearch(TSQueryParserState state, int8 *operator,
399 : : int *lenval, char **strval,
400 : : int16 *weight, bool *prefix)
401 : : {
402 : 377 : *weight = 0;
403 : 377 : *prefix = false;
404 : :
405 : 490 : while (true)
406 : : {
407 [ + - + ]: 526 : switch (state->state)
408 : : {
409 : : case WAITFIRSTOPERAND:
410 : : case WAITOPERAND:
411 [ + + ]: 224 : if (t_iseq(state->buf, '-'))
412 : : {
413 : 11 : state->buf++;
414 : 11 : state->state = WAITOPERAND;
415 : :
416 : 11 : *operator = OP_NOT;
417 : 11 : return PT_OPR;
418 : : }
419 [ + + ]: 213 : else if (t_iseq(state->buf, '"'))
420 : : {
421 : : /* Everything in quotes is processed as a single token */
422 : :
423 : : /* skip opening quote */
424 : 32 : state->buf++;
425 : 32 : *strval = state->buf;
426 : :
427 : : /* iterate to the closing quote or end of the string */
428 [ + + + + ]: 290 : while (*state->buf != '\0' && !t_iseq(state->buf, '"'))
429 : 258 : state->buf++;
430 : 32 : *lenval = state->buf - *strval;
431 : :
432 : : /* skip closing quote if not end of the string */
433 [ + + ]: 32 : if (*state->buf != '\0')
434 : 28 : state->buf++;
435 : :
436 : 32 : state->state = WAITOPERATOR;
437 : 32 : state->count++;
438 : 32 : return PT_VAL;
439 : : }
440 [ + - + + : 181 : else if (ISOPERATOR(state->buf))
+ + + + +
+ + + +
+ ]
441 : : {
442 : : /* ignore, else gettoken_tsvector() will raise an error */
443 : 17 : state->buf++;
444 : 17 : state->state = WAITOPERAND;
445 : 17 : continue;
446 : : }
447 [ + + ]: 164 : else if (!isspace((unsigned char) *state->buf))
448 : : {
449 : : /*
450 : : * We rely on the tsvector parser to parse the value for
451 : : * us
452 : : */
453 : 151 : reset_tsvector_parser(state->valstate, state->buf);
454 [ + - + - ]: 302 : if (gettoken_tsvector(state->valstate, strval, lenval,
455 : 151 : NULL, NULL, &state->buf))
456 : : {
457 : 151 : state->state = WAITOPERATOR;
458 : 151 : return PT_VAL;
459 : : }
460 [ # # # # : 0 : else if (SOFT_ERROR_OCCURRED(state->escontext))
# # ]
461 : : {
462 : : /* gettoken_tsvector reported a soft error */
463 : 0 : return PT_ERR;
464 : : }
465 [ # # ]: 0 : else if (state->state == WAITFIRSTOPERAND)
466 : : {
467 : 0 : return PT_END;
468 : : }
469 : : else
470 : : {
471 : : /* finally, we have to provide an operand */
472 : 0 : pushStop(state);
473 : 0 : return PT_END;
474 : : }
475 : : }
476 : 13 : break;
477 : :
478 : : case WAITOPERATOR:
479 [ + + ]: 302 : if (*state->buf == '\0')
480 : : {
481 : 69 : return PT_END;
482 : : }
483 [ + + ]: 233 : else if (parse_or_operator(state))
484 : : {
485 : 18 : state->state = WAITOPERAND;
486 : 18 : *operator = OP_OR;
487 : 18 : return PT_OPR;
488 : : }
489 [ + - + + : 215 : else if (ISOPERATOR(state->buf))
+ + + + +
+ + + +
+ ]
490 : : {
491 : : /* ignore other operators in this state too */
492 : 19 : state->buf++;
493 : 19 : continue;
494 : : }
495 [ + + ]: 196 : else if (!isspace((unsigned char) *state->buf))
496 : : {
497 : : /* insert implicit AND between operands */
498 : 96 : state->state = WAITOPERAND;
499 : 96 : *operator = OP_AND;
500 : 96 : return PT_OPR;
501 : : }
502 : 100 : break;
503 : : }
504 : :
505 : 113 : state->buf += pg_mblen(state->buf);
506 : : }
507 : 377 : }
508 : :
509 : : static ts_tokentype
510 : 36 : gettoken_query_plain(TSQueryParserState state, int8 *operator,
511 : : int *lenval, char **strval,
512 : : int16 *weight, bool *prefix)
513 : : {
514 : 36 : *weight = 0;
515 : 36 : *prefix = false;
516 : :
517 [ + + ]: 36 : if (*state->buf == '\0')
518 : 18 : return PT_END;
519 : :
520 : 18 : *strval = state->buf;
521 : 18 : *lenval = strlen(state->buf);
522 : 18 : state->buf += *lenval;
523 : 18 : state->count++;
524 : 18 : return PT_VAL;
525 : 36 : }
526 : :
527 : : /*
528 : : * Push an operator to state->polstr
529 : : */
530 : : void
531 : 1038 : pushOperator(TSQueryParserState state, int8 oper, int16 distance)
532 : : {
533 : 1038 : QueryOperator *tmp;
534 : :
535 [ + + + + : 1038 : Assert(oper == OP_NOT || oper == OP_AND || oper == OP_OR || oper == OP_PHRASE);
+ + + - ]
536 : :
537 : 1038 : tmp = palloc0_object(QueryOperator);
538 : 1038 : tmp->type = QI_OPR;
539 : 1038 : tmp->oper = oper;
540 [ + + ]: 1038 : tmp->distance = (oper == OP_PHRASE) ? distance : 0;
541 : : /* left is filled in later with findoprnd */
542 : :
543 : 1038 : state->polstr = lcons(tmp, state->polstr);
544 : 1038 : }
545 : :
546 : : static void
547 : 1399 : pushValue_internal(TSQueryParserState state, pg_crc32 valcrc, int distance, int lenval, int weight, bool prefix)
548 : : {
549 : 1399 : QueryOperand *tmp;
550 : :
551 [ + - ]: 1399 : if (distance >= MAXSTRPOS)
552 [ # # ]: 0 : ereturn(state->escontext,,
553 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
554 : : errmsg("value is too big in tsquery: \"%s\"",
555 : : state->buffer)));
556 [ + - ]: 1399 : if (lenval >= MAXSTRLEN)
557 [ # # ]: 0 : ereturn(state->escontext,,
558 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
559 : : errmsg("operand is too long in tsquery: \"%s\"",
560 : : state->buffer)));
561 : :
562 : 1399 : tmp = palloc0_object(QueryOperand);
563 : 1399 : tmp->type = QI_VAL;
564 : 1399 : tmp->weight = weight;
565 : 1399 : tmp->prefix = prefix;
566 : 1399 : tmp->valcrc = (int32) valcrc;
567 : 1399 : tmp->length = lenval;
568 : 1399 : tmp->distance = distance;
569 : :
570 : 1399 : state->polstr = lcons(tmp, state->polstr);
571 [ - + ]: 1399 : }
572 : :
573 : : /*
574 : : * Push an operand to state->polstr.
575 : : *
576 : : * strval must point to a string equal to state->curop. lenval is the length
577 : : * of the string.
578 : : */
579 : : void
580 : 1399 : pushValue(TSQueryParserState state, char *strval, int lenval, int16 weight, bool prefix)
581 : : {
582 : 1399 : pg_crc32 valcrc;
583 : :
584 [ + - ]: 1399 : if (lenval >= MAXSTRLEN)
585 [ # # ]: 0 : ereturn(state->escontext,,
586 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
587 : : errmsg("word is too long in tsquery: \"%s\"",
588 : : state->buffer)));
589 : :
590 : 1399 : INIT_LEGACY_CRC32(valcrc);
591 [ + + ]: 4935 : COMP_LEGACY_CRC32(valcrc, strval, lenval);
592 : 1399 : FIN_LEGACY_CRC32(valcrc);
593 : 1399 : pushValue_internal(state, valcrc, state->curop - state->op, lenval, weight, prefix);
594 : :
595 : : /* append the value string to state.op, enlarging buffer if needed first */
596 [ - + ]: 1399 : while (state->curop - state->op + lenval + 1 >= state->lenop)
597 : : {
598 : 0 : int used = state->curop - state->op;
599 : :
600 : 0 : state->lenop *= 2;
601 : 0 : state->op = (char *) repalloc(state->op, state->lenop);
602 : 0 : state->curop = state->op + used;
603 : 0 : }
604 : 1399 : memcpy(state->curop, strval, lenval);
605 : 1399 : state->curop += lenval;
606 : 1399 : *(state->curop) = '\0';
607 : 1399 : state->curop++;
608 : 1399 : state->sumlen += lenval + 1 /* \0 */ ;
609 [ - + ]: 1399 : }
610 : :
611 : :
612 : : /*
613 : : * Push a stopword placeholder to state->polstr
614 : : */
615 : : void
616 : 111 : pushStop(TSQueryParserState state)
617 : : {
618 : 111 : QueryOperand *tmp;
619 : :
620 : 111 : tmp = palloc0_object(QueryOperand);
621 : 111 : tmp->type = QI_VALSTOP;
622 : :
623 : 111 : state->polstr = lcons(tmp, state->polstr);
624 : 111 : }
625 : :
626 : :
627 : : #define STACKDEPTH 32
628 : :
629 : : typedef struct OperatorElement
630 : : {
631 : : int8 op;
632 : : int16 distance;
633 : : } OperatorElement;
634 : :
635 : : static void
636 : 926 : pushOpStack(OperatorElement *stack, int *lenstack, int8 op, int16 distance)
637 : : {
638 [ + - ]: 926 : if (*lenstack == STACKDEPTH) /* internal error */
639 [ # # # # ]: 0 : elog(ERROR, "tsquery stack too small");
640 : :
641 : 926 : stack[*lenstack].op = op;
642 : 926 : stack[*lenstack].distance = distance;
643 : :
644 : 926 : (*lenstack)++;
645 : 926 : }
646 : :
647 : : static void
648 : 1742 : cleanOpStack(TSQueryParserState state,
649 : : OperatorElement *stack, int *lenstack, int8 op)
650 : : {
651 : 1742 : int opPriority = OP_PRIORITY(op);
652 : :
653 [ + + ]: 2668 : while (*lenstack)
654 : : {
655 : : /* NOT is right associative unlike to others */
656 [ + + + + ]: 1060 : if ((op != OP_NOT && opPriority > OP_PRIORITY(stack[*lenstack - 1].op)) ||
657 [ + + ]: 926 : (op == OP_NOT && opPriority >= OP_PRIORITY(stack[*lenstack - 1].op)))
658 : 81 : break;
659 : :
660 : 926 : (*lenstack)--;
661 : 1852 : pushOperator(state, stack[*lenstack].op,
662 : 926 : stack[*lenstack].distance);
663 : : }
664 : 1742 : }
665 : :
666 : : /*
667 : : * Make polish (prefix) notation of query.
668 : : *
669 : : * See parse_tsquery for explanation of pushval.
670 : : */
671 : : static void
672 : 819 : makepol(TSQueryParserState state,
673 : : PushFunction pushval,
674 : : void *opaque)
675 : : {
676 : 819 : int8 operator = 0;
677 : 819 : ts_tokentype type;
678 : 819 : int lenval = 0;
679 : 819 : char *strval = NULL;
680 : 819 : OperatorElement opstack[STACKDEPTH];
681 : 819 : int lenstack = 0;
682 : 819 : int16 weight = 0;
683 : 819 : bool prefix;
684 : :
685 : : /* since this function recurses, it could be driven to stack overflow */
686 : 819 : check_stack_depth();
687 : :
688 [ + + + + ]: 3320 : while ((type = state->gettoken(state, &operator,
689 : : &lenval, &strval,
690 : 3320 : &weight, &prefix)) != PT_END)
691 : : {
692 [ + + + + : 2681 : switch (type)
+ - ]
693 : : {
694 : : case PT_VAL:
695 : 1398 : pushval(opaque, state, strval, lenval, weight, prefix);
696 : 1398 : break;
697 : : case PT_OPR:
698 : 926 : cleanOpStack(state, opstack, &lenstack, operator);
699 : 926 : pushOpStack(opstack, &lenstack, operator, weight);
700 : 926 : break;
701 : : case PT_OPEN:
702 : 177 : makepol(state, pushval, opaque);
703 : 177 : break;
704 : : case PT_CLOSE:
705 : 177 : cleanOpStack(state, opstack, &lenstack, OP_OR /* lowest */ );
706 : 177 : return;
707 : 3 : case PT_ERR:
708 : : default:
709 : : /* don't overwrite a soft error saved by gettoken function */
710 [ + - + - : 3 : if (!SOFT_ERROR_OCCURRED(state->escontext))
+ + ]
711 [ + + ]: 2 : errsave(state->escontext,
712 : : (errcode(ERRCODE_SYNTAX_ERROR),
713 : : errmsg("syntax error in tsquery: \"%s\"",
714 : : state->buffer)));
715 : 3 : return;
716 : : }
717 : : /* detect soft error in pushval or recursion */
718 [ + + + - : 2501 : if (SOFT_ERROR_OCCURRED(state->escontext))
+ - ]
719 : 0 : return;
720 : : }
721 : :
722 : 639 : cleanOpStack(state, opstack, &lenstack, OP_OR /* lowest */ );
723 [ - + ]: 819 : }
724 : :
725 : : static void
726 : 2545 : findoprnd_recurse(QueryItem *ptr, uint32 *pos, int nnodes, bool *needcleanup)
727 : : {
728 : : /* since this function recurses, it could be driven to stack overflow. */
729 : 2545 : check_stack_depth();
730 : :
731 [ + - ]: 2545 : if (*pos >= nnodes)
732 [ # # # # ]: 0 : elog(ERROR, "malformed tsquery: operand not found");
733 : :
734 [ + + ]: 2545 : if (ptr[*pos].type == QI_VAL)
735 : : {
736 : 1396 : (*pos)++;
737 : 1396 : }
738 [ + + ]: 1149 : else if (ptr[*pos].type == QI_VALSTOP)
739 : : {
740 : 111 : *needcleanup = true; /* we'll have to remove stop words */
741 : 111 : (*pos)++;
742 : 111 : }
743 : : else
744 : : {
745 [ + - ]: 1038 : Assert(ptr[*pos].type == QI_OPR);
746 : :
747 [ + + ]: 1038 : if (ptr[*pos].qoperator.oper == OP_NOT)
748 : : {
749 : 166 : ptr[*pos].qoperator.left = 1; /* fixed offset */
750 : 166 : (*pos)++;
751 : :
752 : : /* process the only argument */
753 : 166 : findoprnd_recurse(ptr, pos, nnodes, needcleanup);
754 : 166 : }
755 : : else
756 : : {
757 : 872 : QueryOperator *curitem = &ptr[*pos].qoperator;
758 : 872 : int tmp = *pos; /* save current position */
759 : :
760 [ + + + + : 872 : Assert(curitem->oper == OP_AND ||
+ - ]
761 : : curitem->oper == OP_OR ||
762 : : curitem->oper == OP_PHRASE);
763 : :
764 : 872 : (*pos)++;
765 : :
766 : : /* process RIGHT argument */
767 : 872 : findoprnd_recurse(ptr, pos, nnodes, needcleanup);
768 : :
769 : 872 : curitem->left = *pos - tmp; /* set LEFT arg's offset */
770 : :
771 : : /* process LEFT argument */
772 : 872 : findoprnd_recurse(ptr, pos, nnodes, needcleanup);
773 : 872 : }
774 : : }
775 : 2545 : }
776 : :
777 : :
778 : : /*
779 : : * Fill in the left-fields previously left unfilled.
780 : : * The input QueryItems must be in polish (prefix) notation.
781 : : * Also, set *needcleanup to true if there are any QI_VALSTOP nodes.
782 : : */
783 : : static void
784 : 635 : findoprnd(QueryItem *ptr, int size, bool *needcleanup)
785 : : {
786 : 635 : uint32 pos;
787 : :
788 : 635 : *needcleanup = false;
789 : 635 : pos = 0;
790 : 635 : findoprnd_recurse(ptr, &pos, size, needcleanup);
791 : :
792 [ + - ]: 635 : if (pos != size)
793 [ # # # # ]: 0 : elog(ERROR, "malformed tsquery: extra nodes");
794 : 635 : }
795 : :
796 : :
797 : : /*
798 : : * Parse the tsquery stored in "buf".
799 : : *
800 : : * Each value (operand) in the query is passed to pushval. pushval can
801 : : * transform the simple value to an arbitrarily complex expression using
802 : : * pushValue and pushOperator. It must push a single value with pushValue,
803 : : * a complete expression with all operands, or a stopword placeholder
804 : : * with pushStop, otherwise the prefix notation representation will be broken,
805 : : * having an operator with no operand.
806 : : *
807 : : * opaque is passed on to pushval as is, pushval can use it to store its
808 : : * private state.
809 : : *
810 : : * The pushval function can record soft errors via escontext.
811 : : * Callers must check SOFT_ERROR_OCCURRED to detect that.
812 : : *
813 : : * A bitmask of flags (see ts_utils.h) and an error context object
814 : : * can be provided as well. If a soft error occurs, NULL is returned.
815 : : */
816 : : TSQuery
817 : 642 : parse_tsquery(char *buf,
818 : : PushFunction pushval,
819 : : void *opaque,
820 : : int flags,
821 : : Node *escontext)
822 : : {
823 : 642 : struct TSQueryParserStateData state;
824 : 642 : int i;
825 : 642 : TSQuery query;
826 : 642 : int commonlen;
827 : 642 : QueryItem *ptr;
828 : 642 : ListCell *cell;
829 : 642 : bool noisy;
830 : 642 : bool needcleanup;
831 : 642 : int tsv_flags = P_TSV_OPR_IS_DELIM | P_TSV_IS_TSQUERY;
832 : :
833 : : /* plain should not be used with web */
834 [ + - ]: 642 : Assert((flags & (P_TSQ_PLAIN | P_TSQ_WEB)) != (P_TSQ_PLAIN | P_TSQ_WEB));
835 : :
836 : : /* select suitable tokenizer */
837 [ + + ]: 642 : if (flags & P_TSQ_PLAIN)
838 : 18 : state.gettoken = gettoken_query_plain;
839 [ + + ]: 624 : else if (flags & P_TSQ_WEB)
840 : : {
841 : 69 : state.gettoken = gettoken_query_websearch;
842 : 69 : tsv_flags |= P_TSV_IS_WEB;
843 : 69 : }
844 : : else
845 : 555 : state.gettoken = gettoken_query_standard;
846 : :
847 : : /* emit nuisance NOTICEs only if not doing soft errors */
848 [ + + ]: 642 : noisy = !(escontext && IsA(escontext, ErrorSaveContext));
849 : :
850 : : /* init state */
851 : 642 : state.buffer = buf;
852 : 642 : state.buf = buf;
853 : 642 : state.count = 0;
854 : 642 : state.state = WAITFIRSTOPERAND;
855 : 642 : state.polstr = NIL;
856 : 642 : state.escontext = escontext;
857 : :
858 : : /* init value parser's state */
859 : 642 : state.valstate = init_tsvector_parser(state.buffer, tsv_flags, escontext);
860 : :
861 : : /* init list of operand */
862 : 642 : state.sumlen = 0;
863 : 642 : state.lenop = 64;
864 : 642 : state.curop = state.op = (char *) palloc(state.lenop);
865 : 642 : *(state.curop) = '\0';
866 : :
867 : : /* parse query & make polish notation (postfix, but in reverse order) */
868 : 642 : makepol(&state, pushval, opaque);
869 : :
870 : 642 : close_tsvector_parser(state.valstate);
871 : :
872 [ + + + - : 642 : if (SOFT_ERROR_OCCURRED(escontext))
+ + ]
873 : 3 : return NULL;
874 : :
875 [ + + ]: 639 : if (state.polstr == NIL)
876 : : {
877 [ - + ]: 4 : if (noisy)
878 [ - + + - ]: 4 : ereport(NOTICE,
879 : : (errmsg("text-search query doesn't contain lexemes: \"%s\"",
880 : : state.buffer)));
881 : 4 : query = (TSQuery) palloc(HDRSIZETQ);
882 : 4 : SET_VARSIZE(query, HDRSIZETQ);
883 : 4 : query->size = 0;
884 : 4 : return query;
885 : : }
886 : :
887 [ + - ]: 635 : if (TSQUERY_TOO_BIG(list_length(state.polstr), state.sumlen))
888 [ # # ]: 0 : ereturn(escontext, NULL,
889 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
890 : : errmsg("tsquery is too large")));
891 : 635 : commonlen = COMPUTESIZE(list_length(state.polstr), state.sumlen);
892 : :
893 : : /* Pack the QueryItems in the final TSQuery struct to return to caller */
894 : 635 : query = (TSQuery) palloc0(commonlen);
895 : 635 : SET_VARSIZE(query, commonlen);
896 : 635 : query->size = list_length(state.polstr);
897 : 635 : ptr = GETQUERY(query);
898 : :
899 : : /* Copy QueryItems to TSQuery */
900 : 635 : i = 0;
901 [ + - + + : 3180 : foreach(cell, state.polstr)
+ + ]
902 : : {
903 : 2545 : QueryItem *item = (QueryItem *) lfirst(cell);
904 : :
905 [ + - + + ]: 2545 : switch (item->type)
906 : : {
907 : : case QI_VAL:
908 : 1396 : memcpy(&ptr[i], item, sizeof(QueryOperand));
909 : 1396 : break;
910 : : case QI_VALSTOP:
911 : 111 : ptr[i].type = QI_VALSTOP;
912 : 111 : break;
913 : : case QI_OPR:
914 : 1038 : memcpy(&ptr[i], item, sizeof(QueryOperator));
915 : 1038 : break;
916 : : default:
917 [ # # # # ]: 0 : elog(ERROR, "unrecognized QueryItem type: %d", item->type);
918 : 0 : }
919 : 2545 : i++;
920 : 2545 : }
921 : :
922 : : /* Copy all the operand strings to TSQuery */
923 : 635 : memcpy(GETOPERAND(query), state.op, state.sumlen);
924 : 635 : pfree(state.op);
925 : :
926 : : /*
927 : : * Set left operand pointers for every operator. While we're at it,
928 : : * detect whether there are any QI_VALSTOP nodes.
929 : : */
930 : 635 : findoprnd(ptr, query->size, &needcleanup);
931 : :
932 : : /*
933 : : * If there are QI_VALSTOP nodes, delete them and simplify the tree.
934 : : */
935 [ + + ]: 635 : if (needcleanup)
936 : 72 : query = cleanup_tsquery_stopwords(query, noisy);
937 : :
938 : 635 : return query;
939 : 642 : }
940 : :
941 : : static void
942 : 880 : pushval_asis(void *opaque, TSQueryParserState state, char *strval, int lenval,
943 : : int16 weight, bool prefix)
944 : : {
945 : 880 : pushValue(state, strval, lenval, weight, prefix);
946 : 880 : }
947 : :
948 : : /*
949 : : * in without morphology
950 : : */
951 : : Datum
952 : 430 : tsqueryin(PG_FUNCTION_ARGS)
953 : : {
954 : 430 : char *in = PG_GETARG_CSTRING(0);
955 : 430 : Node *escontext = fcinfo->context;
956 : :
957 : 860 : PG_RETURN_TSQUERY(parse_tsquery(in,
958 : : pushval_asis,
959 : : NULL,
960 : : 0,
961 : : escontext));
962 : 430 : }
963 : :
964 : : /*
965 : : * out function
966 : : */
967 : : typedef struct
968 : : {
969 : : QueryItem *curpol;
970 : : char *buf;
971 : : char *cur;
972 : : char *op;
973 : : int buflen;
974 : : } INFIX;
975 : :
976 : : /* Makes sure inf->buf is large enough for adding 'addsize' bytes */
977 : : #define RESIZEBUF(inf, addsize) \
978 : : while( ( (inf)->cur - (inf)->buf ) + (addsize) + 1 >= (inf)->buflen ) \
979 : : { \
980 : : int len = (inf)->cur - (inf)->buf; \
981 : : (inf)->buflen *= 2; \
982 : : (inf)->buf = (char*) repalloc( (void*)(inf)->buf, (inf)->buflen ); \
983 : : (inf)->cur = (inf)->buf + len; \
984 : : }
985 : :
986 : : /*
987 : : * recursively traverse the tree and
988 : : * print it in infix (human-readable) form
989 : : */
990 : : static void
991 : 1138 : infix(INFIX *in, int parentPriority, bool rightPhraseOp)
992 : : {
993 : : /* since this function recurses, it could be driven to stack overflow. */
994 : 1138 : check_stack_depth();
995 : :
996 [ + + ]: 1138 : if (in->curpol->type == QI_VAL)
997 : : {
998 : 655 : QueryOperand *curpol = &in->curpol->qoperand;
999 : 655 : char *op = in->op + curpol->distance;
1000 : 655 : int clen;
1001 : :
1002 [ + + ]: 1065 : RESIZEBUF(in, curpol->length * (pg_database_encoding_max_length() + 1) + 2 + 6);
1003 : 655 : *(in->cur) = '\'';
1004 : 655 : in->cur++;
1005 [ + + ]: 2519 : while (*op)
1006 : : {
1007 [ + + ]: 1864 : if (t_iseq(op, '\''))
1008 : : {
1009 : 2 : *(in->cur) = '\'';
1010 : 2 : in->cur++;
1011 : 2 : }
1012 [ + + ]: 1862 : else if (t_iseq(op, '\\'))
1013 : : {
1014 : 1 : *(in->cur) = '\\';
1015 : 1 : in->cur++;
1016 : 1 : }
1017 : 1864 : COPYCHAR(in->cur, op);
1018 : :
1019 : 1864 : clen = pg_mblen(op);
1020 : 1864 : op += clen;
1021 : 1864 : in->cur += clen;
1022 : : }
1023 : 655 : *(in->cur) = '\'';
1024 : 655 : in->cur++;
1025 [ + + + + ]: 655 : if (curpol->weight || curpol->prefix)
1026 : : {
1027 : 29 : *(in->cur) = ':';
1028 : 29 : in->cur++;
1029 [ + + ]: 29 : if (curpol->prefix)
1030 : : {
1031 : 4 : *(in->cur) = '*';
1032 : 4 : in->cur++;
1033 : 4 : }
1034 [ + + ]: 29 : if (curpol->weight & (1 << 3))
1035 : : {
1036 : 10 : *(in->cur) = 'A';
1037 : 10 : in->cur++;
1038 : 10 : }
1039 [ + + ]: 29 : if (curpol->weight & (1 << 2))
1040 : : {
1041 : 16 : *(in->cur) = 'B';
1042 : 16 : in->cur++;
1043 : 16 : }
1044 [ + + ]: 29 : if (curpol->weight & (1 << 1))
1045 : : {
1046 : 3 : *(in->cur) = 'C';
1047 : 3 : in->cur++;
1048 : 3 : }
1049 [ + + ]: 29 : if (curpol->weight & 1)
1050 : : {
1051 : 1 : *(in->cur) = 'D';
1052 : 1 : in->cur++;
1053 : 1 : }
1054 : 29 : }
1055 : 655 : *(in->cur) = '\0';
1056 : 655 : in->curpol++;
1057 : 655 : }
1058 [ + + ]: 483 : else if (in->curpol->qoperator.oper == OP_NOT)
1059 : : {
1060 : 62 : int priority = QO_PRIORITY(in->curpol);
1061 : :
1062 [ + - ]: 62 : if (priority < parentPriority)
1063 : : {
1064 [ # # ]: 0 : RESIZEBUF(in, 2);
1065 : 0 : sprintf(in->cur, "( ");
1066 : 0 : in->cur = strchr(in->cur, '\0');
1067 : 0 : }
1068 [ - + ]: 62 : RESIZEBUF(in, 1);
1069 : 62 : *(in->cur) = '!';
1070 : 62 : in->cur++;
1071 : 62 : *(in->cur) = '\0';
1072 : 62 : in->curpol++;
1073 : :
1074 : 62 : infix(in, priority, false);
1075 [ + - ]: 62 : if (priority < parentPriority)
1076 : : {
1077 [ # # ]: 0 : RESIZEBUF(in, 2);
1078 : 0 : sprintf(in->cur, " )");
1079 : 0 : in->cur = strchr(in->cur, '\0');
1080 : 0 : }
1081 : 62 : }
1082 : : else
1083 : : {
1084 : 421 : int8 op = in->curpol->qoperator.oper;
1085 : 421 : int priority = QO_PRIORITY(in->curpol);
1086 : 421 : int16 distance = in->curpol->qoperator.distance;
1087 : 421 : INFIX nrm;
1088 : 421 : bool needParenthesis = false;
1089 : :
1090 : 421 : in->curpol++;
1091 [ + + + + ]: 529 : if (priority < parentPriority ||
1092 : : /* phrase operator depends on order */
1093 [ + + ]: 369 : (op == OP_PHRASE && rightPhraseOp))
1094 : : {
1095 : 54 : needParenthesis = true;
1096 [ - + ]: 54 : RESIZEBUF(in, 2);
1097 : 54 : sprintf(in->cur, "( ");
1098 : 54 : in->cur = strchr(in->cur, '\0');
1099 : 54 : }
1100 : :
1101 : 421 : nrm.curpol = in->curpol;
1102 : 421 : nrm.op = in->op;
1103 : 421 : nrm.buflen = 16;
1104 : 421 : nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
1105 : :
1106 : : /* get right operand */
1107 : 421 : infix(&nrm, priority, (op == OP_PHRASE));
1108 : :
1109 : : /* get & print left operand */
1110 : 421 : in->curpol = nrm.curpol;
1111 : 421 : infix(in, priority, false);
1112 : :
1113 : : /* print operator & right operand */
1114 [ + + ]: 579 : RESIZEBUF(in, 3 + (2 + 10 /* distance */ ) + (nrm.cur - nrm.buf));
1115 [ + + + - ]: 421 : switch (op)
1116 : : {
1117 : : case OP_OR:
1118 : 114 : sprintf(in->cur, " | %s", nrm.buf);
1119 : 114 : break;
1120 : : case OP_AND:
1121 : 197 : sprintf(in->cur, " & %s", nrm.buf);
1122 : 197 : break;
1123 : : case OP_PHRASE:
1124 [ + + ]: 110 : if (distance != 1)
1125 : 29 : sprintf(in->cur, " <%d> %s", distance, nrm.buf);
1126 : : else
1127 : 81 : sprintf(in->cur, " <-> %s", nrm.buf);
1128 : 110 : break;
1129 : : default:
1130 : : /* OP_NOT is handled in above if-branch */
1131 [ # # # # ]: 0 : elog(ERROR, "unrecognized operator type: %d", op);
1132 : 0 : }
1133 : 421 : in->cur = strchr(in->cur, '\0');
1134 : 421 : pfree(nrm.buf);
1135 : :
1136 [ + + ]: 421 : if (needParenthesis)
1137 : : {
1138 [ - + ]: 54 : RESIZEBUF(in, 2);
1139 : 54 : sprintf(in->cur, " )");
1140 : 54 : in->cur = strchr(in->cur, '\0');
1141 : 54 : }
1142 : 421 : }
1143 : 1138 : }
1144 : :
1145 : : Datum
1146 : 239 : tsqueryout(PG_FUNCTION_ARGS)
1147 : : {
1148 : 239 : TSQuery query = PG_GETARG_TSQUERY(0);
1149 : 239 : INFIX nrm;
1150 : :
1151 [ + + ]: 239 : if (query->size == 0)
1152 : : {
1153 : 5 : char *b = palloc(1);
1154 : :
1155 : 5 : *b = '\0';
1156 : 5 : PG_RETURN_POINTER(b);
1157 : 5 : }
1158 : 234 : nrm.curpol = GETQUERY(query);
1159 : 234 : nrm.buflen = 32;
1160 : 234 : nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
1161 : 234 : *(nrm.cur) = '\0';
1162 : 234 : nrm.op = GETOPERAND(query);
1163 : 234 : infix(&nrm, -1 /* lowest priority */ , false);
1164 : :
1165 [ + - ]: 234 : PG_FREE_IF_COPY(query, 0);
1166 : 234 : PG_RETURN_CSTRING(nrm.buf);
1167 : 239 : }
1168 : :
1169 : : /*
1170 : : * Binary Input / Output functions. The binary format is as follows:
1171 : : *
1172 : : * uint32 number of operators/operands in the query
1173 : : *
1174 : : * Followed by the operators and operands, in prefix notation. For each
1175 : : * operand:
1176 : : *
1177 : : * uint8 type, QI_VAL
1178 : : * uint8 weight
1179 : : * uint8 prefix
1180 : : * operand text in client encoding, null-terminated
1181 : : *
1182 : : * For each operator:
1183 : : *
1184 : : * uint8 type, QI_OPR
1185 : : * uint8 operator, one of OP_AND, OP_PHRASE OP_OR, OP_NOT.
1186 : : * uint16 distance (only for OP_PHRASE)
1187 : : */
1188 : : Datum
1189 : 0 : tsquerysend(PG_FUNCTION_ARGS)
1190 : : {
1191 : 0 : TSQuery query = PG_GETARG_TSQUERY(0);
1192 : 0 : StringInfoData buf;
1193 : 0 : int i;
1194 : 0 : QueryItem *item = GETQUERY(query);
1195 : :
1196 : 0 : pq_begintypsend(&buf);
1197 : :
1198 : 0 : pq_sendint32(&buf, query->size);
1199 [ # # ]: 0 : for (i = 0; i < query->size; i++)
1200 : : {
1201 : 0 : pq_sendint8(&buf, item->type);
1202 : :
1203 [ # # # ]: 0 : switch (item->type)
1204 : : {
1205 : : case QI_VAL:
1206 : 0 : pq_sendint8(&buf, item->qoperand.weight);
1207 : 0 : pq_sendint8(&buf, item->qoperand.prefix);
1208 : 0 : pq_sendstring(&buf, GETOPERAND(query) + item->qoperand.distance);
1209 : 0 : break;
1210 : : case QI_OPR:
1211 : 0 : pq_sendint8(&buf, item->qoperator.oper);
1212 [ # # ]: 0 : if (item->qoperator.oper == OP_PHRASE)
1213 : 0 : pq_sendint16(&buf, item->qoperator.distance);
1214 : 0 : break;
1215 : : default:
1216 [ # # # # ]: 0 : elog(ERROR, "unrecognized tsquery node type: %d", item->type);
1217 : 0 : }
1218 : 0 : item++;
1219 : 0 : }
1220 : :
1221 [ # # ]: 0 : PG_FREE_IF_COPY(query, 0);
1222 : :
1223 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1224 : 0 : }
1225 : :
1226 : : Datum
1227 : 0 : tsqueryrecv(PG_FUNCTION_ARGS)
1228 : : {
1229 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1230 : 0 : TSQuery query;
1231 : 0 : int i,
1232 : : len;
1233 : 0 : QueryItem *item;
1234 : 0 : int datalen;
1235 : 0 : char *ptr;
1236 : 0 : uint32 size;
1237 : 0 : const char **operands;
1238 : 0 : bool needcleanup;
1239 : :
1240 : 0 : size = pq_getmsgint(buf, sizeof(uint32));
1241 [ # # ]: 0 : if (size > (MaxAllocSize / sizeof(QueryItem)))
1242 [ # # # # ]: 0 : elog(ERROR, "invalid size of tsquery");
1243 : :
1244 : : /* Allocate space to temporarily hold operand strings */
1245 : 0 : operands = palloc(size * sizeof(char *));
1246 : :
1247 : : /* Allocate space for all the QueryItems. */
1248 : 0 : len = HDRSIZETQ + sizeof(QueryItem) * size;
1249 : 0 : query = (TSQuery) palloc0(len);
1250 : 0 : query->size = size;
1251 : 0 : item = GETQUERY(query);
1252 : :
1253 : 0 : datalen = 0;
1254 [ # # ]: 0 : for (i = 0; i < size; i++)
1255 : : {
1256 : 0 : item->type = (int8) pq_getmsgint(buf, sizeof(int8));
1257 : :
1258 [ # # ]: 0 : if (item->type == QI_VAL)
1259 : : {
1260 : 0 : size_t val_len; /* length after recoding to server
1261 : : * encoding */
1262 : 0 : uint8 weight;
1263 : 0 : uint8 prefix;
1264 : 0 : const char *val;
1265 : 0 : pg_crc32 valcrc;
1266 : :
1267 : 0 : weight = (uint8) pq_getmsgint(buf, sizeof(uint8));
1268 : 0 : prefix = (uint8) pq_getmsgint(buf, sizeof(uint8));
1269 : 0 : val = pq_getmsgstring(buf);
1270 : 0 : val_len = strlen(val);
1271 : :
1272 : : /* Sanity checks */
1273 : :
1274 [ # # ]: 0 : if (weight > 0xF)
1275 [ # # # # ]: 0 : elog(ERROR, "invalid tsquery: invalid weight bitmap");
1276 : :
1277 [ # # ]: 0 : if (val_len > MAXSTRLEN)
1278 [ # # # # ]: 0 : elog(ERROR, "invalid tsquery: operand too long");
1279 : :
1280 [ # # ]: 0 : if (datalen > MAXSTRPOS)
1281 [ # # # # ]: 0 : elog(ERROR, "invalid tsquery: total operand length exceeded");
1282 : :
1283 : : /* Looks valid. */
1284 : :
1285 : 0 : INIT_LEGACY_CRC32(valcrc);
1286 [ # # ]: 0 : COMP_LEGACY_CRC32(valcrc, val, val_len);
1287 : 0 : FIN_LEGACY_CRC32(valcrc);
1288 : :
1289 : 0 : item->qoperand.weight = weight;
1290 : 0 : item->qoperand.prefix = (prefix) ? true : false;
1291 : 0 : item->qoperand.valcrc = (int32) valcrc;
1292 : 0 : item->qoperand.length = val_len;
1293 : 0 : item->qoperand.distance = datalen;
1294 : :
1295 : : /*
1296 : : * Operand strings are copied to the final struct after this loop;
1297 : : * here we just collect them to an array
1298 : : */
1299 : 0 : operands[i] = val;
1300 : :
1301 : 0 : datalen += val_len + 1; /* + 1 for the '\0' terminator */
1302 : 0 : }
1303 [ # # ]: 0 : else if (item->type == QI_OPR)
1304 : : {
1305 : 0 : int8 oper;
1306 : :
1307 : 0 : oper = (int8) pq_getmsgint(buf, sizeof(int8));
1308 [ # # # # : 0 : if (oper != OP_NOT && oper != OP_OR && oper != OP_AND && oper != OP_PHRASE)
# # # # ]
1309 [ # # # # ]: 0 : elog(ERROR, "invalid tsquery: unrecognized operator type %d",
1310 : : (int) oper);
1311 [ # # ]: 0 : if (i == size - 1)
1312 [ # # # # ]: 0 : elog(ERROR, "invalid pointer to right operand");
1313 : :
1314 : 0 : item->qoperator.oper = oper;
1315 [ # # ]: 0 : if (oper == OP_PHRASE)
1316 : 0 : item->qoperator.distance = (int16) pq_getmsgint(buf, sizeof(int16));
1317 : 0 : }
1318 : : else
1319 [ # # # # ]: 0 : elog(ERROR, "unrecognized tsquery node type: %d", item->type);
1320 : :
1321 : 0 : item++;
1322 : 0 : }
1323 : :
1324 : : /* Enlarge buffer to make room for the operand values. */
1325 : 0 : query = (TSQuery) repalloc(query, len + datalen);
1326 : 0 : item = GETQUERY(query);
1327 : 0 : ptr = GETOPERAND(query);
1328 : :
1329 : : /*
1330 : : * Fill in the left-pointers. Checks that the tree is well-formed as a
1331 : : * side-effect.
1332 : : */
1333 : 0 : findoprnd(item, size, &needcleanup);
1334 : :
1335 : : /* Can't have found any QI_VALSTOP nodes */
1336 [ # # ]: 0 : Assert(!needcleanup);
1337 : :
1338 : : /* Copy operands to output struct */
1339 [ # # ]: 0 : for (i = 0; i < size; i++)
1340 : : {
1341 [ # # ]: 0 : if (item->type == QI_VAL)
1342 : : {
1343 : 0 : memcpy(ptr, operands[i], item->qoperand.length + 1);
1344 : 0 : ptr += item->qoperand.length + 1;
1345 : 0 : }
1346 : 0 : item++;
1347 : 0 : }
1348 : :
1349 : 0 : pfree(operands);
1350 : :
1351 [ # # ]: 0 : Assert(ptr - GETOPERAND(query) == datalen);
1352 : :
1353 : 0 : SET_VARSIZE(query, len + datalen);
1354 : :
1355 : 0 : PG_RETURN_TSQUERY(query);
1356 : 0 : }
1357 : :
1358 : : /*
1359 : : * debug function, used only for view query
1360 : : * which will be executed in non-leaf pages in index
1361 : : */
1362 : : Datum
1363 : 0 : tsquerytree(PG_FUNCTION_ARGS)
1364 : : {
1365 : 0 : TSQuery query = PG_GETARG_TSQUERY(0);
1366 : 0 : INFIX nrm;
1367 : 0 : text *res;
1368 : 0 : QueryItem *q;
1369 : 0 : int len;
1370 : :
1371 [ # # ]: 0 : if (query->size == 0)
1372 : : {
1373 : 0 : res = (text *) palloc(VARHDRSZ);
1374 : 0 : SET_VARSIZE(res, VARHDRSZ);
1375 : 0 : PG_RETURN_POINTER(res);
1376 : : }
1377 : :
1378 : 0 : q = clean_NOT(GETQUERY(query), &len);
1379 : :
1380 [ # # ]: 0 : if (!q)
1381 : : {
1382 : 0 : res = cstring_to_text("T");
1383 : 0 : }
1384 : : else
1385 : : {
1386 : 0 : nrm.curpol = q;
1387 : 0 : nrm.buflen = 32;
1388 : 0 : nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
1389 : 0 : *(nrm.cur) = '\0';
1390 : 0 : nrm.op = GETOPERAND(query);
1391 : 0 : infix(&nrm, -1, false);
1392 : 0 : res = cstring_to_text_with_len(nrm.buf, nrm.cur - nrm.buf);
1393 : 0 : pfree(q);
1394 : : }
1395 : :
1396 [ # # ]: 0 : PG_FREE_IF_COPY(query, 0);
1397 : :
1398 : 0 : PG_RETURN_TEXT_P(res);
1399 : 0 : }
|