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