@@ -1405,20 +1405,26 @@ checkcondition_str(void *checkval, QueryOperand *val, ExecPhraseData *data)
1405
1405
}
1406
1406
1407
1407
/*
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.)
1410
1416
*/
1411
1417
static 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 )
1415
1421
{
1416
1422
/* since this function recurses, it could be driven to stack overflow */
1417
1423
check_stack_depth ();
1418
1424
1419
1425
if (curitem -> type == QI_VAL )
1420
1426
{
1421
- return chkcond (checkval , (QueryOperand * ) curitem , data );
1427
+ return chkcond (arg , (QueryOperand * ) curitem , data );
1422
1428
}
1423
1429
else
1424
1430
{
@@ -1432,33 +1438,31 @@ TS_phrase_execute(QueryItem *curitem,
1432
1438
Assert (curitem -> qoperator .oper == OP_PHRASE );
1433
1439
1434
1440
if (!TS_phrase_execute (curitem + curitem -> qoperator .left ,
1435
- checkval , flags , & Ldata , chkcond ))
1441
+ arg , flags , & Ldata , chkcond ))
1436
1442
return false;
1437
1443
1438
- if (!TS_phrase_execute (curitem + 1 , checkval , flags , & Rdata , chkcond ))
1444
+ if (!TS_phrase_execute (curitem + 1 , arg , flags , & Rdata , chkcond ))
1439
1445
return false;
1440
1446
1441
1447
/*
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.
1445
1451
*/
1446
1452
if (Ldata .npos == 0 || Rdata .npos == 0 )
1447
1453
return (flags & TS_EXEC_PHRASE_AS_AND ) ? true : false;
1448
1454
1449
1455
/*
1450
- * Result of the operation is a list of the corresponding positions of
1451
- * RIGHT operand.
1456
+ * Prepare output position array if needed.
1452
1457
*/
1453
1458
if (data )
1454
1459
{
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
+ */
1455
1465
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
- */
1462
1466
data -> pos = palloc (sizeof (WordEntryPos ) * Min (Ldata .npos , Rdata .npos ));
1463
1467
else
1464
1468
data -> pos = Rdata .pos ;
@@ -1469,10 +1473,12 @@ TS_phrase_execute(QueryItem *curitem,
1469
1473
}
1470
1474
1471
1475
/*
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.
1474
1481
*/
1475
-
1476
1482
Rpos = Rdata .pos ;
1477
1483
LposStart = Ldata .pos ;
1478
1484
while (Rpos < Rdata .pos + Rdata .npos )
@@ -1505,8 +1511,9 @@ TS_phrase_execute(QueryItem *curitem,
1505
1511
else
1506
1512
{
1507
1513
/*
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.
1510
1517
*/
1511
1518
return true;
1512
1519
}
@@ -1546,50 +1553,53 @@ TS_phrase_execute(QueryItem *curitem,
1546
1553
/*
1547
1554
* Evaluate tsquery boolean expression.
1548
1555
*
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.
1554
1564
*/
1555
1565
bool
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 )
1558
1568
{
1559
1569
/* since this function recurses, it could be driven to stack overflow */
1560
1570
check_stack_depth ();
1561
1571
1562
1572
if (curitem -> type == QI_VAL )
1563
- return chkcond (checkval , (QueryOperand * ) curitem ,
1573
+ return chkcond (arg , (QueryOperand * ) curitem ,
1564
1574
NULL /* we don't need position info */ );
1565
1575
1566
1576
switch (curitem -> qoperator .oper )
1567
1577
{
1568
1578
case OP_NOT :
1569
1579
if (flags & TS_EXEC_CALC_NOT )
1570
- return !TS_execute (curitem + 1 , checkval , flags , chkcond );
1580
+ return !TS_execute (curitem + 1 , arg , flags , chkcond );
1571
1581
else
1572
1582
return true;
1573
1583
1574
1584
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 );
1577
1587
else
1578
1588
return false;
1579
1589
1580
1590
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 ))
1582
1592
return true;
1583
1593
else
1584
- return TS_execute (curitem + 1 , checkval , flags , chkcond );
1594
+ return TS_execute (curitem + 1 , arg , flags , chkcond );
1585
1595
1586
1596
case OP_PHRASE :
1587
1597
1588
1598
/*
1589
1599
* do not check TS_EXEC_PHRASE_AS_AND here because chkcond() could
1590
1600
* do something more if it's called from TS_phrase_execute()
1591
1601
*/
1592
- return TS_phrase_execute (curitem , checkval , flags , NULL , chkcond );
1602
+ return TS_phrase_execute (curitem , arg , flags , NULL , chkcond );
1593
1603
1594
1604
default :
1595
1605
elog (ERROR , "unrecognized operator: %d" , curitem -> qoperator .oper );
@@ -1684,12 +1694,10 @@ ts_match_vq(PG_FUNCTION_ARGS)
1684
1694
chkval .arre = chkval .arrb + val -> size ;
1685
1695
chkval .values = STRPTR (val );
1686
1696
chkval .operand = GETOPERAND (query );
1687
- result = TS_execute (
1688
- GETQUERY (query ),
1697
+ result = TS_execute (GETQUERY (query ),
1689
1698
& chkval ,
1690
1699
TS_EXEC_CALC_NOT ,
1691
- checkcondition_str
1692
- );
1700
+ checkcondition_str );
1693
1701
1694
1702
PG_FREE_IF_COPY (val , 0 );
1695
1703
PG_FREE_IF_COPY (query , 1 );
0 commit comments