@@ -1405,20 +1405,26 @@ checkcondition_str(void *checkval, QueryOperand *val, ExecPhraseData *data)
14051405}
14061406
14071407/*
1408- * Check for phrase condition. Fallback to the AND operation
1409- * if there is no positional information.
1408+ * Execute tsquery at or below an OP_PHRASE operator.
1409+ *
1410+ * This handles the recursion at levels where we need to care about
1411+ * match locations. In addition to the same arguments used for TS_execute,
1412+ * the caller may pass a preinitialized-to-zeroes ExecPhraseData struct to
1413+ * be filled with lexeme match positions on success. data == NULL if no
1414+ * match data need be returned. (In practice, outside callers pass NULL,
1415+ * and only the internal recursion cases pass a data pointer.)
14101416 */
14111417static bool
1412- TS_phrase_execute (QueryItem * curitem ,
1413- void * checkval , uint32 flags , ExecPhraseData * data ,
1414- bool ( * chkcond ) ( void * , QueryOperand * , ExecPhraseData * ) )
1418+ TS_phrase_execute (QueryItem * curitem , void * arg , uint32 flags ,
1419+ ExecPhraseData * data ,
1420+ TSExecuteCallback chkcond )
14151421{
14161422 /* since this function recurses, it could be driven to stack overflow */
14171423 check_stack_depth ();
14181424
14191425 if (curitem -> type == QI_VAL )
14201426 {
1421- return chkcond (checkval , (QueryOperand * ) curitem , data );
1427+ return chkcond (arg , (QueryOperand * ) curitem , data );
14221428 }
14231429 else
14241430 {
@@ -1432,33 +1438,31 @@ TS_phrase_execute(QueryItem *curitem,
14321438 Assert (curitem -> qoperator .oper == OP_PHRASE );
14331439
14341440 if (!TS_phrase_execute (curitem + curitem -> qoperator .left ,
1435- checkval , flags , & Ldata , chkcond ))
1441+ arg , flags , & Ldata , chkcond ))
14361442 return false;
14371443
1438- if (!TS_phrase_execute (curitem + 1 , checkval , flags , & Rdata , chkcond ))
1444+ if (!TS_phrase_execute (curitem + 1 , arg , flags , & Rdata , chkcond ))
14391445 return false;
14401446
14411447 /*
1442- * if at least one of the operands has no position information, then
1443- * return false. But if TS_EXEC_PHRASE_AS_AND flag is set then we
1444- * return true as it is a AND operation
1448+ * If either operand has no position information, then we normally
1449+ * return false. But if TS_EXEC_PHRASE_AS_AND flag is set then we
1450+ * return true, treating OP_PHRASE as if it were OP_AND.
14451451 */
14461452 if (Ldata .npos == 0 || Rdata .npos == 0 )
14471453 return (flags & TS_EXEC_PHRASE_AS_AND ) ? true : false;
14481454
14491455 /*
1450- * Result of the operation is a list of the corresponding positions of
1451- * RIGHT operand.
1456+ * Prepare output position array if needed.
14521457 */
14531458 if (data )
14541459 {
1460+ /*
1461+ * We can recycle the righthand operand's result array if it was
1462+ * palloc'd, else must allocate our own. The number of matches
1463+ * couldn't be more than the smaller of the two operands' matches.
1464+ */
14551465 if (!Rdata .allocated )
1456-
1457- /*
1458- * OP_PHRASE is based on the OP_AND, so the number of
1459- * resulting positions could not be greater than the total
1460- * amount of operands.
1461- */
14621466 data -> pos = palloc (sizeof (WordEntryPos ) * Min (Ldata .npos , Rdata .npos ));
14631467 else
14641468 data -> pos = Rdata .pos ;
@@ -1469,10 +1473,12 @@ TS_phrase_execute(QueryItem *curitem,
14691473 }
14701474
14711475 /*
1472- * Find matches by distance, WEP_GETPOS() is needed because
1473- * ExecPhraseData->data can point to the tsvector's WordEntryPosVector
1476+ * Find matches by distance. WEP_GETPOS() is needed because
1477+ * ExecPhraseData->data can point to a tsvector's WordEntryPosVector.
1478+ *
1479+ * Note that the output positions are those of the matching RIGHT
1480+ * operands.
14741481 */
1475-
14761482 Rpos = Rdata .pos ;
14771483 LposStart = Ldata .pos ;
14781484 while (Rpos < Rdata .pos + Rdata .npos )
@@ -1505,8 +1511,9 @@ TS_phrase_execute(QueryItem *curitem,
15051511 else
15061512 {
15071513 /*
1508- * We are in the root of the phrase tree and hence we
1509- * don't have to store the resulting positions
1514+ * We are at the root of the phrase tree and hence we
1515+ * don't have to identify all the match positions.
1516+ * Just report success.
15101517 */
15111518 return true;
15121519 }
@@ -1546,50 +1553,53 @@ TS_phrase_execute(QueryItem *curitem,
15461553/*
15471554 * Evaluate tsquery boolean expression.
15481555 *
1549- * chkcond is a callback function used to evaluate each VAL node in the query.
1550- * checkval can be used to pass information to the callback. TS_execute doesn't
1551- * do anything with it.
1552- * It believes that ordinary operators are always closier to root than phrase
1553- * operator, so, TS_execute() may not take care of lexeme's position at all.
1556+ * curitem: current tsquery item (initially, the first one)
1557+ * arg: opaque value to pass through to callback function
1558+ * flags: bitmask of flag bits shown in ts_utils.h
1559+ * chkcond: callback function to check whether a primitive value is present
1560+ *
1561+ * The logic here deals only with operators above any phrase operator, for
1562+ * which we do not need to worry about lexeme positions. As soon as we hit an
1563+ * OP_PHRASE operator, we pass it off to TS_phrase_execute which does worry.
15541564 */
15551565bool
1556- TS_execute (QueryItem * curitem , void * checkval , uint32 flags ,
1557- bool ( * chkcond ) ( void * checkval , QueryOperand * val , ExecPhraseData * data ) )
1566+ TS_execute (QueryItem * curitem , void * arg , uint32 flags ,
1567+ TSExecuteCallback chkcond )
15581568{
15591569 /* since this function recurses, it could be driven to stack overflow */
15601570 check_stack_depth ();
15611571
15621572 if (curitem -> type == QI_VAL )
1563- return chkcond (checkval , (QueryOperand * ) curitem ,
1573+ return chkcond (arg , (QueryOperand * ) curitem ,
15641574 NULL /* we don't need position info */ );
15651575
15661576 switch (curitem -> qoperator .oper )
15671577 {
15681578 case OP_NOT :
15691579 if (flags & TS_EXEC_CALC_NOT )
1570- return !TS_execute (curitem + 1 , checkval , flags , chkcond );
1580+ return !TS_execute (curitem + 1 , arg , flags , chkcond );
15711581 else
15721582 return true;
15731583
15741584 case OP_AND :
1575- if (TS_execute (curitem + curitem -> qoperator .left , checkval , flags , chkcond ))
1576- return TS_execute (curitem + 1 , checkval , flags , chkcond );
1585+ if (TS_execute (curitem + curitem -> qoperator .left , arg , flags , chkcond ))
1586+ return TS_execute (curitem + 1 , arg , flags , chkcond );
15771587 else
15781588 return false;
15791589
15801590 case OP_OR :
1581- if (TS_execute (curitem + curitem -> qoperator .left , checkval , flags , chkcond ))
1591+ if (TS_execute (curitem + curitem -> qoperator .left , arg , flags , chkcond ))
15821592 return true;
15831593 else
1584- return TS_execute (curitem + 1 , checkval , flags , chkcond );
1594+ return TS_execute (curitem + 1 , arg , flags , chkcond );
15851595
15861596 case OP_PHRASE :
15871597
15881598 /*
15891599 * do not check TS_EXEC_PHRASE_AS_AND here because chkcond() could
15901600 * do something more if it's called from TS_phrase_execute()
15911601 */
1592- return TS_phrase_execute (curitem , checkval , flags , NULL , chkcond );
1602+ return TS_phrase_execute (curitem , arg , flags , NULL , chkcond );
15931603
15941604 default :
15951605 elog (ERROR , "unrecognized operator: %d" , curitem -> qoperator .oper );
@@ -1684,12 +1694,10 @@ ts_match_vq(PG_FUNCTION_ARGS)
16841694 chkval .arre = chkval .arrb + val -> size ;
16851695 chkval .values = STRPTR (val );
16861696 chkval .operand = GETOPERAND (query );
1687- result = TS_execute (
1688- GETQUERY (query ),
1697+ result = TS_execute (GETQUERY (query ),
16891698 & chkval ,
16901699 TS_EXEC_CALC_NOT ,
1691- checkcondition_str
1692- );
1700+ checkcondition_str );
16931701
16941702 PG_FREE_IF_COPY (val , 0 );
16951703 PG_FREE_IF_COPY (query , 1 );
0 commit comments