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