Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * tsquery_util.c
4 : : * Utilities for tsquery datatype
5 : : *
6 : : * Portions Copyright (c) 1996-2024, 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 *
5995 bruce@momjian.us 25 :CBC 4419 : QT2QTN(QueryItem *in, char *operand)
26 : : {
6081 tgl@sss.pgh.pa.us 27 : 4419 : QTNode *node = (QTNode *) palloc0(sizeof(QTNode));
28 : :
29 : : /* since this function recurses, it could be driven to stack overflow. */
6064 teodor@sigaev.ru 30 : 4419 : check_stack_depth();
31 : :
6081 tgl@sss.pgh.pa.us 32 : 4419 : node->valnode = in;
33 : :
6064 teodor@sigaev.ru 34 [ + + ]: 4419 : if (in->type == QI_OPR)
35 : : {
6081 tgl@sss.pgh.pa.us 36 : 1667 : node->child = (QTNode **) palloc0(sizeof(QTNode *) * 2);
37 : 1667 : node->child[0] = QT2QTN(in + 1, operand);
38 : 1667 : node->sign = node->child[0]->sign;
5386 peter_e@gmx.net 39 [ + + ]: 1667 : if (in->qoperator.oper == OP_NOT)
6081 tgl@sss.pgh.pa.us 40 : 21 : node->nchild = 1;
41 : : else
42 : : {
43 : 1646 : node->nchild = 2;
5386 peter_e@gmx.net 44 : 1646 : node->child[1] = QT2QTN(in + in->qoperator.left, operand);
6081 tgl@sss.pgh.pa.us 45 : 1646 : node->sign |= node->child[1]->sign;
46 : : }
47 : : }
48 [ + - ]: 2752 : else if (operand)
49 : : {
5386 peter_e@gmx.net 50 : 2752 : node->word = operand + in->qoperand.distance;
5003 tgl@sss.pgh.pa.us 51 : 2752 : node->sign = ((uint32) 1) << (((unsigned int) in->qoperand.valcrc) % 32);
52 : : }
53 : :
6081 54 : 4419 : return node;
55 : : }
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
5995 bruce@momjian.us 64 : 4884 : QTNFree(QTNode *in)
65 : : {
6081 tgl@sss.pgh.pa.us 66 [ + + ]: 4884 : if (!in)
67 : 6 : return;
68 : :
69 : : /* since this function recurses, it could be driven to stack overflow. */
6064 teodor@sigaev.ru 70 : 4878 : check_stack_depth();
71 : :
72 [ + + + - : 4878 : if (in->valnode->type == QI_VAL && in->word && (in->flags & QTN_WORDFREE) != 0)
+ + ]
6081 tgl@sss.pgh.pa.us 73 : 306 : pfree(in->word);
74 : :
2723 75 [ + + ]: 4878 : if (in->valnode->type == QI_OPR)
76 : : {
77 : : int i;
78 : :
79 [ + + ]: 5493 : for (i = 0; i < in->nchild; i++)
80 : 3673 : QTNFree(in->child[i]);
81 : : }
82 [ + + ]: 4878 : if (in->child)
83 : 1820 : pfree(in->child);
84 : :
85 [ + + ]: 4878 : if (in->flags & QTN_NEEDFREE)
86 : 576 : pfree(in->valnode);
87 : :
6081 88 : 4878 : pfree(in);
89 : : }
90 : :
91 : : /*
92 : : * Sort comparator for QTNodes.
93 : : *
94 : : * The sort order is somewhat arbitrary.
95 : : */
96 : : int
5995 bruce@momjian.us 97 : 1989 : QTNodeCompare(QTNode *an, QTNode *bn)
98 : : {
99 : : /* since this function recurses, it could be driven to stack overflow. */
6064 teodor@sigaev.ru 100 : 1989 : check_stack_depth();
101 : :
6081 tgl@sss.pgh.pa.us 102 [ + + ]: 1989 : if (an->valnode->type != bn->valnode->type)
103 [ + + ]: 591 : return (an->valnode->type > bn->valnode->type) ? -1 : 1;
104 : :
6064 teodor@sigaev.ru 105 [ + + ]: 1398 : if (an->valnode->type == QI_OPR)
106 : : {
5386 peter_e@gmx.net 107 : 271 : QueryOperator *ao = &an->valnode->qoperator;
108 : 271 : QueryOperator *bo = &bn->valnode->qoperator;
109 : :
5995 bruce@momjian.us 110 [ + + ]: 271 : if (ao->oper != bo->oper)
6064 teodor@sigaev.ru 111 [ + + ]: 24 : return (ao->oper > bo->oper) ? -1 : 1;
112 : :
113 [ + + ]: 247 : if (an->nchild != bn->nchild)
114 [ + - ]: 54 : return (an->nchild > bn->nchild) ? -1 : 1;
115 : :
116 : : {
117 : : int i,
118 : : res;
119 : :
120 [ + + ]: 321 : for (i = 0; i < an->nchild; i++)
121 [ + + ]: 257 : if ((res = QTNodeCompare(an->child[i], bn->child[i])) != 0)
122 : 129 : return res;
123 : : }
124 : :
2929 125 [ + + + + ]: 64 : if (ao->oper == OP_PHRASE && ao->distance != bo->distance)
126 [ + - ]: 3 : return (ao->distance > bo->distance) ? -1 : 1;
127 : :
6064 128 : 61 : return 0;
129 : : }
5003 tgl@sss.pgh.pa.us 130 [ + - ]: 1127 : else if (an->valnode->type == QI_VAL)
131 : : {
5386 peter_e@gmx.net 132 : 1127 : QueryOperand *ao = &an->valnode->qoperand;
133 : 1127 : QueryOperand *bo = &bn->valnode->qoperand;
134 : :
6064 teodor@sigaev.ru 135 [ + + ]: 1127 : if (ao->valcrc != bo->valcrc)
136 : : {
137 [ + + ]: 876 : return (ao->valcrc > bo->valcrc) ? -1 : 1;
138 : : }
139 : :
5421 bruce@momjian.us 140 : 251 : return tsCompareString(an->word, ao->length, bn->word, bo->length, false);
141 : : }
142 : : else
143 : : {
5003 tgl@sss.pgh.pa.us 144 [ # # ]:UBC 0 : elog(ERROR, "unrecognized QueryItem type: %d", an->valnode->type);
145 : : return 0; /* keep compiler quiet */
146 : : }
147 : : }
148 : :
149 : : /*
150 : : * qsort comparator for QTNode pointers.
151 : : */
152 : : static int
6081 tgl@sss.pgh.pa.us 153 :CBC 1518 : cmpQTN(const void *a, const void *b)
154 : : {
4326 bruce@momjian.us 155 : 1518 : 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
5995 163 : 4542 : QTNSort(QTNode *in)
164 : : {
165 : : int i;
166 : :
167 : : /* since this function recurses, it could be driven to stack overflow. */
6064 teodor@sigaev.ru 168 : 4542 : check_stack_depth();
169 : :
170 [ + + ]: 4542 : if (in->valnode->type != QI_OPR)
6081 tgl@sss.pgh.pa.us 171 : 2958 : return;
172 : :
173 [ + + ]: 5199 : for (i = 0; i < in->nchild; i++)
174 : 3615 : QTNSort(in->child[i]);
2929 teodor@sigaev.ru 175 [ + + + + ]: 1584 : if (in->nchild > 1 && in->valnode->qoperator.oper != OP_PHRASE)
432 peter@eisentraut.org 176 : 924 : qsort(in->child, in->nchild, sizeof(QTNode *), cmpQTN);
177 : : }
178 : :
179 : : /*
180 : : * Are two QTNode trees equal according to QTNodeCompare?
181 : : */
182 : : bool
5995 bruce@momjian.us 183 : 99 : QTNEq(QTNode *a, QTNode *b)
184 : : {
6081 tgl@sss.pgh.pa.us 185 : 99 : uint32 sign = a->sign & b->sign;
186 : :
187 [ + + - + ]: 99 : if (!(sign == a->sign && sign == b->sign))
2723 188 : 3 : return false;
189 : :
949 michael@paquier.xyz 190 : 96 : return (QTNodeCompare(a, b) == 0);
191 : : }
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
5995 bruce@momjian.us 201 : 4374 : QTNTernary(QTNode *in)
202 : : {
203 : : int i;
204 : :
205 : : /* since this function recurses, it could be driven to stack overflow. */
6064 teodor@sigaev.ru 206 : 4374 : check_stack_depth();
207 : :
208 [ + + ]: 4374 : if (in->valnode->type != QI_OPR)
6081 tgl@sss.pgh.pa.us 209 : 2769 : return;
210 : :
211 [ + + ]: 5073 : for (i = 0; i < in->nchild; i++)
212 : 3468 : QTNTernary(in->child[i]);
213 : :
214 : : /* Only AND and OR are associative, so don't flatten other node types */
2723 215 [ + + ]: 1605 : if (in->valnode->qoperator.oper != OP_AND &&
216 [ + + ]: 1008 : in->valnode->qoperator.oper != OP_OR)
217 : 624 : return;
218 : :
6081 219 [ + + ]: 3213 : for (i = 0; i < in->nchild; i++)
220 : : {
6064 teodor@sigaev.ru 221 : 2232 : QTNode *cc = in->child[i];
222 : :
2929 223 [ + + ]: 2232 : if (cc->valnode->type == QI_OPR &&
2723 tgl@sss.pgh.pa.us 224 [ + + ]: 777 : in->valnode->qoperator.oper == cc->valnode->qoperator.oper)
225 : : {
6081 226 : 165 : int oldnchild = in->nchild;
227 : :
228 : 165 : in->nchild += cc->nchild - 1;
229 : 165 : in->child = (QTNode **) repalloc(in->child, in->nchild * sizeof(QTNode *));
230 : :
231 [ + + ]: 165 : if (i + 1 != oldnchild)
232 : 18 : memmove(in->child + i + cc->nchild, in->child + i + 1,
233 : 18 : (oldnchild - i - 1) * sizeof(QTNode *));
234 : :
235 : 165 : memcpy(in->child + i, cc->child, cc->nchild * sizeof(QTNode *));
236 : 165 : i += cc->nchild - 1;
237 : :
5995 bruce@momjian.us 238 [ + + ]: 165 : if (cc->flags & QTN_NEEDFREE)
6064 teodor@sigaev.ru 239 : 54 : pfree(cc->valnode);
6081 tgl@sss.pgh.pa.us 240 : 165 : pfree(cc);
241 : : }
242 : : }
243 : : }
244 : :
245 : : /*
246 : : * Convert a tree to binary tree by inserting intermediate nodes.
247 : : * (Opposite of QTNTernary)
248 : : */
249 : : void
5995 bruce@momjian.us 250 : 591 : QTNBinary(QTNode *in)
251 : : {
252 : : int i;
253 : :
254 : : /* since this function recurses, it could be driven to stack overflow. */
6064 teodor@sigaev.ru 255 : 591 : check_stack_depth();
256 : :
257 [ + + ]: 591 : if (in->valnode->type != QI_OPR)
6081 tgl@sss.pgh.pa.us 258 : 363 : return;
259 : :
260 [ + + ]: 732 : for (i = 0; i < in->nchild; i++)
261 : 504 : QTNBinary(in->child[i]);
262 : :
263 [ + + ]: 288 : while (in->nchild > 2)
264 : : {
265 : 60 : QTNode *nn = (QTNode *) palloc0(sizeof(QTNode));
266 : :
267 : 60 : nn->valnode = (QueryItem *) palloc0(sizeof(QueryItem));
268 : 60 : nn->child = (QTNode **) palloc0(sizeof(QTNode *) * 2);
269 : :
270 : 60 : nn->nchild = 2;
271 : 60 : nn->flags = QTN_NEEDFREE;
272 : :
273 : 60 : nn->child[0] = in->child[0];
274 : 60 : nn->child[1] = in->child[1];
275 : 60 : nn->sign = nn->child[0]->sign | nn->child[1]->sign;
276 : :
277 : 60 : nn->valnode->type = in->valnode->type;
5386 peter_e@gmx.net 278 : 60 : nn->valnode->qoperator.oper = in->valnode->qoperator.oper;
279 : :
6081 tgl@sss.pgh.pa.us 280 : 60 : in->child[0] = nn;
281 : 60 : in->child[1] = in->child[in->nchild - 1];
282 : 60 : in->nchild--;
283 : : }
284 : : }
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
5995 bruce@momjian.us 292 : 993 : cntsize(QTNode *in, int *sumlen, int *nnode)
293 : : {
294 : : /* since this function recurses, it could be driven to stack overflow. */
6064 teodor@sigaev.ru 295 : 993 : check_stack_depth();
296 : :
6081 tgl@sss.pgh.pa.us 297 : 993 : *nnode += 1;
6064 teodor@sigaev.ru 298 [ + + ]: 993 : if (in->valnode->type == QI_OPR)
299 : : {
300 : : int i;
301 : :
6081 tgl@sss.pgh.pa.us 302 [ + + ]: 1281 : for (i = 0; i < in->nchild; i++)
303 : 846 : cntsize(in->child[i], sumlen, nnode);
304 : : }
305 : : else
306 : : {
5386 peter_e@gmx.net 307 : 558 : *sumlen += in->valnode->qoperand.length + 1;
308 : : }
6081 tgl@sss.pgh.pa.us 309 : 993 : }
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
5995 bruce@momjian.us 323 : 993 : fillQT(QTN2QTState *state, QTNode *in)
324 : : {
325 : : /* since this function recurses, it could be driven to stack overflow. */
6064 teodor@sigaev.ru 326 : 993 : check_stack_depth();
327 : :
328 [ + + ]: 993 : if (in->valnode->type == QI_VAL)
329 : : {
330 : 558 : memcpy(state->curitem, in->valnode, sizeof(QueryOperand));
331 : :
5386 peter_e@gmx.net 332 : 558 : memcpy(state->curoperand, in->word, in->valnode->qoperand.length);
333 : 558 : state->curitem->qoperand.distance = state->curoperand - state->operand;
334 : 558 : state->curoperand[in->valnode->qoperand.length] = '\0';
335 : 558 : state->curoperand += in->valnode->qoperand.length + 1;
6081 tgl@sss.pgh.pa.us 336 : 558 : state->curitem++;
337 : : }
338 : : else
339 : : {
340 : 435 : QueryItem *curitem = state->curitem;
341 : :
6064 teodor@sigaev.ru 342 [ - + ]: 435 : Assert(in->valnode->type == QI_OPR);
343 : :
344 : 435 : memcpy(state->curitem, in->valnode, sizeof(QueryOperator));
345 : :
6081 tgl@sss.pgh.pa.us 346 [ - + ]: 435 : Assert(in->nchild <= 2);
347 : 435 : state->curitem++;
348 : :
349 : 435 : fillQT(state, in->child[0]);
350 : :
351 [ + + ]: 435 : if (in->nchild == 2)
352 : : {
5386 peter_e@gmx.net 353 : 411 : curitem->qoperator.left = state->curitem - curitem;
6081 tgl@sss.pgh.pa.us 354 : 411 : fillQT(state, in->child[1]);
355 : : }
356 : : }
357 : 993 : }
358 : :
359 : : /*
360 : : * Build flat tsquery from a QTNode tree.
361 : : */
362 : : TSQuery
5995 bruce@momjian.us 363 : 147 : QTN2QT(QTNode *in)
364 : : {
365 : : TSQuery out;
366 : : int len;
6081 tgl@sss.pgh.pa.us 367 : 147 : int sumlen = 0,
368 : 147 : nnode = 0;
369 : : QTN2QTState state;
370 : :
371 : 147 : cntsize(in, &sumlen, &nnode);
372 : :
3709 noah@leadboat.com 373 [ - + ]: 147 : if (TSQUERY_TOO_BIG(nnode, sumlen))
3709 noah@leadboat.com 374 [ # # ]:UBC 0 : ereport(ERROR,
375 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
376 : : errmsg("tsquery is too large")));
6081 tgl@sss.pgh.pa.us 377 :CBC 147 : len = COMPUTESIZE(nnode, sumlen);
378 : :
4709 379 : 147 : out = (TSQuery) palloc0(len);
6081 380 : 147 : SET_VARSIZE(out, len);
381 : 147 : out->size = nnode;
382 : :
383 : 147 : state.curitem = GETQUERY(out);
384 : 147 : state.operand = state.curoperand = GETOPERAND(out);
385 : :
386 : 147 : fillQT(&state, in);
387 : 147 : return out;
388 : : }
389 : :
390 : : /*
391 : : * Copy a QTNode tree.
392 : : *
393 : : * Modifiable copies of the words and valnodes are made, too.
394 : : */
395 : : QTNode *
5995 bruce@momjian.us 396 : 510 : QTNCopy(QTNode *in)
397 : : {
398 : : QTNode *out;
399 : :
400 : : /* since this function recurses, it could be driven to stack overflow. */
6064 teodor@sigaev.ru 401 : 510 : check_stack_depth();
402 : :
403 : 510 : out = (QTNode *) palloc(sizeof(QTNode));
404 : :
6081 tgl@sss.pgh.pa.us 405 : 510 : *out = *in;
406 : 510 : out->valnode = (QueryItem *) palloc(sizeof(QueryItem));
407 : 510 : *(out->valnode) = *(in->valnode);
408 : 510 : out->flags |= QTN_NEEDFREE;
409 : :
6064 teodor@sigaev.ru 410 [ + + ]: 510 : if (in->valnode->type == QI_VAL)
411 : : {
5386 peter_e@gmx.net 412 : 306 : out->word = palloc(in->valnode->qoperand.length + 1);
413 : 306 : memcpy(out->word, in->word, in->valnode->qoperand.length);
414 : 306 : out->word[in->valnode->qoperand.length] = '\0';
6081 tgl@sss.pgh.pa.us 415 : 306 : out->flags |= QTN_WORDFREE;
416 : : }
417 : : else
418 : : {
419 : : int i;
420 : :
421 : 204 : out->child = (QTNode **) palloc(sizeof(QTNode *) * in->nchild);
422 : :
423 [ + + ]: 609 : for (i = 0; i < in->nchild; i++)
424 : 405 : out->child[i] = QTNCopy(in->child[i]);
425 : : }
426 : :
427 : 510 : return out;
428 : : }
429 : :
430 : : /*
431 : : * Clear the specified flag bit(s) in all nodes of a QTNode tree.
432 : : */
433 : : void
5995 bruce@momjian.us 434 : 2622 : QTNClearFlags(QTNode *in, uint32 flags)
435 : : {
436 : : /* since this function recurses, it could be driven to stack overflow. */
6018 tgl@sss.pgh.pa.us 437 : 2622 : check_stack_depth();
438 : :
439 : 2622 : in->flags &= ~flags;
440 : :
441 [ + + ]: 2622 : if (in->valnode->type != QI_VAL)
442 : : {
443 : : int i;
444 : :
445 [ + + ]: 3204 : for (i = 0; i < in->nchild; i++)
446 : 2226 : QTNClearFlags(in->child[i], flags);
447 : : }
448 : 2622 : }
|