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

Generated by: LCOV version 1.14