2007-08-21 03:11:32 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* tsquery_rewrite.c
|
|
|
|
* Utilities for reconstructing tsquery
|
|
|
|
*
|
2016-01-02 19:33:40 +01:00
|
|
|
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
|
2007-08-21 03:11:32 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/utils/adt/tsquery_rewrite.c
|
2007-08-21 03:11:32 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2009-01-07 14:44:37 +01:00
|
|
|
#include "catalog/pg_type.h"
|
2007-08-21 03:11:32 +02:00
|
|
|
#include "executor/spi.h"
|
2009-01-07 14:44:37 +01:00
|
|
|
#include "miscadmin.h"
|
2007-08-21 03:11:32 +02:00
|
|
|
#include "tsearch/ts_utils.h"
|
2009-01-07 14:44:37 +01:00
|
|
|
#include "utils/builtins.h"
|
2007-08-21 03:11:32 +02:00
|
|
|
|
|
|
|
|
2007-09-07 17:09:56 +02:00
|
|
|
/*
|
2016-10-30 22:35:42 +01:00
|
|
|
* If "node" is equal to "ex", return a copy of "subs" instead.
|
|
|
|
* If "ex" matches a subset of node's children, return a modified version
|
|
|
|
* of "node" in which those children are replaced with a copy of "subs".
|
|
|
|
* Otherwise return "node" unmodified.
|
|
|
|
*
|
|
|
|
* The QTN_NOCHANGE bit is set in successfully modified nodes, so that
|
|
|
|
* we won't uselessly recurse into them.
|
|
|
|
* Also, set *isfind true if we make a replacement.
|
2007-09-07 17:09:56 +02:00
|
|
|
*/
|
2007-08-21 03:11:32 +02:00
|
|
|
static QTNode *
|
2007-11-15 23:25:18 +01:00
|
|
|
findeq(QTNode *node, QTNode *ex, QTNode *subs, bool *isfind)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
2016-10-30 22:35:42 +01:00
|
|
|
/* Can't match unless signature matches and node type matches. */
|
2007-11-15 22:14:46 +01:00
|
|
|
if ((node->sign & ex->sign) != ex->sign ||
|
2007-09-07 17:09:56 +02:00
|
|
|
node->valnode->type != ex->valnode->type)
|
2007-08-21 03:11:32 +02:00
|
|
|
return node;
|
|
|
|
|
2016-10-30 22:35:42 +01:00
|
|
|
/* Ignore nodes marked NOCHANGE, too. */
|
2007-08-21 03:11:32 +02:00
|
|
|
if (node->flags & QTN_NOCHANGE)
|
|
|
|
return node;
|
2007-11-15 22:14:46 +01:00
|
|
|
|
2007-09-07 17:09:56 +02:00
|
|
|
if (node->valnode->type == QI_OPR)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
2016-10-30 22:35:42 +01:00
|
|
|
/* Must be same operator. */
|
2009-07-16 08:33:46 +02:00
|
|
|
if (node->valnode->qoperator.oper != ex->valnode->qoperator.oper)
|
2007-09-07 17:09:56 +02:00
|
|
|
return node;
|
|
|
|
|
2007-08-21 03:11:32 +02:00
|
|
|
if (node->nchild == ex->nchild)
|
|
|
|
{
|
2016-10-30 22:35:42 +01:00
|
|
|
/*
|
|
|
|
* Simple case: when same number of children, match if equal.
|
|
|
|
* (This is reliable when the children were sorted earlier.)
|
|
|
|
*/
|
2007-08-21 03:11:32 +02:00
|
|
|
if (QTNEq(node, ex))
|
|
|
|
{
|
2016-10-30 22:35:42 +01:00
|
|
|
/* Match; delete node and return a copy of subs instead. */
|
2007-08-21 03:11:32 +02:00
|
|
|
QTNFree(node);
|
|
|
|
if (subs)
|
|
|
|
{
|
|
|
|
node = QTNCopy(subs);
|
|
|
|
node->flags |= QTN_NOCHANGE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
node = NULL;
|
|
|
|
*isfind = true;
|
|
|
|
}
|
|
|
|
}
|
2016-10-30 22:35:42 +01:00
|
|
|
else if (node->nchild > ex->nchild && ex->nchild > 0)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
2007-09-07 17:09:56 +02:00
|
|
|
/*
|
2016-10-30 22:35:42 +01:00
|
|
|
* AND and OR are commutative/associative, so we should check if a
|
|
|
|
* subset of the children match. For example, if node is A|B|C,
|
|
|
|
* and ex is B|C, we have a match after we notionally convert node
|
|
|
|
* to A|(B|C). This does not work for NOT or PHRASE nodes, but we
|
|
|
|
* can't get here for those node types because they have a fixed
|
|
|
|
* number of children.
|
|
|
|
*
|
|
|
|
* Because we expect that the children are sorted, it suffices to
|
|
|
|
* make one pass through the two lists to find the matches.
|
2007-09-07 17:09:56 +02:00
|
|
|
*/
|
2016-10-30 22:35:42 +01:00
|
|
|
bool *matched;
|
|
|
|
int nmatched;
|
|
|
|
int i,
|
|
|
|
j;
|
|
|
|
|
|
|
|
/* Assert that the subset rule is OK */
|
|
|
|
Assert(node->valnode->qoperator.oper == OP_AND ||
|
|
|
|
node->valnode->qoperator.oper == OP_OR);
|
|
|
|
|
|
|
|
/* matched[] will record which children of node matched */
|
|
|
|
matched = (bool *) palloc0(node->nchild * sizeof(bool));
|
|
|
|
nmatched = 0;
|
|
|
|
i = j = 0;
|
|
|
|
while (i < node->nchild && j < ex->nchild)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
2016-10-30 22:35:42 +01:00
|
|
|
int cmp = QTNodeCompare(node->child[i], ex->child[j]);
|
|
|
|
|
|
|
|
if (cmp == 0)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
2016-10-30 22:35:42 +01:00
|
|
|
/* match! */
|
|
|
|
matched[i] = true;
|
|
|
|
nmatched++;
|
|
|
|
i++, j++;
|
2007-08-21 03:11:32 +02:00
|
|
|
}
|
2016-10-30 22:35:42 +01:00
|
|
|
else if (cmp < 0)
|
|
|
|
{
|
|
|
|
/* node->child[i] has no match, ignore it */
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* ex->child[j] has no match; we can give up immediately */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2007-08-21 03:11:32 +02:00
|
|
|
|
2016-10-30 22:35:42 +01:00
|
|
|
if (nmatched == ex->nchild)
|
|
|
|
{
|
|
|
|
/* collapse out the matched children of node */
|
|
|
|
j = 0;
|
|
|
|
for (i = 0; i < node->nchild; i++)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
2016-10-30 22:35:42 +01:00
|
|
|
if (matched[i])
|
|
|
|
QTNFree(node->child[i]);
|
2007-08-21 03:11:32 +02:00
|
|
|
else
|
2016-10-30 22:35:42 +01:00
|
|
|
node->child[j++] = node->child[i];
|
|
|
|
}
|
2007-08-21 03:11:32 +02:00
|
|
|
|
2016-10-30 22:35:42 +01:00
|
|
|
/* and instead insert a copy of subs */
|
|
|
|
if (subs)
|
|
|
|
{
|
|
|
|
subs = QTNCopy(subs);
|
|
|
|
subs->flags |= QTN_NOCHANGE;
|
|
|
|
node->child[j++] = subs;
|
|
|
|
}
|
2007-08-21 03:11:32 +02:00
|
|
|
|
2016-10-30 22:35:42 +01:00
|
|
|
node->nchild = j;
|
|
|
|
|
2016-12-11 19:09:57 +01:00
|
|
|
/*
|
|
|
|
* At this point we might have a node with zero or one child,
|
|
|
|
* which should be simplified. But we leave it to our caller
|
|
|
|
* (dofindsubquery) to take care of that.
|
|
|
|
*/
|
|
|
|
|
2016-10-30 22:35:42 +01:00
|
|
|
/*
|
|
|
|
* Re-sort the node to put new child in the right place. This
|
|
|
|
* is a bit bogus, because it won't matter for findsubquery's
|
|
|
|
* remaining processing, and it's insufficient to prepare the
|
|
|
|
* tree for another search (we would need to re-flatten as
|
|
|
|
* well, and we don't want to do that because we'd lose the
|
|
|
|
* QTN_NOCHANGE marking on the new child). But it's needed to
|
|
|
|
* keep the results the same as the regression tests expect.
|
|
|
|
*/
|
|
|
|
QTNSort(node);
|
2007-08-21 03:11:32 +02:00
|
|
|
|
2016-10-30 22:35:42 +01:00
|
|
|
*isfind = true;
|
2007-08-21 03:11:32 +02:00
|
|
|
}
|
2016-10-30 22:35:42 +01:00
|
|
|
|
|
|
|
pfree(matched);
|
2007-08-21 03:11:32 +02:00
|
|
|
}
|
|
|
|
}
|
2007-11-15 22:14:46 +01:00
|
|
|
else
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
2007-09-07 17:09:56 +02:00
|
|
|
Assert(node->valnode->type == QI_VAL);
|
|
|
|
|
2009-07-16 08:33:46 +02:00
|
|
|
if (node->valnode->qoperand.valcrc != ex->valnode->qoperand.valcrc)
|
2007-09-07 17:09:56 +02:00
|
|
|
return node;
|
|
|
|
else if (QTNEq(node, ex))
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
2007-09-07 17:09:56 +02:00
|
|
|
QTNFree(node);
|
|
|
|
if (subs)
|
|
|
|
{
|
|
|
|
node = QTNCopy(subs);
|
|
|
|
node->flags |= QTN_NOCHANGE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
node = NULL;
|
|
|
|
}
|
|
|
|
*isfind = true;
|
2007-08-21 03:11:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2016-10-30 22:35:42 +01:00
|
|
|
/*
|
|
|
|
* Recursive guts of findsubquery(): attempt to replace "ex" with "subs"
|
|
|
|
* at the root node, and if we failed to do so, recursively match against
|
|
|
|
* child nodes.
|
2016-12-11 19:09:57 +01:00
|
|
|
*
|
|
|
|
* Delete any void subtrees resulting from the replacement.
|
|
|
|
* In the following example '5' is replaced by empty operand:
|
|
|
|
*
|
|
|
|
* AND -> 6
|
|
|
|
* / \
|
|
|
|
* 5 OR
|
|
|
|
* / \
|
|
|
|
* 6 5
|
2016-10-30 22:35:42 +01:00
|
|
|
*/
|
2007-08-21 03:11:32 +02:00
|
|
|
static QTNode *
|
2007-11-15 23:25:18 +01:00
|
|
|
dofindsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
2007-09-07 17:35:11 +02:00
|
|
|
/* since this function recurses, it could be driven to stack overflow. */
|
|
|
|
check_stack_depth();
|
|
|
|
|
2016-10-30 22:35:42 +01:00
|
|
|
/* also, since it's a bit expensive, let's check for query cancel. */
|
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
|
2016-12-11 19:09:57 +01:00
|
|
|
/* match at the node itself */
|
2007-08-21 03:11:32 +02:00
|
|
|
root = findeq(root, ex, subs, isfind);
|
|
|
|
|
2016-12-11 19:09:57 +01:00
|
|
|
/* unless we matched here, consider matches at child nodes */
|
|
|
|
if (root && (root->flags & QTN_NOCHANGE) == 0 &&
|
|
|
|
root->valnode->type == QI_OPR)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
int i,
|
|
|
|
j = 0;
|
|
|
|
|
2016-12-11 19:09:57 +01:00
|
|
|
/*
|
|
|
|
* Any subtrees that are replaced by NULL must be dropped from the
|
|
|
|
* tree.
|
|
|
|
*/
|
2007-08-21 03:11:32 +02:00
|
|
|
for (i = 0; i < root->nchild; i++)
|
|
|
|
{
|
2016-12-11 19:09:57 +01:00
|
|
|
root->child[j] = dofindsubquery(root->child[i], ex, subs, isfind);
|
|
|
|
if (root->child[j])
|
2007-08-21 03:11:32 +02:00
|
|
|
j++;
|
|
|
|
}
|
|
|
|
|
|
|
|
root->nchild = j;
|
|
|
|
|
2016-12-11 19:09:57 +01:00
|
|
|
/*
|
|
|
|
* If we have just zero or one remaining child node, simplify out this
|
|
|
|
* operator node.
|
|
|
|
*/
|
2009-07-28 11:31:55 +02:00
|
|
|
if (root->nchild == 0)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
QTNFree(root);
|
|
|
|
root = NULL;
|
|
|
|
}
|
2009-07-28 11:31:55 +02:00
|
|
|
else if (root->nchild == 1 && root->valnode->qoperator.oper != OP_NOT)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
QTNode *nroot = root->child[0];
|
|
|
|
|
|
|
|
pfree(root);
|
|
|
|
root = nroot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
2016-10-30 22:35:42 +01:00
|
|
|
/*
|
|
|
|
* Substitute "subs" for "ex" throughout the QTNode tree at root.
|
|
|
|
*
|
|
|
|
* If isfind isn't NULL, set *isfind to show whether we made any substitution.
|
|
|
|
*
|
|
|
|
* Both "root" and "ex" must have been through QTNTernary and QTNSort
|
|
|
|
* to ensure reliable matching.
|
|
|
|
*/
|
2007-11-13 23:14:50 +01:00
|
|
|
QTNode *
|
2007-11-15 23:25:18 +01:00
|
|
|
findsubquery(QTNode *root, QTNode *ex, QTNode *subs, bool *isfind)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
bool DidFind = false;
|
|
|
|
|
|
|
|
root = dofindsubquery(root, ex, subs, &DidFind);
|
|
|
|
|
|
|
|
if (isfind)
|
|
|
|
*isfind = DidFind;
|
|
|
|
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
2007-10-24 04:24:49 +02:00
|
|
|
tsquery_rewrite_query(PG_FUNCTION_ARGS)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
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;
|
2007-10-24 05:30:03 +02:00
|
|
|
SPIPlanPtr plan;
|
2007-08-21 03:11:32 +02:00
|
|
|
Portal portal;
|
|
|
|
bool isnull;
|
|
|
|
|
|
|
|
if (query->size == 0)
|
|
|
|
{
|
|
|
|
PG_FREE_IF_COPY(in, 1);
|
|
|
|
PG_RETURN_POINTER(rewrited);
|
|
|
|
}
|
|
|
|
|
|
|
|
tree = QT2QTN(GETQUERY(query), GETOPERAND(query));
|
|
|
|
QTNTernary(tree);
|
|
|
|
QTNSort(tree);
|
|
|
|
|
2008-03-25 23:42:46 +01:00
|
|
|
buf = text_to_cstring(in);
|
2007-08-21 03:11:32 +02:00
|
|
|
|
|
|
|
SPI_connect();
|
|
|
|
|
|
|
|
if ((plan = SPI_prepare(buf, 0, NULL)) == NULL)
|
|
|
|
elog(ERROR, "SPI_prepare(\"%s\") failed", buf);
|
|
|
|
|
2007-10-24 05:30:03 +02:00
|
|
|
if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, true)) == NULL)
|
2007-08-21 03:11:32 +02:00
|
|
|
elog(ERROR, "SPI_cursor_open(\"%s\") failed", buf);
|
|
|
|
|
|
|
|
SPI_cursor_fetch(portal, true, 100);
|
|
|
|
|
2007-10-24 05:30:03 +02:00
|
|
|
if (SPI_tuptable == NULL ||
|
|
|
|
SPI_tuptable->tupdesc->natts != 2 ||
|
2007-08-21 03:11:32 +02:00
|
|
|
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)
|
|
|
|
{
|
Widen query numbers-of-tuples-processed counters to uint64.
This patch widens SPI_processed, EState's es_processed field, PortalData's
portalPos field, FuncCallContext's call_cntr and max_calls fields,
ExecutorRun's count argument, PortalRunFetch's result, and the max number
of rows in a SPITupleTable to uint64, and deals with (I hope) all the
ensuing fallout. Some of these values were declared uint32 before, and
others "long".
I also removed PortalData's posOverflow field, since that logic seems
pretty useless given that portalPos is now always 64 bits.
The user-visible results are that command tags for SELECT etc will
correctly report tuple counts larger than 4G, as will plpgsql's GET
GET DIAGNOSTICS ... ROW_COUNT command. Queries processing more tuples
than that are still not exactly the norm, but they're becoming more
common.
Most values associated with FETCH/MOVE distances, such as PortalRun's count
argument and the count argument of most SPI functions that have one, remain
declared as "long". It's not clear whether it would be worth promoting
those to int64; but it would definitely be a large dollop of additional
API churn on top of this, and it would only help 32-bit platforms which
seem relatively less likely to see any benefit.
Andreas Scherbaum, reviewed by Christian Ullrich, additional hacking by me
2016-03-12 22:05:10 +01:00
|
|
|
uint64 i;
|
|
|
|
|
2007-08-21 03:11:32 +02:00
|
|
|
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);
|
2007-10-23 03:44:40 +02:00
|
|
|
|
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
/* ready the tree for another pass */
|
|
|
|
QTNClearFlags(tree, QTN_NOCHANGE);
|
2016-10-30 22:35:42 +01:00
|
|
|
QTNTernary(tree);
|
2007-10-23 03:44:40 +02:00
|
|
|
QTNSort(tree);
|
|
|
|
}
|
2007-08-21 03:11:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2007-10-24 04:24:49 +02:00
|
|
|
tsquery_rewrite(PG_FUNCTION_ARGS)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
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);
|
2007-09-07 17:09:56 +02:00
|
|
|
|
2007-08-21 03:11:32 +02:00
|
|
|
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);
|
|
|
|
}
|