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