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-12 06:14:44 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         4425 : QT2QTN(QueryItem *in, char *operand)
      26              : {
      27         4425 :     QTNode     *node = palloc0_object(QTNode);
      28              : 
      29              :     /* since this function recurses, it could be driven to stack overflow. */
      30         4425 :     check_stack_depth();
      31              : 
      32         4425 :     node->valnode = in;
      33              : 
      34         4425 :     if (in->type == QI_OPR)
      35              :     {
      36         1669 :         node->child = palloc0_array(QTNode *, 2);
      37         1669 :         node->child[0] = QT2QTN(in + 1, operand);
      38         1669 :         node->sign = node->child[0]->sign;
      39         1669 :         if (in->qoperator.oper == OP_NOT)
      40           21 :             node->nchild = 1;
      41              :         else
      42              :         {
      43         1648 :             node->nchild = 2;
      44         1648 :             node->child[1] = QT2QTN(in + in->qoperator.left, operand);
      45         1648 :             node->sign |= node->child[1]->sign;
      46              :         }
      47              :     }
      48         2756 :     else if (operand)
      49              :     {
      50         2756 :         node->word = operand + in->qoperand.distance;
      51         2756 :         node->sign = ((uint32) 1) << (((unsigned int) in->qoperand.valcrc) % 32);
      52              :     }
      53              : 
      54         4425 :     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         4890 : QTNFree(QTNode *in)
      65              : {
      66         4890 :     if (!in)
      67            6 :         return;
      68              : 
      69              :     /* since this function recurses, it could be driven to stack overflow. */
      70         4884 :     check_stack_depth();
      71              : 
      72         4884 :     if (in->valnode->type == QI_VAL && in->word && (in->flags & QTN_WORDFREE) != 0)
      73          306 :         pfree(in->word);
      74              : 
      75         4884 :     if (in->valnode->type == QI_OPR)
      76              :     {
      77              :         int         i;
      78              : 
      79         5499 :         for (i = 0; i < in->nchild; i++)
      80         3677 :             QTNFree(in->child[i]);
      81              :     }
      82         4884 :     if (in->child)
      83         1822 :         pfree(in->child);
      84              : 
      85         4884 :     if (in->flags & QTN_NEEDFREE)
      86          576 :         pfree(in->valnode);
      87              : 
      88         4884 :     pfree(in);
      89              : }
      90              : 
      91              : /*
      92              :  * Sort comparator for QTNodes.
      93              :  *
      94              :  * The sort order is somewhat arbitrary.
      95              :  */
      96              : int
      97         1992 : QTNodeCompare(QTNode *an, QTNode *bn)
      98              : {
      99              :     /* since this function recurses, it could be driven to stack overflow. */
     100         1992 :     check_stack_depth();
     101              : 
     102         1992 :     if (an->valnode->type != bn->valnode->type)
     103          591 :         return (an->valnode->type > bn->valnode->type) ? -1 : 1;
     104              : 
     105         1401 :     if (an->valnode->type == QI_OPR)
     106              :     {
     107          272 :         QueryOperator *ao = &an->valnode->qoperator;
     108          272 :         QueryOperator *bo = &bn->valnode->qoperator;
     109              : 
     110          272 :         if (ao->oper != bo->oper)
     111           24 :             return (ao->oper > bo->oper) ? -1 : 1;
     112              : 
     113          248 :         if (an->nchild != bn->nchild)
     114           54 :             return (an->nchild > bn->nchild) ? -1 : 1;
     115              : 
     116              :         {
     117              :             int         i,
     118              :                         res;
     119              : 
     120          324 :             for (i = 0; i < an->nchild; i++)
     121          259 :                 if ((res = QTNodeCompare(an->child[i], bn->child[i])) != 0)
     122          129 :                     return res;
     123              :         }
     124              : 
     125           65 :         if (ao->oper == OP_PHRASE && ao->distance != bo->distance)
     126            3 :             return (ao->distance > bo->distance) ? -1 : 1;
     127              : 
     128           62 :         return 0;
     129              :     }
     130         1129 :     else if (an->valnode->type == QI_VAL)
     131              :     {
     132         1129 :         QueryOperand *ao = &an->valnode->qoperand;
     133         1129 :         QueryOperand *bo = &bn->valnode->qoperand;
     134              : 
     135         1129 :         if (ao->valcrc != bo->valcrc)
     136              :         {
     137          876 :             return (ao->valcrc > bo->valcrc) ? -1 : 1;
     138              :         }
     139              : 
     140          253 :         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         1518 : cmpQTN(const void *a, const void *b)
     154              : {
     155         1518 :     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         4542 : QTNSort(QTNode *in)
     164              : {
     165              :     int         i;
     166              : 
     167              :     /* since this function recurses, it could be driven to stack overflow. */
     168         4542 :     check_stack_depth();
     169              : 
     170         4542 :     if (in->valnode->type != QI_OPR)
     171         2958 :         return;
     172              : 
     173         5199 :     for (i = 0; i < in->nchild; i++)
     174         3615 :         QTNSort(in->child[i]);
     175         1584 :     if (in->nchild > 1 && in->valnode->qoperator.oper != OP_PHRASE)
     176          924 :         qsort(in->child, in->nchild, sizeof(QTNode *), cmpQTN);
     177              : }
     178              : 
     179              : /*
     180              :  * Are two QTNode trees equal according to QTNodeCompare?
     181              :  */
     182              : bool
     183           99 : QTNEq(QTNode *a, QTNode *b)
     184              : {
     185           99 :     uint32      sign = a->sign & b->sign;
     186              : 
     187           99 :     if (!(sign == a->sign && sign == b->sign))
     188            3 :         return false;
     189              : 
     190           96 :     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         4374 : QTNTernary(QTNode *in)
     202              : {
     203              :     int         i;
     204              : 
     205              :     /* since this function recurses, it could be driven to stack overflow. */
     206         4374 :     check_stack_depth();
     207              : 
     208         4374 :     if (in->valnode->type != QI_OPR)
     209         2769 :         return;
     210              : 
     211         5073 :     for (i = 0; i < in->nchild; i++)
     212         3468 :         QTNTernary(in->child[i]);
     213              : 
     214              :     /* Only AND and OR are associative, so don't flatten other node types */
     215         1605 :     if (in->valnode->qoperator.oper != OP_AND &&
     216         1008 :         in->valnode->qoperator.oper != OP_OR)
     217          624 :         return;
     218              : 
     219         3213 :     for (i = 0; i < in->nchild; i++)
     220              :     {
     221         2232 :         QTNode     *cc = in->child[i];
     222              : 
     223         2232 :         if (cc->valnode->type == QI_OPR &&
     224          777 :             in->valnode->qoperator.oper == cc->valnode->qoperator.oper)
     225              :         {
     226          165 :             int         oldnchild = in->nchild;
     227              : 
     228          165 :             in->nchild += cc->nchild - 1;
     229          165 :             in->child = repalloc_array(in->child, QTNode *, in->nchild);
     230              : 
     231          165 :             if (i + 1 != oldnchild)
     232           18 :                 memmove(in->child + i + cc->nchild, in->child + i + 1,
     233           18 :                         (oldnchild - i - 1) * sizeof(QTNode *));
     234              : 
     235          165 :             memcpy(in->child + i, cc->child, cc->nchild * sizeof(QTNode *));
     236          165 :             i += cc->nchild - 1;
     237              : 
     238          165 :             if (cc->flags & QTN_NEEDFREE)
     239           54 :                 pfree(cc->valnode);
     240          165 :             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          591 : QTNBinary(QTNode *in)
     251              : {
     252              :     int         i;
     253              : 
     254              :     /* since this function recurses, it could be driven to stack overflow. */
     255          591 :     check_stack_depth();
     256              : 
     257          591 :     if (in->valnode->type != QI_OPR)
     258          363 :         return;
     259              : 
     260          732 :     for (i = 0; i < in->nchild; i++)
     261          504 :         QTNBinary(in->child[i]);
     262              : 
     263          288 :     while (in->nchild > 2)
     264              :     {
     265           60 :         QTNode     *nn = palloc0_object(QTNode);
     266              : 
     267           60 :         nn->valnode = palloc0_object(QueryItem);
     268           60 :         nn->child = palloc0_array(QTNode *, 2);
     269              : 
     270           60 :         nn->nchild = 2;
     271           60 :         nn->flags = QTN_NEEDFREE;
     272              : 
     273           60 :         nn->child[0] = in->child[0];
     274           60 :         nn->child[1] = in->child[1];
     275           60 :         nn->sign = nn->child[0]->sign | nn->child[1]->sign;
     276              : 
     277           60 :         nn->valnode->type = in->valnode->type;
     278           60 :         nn->valnode->qoperator.oper = in->valnode->qoperator.oper;
     279              : 
     280           60 :         in->child[0] = nn;
     281           60 :         in->child[1] = in->child[in->nchild - 1];
     282           60 :         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          993 : cntsize(QTNode *in, int *sumlen, int *nnode)
     293              : {
     294              :     /* since this function recurses, it could be driven to stack overflow. */
     295          993 :     check_stack_depth();
     296              : 
     297          993 :     *nnode += 1;
     298          993 :     if (in->valnode->type == QI_OPR)
     299              :     {
     300              :         int         i;
     301              : 
     302         1281 :         for (i = 0; i < in->nchild; i++)
     303          846 :             cntsize(in->child[i], sumlen, nnode);
     304              :     }
     305              :     else
     306              :     {
     307          558 :         *sumlen += in->valnode->qoperand.length + 1;
     308              :     }
     309          993 : }
     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          993 : fillQT(QTN2QTState *state, QTNode *in)
     324              : {
     325              :     /* since this function recurses, it could be driven to stack overflow. */
     326          993 :     check_stack_depth();
     327              : 
     328          993 :     if (in->valnode->type == QI_VAL)
     329              :     {
     330          558 :         memcpy(state->curitem, in->valnode, sizeof(QueryOperand));
     331              : 
     332          558 :         memcpy(state->curoperand, in->word, in->valnode->qoperand.length);
     333          558 :         state->curitem->qoperand.distance = state->curoperand - state->operand;
     334          558 :         state->curoperand[in->valnode->qoperand.length] = '\0';
     335          558 :         state->curoperand += in->valnode->qoperand.length + 1;
     336          558 :         state->curitem++;
     337              :     }
     338              :     else
     339              :     {
     340          435 :         QueryItem  *curitem = state->curitem;
     341              : 
     342              :         Assert(in->valnode->type == QI_OPR);
     343              : 
     344          435 :         memcpy(state->curitem, in->valnode, sizeof(QueryOperator));
     345              : 
     346              :         Assert(in->nchild <= 2);
     347          435 :         state->curitem++;
     348              : 
     349          435 :         fillQT(state, in->child[0]);
     350              : 
     351          435 :         if (in->nchild == 2)
     352              :         {
     353          411 :             curitem->qoperator.left = state->curitem - curitem;
     354          411 :             fillQT(state, in->child[1]);
     355              :         }
     356              :     }
     357          993 : }
     358              : 
     359              : /*
     360              :  * Build flat tsquery from a QTNode tree.
     361              :  */
     362              : TSQuery
     363          147 : QTN2QT(QTNode *in)
     364              : {
     365              :     TSQuery     out;
     366              :     int         len;
     367          147 :     int         sumlen = 0,
     368          147 :                 nnode = 0;
     369              :     QTN2QTState state;
     370              : 
     371          147 :     cntsize(in, &sumlen, &nnode);
     372              : 
     373          147 :     if (TSQUERY_TOO_BIG(nnode, sumlen))
     374            0 :         ereport(ERROR,
     375              :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     376              :                  errmsg("tsquery is too large")));
     377          147 :     len = COMPUTESIZE(nnode, sumlen);
     378              : 
     379          147 :     out = (TSQuery) palloc0(len);
     380          147 :     SET_VARSIZE(out, len);
     381          147 :     out->size = nnode;
     382              : 
     383          147 :     state.curitem = GETQUERY(out);
     384          147 :     state.operand = state.curoperand = GETOPERAND(out);
     385              : 
     386          147 :     fillQT(&state, in);
     387          147 :     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          510 : QTNCopy(QTNode *in)
     397              : {
     398              :     QTNode     *out;
     399              : 
     400              :     /* since this function recurses, it could be driven to stack overflow. */
     401          510 :     check_stack_depth();
     402              : 
     403          510 :     out = palloc_object(QTNode);
     404              : 
     405          510 :     *out = *in;
     406          510 :     out->valnode = palloc_object(QueryItem);
     407          510 :     *(out->valnode) = *(in->valnode);
     408          510 :     out->flags |= QTN_NEEDFREE;
     409              : 
     410          510 :     if (in->valnode->type == QI_VAL)
     411              :     {
     412          306 :         out->word = palloc(in->valnode->qoperand.length + 1);
     413          306 :         memcpy(out->word, in->word, in->valnode->qoperand.length);
     414          306 :         out->word[in->valnode->qoperand.length] = '\0';
     415          306 :         out->flags |= QTN_WORDFREE;
     416              :     }
     417              :     else
     418              :     {
     419              :         int         i;
     420              : 
     421          204 :         out->child = palloc_array(QTNode *, in->nchild);
     422              : 
     423          609 :         for (i = 0; i < in->nchild; i++)
     424          405 :             out->child[i] = QTNCopy(in->child[i]);
     425              :     }
     426              : 
     427          510 :     return out;
     428              : }
     429              : 
     430              : /*
     431              :  * Clear the specified flag bit(s) in all nodes of a QTNode tree.
     432              :  */
     433              : void
     434         2622 : QTNClearFlags(QTNode *in, uint32 flags)
     435              : {
     436              :     /* since this function recurses, it could be driven to stack overflow. */
     437         2622 :     check_stack_depth();
     438              : 
     439         2622 :     in->flags &= ~flags;
     440              : 
     441         2622 :     if (in->valnode->type != QI_VAL)
     442              :     {
     443              :         int         i;
     444              : 
     445         3204 :         for (i = 0; i < in->nchild; i++)
     446         2226 :             QTNClearFlags(in->child[i], flags);
     447              :     }
     448         2622 : }
        

Generated by: LCOV version 2.0-1