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