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

Generated by: LCOV version 1.13