1 /*-------------------------------------------------------------------------
4 * Utilities for reconstructing tsquery
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
10 * $PostgreSQL: pgsql/src/backend/utils/adt/tsquery_rewrite.c,v 1.15 2009/07/16 06:33:44 petere Exp $
12 *-------------------------------------------------------------------------
17 #include "catalog/pg_type.h"
18 #include "executor/spi.h"
19 #include "miscadmin.h"
20 #include "tsearch/ts_type.h"
21 #include "tsearch/ts_utils.h"
22 #include "utils/builtins.h"
26 addone(int *counters, int last, int total)
28 /* since this function recurses, it could be driven to stack overflow. */
32 if (counters[last] >= total)
36 if (addone(counters, last - 1, total - 1) == 0)
38 counters[last] = counters[last - 1] + 1;
44 * If node is equal to ex, replace it with subs. Replacement is actually done
45 * by returning either node or a copy of subs.
48 findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
51 if ((node->sign & ex->sign) != ex->sign ||
52 node->valnode->type != ex->valnode->type)
55 if (node->flags & QTN_NOCHANGE)
58 if (node->valnode->type == QI_OPR)
60 if (node->valnode->qoperator.oper != ex->valnode->qoperator.oper)
63 if (node->nchild == ex->nchild)
71 node->flags |= QTN_NOCHANGE;
78 else if (node->nchild > ex->nchild)
81 * AND and NOT are commutative, so we check if a subset of the
82 * children match. For example, if tnode is A | B | C, and ex is B
83 * | C, we have a match after we convert tnode to A | (B | C).
85 int *counters = (int *) palloc(sizeof(int) * node->nchild);
87 QTNode *tnode = (QTNode *) palloc(sizeof(QTNode));
89 memset(tnode, 0, sizeof(QTNode));
90 tnode->child = (QTNode **) palloc(sizeof(QTNode *) * ex->nchild);
91 tnode->nchild = ex->nchild;
92 tnode->valnode = (QueryItem *) palloc(sizeof(QueryItem));
93 *(tnode->valnode) = *(ex->valnode);
95 for (i = 0; i < ex->nchild; i++)
101 for (i = 0; i < ex->nchild; i++)
103 tnode->child[i] = node->child[counters[i]];
104 tnode->sign |= tnode->child[i]->sign;
107 if (QTNEq(tnode, ex))
111 pfree(tnode->valnode);
116 tnode = QTNCopy(subs);
117 tnode->flags = QTN_NOCHANGE | QTN_NEEDFREE;
122 node->child[counters[0]] = tnode;
124 for (i = 1; i < ex->nchild; i++)
125 node->child[counters[i]] = NULL;
126 for (i = 0; i < node->nchild; i++)
130 node->child[j] = node->child[i];
141 } while (addone(counters, ex->nchild - 1, node->nchild));
142 if (tnode && (tnode->flags & QTN_NOCHANGE) == 0)
144 pfree(tnode->valnode);
155 Assert(node->valnode->type == QI_VAL);
157 if (node->valnode->qoperand.valcrc != ex->valnode->qoperand.valcrc)
159 else if (QTNEq(node, ex))
164 node = QTNCopy(subs);
165 node->flags |= QTN_NOCHANGE;
179 dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
181 /* since this function recurses, it could be driven to stack overflow. */
184 root = findeq(root, ex, subs, isfind);
186 if (root && (root->flags & QTN_NOCHANGE) == 0 && root->valnode->type == QI_OPR)
190 for (i = 0; i < root->nchild; i++)
191 root->child[i] = dofindsubquery(root->child[i], ex, subs, isfind);
198 dropvoidsubtree(QTNode *root)
204 if (root->valnode->type == QI_OPR)
209 for (i = 0; i < root->nchild; i++)
213 root->child[j] = root->child[i];
220 if (root->valnode->qoperator.oper == OP_NOT && root->nchild == 0)
225 else if (root->nchild == 1)
227 QTNode *nroot = root->child[0];
238 findsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
240 bool DidFind = false;
242 root = dofindsubquery(root, ex, subs, &DidFind);
244 if (!subs && DidFind)
245 root = dropvoidsubtree(root);
254 tsquery_rewrite_query(PG_FUNCTION_ARGS)
256 TSQuery query = PG_GETARG_TSQUERY_COPY(0);
257 text *in = PG_GETARG_TEXT_P(1);
258 TSQuery rewrited = query;
259 MemoryContext outercontext = CurrentMemoryContext;
260 MemoryContext oldcontext;
268 if (query->size == 0)
270 PG_FREE_IF_COPY(in, 1);
271 PG_RETURN_POINTER(rewrited);
274 tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
278 buf = text_to_cstring(in);
282 if ((plan = SPI_prepare(buf, 0, NULL)) == NULL)
283 elog(ERROR, "SPI_prepare(\"%s\") failed", buf);
285 if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
286 elog(ERROR, "SPI_cursor_open(\"%s\") failed", buf);
288 SPI_cursor_fetch(portal, true, 100);
290 if (SPI_tuptable == NULL ||
291 SPI_tuptable->tupdesc->natts != 2 ||
292 SPI_gettypeid(SPI_tuptable->tupdesc, 1) != TSQUERYOID ||
293 SPI_gettypeid(SPI_tuptable->tupdesc, 2) != TSQUERYOID)
295 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
296 errmsg("ts_rewrite query must return two tsquery columns")));
298 while (SPI_processed > 0 && tree)
300 for (i = 0; i < SPI_processed && tree; i++)
302 Datum qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
308 sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull);
312 TSQuery qtex = DatumGetTSQuery(qdata);
313 TSQuery qtsubs = DatumGetTSQuery(sdata);
319 if (qtex != (TSQuery) DatumGetPointer(qdata))
321 if (qtsubs != (TSQuery) DatumGetPointer(sdata))
326 qex = QT2QTN(GETQUERY(qtex), GETOPERAND(qtex));
332 qsubs = QT2QTN(GETQUERY(qtsubs), GETOPERAND(qtsubs));
334 oldcontext = MemoryContextSwitchTo(outercontext);
335 tree = findsubquery(tree, qex, qsubs, NULL);
336 MemoryContextSwitchTo(oldcontext);
339 if (qtex != (TSQuery) DatumGetPointer(qdata))
342 if (qtsubs != (TSQuery) DatumGetPointer(sdata))
347 /* ready the tree for another pass */
348 QTNClearFlags(tree, QTN_NOCHANGE);
354 SPI_freetuptable(SPI_tuptable);
355 SPI_cursor_fetch(portal, true, 100);
358 SPI_freetuptable(SPI_tuptable);
359 SPI_cursor_close(portal);
366 rewrited = QTN2QT(tree);
368 PG_FREE_IF_COPY(query, 0);
372 SET_VARSIZE(rewrited, HDRSIZETQ);
377 PG_FREE_IF_COPY(in, 1);
378 PG_RETURN_POINTER(rewrited);
382 tsquery_rewrite(PG_FUNCTION_ARGS)
384 TSQuery query = PG_GETARG_TSQUERY_COPY(0);
385 TSQuery ex = PG_GETARG_TSQUERY(1);
386 TSQuery subst = PG_GETARG_TSQUERY(2);
387 TSQuery rewrited = query;
392 if (query->size == 0 || ex->size == 0)
394 PG_FREE_IF_COPY(ex, 1);
395 PG_FREE_IF_COPY(subst, 2);
396 PG_RETURN_POINTER(rewrited);
399 tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
403 qex = QT2QTN(GETQUERY(ex), GETOPERAND(ex));
408 subs = QT2QTN(GETQUERY(subst), GETOPERAND(subst));
410 tree = findsubquery(tree, qex, subs, NULL);
417 SET_VARSIZE(rewrited, HDRSIZETQ);
419 PG_FREE_IF_COPY(ex, 1);
420 PG_FREE_IF_COPY(subst, 2);
421 PG_RETURN_POINTER(rewrited);
426 rewrited = QTN2QT(tree);
430 PG_FREE_IF_COPY(query, 0);
431 PG_FREE_IF_COPY(ex, 1);
432 PG_FREE_IF_COPY(subst, 2);
433 PG_RETURN_POINTER(rewrited);