LCOV - code coverage report
Current view: top level - src/backend/utils/adt - tsquery_util.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 98.9 % 176 174
Test Date: 2026-03-18 04:15:34 Functions: 100.0 % 13 13
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * tsquery_util.c
       4              :  *    Utilities for tsquery datatype
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  *
       8              :  *
       9              :  * IDENTIFICATION
      10              :  *    src/backend/utils/adt/tsquery_util.c
      11              :  *
      12              :  *-------------------------------------------------------------------------
      13              :  */
      14              : 
      15              : #include "postgres.h"
      16              : 
      17              : #include "miscadmin.h"
      18              : #include "tsearch/ts_utils.h"
      19              : #include "varatt.h"
      20              : 
      21              : /*
      22              :  * Build QTNode tree for a tsquery given in QueryItem array format.
      23              :  */
      24              : QTNode *
      25         6072 : QT2QTN(QueryItem *in, char *operand)
      26              : {
      27         6072 :     QTNode     *node = palloc0_object(QTNode);
      28              : 
      29              :     /* since this function recurses, it could be driven to stack overflow. */
      30         6072 :     check_stack_depth();
      31              : 
      32         6072 :     node->valnode = in;
      33              : 
      34         6072 :     if (in->type == QI_OPR)
      35              :     {
      36         2283 :         node->child = palloc0_array(QTNode *, 2);
      37         2283 :         node->child[0] = QT2QTN(in + 1, operand);
      38         2283 :         node->sign = node->child[0]->sign;
      39         2283 :         if (in->qoperator.oper == OP_NOT)
      40           34 :             node->nchild = 1;
      41              :         else
      42              :         {
      43         2249 :             node->nchild = 2;
      44         2249 :             node->child[1] = QT2QTN(in + in->qoperator.left, operand);
      45         2249 :             node->sign |= node->child[1]->sign;
      46              :         }
      47              :     }
      48         3789 :     else if (operand)
      49              :     {
      50         3789 :         node->word = operand + in->qoperand.distance;
      51         3789 :         node->sign = ((uint32) 1) << (((unsigned int) in->qoperand.valcrc) % 32);
      52              :     }
      53              : 
      54         6072 :     return node;
      55              : }
      56              : 
      57              : /*
      58              :  * Free a QTNode tree.
      59              :  *
      60              :  * Referenced "word" and "valnode" items are freed if marked as transient
      61              :  * by flags.
      62              :  */
      63              : void
      64         6743 : QTNFree(QTNode *in)
      65              : {
      66         6743 :     if (!in)
      67            8 :         return;
      68              : 
      69              :     /* since this function recurses, it could be driven to stack overflow. */
      70         6735 :     check_stack_depth();
      71              : 
      72         6735 :     if (in->valnode->type == QI_VAL && in->word && (in->flags & QTN_WORDFREE) != 0)
      73          428 :         pfree(in->word);
      74              : 
      75         6735 :     if (in->valnode->type == QI_OPR)
      76              :     {
      77              :         int         i;
      78              : 
      79         7589 :         for (i = 0; i < in->nchild; i++)
      80         5071 :             QTNFree(in->child[i]);
      81              :     }
      82         6735 :     if (in->child)
      83         2518 :         pfree(in->child);
      84              : 
      85         6735 :     if (in->flags & QTN_NEEDFREE)
      86          822 :         pfree(in->valnode);
      87              : 
      88         6735 :     pfree(in);
      89              : }
      90              : 
      91              : /*
      92              :  * Sort comparator for QTNodes.
      93              :  *
      94              :  * The sort order is somewhat arbitrary.
      95              :  */
      96              : int
      97         2696 : QTNodeCompare(QTNode *an, QTNode *bn)
      98              : {
      99              :     /* since this function recurses, it could be driven to stack overflow. */
     100         2696 :     check_stack_depth();
     101              : 
     102         2696 :     if (an->valnode->type != bn->valnode->type)
     103          795 :         return (an->valnode->type > bn->valnode->type) ? -1 : 1;
     104              : 
     105         1901 :     if (an->valnode->type == QI_OPR)
     106              :     {
     107          367 :         QueryOperator *ao = &an->valnode->qoperator;
     108          367 :         QueryOperator *bo = &bn->valnode->qoperator;
     109              : 
     110          367 :         if (ao->oper != bo->oper)
     111           34 :             return (ao->oper > bo->oper) ? -1 : 1;
     112              : 
     113          333 :         if (an->nchild != bn->nchild)
     114           72 :             return (an->nchild > bn->nchild) ? -1 : 1;
     115              : 
     116              :         {
     117              :             int         i,
     118              :                         res;
     119              : 
     120          439 :             for (i = 0; i < an->nchild; i++)
     121          350 :                 if ((res = QTNodeCompare(an->child[i], bn->child[i])) != 0)
     122          172 :                     return res;
     123              :         }
     124              : 
     125           89 :         if (ao->oper == OP_PHRASE && ao->distance != bo->distance)
     126            4 :             return (ao->distance > bo->distance) ? -1 : 1;
     127              : 
     128           85 :         return 0;
     129              :     }
     130         1534 :     else if (an->valnode->type == QI_VAL)
     131              :     {
     132         1534 :         QueryOperand *ao = &an->valnode->qoperand;
     133         1534 :         QueryOperand *bo = &bn->valnode->qoperand;
     134              : 
     135         1534 :         if (ao->valcrc != bo->valcrc)
     136              :         {
     137         1182 :             return (ao->valcrc > bo->valcrc) ? -1 : 1;
     138              :         }
     139              : 
     140          352 :         return tsCompareString(an->word, ao->length, bn->word, bo->length, false);
     141              :     }
     142              :     else
     143              :     {
     144            0 :         elog(ERROR, "unrecognized QueryItem type: %d", an->valnode->type);
     145              :         return 0;               /* keep compiler quiet */
     146              :     }
     147              : }
     148              : 
     149              : /*
     150              :  * qsort comparator for QTNode pointers.
     151              :  */
     152              : static int
     153         2044 : cmpQTN(const void *a, const void *b)
     154              : {
     155         2044 :     return QTNodeCompare(*(QTNode *const *) a, *(QTNode *const *) b);
     156              : }
     157              : 
     158              : /*
     159              :  * Canonicalize a QTNode tree by sorting the children of AND/OR nodes
     160              :  * into an arbitrary but well-defined order.
     161              :  */
     162              : void
     163         6103 : QTNSort(QTNode *in)
     164              : {
     165              :     int         i;
     166              : 
     167              :     /* since this function recurses, it could be driven to stack overflow. */
     168         6103 :     check_stack_depth();
     169              : 
     170         6103 :     if (in->valnode->type != QI_OPR)
     171         3975 :         return;
     172              : 
     173         6982 :     for (i = 0; i < in->nchild; i++)
     174         4854 :         QTNSort(in->child[i]);
     175         2128 :     if (in->nchild > 1 && in->valnode->qoperator.oper != OP_PHRASE)
     176         1242 :         qsort(in->child, in->nchild, sizeof(QTNode *), cmpQTN);
     177              : }
     178              : 
     179              : /*
     180              :  * Are two QTNode trees equal according to QTNodeCompare?
     181              :  */
     182              : bool
     183          140 : QTNEq(QTNode *a, QTNode *b)
     184              : {
     185          140 :     uint32      sign = a->sign & b->sign;
     186              : 
     187          140 :     if (!(sign == a->sign && sign == b->sign))
     188            4 :         return false;
     189              : 
     190          136 :     return (QTNodeCompare(a, b) == 0);
     191              : }
     192              : 
     193              : /*
     194              :  * Remove unnecessary intermediate nodes. For example:
     195              :  *
     196              :  *  OR          OR
     197              :  * a  OR    -> a b c
     198              :  *   b  c
     199              :  */
     200              : void
     201         5867 : QTNTernary(QTNode *in)
     202              : {
     203              :     int         i;
     204              : 
     205              :     /* since this function recurses, it could be driven to stack overflow. */
     206         5867 :     check_stack_depth();
     207              : 
     208         5867 :     if (in->valnode->type != QI_OPR)
     209         3714 :         return;
     210              : 
     211         6800 :     for (i = 0; i < in->nchild; i++)
     212         4647 :         QTNTernary(in->child[i]);
     213              : 
     214              :     /* Only AND and OR are associative, so don't flatten other node types */
     215         2153 :     if (in->valnode->qoperator.oper != OP_AND &&
     216         1350 :         in->valnode->qoperator.oper != OP_OR)
     217          838 :         return;
     218              : 
     219         4305 :     for (i = 0; i < in->nchild; i++)
     220              :     {
     221         2990 :         QTNode     *cc = in->child[i];
     222              : 
     223         2990 :         if (cc->valnode->type == QI_OPR &&
     224         1041 :             in->valnode->qoperator.oper == cc->valnode->qoperator.oper)
     225              :         {
     226          223 :             int         oldnchild = in->nchild;
     227              : 
     228          223 :             in->nchild += cc->nchild - 1;
     229          223 :             in->child = repalloc_array(in->child, QTNode *, in->nchild);
     230              : 
     231          223 :             if (i + 1 != oldnchild)
     232           24 :                 memmove(in->child + i + cc->nchild, in->child + i + 1,
     233           24 :                         (oldnchild - i - 1) * sizeof(QTNode *));
     234              : 
     235          223 :             memcpy(in->child + i, cc->child, cc->nchild * sizeof(QTNode *));
     236          223 :             i += cc->nchild - 1;
     237              : 
     238          223 :             if (cc->flags & QTN_NEEDFREE)
     239           72 :                 pfree(cc->valnode);
     240          223 :             pfree(cc);
     241              :         }
     242              :     }
     243              : }
     244              : 
     245              : /*
     246              :  * Convert a tree to binary tree by inserting intermediate nodes.
     247              :  * (Opposite of QTNTernary)
     248              :  */
     249              : void
     250          834 : QTNBinary(QTNode *in)
     251              : {
     252              :     int         i;
     253              : 
     254              :     /* since this function recurses, it could be driven to stack overflow. */
     255          834 :     check_stack_depth();
     256              : 
     257          834 :     if (in->valnode->type != QI_OPR)
     258          509 :         return;
     259              : 
     260         1037 :     for (i = 0; i < in->nchild; i++)
     261          712 :         QTNBinary(in->child[i]);
     262              : 
     263          407 :     while (in->nchild > 2)
     264              :     {
     265           82 :         QTNode     *nn = palloc0_object(QTNode);
     266              : 
     267           82 :         nn->valnode = palloc0_object(QueryItem);
     268           82 :         nn->child = palloc0_array(QTNode *, 2);
     269              : 
     270           82 :         nn->nchild = 2;
     271           82 :         nn->flags = QTN_NEEDFREE;
     272              : 
     273           82 :         nn->child[0] = in->child[0];
     274           82 :         nn->child[1] = in->child[1];
     275           82 :         nn->sign = nn->child[0]->sign | nn->child[1]->sign;
     276              : 
     277           82 :         nn->valnode->type = in->valnode->type;
     278           82 :         nn->valnode->qoperator.oper = in->valnode->qoperator.oper;
     279              : 
     280           82 :         in->child[0] = nn;
     281           82 :         in->child[1] = in->child[in->nchild - 1];
     282           82 :         in->nchild--;
     283              :     }
     284              : }
     285              : 
     286              : /*
     287              :  * Count the total length of operand strings in tree (including '\0'-
     288              :  * terminators) and the total number of nodes.
     289              :  * Caller must initialize *sumlen and *nnode to zeroes.
     290              :  */
     291              : static void
     292         1486 : cntsize(QTNode *in, int *sumlen, int *nnode)
     293              : {
     294              :     /* since this function recurses, it could be driven to stack overflow. */
     295         1486 :     check_stack_depth();
     296              : 
     297         1486 :     *nnode += 1;
     298         1486 :     if (in->valnode->type == QI_OPR)
     299              :     {
     300              :         int         i;
     301              : 
     302         1916 :         for (i = 0; i < in->nchild; i++)
     303         1264 :             cntsize(in->child[i], sumlen, nnode);
     304              :     }
     305              :     else
     306              :     {
     307          834 :         *sumlen += in->valnode->qoperand.length + 1;
     308              :     }
     309         1486 : }
     310              : 
     311              : typedef struct
     312              : {
     313              :     QueryItem  *curitem;
     314              :     char       *operand;
     315              :     char       *curoperand;
     316              : } QTN2QTState;
     317              : 
     318              : /*
     319              :  * Recursively convert a QTNode tree into flat tsquery format.
     320              :  * Caller must have allocated arrays of the correct size.
     321              :  */
     322              : static void
     323         1486 : fillQT(QTN2QTState *state, QTNode *in)
     324              : {
     325              :     /* since this function recurses, it could be driven to stack overflow. */
     326         1486 :     check_stack_depth();
     327              : 
     328         1486 :     if (in->valnode->type == QI_VAL)
     329              :     {
     330          834 :         memcpy(state->curitem, in->valnode, sizeof(QueryOperand));
     331              : 
     332          834 :         memcpy(state->curoperand, in->word, in->valnode->qoperand.length);
     333          834 :         state->curitem->qoperand.distance = state->curoperand - state->operand;
     334          834 :         state->curoperand[in->valnode->qoperand.length] = '\0';
     335          834 :         state->curoperand += in->valnode->qoperand.length + 1;
     336          834 :         state->curitem++;
     337              :     }
     338              :     else
     339              :     {
     340          652 :         QueryItem  *curitem = state->curitem;
     341              : 
     342              :         Assert(in->valnode->type == QI_OPR);
     343              : 
     344          652 :         memcpy(state->curitem, in->valnode, sizeof(QueryOperator));
     345              : 
     346              :         Assert(in->nchild <= 2);
     347          652 :         state->curitem++;
     348              : 
     349          652 :         fillQT(state, in->child[0]);
     350              : 
     351          652 :         if (in->nchild == 2)
     352              :         {
     353          612 :             curitem->qoperator.left = state->curitem - curitem;
     354          612 :             fillQT(state, in->child[1]);
     355              :         }
     356              :     }
     357         1486 : }
     358              : 
     359              : /*
     360              :  * Build flat tsquery from a QTNode tree.
     361              :  */
     362              : TSQuery
     363          222 : QTN2QT(QTNode *in)
     364              : {
     365              :     TSQuery     out;
     366              :     int         len;
     367          222 :     int         sumlen = 0,
     368          222 :                 nnode = 0;
     369              :     QTN2QTState state;
     370              : 
     371          222 :     cntsize(in, &sumlen, &nnode);
     372              : 
     373          222 :     if (TSQUERY_TOO_BIG(nnode, sumlen))
     374            0 :         ereport(ERROR,
     375              :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     376              :                  errmsg("tsquery is too large")));
     377          222 :     len = COMPUTESIZE(nnode, sumlen);
     378              : 
     379          222 :     out = (TSQuery) palloc0(len);
     380          222 :     SET_VARSIZE(out, len);
     381          222 :     out->size = nnode;
     382              : 
     383          222 :     state.curitem = GETQUERY(out);
     384          222 :     state.operand = state.curoperand = GETOPERAND(out);
     385              : 
     386          222 :     fillQT(&state, in);
     387          222 :     return out;
     388              : }
     389              : 
     390              : /*
     391              :  * Copy a QTNode tree.
     392              :  *
     393              :  * Modifiable copies of the words and valnodes are made, too.
     394              :  */
     395              : QTNode *
     396          712 : QTNCopy(QTNode *in)
     397              : {
     398              :     QTNode     *out;
     399              : 
     400              :     /* since this function recurses, it could be driven to stack overflow. */
     401          712 :     check_stack_depth();
     402              : 
     403          712 :     out = palloc_object(QTNode);
     404              : 
     405          712 :     *out = *in;
     406          712 :     out->valnode = palloc_object(QueryItem);
     407          712 :     *(out->valnode) = *(in->valnode);
     408          712 :     out->flags |= QTN_NEEDFREE;
     409              : 
     410          712 :     if (in->valnode->type == QI_VAL)
     411              :     {
     412          428 :         out->word = palloc(in->valnode->qoperand.length + 1);
     413          428 :         memcpy(out->word, in->word, in->valnode->qoperand.length);
     414          428 :         out->word[in->valnode->qoperand.length] = '\0';
     415          428 :         out->flags |= QTN_WORDFREE;
     416              :     }
     417              :     else
     418              :     {
     419              :         int         i;
     420              : 
     421          284 :         out->child = palloc_array(QTNode *, in->nchild);
     422              : 
     423          847 :         for (i = 0; i < in->nchild; i++)
     424          563 :             out->child[i] = QTNCopy(in->child[i]);
     425              :     }
     426              : 
     427          712 :     return out;
     428              : }
     429              : 
     430              : /*
     431              :  * Clear the specified flag bit(s) in all nodes of a QTNode tree.
     432              :  */
     433              : void
     434         3496 : QTNClearFlags(QTNode *in, uint32 flags)
     435              : {
     436              :     /* since this function recurses, it could be driven to stack overflow. */
     437         3496 :     check_stack_depth();
     438              : 
     439         3496 :     in->flags &= ~flags;
     440              : 
     441         3496 :     if (in->valnode->type != QI_VAL)
     442              :     {
     443              :         int         i;
     444              : 
     445         4272 :         for (i = 0; i < in->nchild; i++)
     446         2968 :             QTNClearFlags(in->child[i], flags);
     447              :     }
     448         3496 : }
        

Generated by: LCOV version 2.0-1