1 /*-------------------------------------------------------------------------
4 * Utilities for reconstructing tsquery
6 * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
10 * $PostgreSQL: pgsql/src/backend/utils/adt/tsquery_rewrite.c,v 1.6 2007/10/24 02:24:47 tgl 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
81 * ex is B | C, we have a match after we convert tnode to
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->operand.valcrc != ex->valnode->operand.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->valnode->operator.oper == OP_NOT && root->nchild == 0)
224 else if (root->nchild == 1)
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 = TextPGetCString(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, false)) == NULL)
285 elog(ERROR, "SPI_cursor_open(\"%s\") failed", buf);
287 SPI_cursor_fetch(portal, true, 100);
289 if (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);