1 /*-------------------------------------------------------------------------
2  *
3  * tsquery_op.c
4  *	  Various operations with tsquery
5  *
6  * Portions Copyright (c) 1996-2018, 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
tsquery_numnode(PG_FUNCTION_ARGS)21 tsquery_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 
30 static QTNode *
join_tsqueries(TSQuery a,TSQuery b,int8 operator,uint16 distance)31 join_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 
51 Datum
tsquery_and(PG_FUNCTION_ARGS)52 tsquery_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 
81 Datum
tsquery_or(PG_FUNCTION_ARGS)82 tsquery_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 
111 Datum
tsquery_phrase_distance(PG_FUNCTION_ARGS)112 tsquery_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 must be an integer value between zero and %d inclusive",
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 
147 Datum
tsquery_phrase(PG_FUNCTION_ARGS)148 tsquery_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 
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 = *((const char **) a);
300 	const char *sb = *((const char **) b);
301 
302 	return strcmp(sa, sb);
303 }
304 
305 static int
remove_duplicates(char ** strings,int n)306 remove_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 
328 Datum
tsq_mcontains(PG_FUNCTION_ARGS)329 tsq_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 
374 Datum
tsq_mcontained(PG_FUNCTION_ARGS)375 tsq_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