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