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