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