LCOV - code coverage report
Current view: top level - src/backend/utils/adt - tsquery.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 77.5 % 592 459
Test Date: 2026-03-12 06:14:44 Functions: 87.0 % 23 20
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1