1 /*-------------------------------------------------------------------------
4 * Utilities for reconstructing tsquery
6 * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
10 * src/backend/utils/adt/tsquery_rewrite.c
12 *-------------------------------------------------------------------------
17 #include "catalog/pg_type.h"
18 #include "executor/spi.h"
19 #include "miscadmin.h"
20 #include "tsearch/ts_utils.h"
21 #include "utils/builtins.h"
25 addone(int *counters, int last, int total)
27 /* since this function recurses, it could be driven to stack overflow. */
31 if (counters[last] >= total)
35 if (addone(counters, last - 1, total - 1) == 0)
37 counters[last] = counters[last - 1] + 1;
43 * If node is equal to ex, replace it with subs. Replacement is actually done
44 * by returning either node or a copy of subs.
47 findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
50 if ((node->sign & ex->sign) != ex->sign ||
51 node->valnode->type != ex->valnode->type)
54 if (node->flags & QTN_NOCHANGE)
57 if (node->valnode->type == QI_OPR)
59 if (node->valnode->qoperator.oper != ex->valnode->qoperator.oper)
62 if (node->nchild == ex->nchild)
70 node->flags |= QTN_NOCHANGE;
77 else if (node->nchild > ex->nchild)
80 * AND and NOT are commutative, so we check if a subset of the
81 * children match. For example, if tnode is A | B | C, and ex is B
82 * | C, we have a match after we convert tnode to A | (B | C).
84 int *counters = (int *) palloc(sizeof(int) * node->nchild);
86 QTNode *tnode = (QTNode *) palloc(sizeof(QTNode));
88 memset(tnode, 0, sizeof(QTNode));
89 tnode->child = (QTNode **) palloc(sizeof(QTNode *) * ex->nchild);
90 tnode->nchild = ex->nchild;
91 tnode->valnode = (QueryItem *) palloc(sizeof(QueryItem));
92 *(tnode->valnode) = *(ex->valnode);
94 for (i = 0; i < ex->nchild; i++)
100 for (i = 0; i < ex->nchild; i++)
102 tnode->child[i] = node->child[counters[i]];
103 tnode->sign |= tnode->child[i]->sign;
106 if (QTNEq(tnode, ex))
110 pfree(tnode->valnode);
115 tnode = QTNCopy(subs);
116 tnode->flags = QTN_NOCHANGE | QTN_NEEDFREE;
121 node->child[counters[0]] = tnode;
123 for (i = 1; i < ex->nchild; i++)
124 node->child[counters[i]] = NULL;
125 for (i = 0; i < node->nchild; i++)
129 node->child[j] = node->child[i];
140 } while (addone(counters, ex->nchild - 1, node->nchild));
141 if (tnode && (tnode->flags & QTN_NOCHANGE) == 0)
143 pfree(tnode->valnode);
154 Assert(node->valnode->type == QI_VAL);
156 if (node->valnode->qoperand.valcrc != ex->valnode->qoperand.valcrc)
158 else if (QTNEq(node, ex))
163 node = QTNCopy(subs);
164 node->flags |= QTN_NOCHANGE;
178 dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
180 /* since this function recurses, it could be driven to stack overflow. */
183 root = findeq(root, ex, subs, isfind);
185 if (root && (root->flags & QTN_NOCHANGE) == 0 && root->valnode->type == QI_OPR)
189 for (i = 0; i < root->nchild; i++)
190 root->child[i] = dofindsubquery(root->child[i], ex, subs, isfind);
197 dropvoidsubtree(QTNode *root)
203 if (root->valnode->type == QI_OPR)
208 for (i = 0; i < root->nchild; i++)
212 root->child[j] = root->child[i];
219 if (root->nchild == 0)
224 else if (root->nchild == 1 && root->valnode->qoperator.oper != OP_NOT)
226 QTNode *nroot = root->child[0];
237 findsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
239 bool DidFind = false;
241 root = dofindsubquery(root, ex, subs, &DidFind);
243 if (!subs && DidFind)
244 root = dropvoidsubtree(root);
253 tsquery_rewrite_query(PG_FUNCTION_ARGS)
255 TSQuery query = PG_GETARG_TSQUERY_COPY(0);
256 text *in = PG_GETARG_TEXT_P(1);
257 TSQuery rewrited = query;
258 MemoryContext outercontext = CurrentMemoryContext;
259 MemoryContext oldcontext;
267 if (query->size == 0)
269 PG_FREE_IF_COPY(in, 1);
270 PG_RETURN_POINTER(rewrited);
273 tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
277 buf = text_to_cstring(in);
281 if ((plan = SPI_prepare(buf, 0, NULL)) == NULL)
282 elog(ERROR, "SPI_prepare(\"%s\") failed", buf);
284 if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
285 elog(ERROR, "SPI_cursor_open(\"%s\") failed", buf);
287 SPI_cursor_fetch(portal, true, 100);
289 if (SPI_tuptable == NULL ||
290 SPI_tuptable->tupdesc->natts != 2 ||
291 SPI_gettypeid(SPI_tuptable->tupdesc, 1) != TSQUERYOID ||
292 SPI_gettypeid(SPI_tuptable->tupdesc, 2) != TSQUERYOID)
294 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
295 errmsg("ts_rewrite query must return two tsquery columns")));
297 while (SPI_processed > 0 && tree)
299 for (i = 0; i < SPI_processed && tree; i++)
301 Datum qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
307 sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull);
311 TSQuery qtex = DatumGetTSQuery(qdata);
312 TSQuery qtsubs = DatumGetTSQuery(sdata);
318 if (qtex != (TSQuery) DatumGetPointer(qdata))
320 if (qtsubs != (TSQuery) DatumGetPointer(sdata))
325 qex = QT2QTN(GETQUERY(qtex), GETOPERAND(qtex));
331 qsubs = QT2QTN(GETQUERY(qtsubs), GETOPERAND(qtsubs));
333 oldcontext = MemoryContextSwitchTo(outercontext);
334 tree = findsubquery(tree, qex, qsubs, NULL);
335 MemoryContextSwitchTo(oldcontext);
338 if (qtex != (TSQuery) DatumGetPointer(qdata))
341 if (qtsubs != (TSQuery) DatumGetPointer(sdata))
346 /* ready the tree for another pass */
347 QTNClearFlags(tree, QTN_NOCHANGE);
353 SPI_freetuptable(SPI_tuptable);
354 SPI_cursor_fetch(portal, true, 100);
357 SPI_freetuptable(SPI_tuptable);
358 SPI_cursor_close(portal);
365 rewrited = QTN2QT(tree);
367 PG_FREE_IF_COPY(query, 0);
371 SET_VARSIZE(rewrited, HDRSIZETQ);
376 PG_FREE_IF_COPY(in, 1);
377 PG_RETURN_POINTER(rewrited);
381 tsquery_rewrite(PG_FUNCTION_ARGS)
383 TSQuery query = PG_GETARG_TSQUERY_COPY(0);
384 TSQuery ex = PG_GETARG_TSQUERY(1);
385 TSQuery subst = PG_GETARG_TSQUERY(2);
386 TSQuery rewrited = query;
391 if (query->size == 0 || ex->size == 0)
393 PG_FREE_IF_COPY(ex, 1);
394 PG_FREE_IF_COPY(subst, 2);
395 PG_RETURN_POINTER(rewrited);
398 tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
402 qex = QT2QTN(GETQUERY(ex), GETOPERAND(ex));
407 subs = QT2QTN(GETQUERY(subst), GETOPERAND(subst));
409 tree = findsubquery(tree, qex, subs, NULL);
416 SET_VARSIZE(rewrited, HDRSIZETQ);
418 PG_FREE_IF_COPY(ex, 1);
419 PG_FREE_IF_COPY(subst, 2);
420 PG_RETURN_POINTER(rewrited);
425 rewrited = QTN2QT(tree);
429 PG_FREE_IF_COPY(query, 0);
430 PG_FREE_IF_COPY(ex, 1);
431 PG_FREE_IF_COPY(subst, 2);
432 PG_RETURN_POINTER(rewrited);