1 /*-------------------------------------------------------------------------
4 * Utilities for reconstructing tsquery
6 * Portions Copyright (c) 1996-2013, 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)
49 if ((node->sign & ex->sign) != ex->sign ||
50 node->valnode->type != ex->valnode->type)
53 if (node->flags & QTN_NOCHANGE)
56 if (node->valnode->type == QI_OPR)
58 if (node->valnode->qoperator.oper != ex->valnode->qoperator.oper)
61 if (node->nchild == ex->nchild)
69 node->flags |= QTN_NOCHANGE;
76 else if (node->nchild > ex->nchild)
79 * AND and NOT are commutative, so we check if a subset of the
80 * children match. For example, if tnode is A | B | C, and ex is B
81 * | C, we have a match after we convert tnode to A | (B | C).
83 int *counters = (int *) palloc(sizeof(int) * node->nchild);
85 QTNode *tnode = (QTNode *) palloc(sizeof(QTNode));
87 memset(tnode, 0, sizeof(QTNode));
88 tnode->child = (QTNode **) palloc(sizeof(QTNode *) * ex->nchild);
89 tnode->nchild = ex->nchild;
90 tnode->valnode = (QueryItem *) palloc(sizeof(QueryItem));
91 *(tnode->valnode) = *(ex->valnode);
93 for (i = 0; i < ex->nchild; i++)
99 for (i = 0; i < ex->nchild; i++)
101 tnode->child[i] = node->child[counters[i]];
102 tnode->sign |= tnode->child[i]->sign;
105 if (QTNEq(tnode, ex))
109 pfree(tnode->valnode);
114 tnode = QTNCopy(subs);
115 tnode->flags = QTN_NOCHANGE | QTN_NEEDFREE;
120 node->child[counters[0]] = tnode;
122 for (i = 1; i < ex->nchild; i++)
123 node->child[counters[i]] = NULL;
124 for (i = 0; i < node->nchild; i++)
128 node->child[j] = node->child[i];
139 } while (addone(counters, ex->nchild - 1, node->nchild));
140 if (tnode && (tnode->flags & QTN_NOCHANGE) == 0)
142 pfree(tnode->valnode);
153 Assert(node->valnode->type == QI_VAL);
155 if (node->valnode->qoperand.valcrc != ex->valnode->qoperand.valcrc)
157 else if (QTNEq(node, ex))
162 node = QTNCopy(subs);
163 node->flags |= QTN_NOCHANGE;
177 dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
179 /* since this function recurses, it could be driven to stack overflow. */
182 root = findeq(root, ex, subs, isfind);
184 if (root && (root->flags & QTN_NOCHANGE) == 0 && root->valnode->type == QI_OPR)
188 for (i = 0; i < root->nchild; i++)
189 root->child[i] = dofindsubquery(root->child[i], ex, subs, isfind);
196 dropvoidsubtree(QTNode *root)
201 if (root->valnode->type == QI_OPR)
206 for (i = 0; i < root->nchild; i++)
210 root->child[j] = root->child[i];
217 if (root->nchild == 0)
222 else if (root->nchild == 1 && root->valnode->qoperator.oper != OP_NOT)
224 QTNode *nroot = root->child[0];
235 findsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
237 bool DidFind = false;
239 root = dofindsubquery(root, ex, subs, &DidFind);
241 if (!subs && DidFind)
242 root = dropvoidsubtree(root);
251 tsquery_rewrite_query(PG_FUNCTION_ARGS)
253 TSQuery query = PG_GETARG_TSQUERY_COPY(0);
254 text *in = PG_GETARG_TEXT_P(1);
255 TSQuery rewrited = query;
256 MemoryContext outercontext = CurrentMemoryContext;
257 MemoryContext oldcontext;
265 if (query->size == 0)
267 PG_FREE_IF_COPY(in, 1);
268 PG_RETURN_POINTER(rewrited);
271 tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
275 buf = text_to_cstring(in);
279 if ((plan = SPI_prepare(buf, 0, NULL)) == NULL)
280 elog(ERROR, "SPI_prepare(\"%s\") failed", buf);
282 if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
283 elog(ERROR, "SPI_cursor_open(\"%s\") failed", buf);
285 SPI_cursor_fetch(portal, true, 100);
287 if (SPI_tuptable == NULL ||
288 SPI_tuptable->tupdesc->natts != 2 ||
289 SPI_gettypeid(SPI_tuptable->tupdesc, 1) != TSQUERYOID ||
290 SPI_gettypeid(SPI_tuptable->tupdesc, 2) != TSQUERYOID)
292 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
293 errmsg("ts_rewrite query must return two tsquery columns")));
295 while (SPI_processed > 0 && tree)
297 for (i = 0; i < SPI_processed && tree; i++)
299 Datum qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
305 sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull);
309 TSQuery qtex = DatumGetTSQuery(qdata);
310 TSQuery qtsubs = DatumGetTSQuery(sdata);
316 if (qtex != (TSQuery) DatumGetPointer(qdata))
318 if (qtsubs != (TSQuery) DatumGetPointer(sdata))
323 qex = QT2QTN(GETQUERY(qtex), GETOPERAND(qtex));
329 qsubs = QT2QTN(GETQUERY(qtsubs), GETOPERAND(qtsubs));
331 oldcontext = MemoryContextSwitchTo(outercontext);
332 tree = findsubquery(tree, qex, qsubs, NULL);
333 MemoryContextSwitchTo(oldcontext);
336 if (qtex != (TSQuery) DatumGetPointer(qdata))
339 if (qtsubs != (TSQuery) DatumGetPointer(sdata))
344 /* ready the tree for another pass */
345 QTNClearFlags(tree, QTN_NOCHANGE);
351 SPI_freetuptable(SPI_tuptable);
352 SPI_cursor_fetch(portal, true, 100);
355 SPI_freetuptable(SPI_tuptable);
356 SPI_cursor_close(portal);
363 rewrited = QTN2QT(tree);
365 PG_FREE_IF_COPY(query, 0);
369 SET_VARSIZE(rewrited, HDRSIZETQ);
374 PG_FREE_IF_COPY(in, 1);
375 PG_RETURN_POINTER(rewrited);
379 tsquery_rewrite(PG_FUNCTION_ARGS)
381 TSQuery query = PG_GETARG_TSQUERY_COPY(0);
382 TSQuery ex = PG_GETARG_TSQUERY(1);
383 TSQuery subst = PG_GETARG_TSQUERY(2);
384 TSQuery rewrited = query;
389 if (query->size == 0 || ex->size == 0)
391 PG_FREE_IF_COPY(ex, 1);
392 PG_FREE_IF_COPY(subst, 2);
393 PG_RETURN_POINTER(rewrited);
396 tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
400 qex = QT2QTN(GETQUERY(ex), GETOPERAND(ex));
405 subs = QT2QTN(GETQUERY(subst), GETOPERAND(subst));
407 tree = findsubquery(tree, qex, subs, NULL);
414 SET_VARSIZE(rewrited, HDRSIZETQ);
416 PG_FREE_IF_COPY(ex, 1);
417 PG_FREE_IF_COPY(subst, 2);
418 PG_RETURN_POINTER(rewrited);
423 rewrited = QTN2QT(tree);
427 PG_FREE_IF_COPY(query, 0);
428 PG_FREE_IF_COPY(ex, 1);
429 PG_FREE_IF_COPY(subst, 2);
430 PG_RETURN_POINTER(rewrited);