aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/tsvector_op.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/tsvector_op.c')
-rw-r--r--src/backend/utils/adt/tsvector_op.c92
1 files changed, 50 insertions, 42 deletions
diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c
index c9d5060f2c7..36cc10c9017 100644
--- a/src/backend/utils/adt/tsvector_op.c
+++ b/src/backend/utils/adt/tsvector_op.c
@@ -1405,20 +1405,26 @@ checkcondition_str(void *checkval, QueryOperand *val, ExecPhraseData *data)
}
/*
- * Check for phrase condition. Fallback to the AND operation
- * if there is no positional information.
+ * Execute tsquery at or below an OP_PHRASE operator.
+ *
+ * This handles the recursion at levels where we need to care about
+ * match locations. In addition to the same arguments used for TS_execute,
+ * the caller may pass a preinitialized-to-zeroes ExecPhraseData struct to
+ * be filled with lexeme match positions on success. data == NULL if no
+ * match data need be returned. (In practice, outside callers pass NULL,
+ * and only the internal recursion cases pass a data pointer.)
*/
static bool
-TS_phrase_execute(QueryItem *curitem,
- void *checkval, uint32 flags, ExecPhraseData *data,
- bool (*chkcond) (void *, QueryOperand *, ExecPhraseData *))
+TS_phrase_execute(QueryItem *curitem, void *arg, uint32 flags,
+ ExecPhraseData *data,
+ TSExecuteCallback chkcond)
{
/* since this function recurses, it could be driven to stack overflow */
check_stack_depth();
if (curitem->type == QI_VAL)
{
- return chkcond(checkval, (QueryOperand *) curitem, data);
+ return chkcond(arg, (QueryOperand *) curitem, data);
}
else
{
@@ -1432,33 +1438,31 @@ TS_phrase_execute(QueryItem *curitem,
Assert(curitem->qoperator.oper == OP_PHRASE);
if (!TS_phrase_execute(curitem + curitem->qoperator.left,
- checkval, flags, &Ldata, chkcond))
+ arg, flags, &Ldata, chkcond))
return false;
- if (!TS_phrase_execute(curitem + 1, checkval, flags, &Rdata, chkcond))
+ if (!TS_phrase_execute(curitem + 1, arg, flags, &Rdata, chkcond))
return false;
/*
- * if at least one of the operands has no position information, then
- * return false. But if TS_EXEC_PHRASE_AS_AND flag is set then we
- * return true as it is a AND operation
+ * If either operand has no position information, then we normally
+ * return false. But if TS_EXEC_PHRASE_AS_AND flag is set then we
+ * return true, treating OP_PHRASE as if it were OP_AND.
*/
if (Ldata.npos == 0 || Rdata.npos == 0)
return (flags & TS_EXEC_PHRASE_AS_AND) ? true : false;
/*
- * Result of the operation is a list of the corresponding positions of
- * RIGHT operand.
+ * Prepare output position array if needed.
*/
if (data)
{
+ /*
+ * We can recycle the righthand operand's result array if it was
+ * palloc'd, else must allocate our own. The number of matches
+ * couldn't be more than the smaller of the two operands' matches.
+ */
if (!Rdata.allocated)
-
- /*
- * OP_PHRASE is based on the OP_AND, so the number of
- * resulting positions could not be greater than the total
- * amount of operands.
- */
data->pos = palloc(sizeof(WordEntryPos) * Min(Ldata.npos, Rdata.npos));
else
data->pos = Rdata.pos;
@@ -1469,10 +1473,12 @@ TS_phrase_execute(QueryItem *curitem,
}
/*
- * Find matches by distance, WEP_GETPOS() is needed because
- * ExecPhraseData->data can point to the tsvector's WordEntryPosVector
+ * Find matches by distance. WEP_GETPOS() is needed because
+ * ExecPhraseData->data can point to a tsvector's WordEntryPosVector.
+ *
+ * Note that the output positions are those of the matching RIGHT
+ * operands.
*/
-
Rpos = Rdata.pos;
LposStart = Ldata.pos;
while (Rpos < Rdata.pos + Rdata.npos)
@@ -1505,8 +1511,9 @@ TS_phrase_execute(QueryItem *curitem,
else
{
/*
- * We are in the root of the phrase tree and hence we
- * don't have to store the resulting positions
+ * We are at the root of the phrase tree and hence we
+ * don't have to identify all the match positions.
+ * Just report success.
*/
return true;
}
@@ -1546,42 +1553,45 @@ TS_phrase_execute(QueryItem *curitem,
/*
* Evaluate tsquery boolean expression.
*
- * chkcond is a callback function used to evaluate each VAL node in the query.
- * checkval can be used to pass information to the callback. TS_execute doesn't
- * do anything with it.
- * It believes that ordinary operators are always closier to root than phrase
- * operator, so, TS_execute() may not take care of lexeme's position at all.
+ * curitem: current tsquery item (initially, the first one)
+ * arg: opaque value to pass through to callback function
+ * flags: bitmask of flag bits shown in ts_utils.h
+ * chkcond: callback function to check whether a primitive value is present
+ *
+ * The logic here deals only with operators above any phrase operator, for
+ * which we do not need to worry about lexeme positions. As soon as we hit an
+ * OP_PHRASE operator, we pass it off to TS_phrase_execute which does worry.
*/
bool
-TS_execute(QueryItem *curitem, void *checkval, uint32 flags,
- bool (*chkcond) (void *checkval, QueryOperand *val, ExecPhraseData *data))
+TS_execute(QueryItem *curitem, void *arg, uint32 flags,
+ TSExecuteCallback chkcond)
{
/* since this function recurses, it could be driven to stack overflow */
check_stack_depth();
if (curitem->type == QI_VAL)
- return chkcond(checkval, (QueryOperand *) curitem,
+ return chkcond(arg, (QueryOperand *) curitem,
NULL /* we don't need position info */ );
switch (curitem->qoperator.oper)
{
case OP_NOT:
if (flags & TS_EXEC_CALC_NOT)
- return !TS_execute(curitem + 1, checkval, flags, chkcond);
+ return !TS_execute(curitem + 1, arg, flags, chkcond);
else
return true;
case OP_AND:
- if (TS_execute(curitem + curitem->qoperator.left, checkval, flags, chkcond))
- return TS_execute(curitem + 1, checkval, flags, chkcond);
+ if (TS_execute(curitem + curitem->qoperator.left, arg, flags, chkcond))
+ return TS_execute(curitem + 1, arg, flags, chkcond);
else
return false;
case OP_OR:
- if (TS_execute(curitem + curitem->qoperator.left, checkval, flags, chkcond))
+ if (TS_execute(curitem + curitem->qoperator.left, arg, flags, chkcond))
return true;
else
- return TS_execute(curitem + 1, checkval, flags, chkcond);
+ return TS_execute(curitem + 1, arg, flags, chkcond);
case OP_PHRASE:
@@ -1589,7 +1599,7 @@ TS_execute(QueryItem *curitem, void *checkval, uint32 flags,
* do not check TS_EXEC_PHRASE_AS_AND here because chkcond() could
* do something more if it's called from TS_phrase_execute()
*/
- return TS_phrase_execute(curitem, checkval, flags, NULL, chkcond);
+ return TS_phrase_execute(curitem, arg, flags, NULL, chkcond);
default:
elog(ERROR, "unrecognized operator: %d", curitem->qoperator.oper);
@@ -1684,12 +1694,10 @@ ts_match_vq(PG_FUNCTION_ARGS)
chkval.arre = chkval.arrb + val->size;
chkval.values = STRPTR(val);
chkval.operand = GETOPERAND(query);
- result = TS_execute(
- GETQUERY(query),
+ result = TS_execute(GETQUERY(query),
&chkval,
TS_EXEC_CALC_NOT,
- checkcondition_str
- );
+ checkcondition_str);
PG_FREE_IF_COPY(val, 0);
PG_FREE_IF_COPY(query, 1);