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.13 2009/01/01 17:23:50 momjian Exp $
12 *-------------------------------------------------------------------------
17 #include "executor/spi.h"
18 #include "tsearch/ts_type.h"
19 #include "tsearch/ts_utils.h"
20 #include "miscadmin.h"
24 addone(int *counters, int last, int total)
26 /* since this function recurses, it could be driven to stack overflow. */
30 if (counters[last] >= total)
34 if (addone(counters, last - 1, total - 1) == 0)
36 counters[last] = counters[last - 1] + 1;
42 * If node is equal to ex, replace it with subs. Replacement is actually done
43 * by returning either node or a copy of subs.
46 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->operator.oper != ex->valnode->operator.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->operand.valcrc != ex->valnode->operand.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)
202 if (root->valnode->type == QI_OPR)
207 for (i = 0; i < root->nchild; i++)
211 root->child[j] = root->child[i];
218 if (root->valnode->operator.oper == OP_NOT && root->nchild == 0)
223 else if (root->nchild == 1)
225 QTNode *nroot = root->child[0];
236 findsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
238 bool DidFind = false;
240 root = dofindsubquery(root, ex, subs, &DidFind);
242 if (!subs && DidFind)
243 root = dropvoidsubtree(root);
252 tsquery_rewrite_query(PG_FUNCTION_ARGS)
254 TSQuery query = PG_GETARG_TSQUERY_COPY(0);
255 text *in = PG_GETARG_TEXT_P(1);
256 TSQuery rewrited = query;
257 MemoryContext outercontext = CurrentMemoryContext;
258 MemoryContext oldcontext;
266 if (query->size == 0)
268 PG_FREE_IF_COPY(in, 1);
269 PG_RETURN_POINTER(rewrited);
272 tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
276 buf = text_to_cstring(in);
280 if ((plan = SPI_prepare(buf, 0, NULL)) == NULL)
281 elog(ERROR, "SPI_prepare(\"%s\") failed", buf);
283 if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
284 elog(ERROR, "SPI_cursor_open(\"%s\") failed", buf);
286 SPI_cursor_fetch(portal, true, 100);
288 if (SPI_tuptable == NULL ||
289 SPI_tuptable->tupdesc->natts != 2 ||
290 SPI_gettypeid(SPI_tuptable->tupdesc, 1) != TSQUERYOID ||
291 SPI_gettypeid(SPI_tuptable->tupdesc, 2) != TSQUERYOID)
293 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
294 errmsg("ts_rewrite query must return two tsquery columns")));
296 while (SPI_processed > 0 && tree)
298 for (i = 0; i < SPI_processed && tree; i++)
300 Datum qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
306 sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull);
310 TSQuery qtex = DatumGetTSQuery(qdata);
311 TSQuery qtsubs = DatumGetTSQuery(sdata);
317 if (qtex != (TSQuery) DatumGetPointer(qdata))
319 if (qtsubs != (TSQuery) DatumGetPointer(sdata))
324 qex = QT2QTN(GETQUERY(qtex), GETOPERAND(qtex));
330 qsubs = QT2QTN(GETQUERY(qtsubs), GETOPERAND(qtsubs));
332 oldcontext = MemoryContextSwitchTo(outercontext);
333 tree = findsubquery(tree, qex, qsubs, NULL);
334 MemoryContextSwitchTo(oldcontext);
337 if (qtex != (TSQuery) DatumGetPointer(qdata))
340 if (qtsubs != (TSQuery) DatumGetPointer(sdata))
345 /* ready the tree for another pass */
346 QTNClearFlags(tree, QTN_NOCHANGE);
352 SPI_freetuptable(SPI_tuptable);
353 SPI_cursor_fetch(portal, true, 100);
356 SPI_freetuptable(SPI_tuptable);
357 SPI_cursor_close(portal);
364 rewrited = QTN2QT(tree);
366 PG_FREE_IF_COPY(query, 0);
370 SET_VARSIZE(rewrited, HDRSIZETQ);
375 PG_FREE_IF_COPY(in, 1);
376 PG_RETURN_POINTER(rewrited);
380 tsquery_rewrite(PG_FUNCTION_ARGS)
382 TSQuery query = PG_GETARG_TSQUERY_COPY(0);
383 TSQuery ex = PG_GETARG_TSQUERY(1);
384 TSQuery subst = PG_GETARG_TSQUERY(2);
385 TSQuery rewrited = query;
390 if (query->size == 0 || ex->size == 0)
392 PG_FREE_IF_COPY(ex, 1);
393 PG_FREE_IF_COPY(subst, 2);
394 PG_RETURN_POINTER(rewrited);
397 tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
401 qex = QT2QTN(GETQUERY(ex), GETOPERAND(ex));
406 subs = QT2QTN(GETQUERY(subst), GETOPERAND(subst));
408 tree = findsubquery(tree, qex, subs, NULL);
415 SET_VARSIZE(rewrited, HDRSIZETQ);
417 PG_FREE_IF_COPY(ex, 1);
418 PG_FREE_IF_COPY(subst, 2);
419 PG_RETURN_POINTER(rewrited);
424 rewrited = QTN2QT(tree);
428 PG_FREE_IF_COPY(query, 0);
429 PG_FREE_IF_COPY(ex, 1);
430 PG_FREE_IF_COPY(subst, 2);
431 PG_RETURN_POINTER(rewrited);