2007-08-21 03:11:32 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* tsquery_util.c
|
|
|
|
* Utilities for tsquery datatype
|
|
|
|
*
|
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_util.c
|
2007-08-21 03:11:32 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "tsearch/ts_utils.h"
|
2007-09-07 18:03:40 +02:00
|
|
|
#include "miscadmin.h"
|
2007-08-21 03:11:32 +02:00
|
|
|
|
|
|
|
QTNode *
|
2007-11-15 23:25:18 +01:00
|
|
|
QT2QTN(QueryItem *in, char *operand)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
QTNode *node = (QTNode *) palloc0(sizeof(QTNode));
|
|
|
|
|
2007-09-07 17:35:11 +02:00
|
|
|
/* since this function recurses, it could be driven to stack overflow. */
|
|
|
|
check_stack_depth();
|
|
|
|
|
2007-08-21 03:11:32 +02:00
|
|
|
node->valnode = in;
|
|
|
|
|
2007-09-07 17:09:56 +02:00
|
|
|
if (in->type == QI_OPR)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
node->child = (QTNode **) palloc0(sizeof(QTNode *) * 2);
|
|
|
|
node->child[0] = QT2QTN(in + 1, operand);
|
|
|
|
node->sign = node->child[0]->sign;
|
2009-07-16 08:33:46 +02:00
|
|
|
if (in->qoperator.oper == OP_NOT)
|
2007-08-21 03:11:32 +02:00
|
|
|
node->nchild = 1;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
node->nchild = 2;
|
2009-07-16 08:33:46 +02:00
|
|
|
node->child[1] = QT2QTN(in + in->qoperator.left, operand);
|
2007-08-21 03:11:32 +02:00
|
|
|
node->sign |= node->child[1]->sign;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (operand)
|
|
|
|
{
|
2009-07-16 08:33:46 +02:00
|
|
|
node->word = operand + in->qoperand.distance;
|
2010-08-03 03:50:27 +02:00
|
|
|
node->sign = ((uint32) 1) << (((unsigned int) in->qoperand.valcrc) % 32);
|
2007-08-21 03:11:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-11-15 23:25:18 +01:00
|
|
|
QTNFree(QTNode *in)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
if (!in)
|
|
|
|
return;
|
|
|
|
|
2007-09-07 17:35:11 +02:00
|
|
|
/* since this function recurses, it could be driven to stack overflow. */
|
|
|
|
check_stack_depth();
|
|
|
|
|
2007-09-07 17:09:56 +02:00
|
|
|
if (in->valnode->type == QI_VAL && in->word && (in->flags & QTN_WORDFREE) != 0)
|
2007-08-21 03:11:32 +02:00
|
|
|
pfree(in->word);
|
|
|
|
|
|
|
|
if (in->child)
|
|
|
|
{
|
|
|
|
if (in->valnode)
|
|
|
|
{
|
2007-09-07 17:09:56 +02:00
|
|
|
if (in->valnode->type == QI_OPR && in->nchild > 0)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < in->nchild; i++)
|
|
|
|
QTNFree(in->child[i]);
|
|
|
|
}
|
|
|
|
if (in->flags & QTN_NEEDFREE)
|
|
|
|
pfree(in->valnode);
|
|
|
|
}
|
|
|
|
pfree(in->child);
|
|
|
|
}
|
|
|
|
|
|
|
|
pfree(in);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-11-15 23:25:18 +01:00
|
|
|
QTNodeCompare(QTNode *an, QTNode *bn)
|
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();
|
|
|
|
|
2007-08-21 03:11:32 +02:00
|
|
|
if (an->valnode->type != bn->valnode->type)
|
|
|
|
return (an->valnode->type > bn->valnode->type) ? -1 : 1;
|
2007-11-15 22:14:46 +01:00
|
|
|
|
2007-09-07 17:09:56 +02:00
|
|
|
if (an->valnode->type == QI_OPR)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
2009-07-16 08:33:46 +02:00
|
|
|
QueryOperator *ao = &an->valnode->qoperator;
|
|
|
|
QueryOperator *bo = &bn->valnode->qoperator;
|
2007-09-07 17:09:56 +02:00
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
if (ao->oper != bo->oper)
|
2007-09-07 17:09:56 +02:00
|
|
|
return (ao->oper > bo->oper) ? -1 : 1;
|
|
|
|
|
|
|
|
if (an->nchild != bn->nchild)
|
|
|
|
return (an->nchild > bn->nchild) ? -1 : 1;
|
|
|
|
|
|
|
|
{
|
|
|
|
int i,
|
|
|
|
res;
|
|
|
|
|
|
|
|
for (i = 0; i < an->nchild; i++)
|
|
|
|
if ((res = QTNodeCompare(an->child[i], bn->child[i])) != 0)
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
return 0;
|
2007-08-21 03:11:32 +02:00
|
|
|
}
|
2010-08-03 02:10:39 +02:00
|
|
|
else if (an->valnode->type == QI_VAL)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
2009-07-16 08:33:46 +02:00
|
|
|
QueryOperand *ao = &an->valnode->qoperand;
|
|
|
|
QueryOperand *bo = &bn->valnode->qoperand;
|
2007-08-21 03:11:32 +02:00
|
|
|
|
2007-09-07 17:09:56 +02:00
|
|
|
if (ao->valcrc != bo->valcrc)
|
|
|
|
{
|
|
|
|
return (ao->valcrc > bo->valcrc) ? -1 : 1;
|
|
|
|
}
|
2007-08-21 03:11:32 +02:00
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
return tsCompareString(an->word, ao->length, bn->word, bo->length, false);
|
2007-09-07 17:09:56 +02:00
|
|
|
}
|
2010-08-03 02:10:39 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
elog(ERROR, "unrecognized QueryItem type: %d", an->valnode->type);
|
|
|
|
return 0; /* keep compiler quiet */
|
|
|
|
}
|
2007-08-21 03:11:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmpQTN(const void *a, const void *b)
|
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
return QTNodeCompare(*(QTNode *const *) a, *(QTNode *const *) b);
|
2007-08-21 03:11:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-11-15 23:25:18 +01:00
|
|
|
QTNSort(QTNode *in)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2007-09-07 17:35:11 +02:00
|
|
|
/* since this function recurses, it could be driven to stack overflow. */
|
|
|
|
check_stack_depth();
|
|
|
|
|
2007-09-07 17:09:56 +02:00
|
|
|
if (in->valnode->type != QI_OPR)
|
2007-08-21 03:11:32 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < in->nchild; i++)
|
|
|
|
QTNSort(in->child[i]);
|
|
|
|
if (in->nchild > 1)
|
|
|
|
qsort((void *) in->child, in->nchild, sizeof(QTNode *), cmpQTN);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2007-11-15 23:25:18 +01:00
|
|
|
QTNEq(QTNode *a, QTNode *b)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
uint32 sign = a->sign & b->sign;
|
|
|
|
|
|
|
|
if (!(sign == a->sign && sign == b->sign))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return (QTNodeCompare(a, b) == 0) ? true : false;
|
|
|
|
}
|
|
|
|
|
2007-09-07 17:09:56 +02:00
|
|
|
/*
|
|
|
|
* Remove unnecessary intermediate nodes. For example:
|
|
|
|
*
|
2007-11-15 22:14:46 +01:00
|
|
|
* OR OR
|
|
|
|
* a OR -> a b c
|
|
|
|
* b c
|
2007-09-07 17:09:56 +02:00
|
|
|
*/
|
2007-08-21 03:11:32 +02:00
|
|
|
void
|
2007-11-15 23:25:18 +01:00
|
|
|
QTNTernary(QTNode *in)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2007-09-07 17:35:11 +02:00
|
|
|
/* since this function recurses, it could be driven to stack overflow. */
|
|
|
|
check_stack_depth();
|
|
|
|
|
2007-09-07 17:09:56 +02:00
|
|
|
if (in->valnode->type != QI_OPR)
|
2007-08-21 03:11:32 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < in->nchild; i++)
|
|
|
|
QTNTernary(in->child[i]);
|
|
|
|
|
|
|
|
for (i = 0; i < in->nchild; i++)
|
|
|
|
{
|
2007-09-07 17:09:56 +02:00
|
|
|
QTNode *cc = in->child[i];
|
|
|
|
|
2009-07-16 08:33:46 +02:00
|
|
|
if (cc->valnode->type == QI_OPR && in->valnode->qoperator.oper == cc->valnode->qoperator.oper)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
int oldnchild = in->nchild;
|
|
|
|
|
|
|
|
in->nchild += cc->nchild - 1;
|
|
|
|
in->child = (QTNode **) repalloc(in->child, in->nchild * sizeof(QTNode *));
|
|
|
|
|
|
|
|
if (i + 1 != oldnchild)
|
|
|
|
memmove(in->child + i + cc->nchild, in->child + i + 1,
|
|
|
|
(oldnchild - i - 1) * sizeof(QTNode *));
|
|
|
|
|
|
|
|
memcpy(in->child + i, cc->child, cc->nchild * sizeof(QTNode *));
|
|
|
|
i += cc->nchild - 1;
|
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
if (cc->flags & QTN_NEEDFREE)
|
2007-09-07 17:09:56 +02:00
|
|
|
pfree(cc->valnode);
|
2007-08-21 03:11:32 +02:00
|
|
|
pfree(cc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-07 17:09:56 +02:00
|
|
|
/*
|
|
|
|
* Convert a tree to binary tree by inserting intermediate nodes.
|
|
|
|
* (Opposite of QTNTernary)
|
|
|
|
*/
|
2007-08-21 03:11:32 +02:00
|
|
|
void
|
2007-11-15 23:25:18 +01:00
|
|
|
QTNBinary(QTNode *in)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2007-09-07 17:35:11 +02:00
|
|
|
/* since this function recurses, it could be driven to stack overflow. */
|
|
|
|
check_stack_depth();
|
|
|
|
|
2007-09-07 17:09:56 +02:00
|
|
|
if (in->valnode->type != QI_OPR)
|
2007-08-21 03:11:32 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < in->nchild; i++)
|
|
|
|
QTNBinary(in->child[i]);
|
|
|
|
|
|
|
|
if (in->nchild <= 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (in->nchild > 2)
|
|
|
|
{
|
|
|
|
QTNode *nn = (QTNode *) palloc0(sizeof(QTNode));
|
|
|
|
|
|
|
|
nn->valnode = (QueryItem *) palloc0(sizeof(QueryItem));
|
|
|
|
nn->child = (QTNode **) palloc0(sizeof(QTNode *) * 2);
|
|
|
|
|
|
|
|
nn->nchild = 2;
|
|
|
|
nn->flags = QTN_NEEDFREE;
|
|
|
|
|
|
|
|
nn->child[0] = in->child[0];
|
|
|
|
nn->child[1] = in->child[1];
|
|
|
|
nn->sign = nn->child[0]->sign | nn->child[1]->sign;
|
|
|
|
|
|
|
|
nn->valnode->type = in->valnode->type;
|
2009-07-16 08:33:46 +02:00
|
|
|
nn->valnode->qoperator.oper = in->valnode->qoperator.oper;
|
2007-08-21 03:11:32 +02:00
|
|
|
|
|
|
|
in->child[0] = nn;
|
|
|
|
in->child[1] = in->child[in->nchild - 1];
|
|
|
|
in->nchild--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-07 17:09:56 +02:00
|
|
|
/*
|
|
|
|
* Count the total length of operand string in tree, including '\0'-
|
|
|
|
* terminators.
|
|
|
|
*/
|
2007-08-21 03:11:32 +02:00
|
|
|
static void
|
2007-11-15 23:25:18 +01:00
|
|
|
cntsize(QTNode *in, int *sumlen, int *nnode)
|
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();
|
|
|
|
|
2007-08-21 03:11:32 +02:00
|
|
|
*nnode += 1;
|
2007-09-07 17:09:56 +02:00
|
|
|
if (in->valnode->type == QI_OPR)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < in->nchild; i++)
|
|
|
|
cntsize(in->child[i], sumlen, nnode);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-07-16 08:33:46 +02:00
|
|
|
*sumlen += in->valnode->qoperand.length + 1;
|
2007-08-21 03:11:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
QueryItem *curitem;
|
|
|
|
char *operand;
|
|
|
|
char *curoperand;
|
2007-11-15 23:25:18 +01:00
|
|
|
} QTN2QTState;
|
2007-08-21 03:11:32 +02:00
|
|
|
|
|
|
|
static void
|
2007-11-15 23:25:18 +01:00
|
|
|
fillQT(QTN2QTState *state, QTNode *in)
|
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();
|
|
|
|
|
2007-09-07 17:09:56 +02:00
|
|
|
if (in->valnode->type == QI_VAL)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
2007-09-07 17:09:56 +02:00
|
|
|
memcpy(state->curitem, in->valnode, sizeof(QueryOperand));
|
|
|
|
|
2009-07-16 08:33:46 +02:00
|
|
|
memcpy(state->curoperand, in->word, in->valnode->qoperand.length);
|
|
|
|
state->curitem->qoperand.distance = state->curoperand - state->operand;
|
|
|
|
state->curoperand[in->valnode->qoperand.length] = '\0';
|
|
|
|
state->curoperand += in->valnode->qoperand.length + 1;
|
2007-08-21 03:11:32 +02:00
|
|
|
state->curitem++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QueryItem *curitem = state->curitem;
|
|
|
|
|
2007-09-07 17:09:56 +02:00
|
|
|
Assert(in->valnode->type == QI_OPR);
|
|
|
|
|
|
|
|
memcpy(state->curitem, in->valnode, sizeof(QueryOperator));
|
|
|
|
|
2007-08-21 03:11:32 +02:00
|
|
|
Assert(in->nchild <= 2);
|
|
|
|
state->curitem++;
|
|
|
|
|
|
|
|
fillQT(state, in->child[0]);
|
|
|
|
|
|
|
|
if (in->nchild == 2)
|
|
|
|
{
|
2009-07-16 08:33:46 +02:00
|
|
|
curitem->qoperator.left = state->curitem - curitem;
|
2007-08-21 03:11:32 +02:00
|
|
|
fillQT(state, in->child[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TSQuery
|
2007-11-15 23:25:18 +01:00
|
|
|
QTN2QT(QTNode *in)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
|
|
|
TSQuery out;
|
|
|
|
int len;
|
|
|
|
int sumlen = 0,
|
|
|
|
nnode = 0;
|
|
|
|
QTN2QTState state;
|
|
|
|
|
|
|
|
cntsize(in, &sumlen, &nnode);
|
2014-02-17 15:33:31 +01:00
|
|
|
|
|
|
|
if (TSQUERY_TOO_BIG(nnode, sumlen))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
|
|
|
errmsg("tsquery is too large")));
|
2007-08-21 03:11:32 +02:00
|
|
|
len = COMPUTESIZE(nnode, sumlen);
|
|
|
|
|
2011-05-24 20:20:08 +02:00
|
|
|
out = (TSQuery) palloc0(len);
|
2007-08-21 03:11:32 +02:00
|
|
|
SET_VARSIZE(out, len);
|
|
|
|
out->size = nnode;
|
|
|
|
|
|
|
|
state.curitem = GETQUERY(out);
|
|
|
|
state.operand = state.curoperand = GETOPERAND(out);
|
|
|
|
|
|
|
|
fillQT(&state, in);
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
QTNode *
|
2007-11-15 23:25:18 +01:00
|
|
|
QTNCopy(QTNode *in)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
2007-09-07 17:35:11 +02:00
|
|
|
QTNode *out;
|
|
|
|
|
|
|
|
/* since this function recurses, it could be driven to stack overflow. */
|
|
|
|
check_stack_depth();
|
|
|
|
|
|
|
|
out = (QTNode *) palloc(sizeof(QTNode));
|
2007-08-21 03:11:32 +02:00
|
|
|
|
|
|
|
*out = *in;
|
|
|
|
out->valnode = (QueryItem *) palloc(sizeof(QueryItem));
|
|
|
|
*(out->valnode) = *(in->valnode);
|
|
|
|
out->flags |= QTN_NEEDFREE;
|
|
|
|
|
2007-09-07 17:09:56 +02:00
|
|
|
if (in->valnode->type == QI_VAL)
|
2007-08-21 03:11:32 +02:00
|
|
|
{
|
2009-07-16 08:33:46 +02:00
|
|
|
out->word = palloc(in->valnode->qoperand.length + 1);
|
|
|
|
memcpy(out->word, in->word, in->valnode->qoperand.length);
|
|
|
|
out->word[in->valnode->qoperand.length] = '\0';
|
2007-08-21 03:11:32 +02:00
|
|
|
out->flags |= QTN_WORDFREE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
out->child = (QTNode **) palloc(sizeof(QTNode *) * in->nchild);
|
|
|
|
|
|
|
|
for (i = 0; i < in->nchild; i++)
|
|
|
|
out->child[i] = QTNCopy(in->child[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return out;
|
|
|
|
}
|
2007-10-23 03:44:40 +02:00
|
|
|
|
|
|
|
void
|
2007-11-15 23:25:18 +01:00
|
|
|
QTNClearFlags(QTNode *in, uint32 flags)
|
2007-10-23 03:44:40 +02:00
|
|
|
{
|
|
|
|
/* since this function recurses, it could be driven to stack overflow. */
|
|
|
|
check_stack_depth();
|
|
|
|
|
|
|
|
in->flags &= ~flags;
|
|
|
|
|
|
|
|
if (in->valnode->type != QI_VAL)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < in->nchild; i++)
|
|
|
|
QTNClearFlags(in->child[i], flags);
|
|
|
|
}
|
|
|
|
}
|