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

Generated by: LCOV version 1.14