aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/tsquery_rewrite.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/tsquery_rewrite.c')
-rw-r--r--src/backend/utils/adt/tsquery_rewrite.c524
1 files changed, 524 insertions, 0 deletions
diff --git a/src/backend/utils/adt/tsquery_rewrite.c b/src/backend/utils/adt/tsquery_rewrite.c
new file mode 100644
index 00000000000..f0d22c644ae
--- /dev/null
+++ b/src/backend/utils/adt/tsquery_rewrite.c
@@ -0,0 +1,524 @@
+/*-------------------------------------------------------------------------
+ *
+ * tsquery_rewrite.c
+ * Utilities for reconstructing tsquery
+ *
+ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/backend/utils/adt/tsquery_rewrite.c,v 1.1 2007/08/21 01:11:19 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "executor/spi.h"
+#include "tsearch/ts_type.h"
+#include "tsearch/ts_utils.h"
+
+
+static int
+addone(int *counters, int last, int total)
+{
+ counters[last]++;
+ if (counters[last] >= total)
+ {
+ if (last == 0)
+ return 0;
+ if (addone(counters, last - 1, total - 1) == 0)
+ return 0;
+ counters[last] = counters[last - 1] + 1;
+ }
+ return 1;
+}
+
+static QTNode *
+findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
+{
+
+ if ((node->sign & ex->sign) != ex->sign || node->valnode->type != ex->valnode->type || node->valnode->val != ex->valnode->val)
+ return node;
+
+ if (node->flags & QTN_NOCHANGE)
+ return node;
+
+ if (node->valnode->type == OPR)
+ {
+ if (node->nchild == ex->nchild)
+ {
+ if (QTNEq(node, ex))
+ {
+ QTNFree(node);
+ if (subs)
+ {
+ node = QTNCopy(subs);
+ node->flags |= QTN_NOCHANGE;
+ }
+ else
+ node = NULL;
+ *isfind = true;
+ }
+ }
+ else if (node->nchild > ex->nchild)
+ {
+ int *counters = (int *) palloc(sizeof(int) * node->nchild);
+ int i;
+ QTNode *tnode = (QTNode *) palloc(sizeof(QTNode));
+
+ memset(tnode, 0, sizeof(QTNode));
+ tnode->child = (QTNode **) palloc(sizeof(QTNode *) * ex->nchild);
+ tnode->nchild = ex->nchild;
+ tnode->valnode = (QueryItem *) palloc(sizeof(QueryItem));
+ *(tnode->valnode) = *(ex->valnode);
+
+ for (i = 0; i < ex->nchild; i++)
+ counters[i] = i;
+
+ do
+ {
+ tnode->sign = 0;
+ for (i = 0; i < ex->nchild; i++)
+ {
+ tnode->child[i] = node->child[counters[i]];
+ tnode->sign |= tnode->child[i]->sign;
+ }
+
+ if (QTNEq(tnode, ex))
+ {
+ int j = 0;
+
+ pfree(tnode->valnode);
+ pfree(tnode->child);
+ pfree(tnode);
+ if (subs)
+ {
+ tnode = QTNCopy(subs);
+ tnode->flags = QTN_NOCHANGE | QTN_NEEDFREE;
+ }
+ else
+ tnode = NULL;
+
+ node->child[counters[0]] = tnode;
+
+ for (i = 1; i < ex->nchild; i++)
+ node->child[counters[i]] = NULL;
+ for (i = 0; i < node->nchild; i++)
+ {
+ if (node->child[i])
+ {
+ node->child[j] = node->child[i];
+ j++;
+ }
+ }
+
+ node->nchild = j;
+
+ *isfind = true;
+
+ break;
+ }
+ } while (addone(counters, ex->nchild - 1, node->nchild));
+ if (tnode && (tnode->flags & QTN_NOCHANGE) == 0)
+ {
+ pfree(tnode->valnode);
+ pfree(tnode->child);
+ pfree(tnode);
+ }
+ else
+ QTNSort(node);
+ pfree(counters);
+ }
+ }
+ else if (QTNEq(node, ex))
+ {
+ QTNFree(node);
+ if (subs)
+ {
+ node = QTNCopy(subs);
+ node->flags |= QTN_NOCHANGE;
+ }
+ else
+ {
+ node = NULL;
+ }
+ *isfind = true;
+ }
+
+ return node;
+}
+
+static QTNode *
+dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
+{
+ root = findeq(root, ex, subs, isfind);
+
+ if (root && (root->flags & QTN_NOCHANGE) == 0 && root->valnode->type == OPR)
+ {
+ int i;
+
+ for (i = 0; i < root->nchild; i++)
+ root->child[i] = dofindsubquery(root->child[i], ex, subs, isfind);
+ }
+
+ return root;
+}
+
+static QTNode *
+dropvoidsubtree(QTNode * root)
+{
+
+ if (!root)
+ return NULL;
+
+ if (root->valnode->type == OPR)
+ {
+ int i,
+ j = 0;
+
+ for (i = 0; i < root->nchild; i++)
+ {
+ if (root->child[i])
+ {
+ root->child[j] = root->child[i];
+ j++;
+ }
+ }
+
+ root->nchild = j;
+
+ if (root->valnode->val == (int4) '!' && root->nchild == 0)
+ {
+ QTNFree(root);
+ root = NULL;
+ }
+ else if (root->nchild == 1)
+ {
+ QTNode *nroot = root->child[0];
+
+ pfree(root);
+ root = nroot;
+ }
+ }
+
+ return root;
+}
+
+static QTNode *
+findsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
+{
+ bool DidFind = false;
+
+ root = dofindsubquery(root, ex, subs, &DidFind);
+
+ if (!subs && DidFind)
+ root = dropvoidsubtree(root);
+
+ if (isfind)
+ *isfind = DidFind;
+
+ return root;
+}
+
+Datum
+ts_rewrite_accum(PG_FUNCTION_ARGS)
+{
+ TSQuery acc;
+ ArrayType *qa;
+ TSQuery q;
+ QTNode *qex = NULL,
+ *subs = NULL,
+ *acctree = NULL;
+ bool isfind = false;
+ Datum *elemsp;
+ int nelemsp;
+ MemoryContext aggcontext;
+ MemoryContext oldcontext;
+
+ aggcontext = ((AggState *) fcinfo->context)->aggcontext;
+
+ if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL)
+ {
+ acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ);
+ SET_VARSIZE(acc, HDRSIZETQ);
+ acc->size = 0;
+ }
+ else
+ acc = PG_GETARG_TSQUERY(0);
+
+ if (PG_ARGISNULL(1) || PG_GETARG_POINTER(1) == NULL)
+ PG_RETURN_TSQUERY(acc);
+ else
+ qa = PG_GETARG_ARRAYTYPE_P_COPY(1);
+
+ if (ARR_NDIM(qa) != 1)
+ elog(ERROR, "array must be one-dimensional, not %d dimensions",
+ ARR_NDIM(qa));
+ if (ArrayGetNItems(ARR_NDIM(qa), ARR_DIMS(qa)) != 3)
+ elog(ERROR, "array should have only three elements");
+ if (ARR_ELEMTYPE(qa) != TSQUERYOID)
+ elog(ERROR, "array should contain tsquery type");
+
+ deconstruct_array(qa, TSQUERYOID, -1, false, 'i', &elemsp, NULL, &nelemsp);
+
+ q = DatumGetTSQuery(elemsp[0]);
+ if (q->size == 0)
+ {
+ pfree(elemsp);
+ PG_RETURN_POINTER(acc);
+ }
+
+ if (!acc->size)
+ {
+ if (VARSIZE(acc) > HDRSIZETQ)
+ {
+ pfree(elemsp);
+ PG_RETURN_POINTER(acc);
+ }
+ else
+ acctree = QT2QTN(GETQUERY(q), GETOPERAND(q));
+ }
+ else
+ acctree = QT2QTN(GETQUERY(acc), GETOPERAND(acc));
+
+ QTNTernary(acctree);
+ QTNSort(acctree);
+
+ q = DatumGetTSQuery(elemsp[1]);
+ if (q->size == 0)
+ {
+ pfree(elemsp);
+ PG_RETURN_POINTER(acc);
+ }
+ qex = QT2QTN(GETQUERY(q), GETOPERAND(q));
+ QTNTernary(qex);
+ QTNSort(qex);
+
+ q = DatumGetTSQuery(elemsp[2]);
+ if (q->size)
+ subs = QT2QTN(GETQUERY(q), GETOPERAND(q));
+
+ acctree = findsubquery(acctree, qex, subs, &isfind);
+
+ if (isfind || !acc->size)
+ {
+ /* pfree( acc ); do not pfree(p), because nodeAgg.c will */
+ if (acctree)
+ {
+ QTNBinary(acctree);
+ oldcontext = MemoryContextSwitchTo(aggcontext);
+ acc = QTN2QT(acctree);
+ MemoryContextSwitchTo(oldcontext);
+ }
+ else
+ {
+ acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ);
+ SET_VARSIZE(acc, HDRSIZETQ);
+ acc->size = 0;
+ }
+ }
+
+ pfree(elemsp);
+ QTNFree(qex);
+ QTNFree(subs);
+ QTNFree(acctree);
+
+ PG_RETURN_TSQUERY(acc);
+}
+
+Datum
+ts_rewrite_finish(PG_FUNCTION_ARGS)
+{
+ TSQuery acc = PG_GETARG_TSQUERY(0);
+ TSQuery rewrited;
+
+ if (acc == NULL || PG_ARGISNULL(0) || acc->size == 0)
+ {
+ rewrited = (TSQuery) palloc(HDRSIZETQ);
+ SET_VARSIZE(rewrited, HDRSIZETQ);
+ rewrited->size = 0;
+ }
+ else
+ {
+ rewrited = (TSQuery) palloc(VARSIZE(acc));
+ memcpy(rewrited, acc, VARSIZE(acc));
+ pfree(acc);
+ }
+
+ PG_RETURN_POINTER(rewrited);
+}
+
+Datum
+tsquery_rewrite(PG_FUNCTION_ARGS)
+{
+ TSQuery query = PG_GETARG_TSQUERY_COPY(0);
+ text *in = PG_GETARG_TEXT_P(1);
+ TSQuery rewrited = query;
+ MemoryContext outercontext = CurrentMemoryContext;
+ MemoryContext oldcontext;
+ QTNode *tree;
+ char *buf;
+ void *plan;
+ Portal portal;
+ bool isnull;
+ int i;
+
+ if (query->size == 0)
+ {
+ PG_FREE_IF_COPY(in, 1);
+ PG_RETURN_POINTER(rewrited);
+ }
+
+ tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
+ QTNTernary(tree);
+ QTNSort(tree);
+
+ buf = TextPGetCString(in);
+
+ SPI_connect();
+
+ if ((plan = SPI_prepare(buf, 0, NULL)) == NULL)
+ elog(ERROR, "SPI_prepare(\"%s\") failed", buf);
+
+ if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, false)) == NULL)
+ elog(ERROR, "SPI_cursor_open(\"%s\") failed", buf);
+
+ SPI_cursor_fetch(portal, true, 100);
+
+ if (SPI_tuptable->tupdesc->natts != 2 ||
+ SPI_gettypeid(SPI_tuptable->tupdesc, 1) != TSQUERYOID ||
+ SPI_gettypeid(SPI_tuptable->tupdesc, 2) != TSQUERYOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("ts_rewrite query must return two tsquery columns")));
+
+ while (SPI_processed > 0 && tree)
+ {
+ for (i = 0; i < SPI_processed && tree; i++)
+ {
+ Datum qdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &isnull);
+ Datum sdata;
+
+ if (isnull)
+ continue;
+
+ sdata = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &isnull);
+
+ if (!isnull)
+ {
+ TSQuery qtex = DatumGetTSQuery(qdata);
+ TSQuery qtsubs = DatumGetTSQuery(sdata);
+ QTNode *qex,
+ *qsubs = NULL;
+
+ if (qtex->size == 0)
+ {
+ if (qtex != (TSQuery) DatumGetPointer(qdata))
+ pfree(qtex);
+ if (qtsubs != (TSQuery) DatumGetPointer(sdata))
+ pfree(qtsubs);
+ continue;
+ }
+
+ qex = QT2QTN(GETQUERY(qtex), GETOPERAND(qtex));
+
+ QTNTernary(qex);
+ QTNSort(qex);
+
+ if (qtsubs->size)
+ qsubs = QT2QTN(GETQUERY(qtsubs), GETOPERAND(qtsubs));
+
+ oldcontext = MemoryContextSwitchTo(outercontext);
+ tree = findsubquery(tree, qex, qsubs, NULL);
+ MemoryContextSwitchTo(oldcontext);
+
+ QTNFree(qex);
+ if (qtex != (TSQuery) DatumGetPointer(qdata))
+ pfree(qtex);
+ QTNFree(qsubs);
+ if (qtsubs != (TSQuery) DatumGetPointer(sdata))
+ pfree(qtsubs);
+ }
+ }
+
+ SPI_freetuptable(SPI_tuptable);
+ SPI_cursor_fetch(portal, true, 100);
+ }
+
+ SPI_freetuptable(SPI_tuptable);
+ SPI_cursor_close(portal);
+ SPI_freeplan(plan);
+ SPI_finish();
+
+ if (tree)
+ {
+ QTNBinary(tree);
+ rewrited = QTN2QT(tree);
+ QTNFree(tree);
+ PG_FREE_IF_COPY(query, 0);
+ }
+ else
+ {
+ SET_VARSIZE(rewrited, HDRSIZETQ);
+ rewrited->size = 0;
+ }
+
+ pfree(buf);
+ PG_FREE_IF_COPY(in, 1);
+ PG_RETURN_POINTER(rewrited);
+}
+
+Datum
+tsquery_rewrite_query(PG_FUNCTION_ARGS)
+{
+ TSQuery query = PG_GETARG_TSQUERY_COPY(0);
+ TSQuery ex = PG_GETARG_TSQUERY(1);
+ TSQuery subst = PG_GETARG_TSQUERY(2);
+ TSQuery rewrited = query;
+ QTNode *tree,
+ *qex,
+ *subs = NULL;
+
+ if (query->size == 0 || ex->size == 0)
+ {
+ PG_FREE_IF_COPY(ex, 1);
+ PG_FREE_IF_COPY(subst, 2);
+ PG_RETURN_POINTER(rewrited);
+ }
+
+ tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
+ QTNTernary(tree);
+ QTNSort(tree);
+
+ qex = QT2QTN(GETQUERY(ex), GETOPERAND(ex));
+ QTNTernary(qex);
+ QTNSort(qex);
+
+ if (subst->size)
+ subs = QT2QTN(GETQUERY(subst), GETOPERAND(subst));
+
+ tree = findsubquery(tree, qex, subs, NULL);
+ QTNFree(qex);
+ QTNFree(subs);
+
+ if (!tree)
+ {
+ SET_VARSIZE(rewrited, HDRSIZETQ);
+ rewrited->size = 0;
+ PG_FREE_IF_COPY(ex, 1);
+ PG_FREE_IF_COPY(subst, 2);
+ PG_RETURN_POINTER(rewrited);
+ }
+ else
+ {
+ QTNBinary(tree);
+ rewrited = QTN2QT(tree);
+ QTNFree(tree);
+ }
+
+ PG_FREE_IF_COPY(query, 0);
+ PG_FREE_IF_COPY(ex, 1);
+ PG_FREE_IF_COPY(subst, 2);
+ PG_RETURN_POINTER(rewrited);
+}