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