Coverage Report

Created: 2025-06-13 06:06

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