Header And Logo

PostgreSQL
| The world's most advanced open source database.

Functions

tsquery_rewrite.c File Reference

#include "postgres.h"
#include "catalog/pg_type.h"
#include "executor/spi.h"
#include "miscadmin.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
Include dependency graph for tsquery_rewrite.c:

Go to the source code of this file.

Functions

static int addone (int *counters, int last, int total)
static QTNodefindeq (QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
static QTNodedofindsubquery (QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
static QTNodedropvoidsubtree (QTNode *root)
QTNodefindsubquery (QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
Datum tsquery_rewrite_query (PG_FUNCTION_ARGS)
Datum tsquery_rewrite (PG_FUNCTION_ARGS)

Function Documentation

static int addone ( int *  counters,
int  last,
int  total 
) [static]

Definition at line 25 of file tsquery_rewrite.c.

References check_stack_depth().

Referenced by findeq().

{
    /* 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;
}

static QTNode* dofindsubquery ( QTNode root,
QTNode ex,
QTNode subs,
bool isfind 
) [static]

Definition at line 178 of file tsquery_rewrite.c.

References check_stack_depth(), QTNode::child, findeq(), QTNode::flags, i, QTNode::nchild, QI_OPR, QTN_NOCHANGE, QueryItem::type, and QTNode::valnode.

Referenced by findsubquery().

{
    /* 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  )  [static]

Definition at line 197 of file tsquery_rewrite.c.

References QTNode::child, i, QTNode::nchild, OP_NOT, QueryOperator::oper, pfree(), QI_OPR, QueryItem::qoperator, QTNFree(), QueryItem::type, and QTNode::valnode.

Referenced by findsubquery().

{

    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->qoperator.oper != OP_NOT)
        {
            QTNode     *nroot = root->child[0];

            pfree(root);
            root = nroot;
        }
    }

    return root;
}

static QTNode* findeq ( QTNode node,
QTNode ex,
QTNode subs,
bool isfind 
) [static]

Definition at line 47 of file tsquery_rewrite.c.

References addone(), Assert, QTNode::child, QTNode::flags, i, QTNode::nchild, NULL, QueryOperator::oper, palloc(), pfree(), QI_OPR, QI_VAL, QueryItem::qoperand, QueryItem::qoperator, QTN_NOCHANGE, QTNCopy(), QTNEq(), QTNFree(), QTNSort(), QTNode::sign, QueryItem::type, QueryOperand::valcrc, and QTNode::valnode.

Referenced by dofindsubquery().

{

    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->qoperator.oper != ex->valnode->qoperator.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->qoperand.valcrc != ex->valnode->qoperand.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;
}

QTNode* findsubquery ( QTNode root,
QTNode ex,
QTNode subs,
bool isfind 
)

Definition at line 237 of file tsquery_rewrite.c.

References dofindsubquery(), and dropvoidsubtree().

Referenced by tsa_rewrite_accum(), tsquery_rewrite(), and tsquery_rewrite_query().

{
    bool        DidFind = false;

    root = dofindsubquery(root, ex, subs, &DidFind);

    if (!subs && DidFind)
        root = dropvoidsubtree(root);

    if (isfind)
        *isfind = DidFind;

    return root;
}

Datum tsquery_rewrite ( PG_FUNCTION_ARGS   ) 

Definition at line 381 of file tsquery_rewrite.c.

References findsubquery(), GETOPERAND, GETQUERY, HDRSIZETQ, NULL, PG_FREE_IF_COPY, PG_GETARG_TSQUERY, PG_GETARG_TSQUERY_COPY, PG_RETURN_POINTER, QT2QTN(), QTN2QT(), QTNBinary(), QTNFree(), QTNSort(), QTNTernary(), SET_VARSIZE, and TSQueryData::size.

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

Datum tsquery_rewrite_query ( PG_FUNCTION_ARGS   ) 

Definition at line 253 of file tsquery_rewrite.c.

References buf, CurrentMemoryContext, DatumGetPointer, DatumGetTSQuery, elog, ereport, errcode(), errmsg(), ERROR, findsubquery(), GETOPERAND, GETQUERY, HDRSIZETQ, i, MemoryContextSwitchTo(), tupleDesc::natts, NULL, pfree(), PG_FREE_IF_COPY, PG_GETARG_TEXT_P, PG_GETARG_TSQUERY_COPY, PG_RETURN_POINTER, QT2QTN(), QTN2QT(), QTN_NOCHANGE, QTNBinary(), QTNClearFlags(), QTNFree(), QTNSort(), QTNTernary(), SET_VARSIZE, TSQueryData::size, SPI_connect(), SPI_cursor_close(), SPI_cursor_fetch(), SPI_cursor_open(), SPI_finish(), SPI_freeplan(), SPI_freetuptable(), SPI_getbinval(), SPI_gettypeid(), SPI_prepare(), SPI_processed, SPI_tuptable, text_to_cstring(), TSQUERYOID, SPITupleTable::tupdesc, and SPITupleTable::vals.

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