LCOV - code coverage report
Current view: top level - src/backend/utils/adt - tsquery.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 77.5 % 592 459
Test Date: 2026-07-22 12:15:41 Functions: 87.0 % 23 20
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 63.0 % 381 240

             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                 :        5076 : get_modifiers(char *buf, int16 *weight, bool *prefix)
     115                 :             : {
     116                 :        5076 :     *weight = 0;
     117                 :        5076 :     *prefix = false;
     118                 :             : 
     119         [ +  + ]:        5076 :     if (!t_iseq(buf, ':'))
     120                 :        4641 :         return buf;
     121                 :             : 
     122                 :         435 :     buf++;
     123   [ +  +  +  - ]:        1016 :     while (*buf && pg_mblen_cstr(buf) == 1)
     124                 :             :     {
     125   [ +  +  +  +  :         736 :         switch (*buf)
                   +  + ]
     126                 :             :         {
     127                 :         160 :             case 'a':
     128                 :             :             case 'A':
     129                 :         160 :                 *weight |= 1 << 3;
     130                 :         160 :                 break;
     131                 :          50 :             case 'b':
     132                 :             :             case 'B':
     133                 :          50 :                 *weight |= 1 << 2;
     134                 :          50 :                 break;
     135                 :          78 :             case 'c':
     136                 :             :             case 'C':
     137                 :          78 :                 *weight |= 1 << 1;
     138                 :          78 :                 break;
     139                 :          81 :             case 'd':
     140                 :             :             case 'D':
     141                 :          81 :                 *weight |= 1;
     142                 :          81 :                 break;
     143                 :         212 :             case '*':
     144                 :         212 :                 *prefix = true;
     145                 :         212 :                 break;
     146                 :         155 :             default:
     147                 :         155 :                 return buf;
     148                 :             :         }
     149                 :         581 :         buf++;
     150                 :             :     }
     151                 :             : 
     152                 :         280 :     return buf;
     153                 :             : }
     154                 :             : 
     155                 :             : /*
     156                 :             :  * Parse phrase operator. The operator
     157                 :             :  * may take the following forms:
     158                 :             :  *
     159                 :             :  *      a <N> b (distance is exactly N lexemes)
     160                 :             :  *      a <-> b (default distance = 1)
     161                 :             :  *
     162                 :             :  * The buffer should begin with '<' char
     163                 :             :  */
     164                 :             : static bool
     165                 :        6469 : parse_phrase_operator(TSQueryParserState pstate, int16 *distance)
     166                 :             : {
     167                 :             :     enum
     168                 :             :     {
     169                 :             :         PHRASE_OPEN = 0,
     170                 :             :         PHRASE_DIST,
     171                 :             :         PHRASE_CLOSE,
     172                 :             :         PHRASE_FINISH
     173                 :        6469 :     }           state = PHRASE_OPEN;
     174                 :        6469 :     char       *ptr = pstate->buf;
     175                 :             :     char       *endptr;
     176                 :        6469 :     long        l = 1;          /* default distance */
     177                 :             : 
     178         [ +  + ]:       16716 :     while (*ptr)
     179                 :             :     {
     180   [ +  +  +  +  :        7939 :         switch (state)
                      - ]
     181                 :             :         {
     182                 :        4161 :             case PHRASE_OPEN:
     183         [ +  + ]:        4161 :                 if (t_iseq(ptr, '<'))
     184                 :             :                 {
     185                 :        1262 :                     state = PHRASE_DIST;
     186                 :        1262 :                     ptr++;
     187                 :             :                 }
     188                 :             :                 else
     189                 :        2899 :                     return false;
     190                 :        1262 :                 break;
     191                 :             : 
     192                 :        1262 :             case PHRASE_DIST:
     193         [ +  + ]:        1262 :                 if (t_iseq(ptr, '-'))
     194                 :             :                 {
     195                 :        1054 :                     state = PHRASE_CLOSE;
     196                 :        1054 :                     ptr++;
     197                 :        1054 :                     continue;
     198                 :             :                 }
     199                 :             : 
     200         [ -  + ]:         208 :                 if (!isdigit((unsigned char) *ptr))
     201                 :           0 :                     return false;
     202                 :             : 
     203                 :         208 :                 errno = 0;
     204                 :         208 :                 l = strtol(ptr, &endptr, 10);
     205         [ -  + ]:         208 :                 if (ptr == endptr)
     206                 :           0 :                     return false;
     207   [ +  -  +  -  :         208 :                 else if (errno == ERANGE || l < 0 || l > MAXENTRYPOS)
                   +  + ]
     208         [ +  - ]:           4 :                     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                 :         204 :                     state = PHRASE_CLOSE;
     215                 :         204 :                     ptr = endptr;
     216                 :             :                 }
     217                 :         204 :                 break;
     218                 :             : 
     219                 :        1258 :             case PHRASE_CLOSE:
     220         [ +  - ]:        1258 :                 if (t_iseq(ptr, '>'))
     221                 :             :                 {
     222                 :        1258 :                     state = PHRASE_FINISH;
     223                 :        1258 :                     ptr++;
     224                 :             :                 }
     225                 :             :                 else
     226                 :           0 :                     return false;
     227                 :        1258 :                 break;
     228                 :             : 
     229                 :        1258 :             case PHRASE_FINISH:
     230                 :        1258 :                 *distance = (int16) l;
     231                 :        1258 :                 pstate->buf = ptr;
     232                 :        1258 :                 return true;
     233                 :             :         }
     234                 :             :     }
     235                 :             : 
     236                 :        2308 :     return false;
     237                 :             : }
     238                 :             : 
     239                 :             : /*
     240                 :             :  * Parse OR operator used in websearch_to_tsquery(), returns true if we
     241                 :             :  * believe that "OR" literal could be an operator OR
     242                 :             :  */
     243                 :             : static bool
     244                 :        1165 : parse_or_operator(TSQueryParserState pstate)
     245                 :             : {
     246                 :        1165 :     char       *ptr = pstate->buf;
     247                 :             : 
     248                 :             :     /* it should begin with "OR" literal */
     249         [ +  + ]:        1165 :     if (pg_strncasecmp(ptr, "or", 2) != 0)
     250                 :        1040 :         return false;
     251                 :             : 
     252                 :         125 :     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         [ +  + ]:         125 :     if (*ptr == '\0')           /* no operand */
     259                 :           5 :         return false;
     260                 :             : 
     261                 :             :     /* it shouldn't be a part of any word */
     262   [ +  +  +  +  :         120 :     if (t_iseq(ptr, '-') || t_iseq(ptr, '_') || t_isalnum_cstr(ptr))
                   +  + ]
     263                 :          20 :         return false;
     264                 :             : 
     265                 :             :     for (;;)
     266                 :             :     {
     267                 :         100 :         ptr += pg_mblen_cstr(ptr);
     268                 :             : 
     269         [ +  + ]:         100 :         if (*ptr == '\0')       /* got end of string without operand */
     270                 :          10 :             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         [ +  - ]:          90 :         if (!isspace((unsigned char) *ptr))
     278                 :          90 :             break;
     279                 :             :     }
     280                 :             : 
     281                 :          90 :     pstate->buf += 2;
     282                 :          90 :     return true;
     283                 :             : }
     284                 :             : 
     285                 :             : static ts_tokentype
     286                 :       12343 : gettoken_query_standard(TSQueryParserState state, int8 *operator,
     287                 :             :                         int *lenval, char **strval,
     288                 :             :                         int16 *weight, bool *prefix)
     289                 :             : {
     290                 :       12343 :     *weight = 0;
     291                 :       12343 :     *prefix = false;
     292                 :             : 
     293                 :             :     while (true)
     294                 :             :     {
     295      [ +  +  - ]:       16556 :         switch (state->state)
     296                 :             :         {
     297                 :        8589 :             case WAITFIRSTOPERAND:
     298                 :             :             case WAITOPERAND:
     299         [ +  + ]:        8589 :                 if (t_iseq(state->buf, '!'))
     300                 :             :                 {
     301                 :         627 :                     state->buf++;
     302                 :         627 :                     state->state = WAITOPERAND;
     303                 :         627 :                     *operator = OP_NOT;
     304                 :         627 :                     return PT_OPR;
     305                 :             :                 }
     306         [ +  + ]:        7962 :                 else if (t_iseq(state->buf, '('))
     307                 :             :                 {
     308                 :         773 :                     state->buf++;
     309                 :         773 :                     state->state = WAITOPERAND;
     310                 :         773 :                     state->count++;
     311                 :         773 :                     return PT_OPEN;
     312                 :             :                 }
     313         [ -  + ]:        7189 :                 else if (t_iseq(state->buf, ':'))
     314                 :             :                 {
     315                 :             :                     /* generic syntax error message is fine */
     316                 :           0 :                     return PT_ERR;
     317                 :             :                 }
     318         [ +  + ]:        7189 :                 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                 :        5094 :                     reset_tsvector_parser(state->valstate, state->buf);
     325         [ +  + ]:        5094 :                     if (gettoken_tsvector(state->valstate, strval, lenval,
     326                 :             :                                           NULL, NULL, &state->buf))
     327                 :             :                     {
     328                 :        5076 :                         state->buf = get_modifiers(state->buf, weight, prefix);
     329                 :        5076 :                         state->state = WAITOPERATOR;
     330                 :        5076 :                         return PT_VAL;
     331                 :             :                     }
     332   [ -  +  -  -  :          18 :                     else if (SOFT_ERROR_OCCURRED(state->escontext))
                   -  - ]
     333                 :             :                     {
     334                 :             :                         /* gettoken_tsvector reported a soft error */
     335                 :           0 :                         return PT_ERR;
     336                 :             :                     }
     337         [ +  - ]:          18 :                     else if (state->state == WAITFIRSTOPERAND)
     338                 :             :                     {
     339                 :          18 :                         return PT_END;
     340                 :             :                     }
     341                 :             :                     else
     342         [ #  # ]:           0 :                         ereturn(state->escontext, PT_ERR,
     343                 :             :                                 (errcode(ERRCODE_SYNTAX_ERROR),
     344                 :             :                                  errmsg("no operand in tsquery: \"%s\"",
     345                 :             :                                         state->buffer)));
     346                 :             :                 }
     347                 :        2095 :                 break;
     348                 :             : 
     349                 :        7967 :             case WAITOPERATOR:
     350         [ +  + ]:        7967 :                 if (t_iseq(state->buf, '&'))
     351                 :             :                 {
     352                 :         942 :                     state->buf++;
     353                 :         942 :                     state->state = WAITOPERAND;
     354                 :         942 :                     *operator = OP_AND;
     355                 :         942 :                     return PT_OPR;
     356                 :             :                 }
     357         [ +  + ]:        7025 :                 else if (t_iseq(state->buf, '|'))
     358                 :             :                 {
     359                 :         556 :                     state->buf++;
     360                 :         556 :                     state->state = WAITOPERAND;
     361                 :         556 :                     *operator = OP_OR;
     362                 :         556 :                     return PT_OPR;
     363                 :             :                 }
     364         [ +  + ]:        6469 :                 else if (parse_phrase_operator(state, weight))
     365                 :             :                 {
     366                 :             :                     /* weight var is used as storage for distance */
     367                 :        1258 :                     state->state = WAITOPERAND;
     368                 :        1258 :                     *operator = OP_PHRASE;
     369                 :        1258 :                     return PT_OPR;
     370                 :             :                 }
     371   [ +  +  +  -  :        5211 :                 else if (SOFT_ERROR_OCCURRED(state->escontext))
                   +  + ]
     372                 :             :                 {
     373                 :             :                     /* parse_phrase_operator reported a soft error */
     374                 :           4 :                     return PT_ERR;
     375                 :             :                 }
     376         [ +  + ]:        5207 :                 else if (t_iseq(state->buf, ')'))
     377                 :             :                 {
     378                 :         773 :                     state->buf++;
     379                 :         773 :                     state->count--;
     380         [ -  + ]:         773 :                     return (state->count < 0) ? PT_ERR : PT_CLOSE;
     381                 :             :                 }
     382         [ +  + ]:        4434 :                 else if (*state->buf == '\0')
     383                 :             :                 {
     384                 :        2308 :                     return (state->count) ? PT_ERR : PT_END;
     385                 :             :                 }
     386         [ +  + ]:        2126 :                 else if (!isspace((unsigned char) *state->buf))
     387                 :             :                 {
     388                 :           8 :                     return PT_ERR;
     389                 :             :                 }
     390                 :        2118 :                 break;
     391                 :             :         }
     392                 :             : 
     393                 :        4213 :         state->buf += pg_mblen_cstr(state->buf);
     394                 :             :     }
     395                 :             : }
     396                 :             : 
     397                 :             : static ts_tokentype
     398                 :        1877 : gettoken_query_websearch(TSQueryParserState state, int8 *operator,
     399                 :             :                          int *lenval, char **strval,
     400                 :             :                          int16 *weight, bool *prefix)
     401                 :             : {
     402                 :        1877 :     *weight = 0;
     403                 :        1877 :     *prefix = false;
     404                 :             : 
     405                 :             :     while (true)
     406                 :             :     {
     407      [ +  +  - ]:        2622 :         switch (state->state)
     408                 :             :         {
     409                 :        1116 :             case WAITFIRSTOPERAND:
     410                 :             :             case WAITOPERAND:
     411         [ +  + ]:        1116 :                 if (t_iseq(state->buf, '-'))
     412                 :             :                 {
     413                 :          55 :                     state->buf++;
     414                 :          55 :                     state->state = WAITOPERAND;
     415                 :             : 
     416                 :          55 :                     *operator = OP_NOT;
     417                 :          55 :                     return PT_OPR;
     418                 :             :                 }
     419         [ +  + ]:        1061 :                 else if (t_iseq(state->buf, '"'))
     420                 :             :                 {
     421                 :             :                     /* Everything in quotes is processed as a single token */
     422                 :             : 
     423                 :             :                     /* skip opening quote */
     424                 :         160 :                     state->buf++;
     425                 :         160 :                     *strval = state->buf;
     426                 :             : 
     427                 :             :                     /* iterate to the closing quote or end of the string */
     428   [ +  +  +  + ]:        1450 :                     while (*state->buf != '\0' && !t_iseq(state->buf, '"'))
     429                 :        1290 :                         state->buf++;
     430                 :         160 :                     *lenval = state->buf - *strval;
     431                 :             : 
     432                 :             :                     /* skip closing quote if not end of the string */
     433         [ +  + ]:         160 :                     if (*state->buf != '\0')
     434                 :         140 :                         state->buf++;
     435                 :             : 
     436                 :         160 :                     state->state = WAITOPERATOR;
     437                 :         160 :                     state->count++;
     438                 :         160 :                     return PT_VAL;
     439                 :             :                 }
     440   [ +  +  +  +  :         901 :                 else if (ISOPERATOR(state->buf))
          +  +  +  +  +  
                +  +  + ]
     441                 :             :                 {
     442                 :             :                     /* ignore, else gettoken_tsvector() will raise an error */
     443                 :          85 :                     state->buf++;
     444                 :          85 :                     state->state = WAITOPERAND;
     445                 :          85 :                     continue;
     446                 :             :                 }
     447         [ +  + ]:         816 :                 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                 :         751 :                     reset_tsvector_parser(state->valstate, state->buf);
     454         [ +  - ]:         751 :                     if (gettoken_tsvector(state->valstate, strval, lenval,
     455                 :             :                                           NULL, NULL, &state->buf))
     456                 :             :                     {
     457                 :         751 :                         state->state = WAITOPERATOR;
     458                 :         751 :                         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                 :          65 :                 break;
     477                 :             : 
     478                 :        1506 :             case WAITOPERATOR:
     479         [ +  + ]:        1506 :                 if (*state->buf == '\0')
     480                 :             :                 {
     481                 :         341 :                     return PT_END;
     482                 :             :                 }
     483         [ +  + ]:        1165 :                 else if (parse_or_operator(state))
     484                 :             :                 {
     485                 :          90 :                     state->state = WAITOPERAND;
     486                 :          90 :                     *operator = OP_OR;
     487                 :          90 :                     return PT_OPR;
     488                 :             :                 }
     489   [ +  +  +  +  :        1075 :                 else if (ISOPERATOR(state->buf))
          +  +  +  +  +  
                +  +  + ]
     490                 :             :                 {
     491                 :             :                     /* ignore other operators in this state too */
     492                 :          95 :                     state->buf++;
     493                 :          95 :                     continue;
     494                 :             :                 }
     495         [ +  + ]:         980 :                 else if (!isspace((unsigned char) *state->buf))
     496                 :             :                 {
     497                 :             :                     /* insert implicit AND between operands */
     498                 :         480 :                     state->state = WAITOPERAND;
     499                 :         480 :                     *operator = OP_AND;
     500                 :         480 :                     return PT_OPR;
     501                 :             :                 }
     502                 :         500 :                 break;
     503                 :             :         }
     504                 :             : 
     505                 :         565 :         state->buf += pg_mblen_cstr(state->buf);
     506                 :             :     }
     507                 :             : }
     508                 :             : 
     509                 :             : static ts_tokentype
     510                 :         176 : gettoken_query_plain(TSQueryParserState state, int8 *operator,
     511                 :             :                      int *lenval, char **strval,
     512                 :             :                      int16 *weight, bool *prefix)
     513                 :             : {
     514                 :         176 :     *weight = 0;
     515                 :         176 :     *prefix = false;
     516                 :             : 
     517         [ +  + ]:         176 :     if (*state->buf == '\0')
     518                 :          88 :         return PT_END;
     519                 :             : 
     520                 :          88 :     *strval = state->buf;
     521                 :          88 :     *lenval = strlen(state->buf);
     522                 :          88 :     state->buf += *lenval;
     523                 :          88 :     state->count++;
     524                 :          88 :     return PT_VAL;
     525                 :             : }
     526                 :             : 
     527                 :             : /*
     528                 :             :  * Push an operator to state->polstr
     529                 :             :  */
     530                 :             : void
     531                 :        4560 : pushOperator(TSQueryParserState state, int8 oper, int16 distance)
     532                 :             : {
     533                 :             :     QueryOperator *tmp;
     534                 :             : 
     535                 :             :     Assert(oper == OP_NOT || oper == OP_AND || oper == OP_OR || oper == OP_PHRASE);
     536                 :             : 
     537                 :        4560 :     tmp = palloc0_object(QueryOperator);
     538                 :        4560 :     tmp->type = QI_OPR;
     539                 :        4560 :     tmp->oper = oper;
     540         [ +  + ]:        4560 :     tmp->distance = (oper == OP_PHRASE) ? distance : 0;
     541                 :             :     /* left is filled in later with findoprnd */
     542                 :             : 
     543                 :        4560 :     state->polstr = lcons(tmp, state->polstr);
     544                 :        4560 : }
     545                 :             : 
     546                 :             : static void
     547                 :        6077 : pushValue_internal(TSQueryParserState state, pg_crc32 valcrc, int distance, int lenval, int weight, bool prefix)
     548                 :             : {
     549                 :             :     QueryOperand *tmp;
     550                 :             : 
     551         [ -  + ]:        6077 :     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         [ -  + ]:        6077 :     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                 :        6077 :     tmp = palloc0_object(QueryOperand);
     563                 :        6077 :     tmp->type = QI_VAL;
     564                 :        6077 :     tmp->weight = weight;
     565                 :        6077 :     tmp->prefix = prefix;
     566                 :        6077 :     tmp->valcrc = (int32) valcrc;
     567                 :        6077 :     tmp->length = lenval;
     568                 :        6077 :     tmp->distance = distance;
     569                 :             : 
     570                 :        6077 :     state->polstr = lcons(tmp, state->polstr);
     571                 :             : }
     572                 :             : 
     573                 :             : /*
     574                 :             :  * Push an operand to state->polstr.
     575                 :             :  *
     576                 :             :  * strval must point to a string equal to state->curop. lenval is the length
     577                 :             :  * of the string.
     578                 :             :  */
     579                 :             : void
     580                 :        6077 : pushValue(TSQueryParserState state, char *strval, int lenval, int16 weight, bool prefix)
     581                 :             : {
     582                 :             :     pg_crc32    valcrc;
     583                 :             : 
     584         [ -  + ]:        6077 :     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                 :        6077 :     INIT_LEGACY_CRC32(valcrc);
     591         [ +  + ]:       21903 :     COMP_LEGACY_CRC32(valcrc, strval, lenval);
     592                 :        6077 :     FIN_LEGACY_CRC32(valcrc);
     593                 :        6077 :     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         [ -  + ]:        6077 :     while (state->curop - state->op + lenval + 1 >= state->lenop)
     597                 :             :     {
     598                 :           0 :         int         used = state->curop - state->op;
     599                 :             : 
     600                 :           0 :         state->lenop *= 2;
     601                 :           0 :         state->op = (char *) repalloc(state->op, state->lenop);
     602                 :           0 :         state->curop = state->op + used;
     603                 :             :     }
     604                 :        6077 :     memcpy(state->curop, strval, lenval);
     605                 :        6077 :     state->curop += lenval;
     606                 :        6077 :     *(state->curop) = '\0';
     607                 :        6077 :     state->curop++;
     608                 :        6077 :     state->sumlen += lenval + 1 /* \0 */ ;
     609                 :             : }
     610                 :             : 
     611                 :             : 
     612                 :             : /*
     613                 :             :  * Push a stopword placeholder to state->polstr
     614                 :             :  */
     615                 :             : void
     616                 :         550 : pushStop(TSQueryParserState state)
     617                 :             : {
     618                 :             :     QueryOperand *tmp;
     619                 :             : 
     620                 :         550 :     tmp = palloc0_object(QueryOperand);
     621                 :         550 :     tmp->type = QI_VALSTOP;
     622                 :             : 
     623                 :         550 :     state->polstr = lcons(tmp, state->polstr);
     624                 :         550 : }
     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                 :        4008 : pushOpStack(OperatorElement *stack, int *lenstack, int8 op, int16 distance)
     637                 :             : {
     638         [ -  + ]:        4008 :     if (*lenstack == STACKDEPTH)    /* internal error */
     639         [ #  # ]:           0 :         elog(ERROR, "tsquery stack too small");
     640                 :             : 
     641                 :        4008 :     stack[*lenstack].op = op;
     642                 :        4008 :     stack[*lenstack].distance = distance;
     643                 :             : 
     644                 :        4008 :     (*lenstack)++;
     645                 :        4008 : }
     646                 :             : 
     647                 :             : static void
     648                 :        7536 : cleanOpStack(TSQueryParserState state,
     649                 :             :              OperatorElement *stack, int *lenstack, int8 op)
     650                 :             : {
     651                 :        7536 :     int         opPriority = OP_PRIORITY(op);
     652                 :             : 
     653         [ +  + ]:       11544 :     while (*lenstack)
     654                 :             :     {
     655                 :             :         /* NOT is right associative unlike to others */
     656   [ +  +  +  +  :        4351 :         if ((op != OP_NOT && opPriority > OP_PRIORITY(stack[*lenstack - 1].op)) ||
                   +  + ]
     657         [ -  + ]:         228 :             (op == OP_NOT && opPriority >= OP_PRIORITY(stack[*lenstack - 1].op)))
     658                 :             :             break;
     659                 :             : 
     660                 :        4008 :         (*lenstack)--;
     661                 :        4008 :         pushOperator(state, stack[*lenstack].op,
     662                 :        4008 :                      stack[*lenstack].distance);
     663                 :             :     }
     664                 :        7536 : }
     665                 :             : 
     666                 :             : /*
     667                 :             :  * Make polish (prefix) notation of query.
     668                 :             :  *
     669                 :             :  * See parse_tsquery for explanation of pushval.
     670                 :             :  */
     671                 :             : static void
     672                 :        3540 : makepol(TSQueryParserState state,
     673                 :             :         PushFunction pushval,
     674                 :             :         void *opaque)
     675                 :             : {
     676                 :        3540 :     int8        operator = 0;
     677                 :             :     ts_tokentype type;
     678                 :        3540 :     int         lenval = 0;
     679                 :        3540 :     char       *strval = NULL;
     680                 :             :     OperatorElement opstack[STACKDEPTH];
     681                 :        3540 :     int         lenstack = 0;
     682                 :        3540 :     int16       weight = 0;
     683                 :             :     bool        prefix;
     684                 :             : 
     685                 :             :     /* since this function recurses, it could be driven to stack overflow */
     686                 :        3540 :     check_stack_depth();
     687                 :             : 
     688                 :       14396 :     while ((type = state->gettoken(state, &operator,
     689                 :             :                                    &lenval, &strval,
     690         [ +  + ]:       14396 :                                    &weight, &prefix)) != PT_END)
     691                 :             :     {
     692   [ +  +  +  +  :       11641 :         switch (type)
                      + ]
     693                 :             :         {
     694                 :        6075 :             case PT_VAL:
     695                 :        6075 :                 pushval(opaque, state, strval, lenval, weight, prefix);
     696                 :        6075 :                 break;
     697                 :        4008 :             case PT_OPR:
     698                 :        4008 :                 cleanOpStack(state, opstack, &lenstack, operator);
     699                 :        4008 :                 pushOpStack(opstack, &lenstack, operator, weight);
     700                 :        4008 :                 break;
     701                 :         773 :             case PT_OPEN:
     702                 :         773 :                 makepol(state, pushval, opaque);
     703                 :         773 :                 break;
     704                 :         773 :             case PT_CLOSE:
     705                 :         773 :                 cleanOpStack(state, opstack, &lenstack, OP_OR /* lowest */ );
     706                 :         785 :                 return;
     707                 :          12 :             case PT_ERR:
     708                 :             :             default:
     709                 :             :                 /* don't overwrite a soft error saved by gettoken function */
     710   [ +  -  +  -  :          12 :                 if (!SOFT_ERROR_OCCURRED(state->escontext))
                   +  + ]
     711         [ +  + ]:           8 :                     errsave(state->escontext,
     712                 :             :                             (errcode(ERRCODE_SYNTAX_ERROR),
     713                 :             :                              errmsg("syntax error in tsquery: \"%s\"",
     714                 :             :                                     state->buffer)));
     715                 :          12 :                 return;
     716                 :             :         }
     717                 :             :         /* detect soft error in pushval or recursion */
     718   [ +  +  +  -  :       10856 :         if (SOFT_ERROR_OCCURRED(state->escontext))
                   -  + ]
     719                 :           0 :             return;
     720                 :             :     }
     721                 :             : 
     722                 :        2755 :     cleanOpStack(state, opstack, &lenstack, OP_OR /* lowest */ );
     723                 :             : }
     724                 :             : 
     725                 :             : static void
     726                 :       11175 : findoprnd_recurse(QueryItem *ptr, uint32 *pos, int nnodes, bool *needcleanup)
     727                 :             : {
     728                 :             :     /* since this function recurses, it could be driven to stack overflow. */
     729                 :       11175 :     check_stack_depth();
     730                 :             : 
     731         [ -  + ]:       11175 :     if (*pos >= nnodes)
     732         [ #  # ]:           0 :         elog(ERROR, "malformed tsquery: operand not found");
     733                 :             : 
     734         [ +  + ]:       11175 :     if (ptr[*pos].type == QI_VAL)
     735                 :             :     {
     736                 :        6065 :         (*pos)++;
     737                 :             :     }
     738         [ +  + ]:        5110 :     else if (ptr[*pos].type == QI_VALSTOP)
     739                 :             :     {
     740                 :         550 :         *needcleanup = true;    /* we'll have to remove stop words */
     741                 :         550 :         (*pos)++;
     742                 :             :     }
     743                 :             :     else
     744                 :             :     {
     745                 :             :         Assert(ptr[*pos].type == QI_OPR);
     746                 :             : 
     747         [ +  + ]:        4560 :         if (ptr[*pos].qoperator.oper == OP_NOT)
     748                 :             :         {
     749                 :         682 :             ptr[*pos].qoperator.left = 1;   /* fixed offset */
     750                 :         682 :             (*pos)++;
     751                 :             : 
     752                 :             :             /* process the only argument */
     753                 :         682 :             findoprnd_recurse(ptr, pos, nnodes, needcleanup);
     754                 :             :         }
     755                 :             :         else
     756                 :             :         {
     757                 :        3878 :             QueryOperator *curitem = &ptr[*pos].qoperator;
     758                 :        3878 :             int         tmp = *pos; /* save current position */
     759                 :             : 
     760                 :             :             Assert(curitem->oper == OP_AND ||
     761                 :             :                    curitem->oper == OP_OR ||
     762                 :             :                    curitem->oper == OP_PHRASE);
     763                 :             : 
     764                 :        3878 :             (*pos)++;
     765                 :             : 
     766                 :             :             /* process RIGHT argument */
     767                 :        3878 :             findoprnd_recurse(ptr, pos, nnodes, needcleanup);
     768                 :             : 
     769                 :        3878 :             curitem->left = *pos - tmp; /* set LEFT arg's offset */
     770                 :             : 
     771                 :             :             /* process LEFT argument */
     772                 :        3878 :             findoprnd_recurse(ptr, pos, nnodes, needcleanup);
     773                 :             :         }
     774                 :             :     }
     775                 :       11175 : }
     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                 :        2737 : findoprnd(QueryItem *ptr, int size, bool *needcleanup)
     785                 :             : {
     786                 :             :     uint32      pos;
     787                 :             : 
     788                 :        2737 :     *needcleanup = false;
     789                 :        2737 :     pos = 0;
     790                 :        2737 :     findoprnd_recurse(ptr, &pos, size, needcleanup);
     791                 :             : 
     792         [ -  + ]:        2737 :     if (pos != size)
     793         [ #  # ]:           0 :         elog(ERROR, "malformed tsquery: extra nodes");
     794                 :        2737 : }
     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                 :        2767 : parse_tsquery(char *buf,
     818                 :             :               PushFunction pushval,
     819                 :             :               void *opaque,
     820                 :             :               int flags,
     821                 :             :               Node *escontext)
     822                 :             : {
     823                 :             :     struct TSQueryParserStateData state;
     824                 :             :     int         i;
     825                 :             :     TSQuery     query;
     826                 :             :     int         commonlen;
     827                 :             :     QueryItem  *ptr;
     828                 :             :     ListCell   *cell;
     829                 :             :     bool        noisy;
     830                 :             :     bool        needcleanup;
     831                 :        2767 :     int         tsv_flags = P_TSV_OPR_IS_DELIM | P_TSV_IS_TSQUERY;
     832                 :             : 
     833                 :             :     /* plain should not be used with web */
     834                 :             :     Assert((flags & (P_TSQ_PLAIN | P_TSQ_WEB)) != (P_TSQ_PLAIN | P_TSQ_WEB));
     835                 :             : 
     836                 :             :     /* select suitable tokenizer */
     837         [ +  + ]:        2767 :     if (flags & P_TSQ_PLAIN)
     838                 :          88 :         state.gettoken = gettoken_query_plain;
     839         [ +  + ]:        2679 :     else if (flags & P_TSQ_WEB)
     840                 :             :     {
     841                 :         341 :         state.gettoken = gettoken_query_websearch;
     842                 :         341 :         tsv_flags |= P_TSV_IS_WEB;
     843                 :             :     }
     844                 :             :     else
     845                 :        2338 :         state.gettoken = gettoken_query_standard;
     846                 :             : 
     847                 :             :     /* emit nuisance NOTICEs only if not doing soft errors */
     848   [ +  +  -  + ]:        2767 :     noisy = !(escontext && IsA(escontext, ErrorSaveContext));
     849                 :             : 
     850                 :             :     /* init state */
     851                 :        2767 :     state.buffer = buf;
     852                 :        2767 :     state.buf = buf;
     853                 :        2767 :     state.count = 0;
     854                 :        2767 :     state.state = WAITFIRSTOPERAND;
     855                 :        2767 :     state.polstr = NIL;
     856                 :        2767 :     state.escontext = escontext;
     857                 :             : 
     858                 :             :     /* init value parser's state */
     859                 :        2767 :     state.valstate = init_tsvector_parser(state.buffer, tsv_flags, escontext);
     860                 :             : 
     861                 :             :     /* init list of operand */
     862                 :        2767 :     state.sumlen = 0;
     863                 :        2767 :     state.lenop = 64;
     864                 :        2767 :     state.curop = state.op = (char *) palloc(state.lenop);
     865                 :        2767 :     *(state.curop) = '\0';
     866                 :             : 
     867                 :             :     /* parse query & make polish notation (postfix, but in reverse order) */
     868                 :        2767 :     makepol(&state, pushval, opaque);
     869                 :             : 
     870                 :        2767 :     close_tsvector_parser(state.valstate);
     871                 :             : 
     872   [ +  +  +  -  :        2767 :     if (SOFT_ERROR_OCCURRED(escontext))
                   +  + ]
     873                 :          12 :         return NULL;
     874                 :             : 
     875         [ +  + ]:        2755 :     if (state.polstr == NIL)
     876                 :             :     {
     877         [ +  - ]:          18 :         if (noisy)
     878         [ +  + ]:          18 :             ereport(NOTICE,
     879                 :             :                     (errmsg("text-search query doesn't contain lexemes: \"%s\"",
     880                 :             :                             state.buffer)));
     881                 :          18 :         query = (TSQuery) palloc(HDRSIZETQ);
     882                 :          18 :         SET_VARSIZE(query, HDRSIZETQ);
     883                 :          18 :         query->size = 0;
     884                 :          18 :         return query;
     885                 :             :     }
     886                 :             : 
     887         [ -  + ]:        2737 :     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                 :        2737 :     commonlen = COMPUTESIZE(list_length(state.polstr), state.sumlen);
     892                 :             : 
     893                 :             :     /* Pack the QueryItems in the final TSQuery struct to return to caller */
     894                 :        2737 :     query = (TSQuery) palloc0(commonlen);
     895                 :        2737 :     SET_VARSIZE(query, commonlen);
     896                 :        2737 :     query->size = list_length(state.polstr);
     897                 :        2737 :     ptr = GETQUERY(query);
     898                 :             : 
     899                 :             :     /* Copy QueryItems to TSQuery */
     900                 :        2737 :     i = 0;
     901   [ +  -  +  +  :       13912 :     foreach(cell, state.polstr)
                   +  + ]
     902                 :             :     {
     903                 :       11175 :         QueryItem  *item = (QueryItem *) lfirst(cell);
     904                 :             : 
     905   [ +  +  +  - ]:       11175 :         switch (item->type)
     906                 :             :         {
     907                 :        6065 :             case QI_VAL:
     908                 :        6065 :                 memcpy(&ptr[i], item, sizeof(QueryOperand));
     909                 :        6065 :                 break;
     910                 :         550 :             case QI_VALSTOP:
     911                 :         550 :                 ptr[i].type = QI_VALSTOP;
     912                 :         550 :                 break;
     913                 :        4560 :             case QI_OPR:
     914                 :        4560 :                 memcpy(&ptr[i], item, sizeof(QueryOperator));
     915                 :        4560 :                 break;
     916                 :           0 :             default:
     917         [ #  # ]:           0 :                 elog(ERROR, "unrecognized QueryItem type: %d", item->type);
     918                 :             :         }
     919                 :       11175 :         i++;
     920                 :             :     }
     921                 :             : 
     922                 :             :     /* Copy all the operand strings to TSQuery */
     923                 :        2737 :     memcpy(GETOPERAND(query), state.op, state.sumlen);
     924                 :        2737 :     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                 :        2737 :     findoprnd(ptr, query->size, &needcleanup);
     931                 :             : 
     932                 :             :     /*
     933                 :             :      * If there are QI_VALSTOP nodes, delete them and simplify the tree.
     934                 :             :      */
     935         [ +  + ]:        2737 :     if (needcleanup)
     936                 :         355 :         query = cleanup_tsquery_stopwords(query, noisy);
     937                 :             : 
     938                 :        2737 :     return query;
     939                 :             : }
     940                 :             : 
     941                 :             : static void
     942                 :        3530 : pushval_asis(void *opaque, TSQueryParserState state, char *strval, int lenval,
     943                 :             :              int16 weight, bool prefix)
     944                 :             : {
     945                 :        3530 :     pushValue(state, strval, lenval, weight, prefix);
     946                 :        3530 : }
     947                 :             : 
     948                 :             : /*
     949                 :             :  * in without morphology
     950                 :             :  */
     951                 :             : Datum
     952                 :        1725 : tsqueryin(PG_FUNCTION_ARGS)
     953                 :             : {
     954                 :        1725 :     char       *in = PG_GETARG_CSTRING(0);
     955                 :        1725 :     Node       *escontext = fcinfo->context;
     956                 :             : 
     957                 :        1725 :     PG_RETURN_TSQUERY(parse_tsquery(in,
     958                 :             :                                     pushval_asis,
     959                 :             :                                     NULL,
     960                 :             :                                     0,
     961                 :             :                                     escontext));
     962                 :             : }
     963                 :             : 
     964                 :             : /*
     965                 :             :  * out function
     966                 :             :  */
     967                 :             : typedef struct
     968                 :             : {
     969                 :             :     QueryItem  *curpol;
     970                 :             :     char       *buf;
     971                 :             :     char       *cur;
     972                 :             :     char       *op;
     973                 :             :     int         buflen;
     974                 :             : } INFIX;
     975                 :             : 
     976                 :             : /* Makes sure inf->buf is large enough for adding 'addsize' bytes */
     977                 :             : #define RESIZEBUF(inf, addsize) \
     978                 :             : while( ( (inf)->cur - (inf)->buf ) + (addsize) + 1 >= (inf)->buflen ) \
     979                 :             : { \
     980                 :             :     int len = (inf)->cur - (inf)->buf; \
     981                 :             :     (inf)->buflen *= 2; \
     982                 :             :     (inf)->buf = (char*) repalloc( (void*)(inf)->buf, (inf)->buflen ); \
     983                 :             :     (inf)->cur = (inf)->buf + len; \
     984                 :             : }
     985                 :             : 
     986                 :             : /*
     987                 :             :  * recursively traverse the tree and
     988                 :             :  * print it in infix (human-readable) form
     989                 :             :  */
     990                 :             : static void
     991                 :        4782 : infix(INFIX *in, int parentPriority, bool rightPhraseOp)
     992                 :             : {
     993                 :             :     /* since this function recurses, it could be driven to stack overflow. */
     994                 :        4782 :     check_stack_depth();
     995                 :             : 
     996         [ +  + ]:        4782 :     if (in->curpol->type == QI_VAL)
     997                 :             :     {
     998                 :        2763 :         QueryOperand *curpol = &in->curpol->qoperand;
     999                 :        2763 :         char       *op = in->op + curpol->distance;
    1000                 :             :         int         clen;
    1001                 :             : 
    1002         [ +  + ]:        4506 :         RESIZEBUF(in, curpol->length * (pg_database_encoding_max_length() + 1) + 2 + 6);
    1003                 :        2763 :         *(in->cur) = '\'';
    1004                 :        2763 :         in->cur++;
    1005         [ +  + ]:       10701 :         while (*op)
    1006                 :             :         {
    1007         [ +  + ]:        7938 :             if (t_iseq(op, '\''))
    1008                 :             :             {
    1009                 :           8 :                 *(in->cur) = '\'';
    1010                 :           8 :                 in->cur++;
    1011                 :             :             }
    1012         [ +  + ]:        7930 :             else if (t_iseq(op, '\\'))
    1013                 :             :             {
    1014                 :           4 :                 *(in->cur) = '\\';
    1015                 :           4 :                 in->cur++;
    1016                 :             :             }
    1017                 :             : 
    1018                 :        7938 :             clen = ts_copychar_cstr(in->cur, op);
    1019                 :        7938 :             op += clen;
    1020                 :        7938 :             in->cur += clen;
    1021                 :             :         }
    1022                 :        2763 :         *(in->cur) = '\'';
    1023                 :        2763 :         in->cur++;
    1024   [ +  +  +  + ]:        2763 :         if (curpol->weight || curpol->prefix)
    1025                 :             :         {
    1026                 :         116 :             *(in->cur) = ':';
    1027                 :         116 :             in->cur++;
    1028         [ +  + ]:         116 :             if (curpol->prefix)
    1029                 :             :             {
    1030                 :          16 :                 *(in->cur) = '*';
    1031                 :          16 :                 in->cur++;
    1032                 :             :             }
    1033         [ +  + ]:         116 :             if (curpol->weight & (1 << 3))
    1034                 :             :             {
    1035                 :          40 :                 *(in->cur) = 'A';
    1036                 :          40 :                 in->cur++;
    1037                 :             :             }
    1038         [ +  + ]:         116 :             if (curpol->weight & (1 << 2))
    1039                 :             :             {
    1040                 :          64 :                 *(in->cur) = 'B';
    1041                 :          64 :                 in->cur++;
    1042                 :             :             }
    1043         [ +  + ]:         116 :             if (curpol->weight & (1 << 1))
    1044                 :             :             {
    1045                 :          12 :                 *(in->cur) = 'C';
    1046                 :          12 :                 in->cur++;
    1047                 :             :             }
    1048         [ +  + ]:         116 :             if (curpol->weight & 1)
    1049                 :             :             {
    1050                 :           4 :                 *(in->cur) = 'D';
    1051                 :           4 :                 in->cur++;
    1052                 :             :             }
    1053                 :             :         }
    1054                 :        2763 :         *(in->cur) = '\0';
    1055                 :        2763 :         in->curpol++;
    1056                 :             :     }
    1057         [ +  + ]:        2019 :     else if (in->curpol->qoperator.oper == OP_NOT)
    1058                 :             :     {
    1059                 :         248 :         int         priority = QO_PRIORITY(in->curpol);
    1060                 :             : 
    1061         [ -  + ]:         248 :         if (priority < parentPriority)
    1062                 :             :         {
    1063         [ #  # ]:           0 :             RESIZEBUF(in, 2);
    1064                 :           0 :             sprintf(in->cur, "( ");
    1065                 :           0 :             in->cur = strchr(in->cur, '\0');
    1066                 :             :         }
    1067         [ -  + ]:         248 :         RESIZEBUF(in, 1);
    1068                 :         248 :         *(in->cur) = '!';
    1069                 :         248 :         in->cur++;
    1070                 :         248 :         *(in->cur) = '\0';
    1071                 :         248 :         in->curpol++;
    1072                 :             : 
    1073                 :         248 :         infix(in, priority, false);
    1074         [ -  + ]:         248 :         if (priority < parentPriority)
    1075                 :             :         {
    1076         [ #  # ]:           0 :             RESIZEBUF(in, 2);
    1077                 :           0 :             sprintf(in->cur, " )");
    1078                 :           0 :             in->cur = strchr(in->cur, '\0');
    1079                 :             :         }
    1080                 :             :     }
    1081                 :             :     else
    1082                 :             :     {
    1083                 :        1771 :         int8        op = in->curpol->qoperator.oper;
    1084                 :        1771 :         int         priority = QO_PRIORITY(in->curpol);
    1085                 :        1771 :         int16       distance = in->curpol->qoperator.distance;
    1086                 :             :         INFIX       nrm;
    1087                 :        1771 :         bool        needParenthesis = false;
    1088                 :             : 
    1089                 :        1771 :         in->curpol++;
    1090   [ +  +  +  + ]:        1771 :         if (priority < parentPriority ||
    1091                 :             :         /* phrase operator depends on order */
    1092         [ +  + ]:         468 :             (op == OP_PHRASE && rightPhraseOp))
    1093                 :             :         {
    1094                 :         220 :             needParenthesis = true;
    1095         [ -  + ]:         220 :             RESIZEBUF(in, 2);
    1096                 :         220 :             sprintf(in->cur, "( ");
    1097                 :         220 :             in->cur = strchr(in->cur, '\0');
    1098                 :             :         }
    1099                 :             : 
    1100                 :        1771 :         nrm.curpol = in->curpol;
    1101                 :        1771 :         nrm.op = in->op;
    1102                 :        1771 :         nrm.buflen = 16;
    1103                 :        1771 :         nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
    1104                 :             : 
    1105                 :             :         /* get right operand */
    1106                 :        1771 :         infix(&nrm, priority, (op == OP_PHRASE));
    1107                 :             : 
    1108                 :             :         /* get & print left operand */
    1109                 :        1771 :         in->curpol = nrm.curpol;
    1110                 :        1771 :         infix(in, priority, false);
    1111                 :             : 
    1112                 :             :         /* print operator & right operand */
    1113         [ +  + ]:        2419 :         RESIZEBUF(in, 3 + (2 + 10 /* distance */ ) + (nrm.cur - nrm.buf));
    1114   [ +  +  +  - ]:        1771 :         switch (op)
    1115                 :             :         {
    1116                 :         480 :             case OP_OR:
    1117                 :         480 :                 sprintf(in->cur, " | %s", nrm.buf);
    1118                 :         480 :                 break;
    1119                 :         815 :             case OP_AND:
    1120                 :         815 :                 sprintf(in->cur, " & %s", nrm.buf);
    1121                 :         815 :                 break;
    1122                 :         476 :             case OP_PHRASE:
    1123         [ +  + ]:         476 :                 if (distance != 1)
    1124                 :         116 :                     sprintf(in->cur, " <%d> %s", distance, nrm.buf);
    1125                 :             :                 else
    1126                 :         360 :                     sprintf(in->cur, " <-> %s", nrm.buf);
    1127                 :         476 :                 break;
    1128                 :           0 :             default:
    1129                 :             :                 /* OP_NOT is handled in above if-branch */
    1130         [ #  # ]:           0 :                 elog(ERROR, "unrecognized operator type: %d", op);
    1131                 :             :         }
    1132                 :        1771 :         in->cur = strchr(in->cur, '\0');
    1133                 :        1771 :         pfree(nrm.buf);
    1134                 :             : 
    1135         [ +  + ]:        1771 :         if (needParenthesis)
    1136                 :             :         {
    1137         [ -  + ]:         220 :             RESIZEBUF(in, 2);
    1138                 :         220 :             sprintf(in->cur, " )");
    1139                 :         220 :             in->cur = strchr(in->cur, '\0');
    1140                 :             :         }
    1141                 :             :     }
    1142                 :        4782 : }
    1143                 :             : 
    1144                 :             : Datum
    1145                 :        1012 : tsqueryout(PG_FUNCTION_ARGS)
    1146                 :             : {
    1147                 :        1012 :     TSQuery     query = PG_GETARG_TSQUERY(0);
    1148                 :             :     INFIX       nrm;
    1149                 :             : 
    1150         [ +  + ]:        1012 :     if (query->size == 0)
    1151                 :             :     {
    1152                 :          20 :         char       *b = palloc(1);
    1153                 :             : 
    1154                 :          20 :         *b = '\0';
    1155                 :          20 :         PG_RETURN_POINTER(b);
    1156                 :             :     }
    1157                 :         992 :     nrm.curpol = GETQUERY(query);
    1158                 :         992 :     nrm.buflen = 32;
    1159                 :         992 :     nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
    1160                 :         992 :     *(nrm.cur) = '\0';
    1161                 :         992 :     nrm.op = GETOPERAND(query);
    1162                 :         992 :     infix(&nrm, -1 /* lowest priority */ , false);
    1163                 :             : 
    1164         [ -  + ]:         992 :     PG_FREE_IF_COPY(query, 0);
    1165                 :         992 :     PG_RETURN_CSTRING(nrm.buf);
    1166                 :             : }
    1167                 :             : 
    1168                 :             : /*
    1169                 :             :  * Binary Input / Output functions. The binary format is as follows:
    1170                 :             :  *
    1171                 :             :  * uint32    number of operators/operands in the query
    1172                 :             :  *
    1173                 :             :  * Followed by the operators and operands, in prefix notation. For each
    1174                 :             :  * operand:
    1175                 :             :  *
    1176                 :             :  * uint8    type, QI_VAL
    1177                 :             :  * uint8    weight
    1178                 :             :  * uint8    prefix
    1179                 :             :  *          operand text in client encoding, null-terminated
    1180                 :             :  *
    1181                 :             :  * For each operator:
    1182                 :             :  *
    1183                 :             :  * uint8    type, QI_OPR
    1184                 :             :  * uint8    operator, one of OP_AND, OP_PHRASE OP_OR, OP_NOT.
    1185                 :             :  * uint16   distance (only for OP_PHRASE)
    1186                 :             :  */
    1187                 :             : Datum
    1188                 :           0 : tsquerysend(PG_FUNCTION_ARGS)
    1189                 :             : {
    1190                 :           0 :     TSQuery     query = PG_GETARG_TSQUERY(0);
    1191                 :             :     StringInfoData buf;
    1192                 :             :     int         i;
    1193                 :           0 :     QueryItem  *item = GETQUERY(query);
    1194                 :             : 
    1195                 :           0 :     pq_begintypsend(&buf);
    1196                 :             : 
    1197                 :           0 :     pq_sendint32(&buf, query->size);
    1198         [ #  # ]:           0 :     for (i = 0; i < query->size; i++)
    1199                 :             :     {
    1200                 :           0 :         pq_sendint8(&buf, item->type);
    1201                 :             : 
    1202      [ #  #  # ]:           0 :         switch (item->type)
    1203                 :             :         {
    1204                 :           0 :             case QI_VAL:
    1205                 :           0 :                 pq_sendint8(&buf, item->qoperand.weight);
    1206                 :           0 :                 pq_sendint8(&buf, item->qoperand.prefix);
    1207                 :           0 :                 pq_sendstring(&buf, GETOPERAND(query) + item->qoperand.distance);
    1208                 :           0 :                 break;
    1209                 :           0 :             case QI_OPR:
    1210                 :           0 :                 pq_sendint8(&buf, item->qoperator.oper);
    1211         [ #  # ]:           0 :                 if (item->qoperator.oper == OP_PHRASE)
    1212                 :           0 :                     pq_sendint16(&buf, item->qoperator.distance);
    1213                 :           0 :                 break;
    1214                 :           0 :             default:
    1215         [ #  # ]:           0 :                 elog(ERROR, "unrecognized tsquery node type: %d", item->type);
    1216                 :             :         }
    1217                 :           0 :         item++;
    1218                 :             :     }
    1219                 :             : 
    1220         [ #  # ]:           0 :     PG_FREE_IF_COPY(query, 0);
    1221                 :             : 
    1222                 :           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
    1223                 :             : }
    1224                 :             : 
    1225                 :             : Datum
    1226                 :           0 : tsqueryrecv(PG_FUNCTION_ARGS)
    1227                 :             : {
    1228                 :           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
    1229                 :             :     TSQuery     query;
    1230                 :             :     int         len;
    1231                 :             :     QueryItem  *item;
    1232                 :             :     int         datalen;
    1233                 :             :     char       *ptr;
    1234                 :             :     uint32      size;
    1235                 :             :     const char **operands;
    1236                 :             :     bool        needcleanup;
    1237                 :             : 
    1238                 :           0 :     size = pq_getmsgint(buf, sizeof(uint32));
    1239         [ #  # ]:           0 :     if (size > (MaxAllocSize / sizeof(QueryItem)))
    1240         [ #  # ]:           0 :         elog(ERROR, "invalid size of tsquery");
    1241                 :             : 
    1242                 :             :     /* Allocate space to temporarily hold operand strings */
    1243                 :           0 :     operands = palloc(size * sizeof(char *));
    1244                 :             : 
    1245                 :             :     /* Allocate space for all the QueryItems. */
    1246                 :           0 :     len = HDRSIZETQ + sizeof(QueryItem) * size;
    1247                 :           0 :     query = (TSQuery) palloc0(len);
    1248                 :           0 :     query->size = size;
    1249                 :           0 :     item = GETQUERY(query);
    1250                 :             : 
    1251                 :           0 :     datalen = 0;
    1252         [ #  # ]:           0 :     for (uint32 i = 0; i < size; i++)
    1253                 :             :     {
    1254                 :           0 :         item->type = (int8) pq_getmsgint(buf, sizeof(int8));
    1255                 :             : 
    1256         [ #  # ]:           0 :         if (item->type == QI_VAL)
    1257                 :             :         {
    1258                 :             :             size_t      val_len;    /* length after recoding to server
    1259                 :             :                                      * encoding */
    1260                 :             :             uint8       weight;
    1261                 :             :             uint8       prefix;
    1262                 :             :             const char *val;
    1263                 :             :             pg_crc32    valcrc;
    1264                 :             : 
    1265                 :           0 :             weight = (uint8) pq_getmsgint(buf, sizeof(uint8));
    1266                 :           0 :             prefix = (uint8) pq_getmsgint(buf, sizeof(uint8));
    1267                 :           0 :             val = pq_getmsgstring(buf);
    1268                 :           0 :             val_len = strlen(val);
    1269                 :             : 
    1270                 :             :             /* Sanity checks */
    1271                 :             : 
    1272         [ #  # ]:           0 :             if (weight > 0xF)
    1273         [ #  # ]:           0 :                 elog(ERROR, "invalid tsquery: invalid weight bitmap");
    1274                 :             : 
    1275         [ #  # ]:           0 :             if (val_len > MAXSTRLEN)
    1276         [ #  # ]:           0 :                 elog(ERROR, "invalid tsquery: operand too long");
    1277                 :             : 
    1278         [ #  # ]:           0 :             if (datalen > MAXSTRPOS)
    1279         [ #  # ]:           0 :                 elog(ERROR, "invalid tsquery: total operand length exceeded");
    1280                 :             : 
    1281                 :             :             /* Looks valid. */
    1282                 :             : 
    1283                 :           0 :             INIT_LEGACY_CRC32(valcrc);
    1284         [ #  # ]:           0 :             COMP_LEGACY_CRC32(valcrc, val, val_len);
    1285                 :           0 :             FIN_LEGACY_CRC32(valcrc);
    1286                 :             : 
    1287                 :           0 :             item->qoperand.weight = weight;
    1288                 :           0 :             item->qoperand.prefix = (prefix) ? true : false;
    1289                 :           0 :             item->qoperand.valcrc = (int32) valcrc;
    1290                 :           0 :             item->qoperand.length = val_len;
    1291                 :           0 :             item->qoperand.distance = datalen;
    1292                 :             : 
    1293                 :             :             /*
    1294                 :             :              * Operand strings are copied to the final struct after this loop;
    1295                 :             :              * here we just collect them to an array
    1296                 :             :              */
    1297                 :           0 :             operands[i] = val;
    1298                 :             : 
    1299                 :           0 :             datalen += val_len + 1; /* + 1 for the '\0' terminator */
    1300                 :             :         }
    1301         [ #  # ]:           0 :         else if (item->type == QI_OPR)
    1302                 :             :         {
    1303                 :             :             int8        oper;
    1304                 :             : 
    1305                 :           0 :             oper = (int8) pq_getmsgint(buf, sizeof(int8));
    1306   [ #  #  #  #  :           0 :             if (oper != OP_NOT && oper != OP_OR && oper != OP_AND && oper != OP_PHRASE)
             #  #  #  # ]
    1307         [ #  # ]:           0 :                 elog(ERROR, "invalid tsquery: unrecognized operator type %d",
    1308                 :             :                      (int) oper);
    1309         [ #  # ]:           0 :             if (i == size - 1)
    1310         [ #  # ]:           0 :                 elog(ERROR, "invalid pointer to right operand");
    1311                 :             : 
    1312                 :           0 :             item->qoperator.oper = oper;
    1313         [ #  # ]:           0 :             if (oper == OP_PHRASE)
    1314                 :           0 :                 item->qoperator.distance = (int16) pq_getmsgint(buf, sizeof(int16));
    1315                 :             :         }
    1316                 :             :         else
    1317         [ #  # ]:           0 :             elog(ERROR, "unrecognized tsquery node type: %d", item->type);
    1318                 :             : 
    1319                 :           0 :         item++;
    1320                 :             :     }
    1321                 :             : 
    1322                 :             :     /* Enlarge buffer to make room for the operand values. */
    1323                 :           0 :     query = (TSQuery) repalloc(query, len + datalen);
    1324                 :           0 :     item = GETQUERY(query);
    1325                 :           0 :     ptr = GETOPERAND(query);
    1326                 :             : 
    1327                 :             :     /*
    1328                 :             :      * Fill in the left-pointers. Checks that the tree is well-formed as a
    1329                 :             :      * side-effect.
    1330                 :             :      */
    1331                 :           0 :     findoprnd(item, size, &needcleanup);
    1332                 :             : 
    1333                 :             :     /* Can't have found any QI_VALSTOP nodes */
    1334                 :             :     Assert(!needcleanup);
    1335                 :             : 
    1336                 :             :     /* Copy operands to output struct */
    1337         [ #  # ]:           0 :     for (uint32 i = 0; i < size; i++)
    1338                 :             :     {
    1339         [ #  # ]:           0 :         if (item->type == QI_VAL)
    1340                 :             :         {
    1341                 :           0 :             memcpy(ptr, operands[i], item->qoperand.length + 1);
    1342                 :           0 :             ptr += item->qoperand.length + 1;
    1343                 :             :         }
    1344                 :           0 :         item++;
    1345                 :             :     }
    1346                 :             : 
    1347                 :           0 :     pfree(operands);
    1348                 :             : 
    1349                 :             :     Assert(ptr - GETOPERAND(query) == datalen);
    1350                 :             : 
    1351                 :           0 :     SET_VARSIZE(query, len + datalen);
    1352                 :             : 
    1353                 :           0 :     PG_RETURN_TSQUERY(query);
    1354                 :             : }
    1355                 :             : 
    1356                 :             : /*
    1357                 :             :  * debug function, used only for view query
    1358                 :             :  * which will be executed in non-leaf pages in index
    1359                 :             :  */
    1360                 :             : Datum
    1361                 :           0 : tsquerytree(PG_FUNCTION_ARGS)
    1362                 :             : {
    1363                 :           0 :     TSQuery     query = PG_GETARG_TSQUERY(0);
    1364                 :             :     INFIX       nrm;
    1365                 :             :     text       *res;
    1366                 :             :     QueryItem  *q;
    1367                 :             :     int         len;
    1368                 :             : 
    1369         [ #  # ]:           0 :     if (query->size == 0)
    1370                 :             :     {
    1371                 :           0 :         res = (text *) palloc(VARHDRSZ);
    1372                 :           0 :         SET_VARSIZE(res, VARHDRSZ);
    1373                 :           0 :         PG_RETURN_POINTER(res);
    1374                 :             :     }
    1375                 :             : 
    1376                 :           0 :     q = clean_NOT(GETQUERY(query), &len);
    1377                 :             : 
    1378         [ #  # ]:           0 :     if (!q)
    1379                 :             :     {
    1380                 :           0 :         res = cstring_to_text("T");
    1381                 :             :     }
    1382                 :             :     else
    1383                 :             :     {
    1384                 :           0 :         nrm.curpol = q;
    1385                 :           0 :         nrm.buflen = 32;
    1386                 :           0 :         nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
    1387                 :           0 :         *(nrm.cur) = '\0';
    1388                 :           0 :         nrm.op = GETOPERAND(query);
    1389                 :           0 :         infix(&nrm, -1, false);
    1390                 :           0 :         res = cstring_to_text_with_len(nrm.buf, nrm.cur - nrm.buf);
    1391                 :           0 :         pfree(q);
    1392                 :             :     }
    1393                 :             : 
    1394         [ #  # ]:           0 :     PG_FREE_IF_COPY(query, 0);
    1395                 :             : 
    1396                 :           0 :     PG_RETURN_TEXT_P(res);
    1397                 :             : }
        

Generated by: LCOV version 2.0-1