1 /*-------------------------------------------------------------------------
2 *
3 * tsquery_op.c
4 * Various operations with tsquery
5 *
6 * Portions Copyright (c) 1996-2021, 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/builtins.h"
20
21 Datum
tsquery_numnode(PG_FUNCTION_ARGS)22 tsquery_numnode(PG_FUNCTION_ARGS)
23 {
24 TSQuery query = PG_GETARG_TSQUERY(0);
25 int nnode = query->size;
26
27 PG_FREE_IF_COPY(query, 0);
28 PG_RETURN_INT32(nnode);
29 }
30
31 static QTNode *
join_tsqueries(TSQuery a,TSQuery b,int8 operator,uint16 distance)32 join_tsqueries(TSQuery a, TSQuery b, int8 operator, uint16 distance)
33 {
34 QTNode *res = (QTNode *) palloc0(sizeof(QTNode));
35
36 res->flags |= QTN_NEEDFREE;
37
38 res->valnode = (QueryItem *) palloc0(sizeof(QueryItem));
39 res->valnode->type = QI_OPR;
40 res->valnode->qoperator.oper = operator;
41 if (operator == OP_PHRASE)
42 res->valnode->qoperator.distance = distance;
43
44 res->child = (QTNode **) palloc0(sizeof(QTNode *) * 2);
45 res->child[0] = QT2QTN(GETQUERY(b), GETOPERAND(b));
46 res->child[1] = QT2QTN(GETQUERY(a), GETOPERAND(a));
47 res->nchild = 2;
48
49 return res;
50 }
51
52 Datum
tsquery_and(PG_FUNCTION_ARGS)53 tsquery_and(PG_FUNCTION_ARGS)
54 {
55 TSQuery a = PG_GETARG_TSQUERY_COPY(0);
56 TSQuery b = PG_GETARG_TSQUERY_COPY(1);
57 QTNode *res;
58 TSQuery query;
59
60 if (a->size == 0)
61 {
62 PG_FREE_IF_COPY(a, 1);
63 PG_RETURN_POINTER(b);
64 }
65 else if (b->size == 0)
66 {
67 PG_FREE_IF_COPY(b, 1);
68 PG_RETURN_POINTER(a);
69 }
70
71 res = join_tsqueries(a, b, OP_AND, 0);
72
73 query = QTN2QT(res);
74
75 QTNFree(res);
76 PG_FREE_IF_COPY(a, 0);
77 PG_FREE_IF_COPY(b, 1);
78
79 PG_RETURN_TSQUERY(query);
80 }
81
82 Datum
tsquery_or(PG_FUNCTION_ARGS)83 tsquery_or(PG_FUNCTION_ARGS)
84 {
85 TSQuery a = PG_GETARG_TSQUERY_COPY(0);
86 TSQuery b = PG_GETARG_TSQUERY_COPY(1);
87 QTNode *res;
88 TSQuery query;
89
90 if (a->size == 0)
91 {
92 PG_FREE_IF_COPY(a, 1);
93 PG_RETURN_POINTER(b);
94 }
95 else if (b->size == 0)
96 {
97 PG_FREE_IF_COPY(b, 1);
98 PG_RETURN_POINTER(a);
99 }
100
101 res = join_tsqueries(a, b, OP_OR, 0);
102
103 query = QTN2QT(res);
104
105 QTNFree(res);
106 PG_FREE_IF_COPY(a, 0);
107 PG_FREE_IF_COPY(b, 1);
108
109 PG_RETURN_TSQUERY(query);
110 }
111
112 Datum
tsquery_phrase_distance(PG_FUNCTION_ARGS)113 tsquery_phrase_distance(PG_FUNCTION_ARGS)
114 {
115 TSQuery a = PG_GETARG_TSQUERY_COPY(0);
116 TSQuery b = PG_GETARG_TSQUERY_COPY(1);
117 QTNode *res;
118 TSQuery query;
119 int32 distance = PG_GETARG_INT32(2);
120
121 if (distance < 0 || distance > MAXENTRYPOS)
122 ereport(ERROR,
123 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
124 errmsg("distance in phrase operator must be an integer value between zero and %d inclusive",
125 MAXENTRYPOS)));
126 if (a->size == 0)
127 {
128 PG_FREE_IF_COPY(a, 1);
129 PG_RETURN_POINTER(b);
130 }
131 else if (b->size == 0)
132 {
133 PG_FREE_IF_COPY(b, 1);
134 PG_RETURN_POINTER(a);
135 }
136
137 res = join_tsqueries(a, b, OP_PHRASE, (uint16) distance);
138
139 query = QTN2QT(res);
140
141 QTNFree(res);
142 PG_FREE_IF_COPY(a, 0);
143 PG_FREE_IF_COPY(b, 1);
144
145 PG_RETURN_TSQUERY(query);
146 }
147
148 Datum
tsquery_phrase(PG_FUNCTION_ARGS)149 tsquery_phrase(PG_FUNCTION_ARGS)
150 {
151 PG_RETURN_POINTER(DirectFunctionCall3(tsquery_phrase_distance,
152 PG_GETARG_DATUM(0),
153 PG_GETARG_DATUM(1),
154 Int32GetDatum(1)));
155 }
156
157 Datum
tsquery_not(PG_FUNCTION_ARGS)158 tsquery_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
187 static int
CompareTSQ(TSQuery a,TSQuery b)188 CompareTSQ(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
213 Datum
tsquery_cmp(PG_FUNCTION_ARGS)214 tsquery_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 ) \
227 Datum \
228 NAME(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 ; */ \
239 extern int no_such_variable
240
241 CMPFUNC(tsquery_lt, res < 0);
242 CMPFUNC(tsquery_le, res <= 0);
243 CMPFUNC(tsquery_eq, res == 0);
244 CMPFUNC(tsquery_ge, res >= 0);
245 CMPFUNC(tsquery_gt, res > 0);
246 CMPFUNC(tsquery_ne, res != 0);
247
248 TSQuerySign
makeTSQuerySign(TSQuery a)249 makeTSQuerySign(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
265 static char **
collectTSQueryValues(TSQuery a,int * nvalues_p)266 collectTSQueryValues(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
296 static int
cmp_string(const void * a,const void * b)297 cmp_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
305 Datum
tsq_mcontains(PG_FUNCTION_ARGS)306 tsq_mcontains(PG_FUNCTION_ARGS)
307 {
308 TSQuery query = PG_GETARG_TSQUERY(0);
309 TSQuery ex = PG_GETARG_TSQUERY(1);
310 char **query_values;
311 int query_nvalues;
312 char **ex_values;
313 int ex_nvalues;
314 bool result = true;
315
316 /* Extract the query terms into arrays */
317 query_values = collectTSQueryValues(query, &query_nvalues);
318 ex_values = collectTSQueryValues(ex, &ex_nvalues);
319
320 /* Sort and remove duplicates from both arrays */
321 qsort(query_values, query_nvalues, sizeof(char *), cmp_string);
322 query_nvalues = qunique(query_values, query_nvalues, sizeof(char *),
323 cmp_string);
324 qsort(ex_values, ex_nvalues, sizeof(char *), cmp_string);
325 ex_nvalues = qunique(ex_values, ex_nvalues, sizeof(char *), cmp_string);
326
327 if (ex_nvalues > query_nvalues)
328 result = false;
329 else
330 {
331 int i;
332 int j = 0;
333
334 for (i = 0; i < ex_nvalues; i++)
335 {
336 for (; j < query_nvalues; j++)
337 {
338 if (strcmp(ex_values[i], query_values[j]) == 0)
339 break;
340 }
341 if (j == query_nvalues)
342 {
343 result = false;
344 break;
345 }
346 }
347 }
348
349 PG_RETURN_BOOL(result);
350 }
351
352 Datum
tsq_mcontained(PG_FUNCTION_ARGS)353 tsq_mcontained(PG_FUNCTION_ARGS)
354 {
355 PG_RETURN_DATUM(DirectFunctionCall2(tsq_mcontains,
356 PG_GETARG_DATUM(1),
357 PG_GETARG_DATUM(0)));
358 }
359