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