LCOV - code coverage report
Current view: top level - src/backend/utils/adt - tsquery_op.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 90.0 % 160 144
Test Date: 2026-02-17 17:20:33 Functions: 95.0 % 20 19
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * tsquery_op.c
       4              :  *    Various operations with tsquery
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  *
       8              :  *
       9              :  * IDENTIFICATION
      10              :  *    src/backend/utils/adt/tsquery_op.c
      11              :  *
      12              :  *-------------------------------------------------------------------------
      13              :  */
      14              : 
      15              : #include "postgres.h"
      16              : 
      17              : #include "lib/qunique.h"
      18              : #include "tsearch/ts_utils.h"
      19              : #include "utils/fmgrprotos.h"
      20              : #include "varatt.h"
      21              : 
      22              : Datum
      23            9 : tsquery_numnode(PG_FUNCTION_ARGS)
      24              : {
      25            9 :     TSQuery     query = PG_GETARG_TSQUERY(0);
      26            9 :     int         nnode = query->size;
      27              : 
      28            9 :     PG_FREE_IF_COPY(query, 0);
      29            9 :     PG_RETURN_INT32(nnode);
      30              : }
      31              : 
      32              : static QTNode *
      33           54 : join_tsqueries(TSQuery a, TSQuery b, int8 operator, uint16 distance)
      34              : {
      35           54 :     QTNode     *res = palloc0_object(QTNode);
      36              : 
      37           54 :     res->flags |= QTN_NEEDFREE;
      38              : 
      39           54 :     res->valnode = palloc0_object(QueryItem);
      40           54 :     res->valnode->type = QI_OPR;
      41           54 :     res->valnode->qoperator.oper = operator;
      42           54 :     if (operator == OP_PHRASE)
      43           24 :         res->valnode->qoperator.distance = distance;
      44              : 
      45           54 :     res->child = palloc0_array(QTNode *, 2);
      46           54 :     res->child[0] = QT2QTN(GETQUERY(b), GETOPERAND(b));
      47           54 :     res->child[1] = QT2QTN(GETQUERY(a), GETOPERAND(a));
      48           54 :     res->nchild = 2;
      49              : 
      50           54 :     return res;
      51              : }
      52              : 
      53              : Datum
      54           18 : tsquery_and(PG_FUNCTION_ARGS)
      55              : {
      56           18 :     TSQuery     a = PG_GETARG_TSQUERY_COPY(0);
      57           18 :     TSQuery     b = PG_GETARG_TSQUERY_COPY(1);
      58              :     QTNode     *res;
      59              :     TSQuery     query;
      60              : 
      61           18 :     if (a->size == 0)
      62              :     {
      63            0 :         PG_FREE_IF_COPY(a, 1);
      64            0 :         PG_RETURN_POINTER(b);
      65              :     }
      66           18 :     else if (b->size == 0)
      67              :     {
      68            0 :         PG_FREE_IF_COPY(b, 1);
      69            0 :         PG_RETURN_POINTER(a);
      70              :     }
      71              : 
      72           18 :     res = join_tsqueries(a, b, OP_AND, 0);
      73              : 
      74           18 :     query = QTN2QT(res);
      75              : 
      76           18 :     QTNFree(res);
      77           18 :     PG_FREE_IF_COPY(a, 0);
      78           18 :     PG_FREE_IF_COPY(b, 1);
      79              : 
      80           18 :     PG_RETURN_TSQUERY(query);
      81              : }
      82              : 
      83              : Datum
      84           12 : tsquery_or(PG_FUNCTION_ARGS)
      85              : {
      86           12 :     TSQuery     a = PG_GETARG_TSQUERY_COPY(0);
      87           12 :     TSQuery     b = PG_GETARG_TSQUERY_COPY(1);
      88              :     QTNode     *res;
      89              :     TSQuery     query;
      90              : 
      91           12 :     if (a->size == 0)
      92              :     {
      93            0 :         PG_FREE_IF_COPY(a, 1);
      94            0 :         PG_RETURN_POINTER(b);
      95              :     }
      96           12 :     else if (b->size == 0)
      97              :     {
      98            0 :         PG_FREE_IF_COPY(b, 1);
      99            0 :         PG_RETURN_POINTER(a);
     100              :     }
     101              : 
     102           12 :     res = join_tsqueries(a, b, OP_OR, 0);
     103              : 
     104           12 :     query = QTN2QT(res);
     105              : 
     106           12 :     QTNFree(res);
     107           12 :     PG_FREE_IF_COPY(a, 0);
     108           12 :     PG_FREE_IF_COPY(b, 1);
     109              : 
     110           12 :     PG_RETURN_TSQUERY(query);
     111              : }
     112              : 
     113              : Datum
     114           24 : tsquery_phrase_distance(PG_FUNCTION_ARGS)
     115              : {
     116           24 :     TSQuery     a = PG_GETARG_TSQUERY_COPY(0);
     117           24 :     TSQuery     b = PG_GETARG_TSQUERY_COPY(1);
     118              :     QTNode     *res;
     119              :     TSQuery     query;
     120           24 :     int32       distance = PG_GETARG_INT32(2);
     121              : 
     122           24 :     if (distance < 0 || distance > MAXENTRYPOS)
     123            0 :         ereport(ERROR,
     124              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     125              :                  errmsg("distance in phrase operator must be an integer value between zero and %d inclusive",
     126              :                         MAXENTRYPOS)));
     127           24 :     if (a->size == 0)
     128              :     {
     129            0 :         PG_FREE_IF_COPY(a, 1);
     130            0 :         PG_RETURN_POINTER(b);
     131              :     }
     132           24 :     else if (b->size == 0)
     133              :     {
     134            0 :         PG_FREE_IF_COPY(b, 1);
     135            0 :         PG_RETURN_POINTER(a);
     136              :     }
     137              : 
     138           24 :     res = join_tsqueries(a, b, OP_PHRASE, (uint16) distance);
     139              : 
     140           24 :     query = QTN2QT(res);
     141              : 
     142           24 :     QTNFree(res);
     143           24 :     PG_FREE_IF_COPY(a, 0);
     144           24 :     PG_FREE_IF_COPY(b, 1);
     145              : 
     146           24 :     PG_RETURN_TSQUERY(query);
     147              : }
     148              : 
     149              : Datum
     150           21 : tsquery_phrase(PG_FUNCTION_ARGS)
     151              : {
     152           21 :     PG_RETURN_DATUM(DirectFunctionCall3(tsquery_phrase_distance,
     153              :                                         PG_GETARG_DATUM(0),
     154              :                                         PG_GETARG_DATUM(1),
     155              :                                         Int32GetDatum(1)));
     156              : }
     157              : 
     158              : Datum
     159            6 : tsquery_not(PG_FUNCTION_ARGS)
     160              : {
     161            6 :     TSQuery     a = PG_GETARG_TSQUERY_COPY(0);
     162              :     QTNode     *res;
     163              :     TSQuery     query;
     164              : 
     165            6 :     if (a->size == 0)
     166            0 :         PG_RETURN_POINTER(a);
     167              : 
     168            6 :     res = palloc0_object(QTNode);
     169              : 
     170            6 :     res->flags |= QTN_NEEDFREE;
     171              : 
     172            6 :     res->valnode = palloc0_object(QueryItem);
     173            6 :     res->valnode->type = QI_OPR;
     174            6 :     res->valnode->qoperator.oper = OP_NOT;
     175              : 
     176            6 :     res->child = palloc0_object(QTNode *);
     177            6 :     res->child[0] = QT2QTN(GETQUERY(a), GETOPERAND(a));
     178            6 :     res->nchild = 1;
     179              : 
     180            6 :     query = QTN2QT(res);
     181              : 
     182            6 :     QTNFree(res);
     183            6 :     PG_FREE_IF_COPY(a, 0);
     184              : 
     185            6 :     PG_RETURN_POINTER(query);
     186              : }
     187              : 
     188              : static int
     189          221 : CompareTSQ(TSQuery a, TSQuery b)
     190              : {
     191          221 :     if (a->size != b->size)
     192              :     {
     193          108 :         return (a->size < b->size) ? -1 : 1;
     194              :     }
     195          113 :     else if (VARSIZE(a) != VARSIZE(b))
     196              :     {
     197           77 :         return (VARSIZE(a) < VARSIZE(b)) ? -1 : 1;
     198              :     }
     199           36 :     else if (a->size != 0)
     200              :     {
     201           36 :         QTNode     *an = QT2QTN(GETQUERY(a), GETOPERAND(a));
     202           36 :         QTNode     *bn = QT2QTN(GETQUERY(b), GETOPERAND(b));
     203           36 :         int         res = QTNodeCompare(an, bn);
     204              : 
     205           36 :         QTNFree(an);
     206           36 :         QTNFree(bn);
     207              : 
     208           36 :         return res;
     209              :     }
     210              : 
     211            0 :     return 0;
     212              : }
     213              : 
     214              : Datum
     215           39 : tsquery_cmp(PG_FUNCTION_ARGS)
     216              : {
     217           39 :     TSQuery     a = PG_GETARG_TSQUERY_COPY(0);
     218           39 :     TSQuery     b = PG_GETARG_TSQUERY_COPY(1);
     219           39 :     int         res = CompareTSQ(a, b);
     220              : 
     221           39 :     PG_FREE_IF_COPY(a, 0);
     222           39 :     PG_FREE_IF_COPY(b, 1);
     223              : 
     224           39 :     PG_RETURN_INT32(res);
     225              : }
     226              : 
     227              : #define CMPFUNC( NAME, CONDITION )              \
     228              : Datum                                           \
     229              : NAME(PG_FUNCTION_ARGS) {                        \
     230              :     TSQuery  a = PG_GETARG_TSQUERY_COPY(0);     \
     231              :     TSQuery  b = PG_GETARG_TSQUERY_COPY(1);     \
     232              :     int res = CompareTSQ(a,b);                  \
     233              :                                                 \
     234              :     PG_FREE_IF_COPY(a,0);                       \
     235              :     PG_FREE_IF_COPY(b,1);                       \
     236              :                                                 \
     237              :     PG_RETURN_BOOL( CONDITION );                \
     238              : }   \
     239              : /* keep compiler quiet - no extra ; */          \
     240              : extern int no_such_variable
     241              : 
     242           45 : CMPFUNC(tsquery_lt, res < 0);
     243           34 : CMPFUNC(tsquery_le, res <= 0);
     244           33 : CMPFUNC(tsquery_eq, res == 0);
     245           34 : CMPFUNC(tsquery_ge, res >= 0);
     246           36 : CMPFUNC(tsquery_gt, res > 0);
     247            0 : CMPFUNC(tsquery_ne, res != 0);
     248              : 
     249              : TSQuerySign
     250           42 : makeTSQuerySign(TSQuery a)
     251              : {
     252              :     int         i;
     253           42 :     QueryItem  *ptr = GETQUERY(a);
     254           42 :     TSQuerySign sign = 0;
     255              : 
     256          126 :     for (i = 0; i < a->size; i++)
     257              :     {
     258           84 :         if (ptr->type == QI_VAL)
     259           63 :             sign |= ((TSQuerySign) 1) << (((unsigned int) ptr->qoperand.valcrc) % TSQS_SIGLEN);
     260           84 :         ptr++;
     261              :     }
     262              : 
     263           42 :     return sign;
     264              : }
     265              : 
     266              : static char **
     267          246 : collectTSQueryValues(TSQuery a, int *nvalues_p)
     268              : {
     269          246 :     QueryItem  *ptr = GETQUERY(a);
     270          246 :     char       *operand = GETOPERAND(a);
     271              :     char      **values;
     272          246 :     int         nvalues = 0;
     273              :     int         i;
     274              : 
     275          246 :     values = palloc_array(char *, a->size);
     276              : 
     277          774 :     for (i = 0; i < a->size; i++)
     278              :     {
     279          528 :         if (ptr->type == QI_VAL)
     280              :         {
     281          387 :             int         len = ptr->qoperand.length;
     282              :             char       *val;
     283              : 
     284          387 :             val = palloc(len + 1);
     285          387 :             memcpy(val, operand + ptr->qoperand.distance, len);
     286          387 :             val[len] = '\0';
     287              : 
     288          387 :             values[nvalues++] = val;
     289              :         }
     290          528 :         ptr++;
     291              :     }
     292              : 
     293          246 :     *nvalues_p = nvalues;
     294          246 :     return values;
     295              : }
     296              : 
     297              : static int
     298          322 : cmp_string(const void *a, const void *b)
     299              : {
     300          322 :     const char *sa = *((char *const *) a);
     301          322 :     const char *sb = *((char *const *) b);
     302              : 
     303          322 :     return strcmp(sa, sb);
     304              : }
     305              : 
     306              : Datum
     307          123 : tsq_mcontains(PG_FUNCTION_ARGS)
     308              : {
     309          123 :     TSQuery     query = PG_GETARG_TSQUERY(0);
     310          123 :     TSQuery     ex = PG_GETARG_TSQUERY(1);
     311              :     char      **query_values;
     312              :     int         query_nvalues;
     313              :     char      **ex_values;
     314              :     int         ex_nvalues;
     315          123 :     bool        result = true;
     316              : 
     317              :     /* Extract the query terms into arrays */
     318          123 :     query_values = collectTSQueryValues(query, &query_nvalues);
     319          123 :     ex_values = collectTSQueryValues(ex, &ex_nvalues);
     320              : 
     321              :     /* Sort and remove duplicates from both arrays */
     322          123 :     qsort(query_values, query_nvalues, sizeof(char *), cmp_string);
     323          123 :     query_nvalues = qunique(query_values, query_nvalues, sizeof(char *),
     324              :                             cmp_string);
     325          123 :     qsort(ex_values, ex_nvalues, sizeof(char *), cmp_string);
     326          123 :     ex_nvalues = qunique(ex_values, ex_nvalues, sizeof(char *), cmp_string);
     327              : 
     328          123 :     if (ex_nvalues > query_nvalues)
     329           50 :         result = false;
     330              :     else
     331              :     {
     332              :         int         i;
     333           73 :         int         j = 0;
     334              : 
     335           91 :         for (i = 0; i < ex_nvalues; i++)
     336              :         {
     337          193 :             for (; j < query_nvalues; j++)
     338              :             {
     339          138 :                 if (strcmp(ex_values[i], query_values[j]) == 0)
     340           18 :                     break;
     341              :             }
     342           73 :             if (j == query_nvalues)
     343              :             {
     344           55 :                 result = false;
     345           55 :                 break;
     346              :             }
     347              :         }
     348              :     }
     349              : 
     350          123 :     PG_RETURN_BOOL(result);
     351              : }
     352              : 
     353              : Datum
     354           61 : tsq_mcontained(PG_FUNCTION_ARGS)
     355              : {
     356           61 :     PG_RETURN_DATUM(DirectFunctionCall2(tsq_mcontains,
     357              :                                         PG_GETARG_DATUM(1),
     358              :                                         PG_GETARG_DATUM(0)));
     359              : }
        

Generated by: LCOV version 2.0-1