LCOV - code coverage report
Current view: top level - src/backend/utils/adt - tsquery.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 75.0 % 668 501
Test Date: 2026-01-26 10:56:24 Functions: 87.0 % 23 20
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 59.3 % 442 262

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

Generated by: LCOV version 2.3.2-1