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