diff options
Diffstat (limited to 'src/backend/utils/adt/tsquery_rewrite.c')
-rw-r--r-- | src/backend/utils/adt/tsquery_rewrite.c | 524 |
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); +} |