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