Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * tsquery_util.c
4 : : * Utilities for tsquery datatype
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : *
8 : : *
9 : : * IDENTIFICATION
10 : : * src/backend/utils/adt/tsquery_util.c
11 : : *
12 : : *-------------------------------------------------------------------------
13 : : */
14 : :
15 : : #include "postgres.h"
16 : :
17 : : #include "miscadmin.h"
18 : : #include "tsearch/ts_utils.h"
19 : : #include "varatt.h"
20 : :
21 : : /*
22 : : * Build QTNode tree for a tsquery given in QueryItem array format.
23 : : */
24 : : QTNode *
25 : 1471 : QT2QTN(QueryItem *in, char *operand)
26 : : {
27 : 1471 : QTNode *node = palloc0_object(QTNode);
28 : :
29 : : /* since this function recurses, it could be driven to stack overflow. */
30 : 1471 : check_stack_depth();
31 : :
32 : 1471 : node->valnode = in;
33 : :
34 [ + + ]: 1471 : if (in->type == QI_OPR)
35 : : {
36 : 555 : node->child = palloc0_array(QTNode *, 2);
37 : 555 : node->child[0] = QT2QTN(in + 1, operand);
38 : 555 : node->sign = node->child[0]->sign;
39 [ + + ]: 555 : if (in->qoperator.oper == OP_NOT)
40 : 7 : node->nchild = 1;
41 : : else
42 : : {
43 : 548 : node->nchild = 2;
44 : 548 : node->child[1] = QT2QTN(in + in->qoperator.left, operand);
45 : 548 : node->sign |= node->child[1]->sign;
46 : : }
47 : 555 : }
48 [ - + ]: 916 : else if (operand)
49 : : {
50 : 916 : node->word = operand + in->qoperand.distance;
51 : 916 : node->sign = ((uint32) 1) << (((unsigned int) in->qoperand.valcrc) % 32);
52 : 916 : }
53 : :
54 : 2942 : return node;
55 : 1471 : }
56 : :
57 : : /*
58 : : * Free a QTNode tree.
59 : : *
60 : : * Referenced "word" and "valnode" items are freed if marked as transient
61 : : * by flags.
62 : : */
63 : : void
64 : 1626 : QTNFree(QTNode *in)
65 : : {
66 [ + + ]: 1626 : if (!in)
67 : 2 : return;
68 : :
69 : : /* since this function recurses, it could be driven to stack overflow. */
70 : 1624 : check_stack_depth();
71 : :
72 [ + + + - : 1624 : if (in->valnode->type == QI_VAL && in->word && (in->flags & QTN_WORDFREE) != 0)
+ + ]
73 : 102 : pfree(in->word);
74 : :
75 [ + + ]: 1624 : if (in->valnode->type == QI_OPR)
76 : : {
77 : 606 : int i;
78 : :
79 [ + + ]: 1829 : for (i = 0; i < in->nchild; i++)
80 : 1223 : QTNFree(in->child[i]);
81 : 606 : }
82 [ + + ]: 1624 : if (in->child)
83 : 606 : pfree(in->child);
84 : :
85 [ + + ]: 1624 : if (in->flags & QTN_NEEDFREE)
86 : 192 : pfree(in->valnode);
87 : :
88 : 1624 : pfree(in);
89 : 1626 : }
90 : :
91 : : /*
92 : : * Sort comparator for QTNodes.
93 : : *
94 : : * The sort order is somewhat arbitrary.
95 : : */
96 : : int
97 : 662 : QTNodeCompare(QTNode *an, QTNode *bn)
98 : : {
99 : : /* since this function recurses, it could be driven to stack overflow. */
100 : 662 : check_stack_depth();
101 : :
102 [ + + ]: 662 : if (an->valnode->type != bn->valnode->type)
103 : 197 : return (an->valnode->type > bn->valnode->type) ? -1 : 1;
104 : :
105 [ + + ]: 465 : if (an->valnode->type == QI_OPR)
106 : : {
107 : 90 : QueryOperator *ao = &an->valnode->qoperator;
108 : 90 : QueryOperator *bo = &bn->valnode->qoperator;
109 : :
110 [ + + ]: 90 : if (ao->oper != bo->oper)
111 : 8 : return (ao->oper > bo->oper) ? -1 : 1;
112 : :
113 [ + + ]: 82 : if (an->nchild != bn->nchild)
114 : 18 : return (an->nchild > bn->nchild) ? -1 : 1;
115 : :
116 : : {
117 : 64 : int i,
118 : : res;
119 : :
120 [ + + ]: 106 : for (i = 0; i < an->nchild; i++)
121 [ + + ]: 85 : if ((res = QTNodeCompare(an->child[i], bn->child[i])) != 0)
122 : 43 : return res;
123 [ + + ]: 64 : }
124 : :
125 [ + + + + ]: 21 : if (ao->oper == OP_PHRASE && ao->distance != bo->distance)
126 : 1 : return (ao->distance > bo->distance) ? -1 : 1;
127 : :
128 : 20 : return 0;
129 : 90 : }
130 [ + - ]: 375 : else if (an->valnode->type == QI_VAL)
131 : : {
132 : 375 : QueryOperand *ao = &an->valnode->qoperand;
133 : 375 : QueryOperand *bo = &bn->valnode->qoperand;
134 : :
135 [ + + ]: 375 : if (ao->valcrc != bo->valcrc)
136 : : {
137 : 292 : return (ao->valcrc > bo->valcrc) ? -1 : 1;
138 : : }
139 : :
140 : 83 : return tsCompareString(an->word, ao->length, bn->word, bo->length, false);
141 : 375 : }
142 : : else
143 : : {
144 [ # # # # ]: 0 : elog(ERROR, "unrecognized QueryItem type: %d", an->valnode->type);
145 : 0 : return 0; /* keep compiler quiet */
146 : : }
147 : 662 : }
148 : :
149 : : /*
150 : : * qsort comparator for QTNode pointers.
151 : : */
152 : : static int
153 : 506 : cmpQTN(const void *a, const void *b)
154 : : {
155 : 506 : return QTNodeCompare(*(QTNode *const *) a, *(QTNode *const *) b);
156 : : }
157 : :
158 : : /*
159 : : * Canonicalize a QTNode tree by sorting the children of AND/OR nodes
160 : : * into an arbitrary but well-defined order.
161 : : */
162 : : void
163 : 1514 : QTNSort(QTNode *in)
164 : : {
165 : 1514 : int i;
166 : :
167 : : /* since this function recurses, it could be driven to stack overflow. */
168 : 1514 : check_stack_depth();
169 : :
170 [ + + ]: 1514 : if (in->valnode->type != QI_OPR)
171 : 986 : return;
172 : :
173 [ + + ]: 1733 : for (i = 0; i < in->nchild; i++)
174 : 1205 : QTNSort(in->child[i]);
175 [ + + + + ]: 528 : if (in->nchild > 1 && in->valnode->qoperator.oper != OP_PHRASE)
176 : 308 : qsort(in->child, in->nchild, sizeof(QTNode *), cmpQTN);
177 [ - + ]: 1514 : }
178 : :
179 : : /*
180 : : * Are two QTNode trees equal according to QTNodeCompare?
181 : : */
182 : : bool
183 : 33 : QTNEq(QTNode *a, QTNode *b)
184 : : {
185 : 33 : uint32 sign = a->sign & b->sign;
186 : :
187 [ + + - + ]: 33 : if (!(sign == a->sign && sign == b->sign))
188 : 1 : return false;
189 : :
190 : 32 : return (QTNodeCompare(a, b) == 0);
191 : 33 : }
192 : :
193 : : /*
194 : : * Remove unnecessary intermediate nodes. For example:
195 : : *
196 : : * OR OR
197 : : * a OR -> a b c
198 : : * b c
199 : : */
200 : : void
201 : 1458 : QTNTernary(QTNode *in)
202 : : {
203 : 1458 : int i;
204 : :
205 : : /* since this function recurses, it could be driven to stack overflow. */
206 : 1458 : check_stack_depth();
207 : :
208 [ + + ]: 1458 : if (in->valnode->type != QI_OPR)
209 : 923 : return;
210 : :
211 [ + + ]: 1691 : for (i = 0; i < in->nchild; i++)
212 : 1156 : QTNTernary(in->child[i]);
213 : :
214 : : /* Only AND and OR are associative, so don't flatten other node types */
215 [ + + + + ]: 535 : if (in->valnode->qoperator.oper != OP_AND &&
216 : 336 : in->valnode->qoperator.oper != OP_OR)
217 : 208 : return;
218 : :
219 [ + + ]: 1071 : for (i = 0; i < in->nchild; i++)
220 : : {
221 : 744 : QTNode *cc = in->child[i];
222 : :
223 [ + + + + ]: 744 : if (cc->valnode->type == QI_OPR &&
224 : 259 : in->valnode->qoperator.oper == cc->valnode->qoperator.oper)
225 : : {
226 : 55 : int oldnchild = in->nchild;
227 : :
228 : 55 : in->nchild += cc->nchild - 1;
229 : 55 : in->child = repalloc_array(in->child, QTNode *, in->nchild);
230 : :
231 [ + + ]: 55 : if (i + 1 != oldnchild)
232 : 6 : memmove(in->child + i + cc->nchild, in->child + i + 1,
233 : : (oldnchild - i - 1) * sizeof(QTNode *));
234 : :
235 : 55 : memcpy(in->child + i, cc->child, cc->nchild * sizeof(QTNode *));
236 : 55 : i += cc->nchild - 1;
237 : :
238 [ + + ]: 55 : if (cc->flags & QTN_NEEDFREE)
239 : 18 : pfree(cc->valnode);
240 : 55 : pfree(cc);
241 : 55 : }
242 : 744 : }
243 [ - + ]: 1458 : }
244 : :
245 : : /*
246 : : * Convert a tree to binary tree by inserting intermediate nodes.
247 : : * (Opposite of QTNTernary)
248 : : */
249 : : void
250 : 197 : QTNBinary(QTNode *in)
251 : : {
252 : 197 : int i;
253 : :
254 : : /* since this function recurses, it could be driven to stack overflow. */
255 : 197 : check_stack_depth();
256 : :
257 [ + + ]: 197 : if (in->valnode->type != QI_OPR)
258 : 121 : return;
259 : :
260 [ + + ]: 244 : for (i = 0; i < in->nchild; i++)
261 : 168 : QTNBinary(in->child[i]);
262 : :
263 [ + + ]: 96 : while (in->nchild > 2)
264 : : {
265 : 20 : QTNode *nn = palloc0_object(QTNode);
266 : :
267 : 20 : nn->valnode = palloc0_object(QueryItem);
268 : 20 : nn->child = palloc0_array(QTNode *, 2);
269 : :
270 : 20 : nn->nchild = 2;
271 : 20 : nn->flags = QTN_NEEDFREE;
272 : :
273 : 20 : nn->child[0] = in->child[0];
274 : 20 : nn->child[1] = in->child[1];
275 : 20 : nn->sign = nn->child[0]->sign | nn->child[1]->sign;
276 : :
277 : 20 : nn->valnode->type = in->valnode->type;
278 : 20 : nn->valnode->qoperator.oper = in->valnode->qoperator.oper;
279 : :
280 : 20 : in->child[0] = nn;
281 : 20 : in->child[1] = in->child[in->nchild - 1];
282 : 20 : in->nchild--;
283 : 20 : }
284 [ - + ]: 197 : }
285 : :
286 : : /*
287 : : * Count the total length of operand strings in tree (including '\0'-
288 : : * terminators) and the total number of nodes.
289 : : * Caller must initialize *sumlen and *nnode to zeroes.
290 : : */
291 : : static void
292 : 331 : cntsize(QTNode *in, int *sumlen, int *nnode)
293 : : {
294 : : /* since this function recurses, it could be driven to stack overflow. */
295 : 331 : check_stack_depth();
296 : :
297 : 331 : *nnode += 1;
298 [ + + ]: 331 : if (in->valnode->type == QI_OPR)
299 : : {
300 : 145 : int i;
301 : :
302 [ + + ]: 427 : for (i = 0; i < in->nchild; i++)
303 : 282 : cntsize(in->child[i], sumlen, nnode);
304 : 145 : }
305 : : else
306 : : {
307 : 186 : *sumlen += in->valnode->qoperand.length + 1;
308 : : }
309 : 331 : }
310 : :
311 : : typedef struct
312 : : {
313 : : QueryItem *curitem;
314 : : char *operand;
315 : : char *curoperand;
316 : : } QTN2QTState;
317 : :
318 : : /*
319 : : * Recursively convert a QTNode tree into flat tsquery format.
320 : : * Caller must have allocated arrays of the correct size.
321 : : */
322 : : static void
323 : 331 : fillQT(QTN2QTState *state, QTNode *in)
324 : : {
325 : : /* since this function recurses, it could be driven to stack overflow. */
326 : 331 : check_stack_depth();
327 : :
328 [ + + ]: 331 : if (in->valnode->type == QI_VAL)
329 : : {
330 : 186 : memcpy(state->curitem, in->valnode, sizeof(QueryOperand));
331 : :
332 : 186 : memcpy(state->curoperand, in->word, in->valnode->qoperand.length);
333 : 186 : state->curitem->qoperand.distance = state->curoperand - state->operand;
334 : 186 : state->curoperand[in->valnode->qoperand.length] = '\0';
335 : 186 : state->curoperand += in->valnode->qoperand.length + 1;
336 : 186 : state->curitem++;
337 : 186 : }
338 : : else
339 : : {
340 : 145 : QueryItem *curitem = state->curitem;
341 : :
342 [ + - ]: 145 : Assert(in->valnode->type == QI_OPR);
343 : :
344 : 145 : memcpy(state->curitem, in->valnode, sizeof(QueryOperator));
345 : :
346 [ + - ]: 145 : Assert(in->nchild <= 2);
347 : 145 : state->curitem++;
348 : :
349 : 145 : fillQT(state, in->child[0]);
350 : :
351 [ + + ]: 145 : if (in->nchild == 2)
352 : : {
353 : 137 : curitem->qoperator.left = state->curitem - curitem;
354 : 137 : fillQT(state, in->child[1]);
355 : 137 : }
356 : 145 : }
357 : 331 : }
358 : :
359 : : /*
360 : : * Build flat tsquery from a QTNode tree.
361 : : */
362 : : TSQuery
363 : 49 : QTN2QT(QTNode *in)
364 : : {
365 : 49 : TSQuery out;
366 : 49 : int len;
367 : 49 : int sumlen = 0,
368 : 49 : nnode = 0;
369 : 49 : QTN2QTState state;
370 : :
371 : 49 : cntsize(in, &sumlen, &nnode);
372 : :
373 [ + - ]: 49 : if (TSQUERY_TOO_BIG(nnode, sumlen))
374 [ # # # # ]: 0 : ereport(ERROR,
375 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
376 : : errmsg("tsquery is too large")));
377 : 49 : len = COMPUTESIZE(nnode, sumlen);
378 : :
379 : 49 : out = (TSQuery) palloc0(len);
380 : 49 : SET_VARSIZE(out, len);
381 : 49 : out->size = nnode;
382 : :
383 : 49 : state.curitem = GETQUERY(out);
384 : 49 : state.operand = state.curoperand = GETOPERAND(out);
385 : :
386 : 49 : fillQT(&state, in);
387 : 98 : return out;
388 : 49 : }
389 : :
390 : : /*
391 : : * Copy a QTNode tree.
392 : : *
393 : : * Modifiable copies of the words and valnodes are made, too.
394 : : */
395 : : QTNode *
396 : 170 : QTNCopy(QTNode *in)
397 : : {
398 : 170 : QTNode *out;
399 : :
400 : : /* since this function recurses, it could be driven to stack overflow. */
401 : 170 : check_stack_depth();
402 : :
403 : 170 : out = palloc_object(QTNode);
404 : :
405 : 170 : *out = *in;
406 : 170 : out->valnode = palloc_object(QueryItem);
407 : 170 : *(out->valnode) = *(in->valnode);
408 : 170 : out->flags |= QTN_NEEDFREE;
409 : :
410 [ + + ]: 170 : if (in->valnode->type == QI_VAL)
411 : : {
412 : 102 : out->word = palloc(in->valnode->qoperand.length + 1);
413 : 102 : memcpy(out->word, in->word, in->valnode->qoperand.length);
414 : 102 : out->word[in->valnode->qoperand.length] = '\0';
415 : 102 : out->flags |= QTN_WORDFREE;
416 : 102 : }
417 : : else
418 : : {
419 : 68 : int i;
420 : :
421 : 68 : out->child = palloc_array(QTNode *, in->nchild);
422 : :
423 [ + + ]: 203 : for (i = 0; i < in->nchild; i++)
424 : 135 : out->child[i] = QTNCopy(in->child[i]);
425 : 68 : }
426 : :
427 : 340 : return out;
428 : 170 : }
429 : :
430 : : /*
431 : : * Clear the specified flag bit(s) in all nodes of a QTNode tree.
432 : : */
433 : : void
434 : 874 : QTNClearFlags(QTNode *in, uint32 flags)
435 : : {
436 : : /* since this function recurses, it could be driven to stack overflow. */
437 : 874 : check_stack_depth();
438 : :
439 : 874 : in->flags &= ~flags;
440 : :
441 [ + + ]: 874 : if (in->valnode->type != QI_VAL)
442 : : {
443 : 326 : int i;
444 : :
445 [ + + ]: 1068 : for (i = 0; i < in->nchild; i++)
446 : 742 : QTNClearFlags(in->child[i], flags);
447 : 326 : }
448 : 874 : }
|