Logo Search packages:      
Sourcecode: postgresql-8.4 version File versions  Download package

tsquery_rewrite.c

/*-------------------------------------------------------------------------
 *
 * tsquery_rewrite.c
 *      Utilities for reconstructing tsquery
 *
 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *      $PostgreSQL: pgsql/src/backend/utils/adt/tsquery_rewrite.c,v 1.14.2.1 2009/07/28 09:32:23 teodor Exp $
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "catalog/pg_type.h"
#include "executor/spi.h"
#include "miscadmin.h"
#include "tsearch/ts_type.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"


static int
addone(int *counters, int last, int total)
{
      /* since this function recurses, it could be driven to stack overflow. */
      check_stack_depth();

      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;
}

/*
 * If node is equal to ex, replace it with subs. Replacement is actually done
 * by returning either node or a copy of subs.
 */
static QTNode *
findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
{

      if ((node->sign & ex->sign) != ex->sign ||
            node->valnode->type != ex->valnode->type)
            return node;

      if (node->flags & QTN_NOCHANGE)
            return node;

      if (node->valnode->type == QI_OPR)
      {
            if (node->valnode->operator.oper != ex->valnode->operator.oper)
                  return node;

            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)
            {
                  /*
                   * AND and NOT are commutative, so we check if a subset of the
                   * children match. For example, if tnode is A | B | C, and ex is B
                   * | C, we have a match after we convert tnode to A | (B | C).
                   */
                  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
      {
            Assert(node->valnode->type == QI_VAL);

            if (node->valnode->operand.valcrc != ex->valnode->operand.valcrc)
                  return node;
            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)
{
      /* since this function recurses, it could be driven to stack overflow. */
      check_stack_depth();

      root = findeq(root, ex, subs, isfind);

      if (root && (root->flags & QTN_NOCHANGE) == 0 && root->valnode->type == QI_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 == QI_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->nchild == 0)
            {
                  QTNFree(root);
                  root = NULL;
            }
            else if (root->nchild == 1 && root->valnode->operator.oper != OP_NOT)
            {
                  QTNode         *nroot = root->child[0];

                  pfree(root);
                  root = nroot;
            }
      }

      return root;
}

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
tsquery_rewrite_query(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;
      SPIPlanPtr  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 = text_to_cstring(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, true)) == NULL)
            elog(ERROR, "SPI_cursor_open(\"%s\") failed", buf);

      SPI_cursor_fetch(portal, true, 100);

      if (SPI_tuptable == NULL ||
            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);

                        if (tree)
                        {
                              /* ready the tree for another pass */
                              QTNClearFlags(tree, QTN_NOCHANGE);
                              QTNSort(tree);
                        }
                  }
            }

            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(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);
}

Generated by  Doxygen 1.6.0   Back to index