mirror of
https://github.com/postgres/postgres.git
synced 2025-08-18 12:22:09 +03:00
Change predecence of phrase operator.
<-> operator now have higher predecence than & (AND) operator. This change was motivated by unexpected difference of similar queries: 'a & b <-> c'::tsquery and 'b <-> c & a'. Before first query means (a & b) <-> c and second one - '(b <-> c) & a', now phrase operator evaluates first. Per suggestion from Tom Lane 32260.1465402409@sss.pgh.pa.us
This commit is contained in:
@@ -26,10 +26,10 @@
|
||||
/* FTS operator priorities, see ts_type.h */
|
||||
const int tsearch_op_priority[OP_COUNT] =
|
||||
{
|
||||
3, /* OP_NOT */
|
||||
4, /* OP_NOT */
|
||||
2, /* OP_AND */
|
||||
1, /* OP_OR */
|
||||
4 /* OP_PHRASE */
|
||||
3 /* OP_PHRASE */
|
||||
};
|
||||
|
||||
struct TSQueryParserStateData
|
||||
@@ -430,6 +430,40 @@ pushStop(TSQueryParserState state)
|
||||
|
||||
#define STACKDEPTH 32
|
||||
|
||||
typedef struct OperatorElement {
|
||||
int8 op;
|
||||
int16 distance;
|
||||
} OperatorElement;
|
||||
|
||||
static void
|
||||
pushOpStack(OperatorElement *stack, int *lenstack, int8 op, int16 distance)
|
||||
{
|
||||
if (*lenstack == STACKDEPTH) /* internal error */
|
||||
elog(ERROR, "tsquery stack too small");
|
||||
|
||||
stack[*lenstack].op = op;
|
||||
stack[*lenstack].distance = distance;
|
||||
|
||||
(*lenstack)++;
|
||||
}
|
||||
|
||||
static void
|
||||
cleanOpStack(TSQueryParserState state,
|
||||
OperatorElement *stack, int *lenstack, int8 op)
|
||||
{
|
||||
int opPriority = OP_PRIORITY(op);
|
||||
|
||||
while(*lenstack)
|
||||
{
|
||||
if (opPriority > OP_PRIORITY(stack[*lenstack - 1].op))
|
||||
break;
|
||||
|
||||
(*lenstack)--;
|
||||
pushOperator(state, stack[*lenstack].op,
|
||||
stack[*lenstack].distance);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make polish (prefix) notation of query.
|
||||
*
|
||||
@@ -444,11 +478,7 @@ makepol(TSQueryParserState state,
|
||||
ts_tokentype type;
|
||||
int lenval = 0;
|
||||
char *strval = NULL;
|
||||
struct
|
||||
{
|
||||
int8 op;
|
||||
int16 distance;
|
||||
} opstack[STACKDEPTH];
|
||||
OperatorElement opstack[STACKDEPTH];
|
||||
int lenstack = 0;
|
||||
int16 weight = 0;
|
||||
bool prefix;
|
||||
@@ -462,49 +492,16 @@ makepol(TSQueryParserState state,
|
||||
{
|
||||
case PT_VAL:
|
||||
pushval(opaque, state, strval, lenval, weight, prefix);
|
||||
while (lenstack && (opstack[lenstack - 1].op == OP_AND ||
|
||||
opstack[lenstack - 1].op == OP_PHRASE ||
|
||||
opstack[lenstack - 1].op == OP_NOT))
|
||||
{
|
||||
lenstack--;
|
||||
pushOperator(state,
|
||||
opstack[lenstack].op,
|
||||
opstack[lenstack].distance);
|
||||
}
|
||||
break;
|
||||
case PT_OPR:
|
||||
if (lenstack && operator == OP_OR)
|
||||
pushOperator(state, OP_OR, 0);
|
||||
else
|
||||
{
|
||||
if (lenstack == STACKDEPTH) /* internal error */
|
||||
elog(ERROR, "tsquery stack too small");
|
||||
opstack[lenstack].op = operator;
|
||||
opstack[lenstack].distance = weight;
|
||||
lenstack++;
|
||||
}
|
||||
cleanOpStack(state, opstack, &lenstack, operator);
|
||||
pushOpStack(opstack, &lenstack, operator, weight);
|
||||
break;
|
||||
case PT_OPEN:
|
||||
makepol(state, pushval, opaque);
|
||||
|
||||
while (lenstack && (opstack[lenstack - 1].op == OP_AND ||
|
||||
opstack[lenstack - 1].op == OP_PHRASE ||
|
||||
opstack[lenstack - 1].op == OP_NOT))
|
||||
{
|
||||
lenstack--;
|
||||
pushOperator(state,
|
||||
opstack[lenstack].op,
|
||||
opstack[lenstack].distance);
|
||||
}
|
||||
break;
|
||||
case PT_CLOSE:
|
||||
while (lenstack)
|
||||
{
|
||||
lenstack--;
|
||||
pushOperator(state,
|
||||
opstack[lenstack].op,
|
||||
opstack[lenstack].distance);
|
||||
};
|
||||
cleanOpStack(state, opstack, &lenstack, OP_OR /* lowest */);
|
||||
return;
|
||||
case PT_ERR:
|
||||
default:
|
||||
@@ -514,13 +511,8 @@ makepol(TSQueryParserState state,
|
||||
state->buffer)));
|
||||
}
|
||||
}
|
||||
while (lenstack)
|
||||
{
|
||||
lenstack--;
|
||||
pushOperator(state,
|
||||
opstack[lenstack].op,
|
||||
opstack[lenstack].distance);
|
||||
}
|
||||
|
||||
cleanOpStack(state, opstack, &lenstack, OP_OR /* lowest */);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -750,7 +742,7 @@ while( ( (inf)->cur - (inf)->buf ) + (addsize) + 1 >= (inf)->buflen ) \
|
||||
* print it in infix (human-readable) form
|
||||
*/
|
||||
static void
|
||||
infix(INFIX *in, int parentPriority)
|
||||
infix(INFIX *in, int parentPriority, bool rightPhraseOp)
|
||||
{
|
||||
/* since this function recurses, it could be driven to stack overflow. */
|
||||
check_stack_depth();
|
||||
@@ -819,7 +811,7 @@ infix(INFIX *in, int parentPriority)
|
||||
}
|
||||
else if (in->curpol->qoperator.oper == OP_NOT)
|
||||
{
|
||||
int priority = PRINT_PRIORITY(in->curpol);
|
||||
int priority = QO_PRIORITY(in->curpol);
|
||||
|
||||
if (priority < parentPriority)
|
||||
{
|
||||
@@ -833,7 +825,7 @@ infix(INFIX *in, int parentPriority)
|
||||
*(in->cur) = '\0';
|
||||
in->curpol++;
|
||||
|
||||
infix(in, priority);
|
||||
infix(in, priority, false);
|
||||
if (priority < parentPriority)
|
||||
{
|
||||
RESIZEBUF(in, 2);
|
||||
@@ -844,17 +836,15 @@ infix(INFIX *in, int parentPriority)
|
||||
else
|
||||
{
|
||||
int8 op = in->curpol->qoperator.oper;
|
||||
int priority = PRINT_PRIORITY(in->curpol);
|
||||
int priority = QO_PRIORITY(in->curpol);
|
||||
int16 distance = in->curpol->qoperator.distance;
|
||||
INFIX nrm;
|
||||
bool needParenthesis = false;
|
||||
|
||||
in->curpol++;
|
||||
if (priority < parentPriority ||
|
||||
(op == OP_PHRASE &&
|
||||
(priority == parentPriority || /* phrases are not
|
||||
* commutative! */
|
||||
parentPriority == OP_PRIORITY(OP_AND))))
|
||||
/* phrase operator depends on order */
|
||||
(op == OP_PHRASE && rightPhraseOp))
|
||||
{
|
||||
needParenthesis = true;
|
||||
RESIZEBUF(in, 2);
|
||||
@@ -868,11 +858,11 @@ infix(INFIX *in, int parentPriority)
|
||||
nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
|
||||
|
||||
/* get right operand */
|
||||
infix(&nrm, priority);
|
||||
infix(&nrm, priority, (op == OP_PHRASE));
|
||||
|
||||
/* get & print left operand */
|
||||
in->curpol = nrm.curpol;
|
||||
infix(in, priority);
|
||||
infix(in, priority, false);
|
||||
|
||||
/* print operator & right operand */
|
||||
RESIZEBUF(in, 3 + (2 + 10 /* distance */ ) + (nrm.cur - nrm.buf));
|
||||
@@ -924,7 +914,7 @@ tsqueryout(PG_FUNCTION_ARGS)
|
||||
nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
|
||||
*(nrm.cur) = '\0';
|
||||
nrm.op = GETOPERAND(query);
|
||||
infix(&nrm, -1 /* lowest priority */ );
|
||||
infix(&nrm, -1 /* lowest priority */, false);
|
||||
|
||||
PG_FREE_IF_COPY(query, 0);
|
||||
PG_RETURN_CSTRING(nrm.buf);
|
||||
@@ -1151,7 +1141,7 @@ tsquerytree(PG_FUNCTION_ARGS)
|
||||
nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
|
||||
*(nrm.cur) = '\0';
|
||||
nrm.op = GETOPERAND(query);
|
||||
infix(&nrm, true);
|
||||
infix(&nrm, -1, false);
|
||||
res = cstring_to_text_with_len(nrm.buf, nrm.cur - nrm.buf);
|
||||
pfree(q);
|
||||
}
|
||||
|
@@ -25,11 +25,18 @@ typedef struct NODE
|
||||
QueryItem *valnode;
|
||||
} NODE;
|
||||
|
||||
/* Non-operator nodes have fake (but highest) priority */
|
||||
/*
|
||||
* To simplify walking on query tree and pushing down of phrase operator
|
||||
* we define some fake priority here: phrase operator has highest priority
|
||||
* of any other operators (and we believe here that OP_PHRASE is a highest
|
||||
* code of operations) and value node has ever highest priority.
|
||||
* Priority values of other operations don't matter until they are less than
|
||||
* phrase operator and value node.
|
||||
*/
|
||||
#define VALUE_PRIORITY (OP_COUNT + 1)
|
||||
#define NODE_PRIORITY(x) \
|
||||
( ((x)->valnode->qoperator.type == QI_OPR) ? \
|
||||
QO_PRIORITY((x)->valnode) : \
|
||||
TOP_PRIORITY )
|
||||
(x)->valnode->qoperator.oper : VALUE_PRIORITY )
|
||||
|
||||
/*
|
||||
* make query tree from plain view of query
|
||||
@@ -416,6 +423,10 @@ normalize_phrase_tree(NODE *node)
|
||||
node->left = normalize_phrase_tree(node->left);
|
||||
node->right = normalize_phrase_tree(node->right);
|
||||
|
||||
/*
|
||||
* if subtree contains only nodes with higher "priority" then
|
||||
* we are done. See comment near NODE_PRIORITY()
|
||||
*/
|
||||
if (NODE_PRIORITY(node) <= NODE_PRIORITY(node->right) &&
|
||||
NODE_PRIORITY(node) <= NODE_PRIORITY(node->left))
|
||||
return node;
|
||||
|
Reference in New Issue
Block a user