LCOV - code coverage report
Current view: top level - src/backend/utils/adt - tsquery.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 353 484 72.9 %
Date: 2017-09-29 13:40:31 Functions: 17 20 85.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.11