LCOV - code coverage report
Current view: top level - src/backend/utils/adt - tsquery_util.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 98.6 % 215 212
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 13 13
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 84.5 % 116 98

             Branch data     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                 :        1471 : QT2QTN(QueryItem *in, char *operand)
      26                 :             : {
      27                 :        1471 :         QTNode     *node = palloc0_object(QTNode);
      28                 :             : 
      29                 :             :         /* since this function recurses, it could be driven to stack overflow. */
      30                 :        1471 :         check_stack_depth();
      31                 :             : 
      32                 :        1471 :         node->valnode = in;
      33                 :             : 
      34         [ +  + ]:        1471 :         if (in->type == QI_OPR)
      35                 :             :         {
      36                 :         555 :                 node->child = palloc0_array(QTNode *, 2);
      37                 :         555 :                 node->child[0] = QT2QTN(in + 1, operand);
      38                 :         555 :                 node->sign = node->child[0]->sign;
      39         [ +  + ]:         555 :                 if (in->qoperator.oper == OP_NOT)
      40                 :           7 :                         node->nchild = 1;
      41                 :             :                 else
      42                 :             :                 {
      43                 :         548 :                         node->nchild = 2;
      44                 :         548 :                         node->child[1] = QT2QTN(in + in->qoperator.left, operand);
      45                 :         548 :                         node->sign |= node->child[1]->sign;
      46                 :             :                 }
      47                 :         555 :         }
      48         [ -  + ]:         916 :         else if (operand)
      49                 :             :         {
      50                 :         916 :                 node->word = operand + in->qoperand.distance;
      51                 :         916 :                 node->sign = ((uint32) 1) << (((unsigned int) in->qoperand.valcrc) % 32);
      52                 :         916 :         }
      53                 :             : 
      54                 :        2942 :         return node;
      55                 :        1471 : }
      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                 :        1626 : QTNFree(QTNode *in)
      65                 :             : {
      66         [ +  + ]:        1626 :         if (!in)
      67                 :           2 :                 return;
      68                 :             : 
      69                 :             :         /* since this function recurses, it could be driven to stack overflow. */
      70                 :        1624 :         check_stack_depth();
      71                 :             : 
      72   [ +  +  +  -  :        1624 :         if (in->valnode->type == QI_VAL && in->word && (in->flags & QTN_WORDFREE) != 0)
                   +  + ]
      73                 :         102 :                 pfree(in->word);
      74                 :             : 
      75         [ +  + ]:        1624 :         if (in->valnode->type == QI_OPR)
      76                 :             :         {
      77                 :         606 :                 int                     i;
      78                 :             : 
      79         [ +  + ]:        1829 :                 for (i = 0; i < in->nchild; i++)
      80                 :        1223 :                         QTNFree(in->child[i]);
      81                 :         606 :         }
      82         [ +  + ]:        1624 :         if (in->child)
      83                 :         606 :                 pfree(in->child);
      84                 :             : 
      85         [ +  + ]:        1624 :         if (in->flags & QTN_NEEDFREE)
      86                 :         192 :                 pfree(in->valnode);
      87                 :             : 
      88                 :        1624 :         pfree(in);
      89                 :        1626 : }
      90                 :             : 
      91                 :             : /*
      92                 :             :  * Sort comparator for QTNodes.
      93                 :             :  *
      94                 :             :  * The sort order is somewhat arbitrary.
      95                 :             :  */
      96                 :             : int
      97                 :         662 : QTNodeCompare(QTNode *an, QTNode *bn)
      98                 :             : {
      99                 :             :         /* since this function recurses, it could be driven to stack overflow. */
     100                 :         662 :         check_stack_depth();
     101                 :             : 
     102         [ +  + ]:         662 :         if (an->valnode->type != bn->valnode->type)
     103                 :         197 :                 return (an->valnode->type > bn->valnode->type) ? -1 : 1;
     104                 :             : 
     105         [ +  + ]:         465 :         if (an->valnode->type == QI_OPR)
     106                 :             :         {
     107                 :          90 :                 QueryOperator *ao = &an->valnode->qoperator;
     108                 :          90 :                 QueryOperator *bo = &bn->valnode->qoperator;
     109                 :             : 
     110         [ +  + ]:          90 :                 if (ao->oper != bo->oper)
     111                 :           8 :                         return (ao->oper > bo->oper) ? -1 : 1;
     112                 :             : 
     113         [ +  + ]:          82 :                 if (an->nchild != bn->nchild)
     114                 :          18 :                         return (an->nchild > bn->nchild) ? -1 : 1;
     115                 :             : 
     116                 :             :                 {
     117                 :          64 :                         int                     i,
     118                 :             :                                                 res;
     119                 :             : 
     120         [ +  + ]:         106 :                         for (i = 0; i < an->nchild; i++)
     121         [ +  + ]:          85 :                                 if ((res = QTNodeCompare(an->child[i], bn->child[i])) != 0)
     122                 :          43 :                                         return res;
     123         [ +  + ]:          64 :                 }
     124                 :             : 
     125   [ +  +  +  + ]:          21 :                 if (ao->oper == OP_PHRASE && ao->distance != bo->distance)
     126                 :           1 :                         return (ao->distance > bo->distance) ? -1 : 1;
     127                 :             : 
     128                 :          20 :                 return 0;
     129                 :          90 :         }
     130         [ +  - ]:         375 :         else if (an->valnode->type == QI_VAL)
     131                 :             :         {
     132                 :         375 :                 QueryOperand *ao = &an->valnode->qoperand;
     133                 :         375 :                 QueryOperand *bo = &bn->valnode->qoperand;
     134                 :             : 
     135         [ +  + ]:         375 :                 if (ao->valcrc != bo->valcrc)
     136                 :             :                 {
     137                 :         292 :                         return (ao->valcrc > bo->valcrc) ? -1 : 1;
     138                 :             :                 }
     139                 :             : 
     140                 :          83 :                 return tsCompareString(an->word, ao->length, bn->word, bo->length, false);
     141                 :         375 :         }
     142                 :             :         else
     143                 :             :         {
     144   [ #  #  #  # ]:           0 :                 elog(ERROR, "unrecognized QueryItem type: %d", an->valnode->type);
     145                 :           0 :                 return 0;                               /* keep compiler quiet */
     146                 :             :         }
     147                 :         662 : }
     148                 :             : 
     149                 :             : /*
     150                 :             :  * qsort comparator for QTNode pointers.
     151                 :             :  */
     152                 :             : static int
     153                 :         506 : cmpQTN(const void *a, const void *b)
     154                 :             : {
     155                 :         506 :         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                 :        1514 : QTNSort(QTNode *in)
     164                 :             : {
     165                 :        1514 :         int                     i;
     166                 :             : 
     167                 :             :         /* since this function recurses, it could be driven to stack overflow. */
     168                 :        1514 :         check_stack_depth();
     169                 :             : 
     170         [ +  + ]:        1514 :         if (in->valnode->type != QI_OPR)
     171                 :         986 :                 return;
     172                 :             : 
     173         [ +  + ]:        1733 :         for (i = 0; i < in->nchild; i++)
     174                 :        1205 :                 QTNSort(in->child[i]);
     175   [ +  +  +  + ]:         528 :         if (in->nchild > 1 && in->valnode->qoperator.oper != OP_PHRASE)
     176                 :         308 :                 qsort(in->child, in->nchild, sizeof(QTNode *), cmpQTN);
     177         [ -  + ]:        1514 : }
     178                 :             : 
     179                 :             : /*
     180                 :             :  * Are two QTNode trees equal according to QTNodeCompare?
     181                 :             :  */
     182                 :             : bool
     183                 :          33 : QTNEq(QTNode *a, QTNode *b)
     184                 :             : {
     185                 :          33 :         uint32          sign = a->sign & b->sign;
     186                 :             : 
     187   [ +  +  -  + ]:          33 :         if (!(sign == a->sign && sign == b->sign))
     188                 :           1 :                 return false;
     189                 :             : 
     190                 :          32 :         return (QTNodeCompare(a, b) == 0);
     191                 :          33 : }
     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                 :        1458 : QTNTernary(QTNode *in)
     202                 :             : {
     203                 :        1458 :         int                     i;
     204                 :             : 
     205                 :             :         /* since this function recurses, it could be driven to stack overflow. */
     206                 :        1458 :         check_stack_depth();
     207                 :             : 
     208         [ +  + ]:        1458 :         if (in->valnode->type != QI_OPR)
     209                 :         923 :                 return;
     210                 :             : 
     211         [ +  + ]:        1691 :         for (i = 0; i < in->nchild; i++)
     212                 :        1156 :                 QTNTernary(in->child[i]);
     213                 :             : 
     214                 :             :         /* Only AND and OR are associative, so don't flatten other node types */
     215   [ +  +  +  + ]:         535 :         if (in->valnode->qoperator.oper != OP_AND &&
     216                 :         336 :                 in->valnode->qoperator.oper != OP_OR)
     217                 :         208 :                 return;
     218                 :             : 
     219         [ +  + ]:        1071 :         for (i = 0; i < in->nchild; i++)
     220                 :             :         {
     221                 :         744 :                 QTNode     *cc = in->child[i];
     222                 :             : 
     223   [ +  +  +  + ]:         744 :                 if (cc->valnode->type == QI_OPR &&
     224                 :         259 :                         in->valnode->qoperator.oper == cc->valnode->qoperator.oper)
     225                 :             :                 {
     226                 :          55 :                         int                     oldnchild = in->nchild;
     227                 :             : 
     228                 :          55 :                         in->nchild += cc->nchild - 1;
     229                 :          55 :                         in->child = repalloc_array(in->child, QTNode *, in->nchild);
     230                 :             : 
     231         [ +  + ]:          55 :                         if (i + 1 != oldnchild)
     232                 :           6 :                                 memmove(in->child + i + cc->nchild, in->child + i + 1,
     233                 :             :                                                 (oldnchild - i - 1) * sizeof(QTNode *));
     234                 :             : 
     235                 :          55 :                         memcpy(in->child + i, cc->child, cc->nchild * sizeof(QTNode *));
     236                 :          55 :                         i += cc->nchild - 1;
     237                 :             : 
     238         [ +  + ]:          55 :                         if (cc->flags & QTN_NEEDFREE)
     239                 :          18 :                                 pfree(cc->valnode);
     240                 :          55 :                         pfree(cc);
     241                 :          55 :                 }
     242                 :         744 :         }
     243         [ -  + ]:        1458 : }
     244                 :             : 
     245                 :             : /*
     246                 :             :  * Convert a tree to binary tree by inserting intermediate nodes.
     247                 :             :  * (Opposite of QTNTernary)
     248                 :             :  */
     249                 :             : void
     250                 :         197 : QTNBinary(QTNode *in)
     251                 :             : {
     252                 :         197 :         int                     i;
     253                 :             : 
     254                 :             :         /* since this function recurses, it could be driven to stack overflow. */
     255                 :         197 :         check_stack_depth();
     256                 :             : 
     257         [ +  + ]:         197 :         if (in->valnode->type != QI_OPR)
     258                 :         121 :                 return;
     259                 :             : 
     260         [ +  + ]:         244 :         for (i = 0; i < in->nchild; i++)
     261                 :         168 :                 QTNBinary(in->child[i]);
     262                 :             : 
     263         [ +  + ]:          96 :         while (in->nchild > 2)
     264                 :             :         {
     265                 :          20 :                 QTNode     *nn = palloc0_object(QTNode);
     266                 :             : 
     267                 :          20 :                 nn->valnode = palloc0_object(QueryItem);
     268                 :          20 :                 nn->child = palloc0_array(QTNode *, 2);
     269                 :             : 
     270                 :          20 :                 nn->nchild = 2;
     271                 :          20 :                 nn->flags = QTN_NEEDFREE;
     272                 :             : 
     273                 :          20 :                 nn->child[0] = in->child[0];
     274                 :          20 :                 nn->child[1] = in->child[1];
     275                 :          20 :                 nn->sign = nn->child[0]->sign | nn->child[1]->sign;
     276                 :             : 
     277                 :          20 :                 nn->valnode->type = in->valnode->type;
     278                 :          20 :                 nn->valnode->qoperator.oper = in->valnode->qoperator.oper;
     279                 :             : 
     280                 :          20 :                 in->child[0] = nn;
     281                 :          20 :                 in->child[1] = in->child[in->nchild - 1];
     282                 :          20 :                 in->nchild--;
     283                 :          20 :         }
     284         [ -  + ]:         197 : }
     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                 :         331 : cntsize(QTNode *in, int *sumlen, int *nnode)
     293                 :             : {
     294                 :             :         /* since this function recurses, it could be driven to stack overflow. */
     295                 :         331 :         check_stack_depth();
     296                 :             : 
     297                 :         331 :         *nnode += 1;
     298         [ +  + ]:         331 :         if (in->valnode->type == QI_OPR)
     299                 :             :         {
     300                 :         145 :                 int                     i;
     301                 :             : 
     302         [ +  + ]:         427 :                 for (i = 0; i < in->nchild; i++)
     303                 :         282 :                         cntsize(in->child[i], sumlen, nnode);
     304                 :         145 :         }
     305                 :             :         else
     306                 :             :         {
     307                 :         186 :                 *sumlen += in->valnode->qoperand.length + 1;
     308                 :             :         }
     309                 :         331 : }
     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                 :         331 : fillQT(QTN2QTState *state, QTNode *in)
     324                 :             : {
     325                 :             :         /* since this function recurses, it could be driven to stack overflow. */
     326                 :         331 :         check_stack_depth();
     327                 :             : 
     328         [ +  + ]:         331 :         if (in->valnode->type == QI_VAL)
     329                 :             :         {
     330                 :         186 :                 memcpy(state->curitem, in->valnode, sizeof(QueryOperand));
     331                 :             : 
     332                 :         186 :                 memcpy(state->curoperand, in->word, in->valnode->qoperand.length);
     333                 :         186 :                 state->curitem->qoperand.distance = state->curoperand - state->operand;
     334                 :         186 :                 state->curoperand[in->valnode->qoperand.length] = '\0';
     335                 :         186 :                 state->curoperand += in->valnode->qoperand.length + 1;
     336                 :         186 :                 state->curitem++;
     337                 :         186 :         }
     338                 :             :         else
     339                 :             :         {
     340                 :         145 :                 QueryItem  *curitem = state->curitem;
     341                 :             : 
     342         [ +  - ]:         145 :                 Assert(in->valnode->type == QI_OPR);
     343                 :             : 
     344                 :         145 :                 memcpy(state->curitem, in->valnode, sizeof(QueryOperator));
     345                 :             : 
     346         [ +  - ]:         145 :                 Assert(in->nchild <= 2);
     347                 :         145 :                 state->curitem++;
     348                 :             : 
     349                 :         145 :                 fillQT(state, in->child[0]);
     350                 :             : 
     351         [ +  + ]:         145 :                 if (in->nchild == 2)
     352                 :             :                 {
     353                 :         137 :                         curitem->qoperator.left = state->curitem - curitem;
     354                 :         137 :                         fillQT(state, in->child[1]);
     355                 :         137 :                 }
     356                 :         145 :         }
     357                 :         331 : }
     358                 :             : 
     359                 :             : /*
     360                 :             :  * Build flat tsquery from a QTNode tree.
     361                 :             :  */
     362                 :             : TSQuery
     363                 :          49 : QTN2QT(QTNode *in)
     364                 :             : {
     365                 :          49 :         TSQuery         out;
     366                 :          49 :         int                     len;
     367                 :          49 :         int                     sumlen = 0,
     368                 :          49 :                                 nnode = 0;
     369                 :          49 :         QTN2QTState state;
     370                 :             : 
     371                 :          49 :         cntsize(in, &sumlen, &nnode);
     372                 :             : 
     373         [ +  - ]:          49 :         if (TSQUERY_TOO_BIG(nnode, sumlen))
     374   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     375                 :             :                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     376                 :             :                                  errmsg("tsquery is too large")));
     377                 :          49 :         len = COMPUTESIZE(nnode, sumlen);
     378                 :             : 
     379                 :          49 :         out = (TSQuery) palloc0(len);
     380                 :          49 :         SET_VARSIZE(out, len);
     381                 :          49 :         out->size = nnode;
     382                 :             : 
     383                 :          49 :         state.curitem = GETQUERY(out);
     384                 :          49 :         state.operand = state.curoperand = GETOPERAND(out);
     385                 :             : 
     386                 :          49 :         fillQT(&state, in);
     387                 :          98 :         return out;
     388                 :          49 : }
     389                 :             : 
     390                 :             : /*
     391                 :             :  * Copy a QTNode tree.
     392                 :             :  *
     393                 :             :  * Modifiable copies of the words and valnodes are made, too.
     394                 :             :  */
     395                 :             : QTNode *
     396                 :         170 : QTNCopy(QTNode *in)
     397                 :             : {
     398                 :         170 :         QTNode     *out;
     399                 :             : 
     400                 :             :         /* since this function recurses, it could be driven to stack overflow. */
     401                 :         170 :         check_stack_depth();
     402                 :             : 
     403                 :         170 :         out = palloc_object(QTNode);
     404                 :             : 
     405                 :         170 :         *out = *in;
     406                 :         170 :         out->valnode = palloc_object(QueryItem);
     407                 :         170 :         *(out->valnode) = *(in->valnode);
     408                 :         170 :         out->flags |= QTN_NEEDFREE;
     409                 :             : 
     410         [ +  + ]:         170 :         if (in->valnode->type == QI_VAL)
     411                 :             :         {
     412                 :         102 :                 out->word = palloc(in->valnode->qoperand.length + 1);
     413                 :         102 :                 memcpy(out->word, in->word, in->valnode->qoperand.length);
     414                 :         102 :                 out->word[in->valnode->qoperand.length] = '\0';
     415                 :         102 :                 out->flags |= QTN_WORDFREE;
     416                 :         102 :         }
     417                 :             :         else
     418                 :             :         {
     419                 :          68 :                 int                     i;
     420                 :             : 
     421                 :          68 :                 out->child = palloc_array(QTNode *, in->nchild);
     422                 :             : 
     423         [ +  + ]:         203 :                 for (i = 0; i < in->nchild; i++)
     424                 :         135 :                         out->child[i] = QTNCopy(in->child[i]);
     425                 :          68 :         }
     426                 :             : 
     427                 :         340 :         return out;
     428                 :         170 : }
     429                 :             : 
     430                 :             : /*
     431                 :             :  * Clear the specified flag bit(s) in all nodes of a QTNode tree.
     432                 :             :  */
     433                 :             : void
     434                 :         874 : QTNClearFlags(QTNode *in, uint32 flags)
     435                 :             : {
     436                 :             :         /* since this function recurses, it could be driven to stack overflow. */
     437                 :         874 :         check_stack_depth();
     438                 :             : 
     439                 :         874 :         in->flags &= ~flags;
     440                 :             : 
     441         [ +  + ]:         874 :         if (in->valnode->type != QI_VAL)
     442                 :             :         {
     443                 :         326 :                 int                     i;
     444                 :             : 
     445         [ +  + ]:        1068 :                 for (i = 0; i < in->nchild; i++)
     446                 :         742 :                         QTNClearFlags(in->child[i], flags);
     447                 :         326 :         }
     448                 :         874 : }
        

Generated by: LCOV version 2.3.2-1