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