Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Support "ORDER BY rowid ASC". |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | fts5 |
Files: | files | file ages | folders |
SHA1: |
b96b5e166990e4ec363b24f66e04cfa5 |
User & Date: | dan 2014-07-10 20:21:12.482 |
Context
2014-07-16
| ||
19:15 | Begin adding interface for auxiliary functions. (check-in: 1e2a7ba088 user: dan tags: fts5) | |
2014-07-10
| ||
20:21 | Support "ORDER BY rowid ASC". (check-in: b96b5e1669 user: dan tags: fts5) | |
2014-07-08
| ||
16:27 | Add support for prefix queries to fts5. (check-in: 75ebd3cd59 user: dan tags: fts5) | |
Changes
Changes to ext/fts5/fts5_expr.c.
︙ | ︙ | |||
410 411 412 413 414 415 416 | ** ** If the iterator reaches EOF, set *pbEof to true before returning. If ** an error occurs, set *pRc to an error code. If either *pbEof or *pRc ** are set, return a non-zero value. Otherwise, return zero. */ static int fts5ExprAdvanceto( Fts5IndexIter *pIter, /* Iterator to advance */ | | > | > | > | > | | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 | ** ** If the iterator reaches EOF, set *pbEof to true before returning. If ** an error occurs, set *pRc to an error code. If either *pbEof or *pRc ** are set, return a non-zero value. Otherwise, return zero. */ static int fts5ExprAdvanceto( Fts5IndexIter *pIter, /* Iterator to advance */ int bAsc, /* True if iterator is "rowid ASC" */ i64 *piLast, /* IN/OUT: Lastest rowid seen so far */ int *pRc, /* OUT: Error code */ int *pbEof /* OUT: Set to true if EOF */ ){ i64 iLast = *piLast; i64 iRowid; while( 1 ){ iRowid = sqlite3Fts5IterRowid(pIter); if( (bAsc==0 && iRowid<=iLast) || (bAsc==1 && iRowid>=iLast) ) break; sqlite3Fts5IterNext(pIter, 0); if( sqlite3Fts5IterEof(pIter) ){ *pbEof = 1; return 1; } } if( iRowid!=iLast ){ assert( (bAsc==0 && iRowid<iLast) || (bAsc==1 && iRowid>iLast) ); *piLast = iRowid; } return 0; } /* ** All individual term iterators in pNear are guaranteed to be valid when |
︙ | ︙ | |||
448 449 450 451 452 453 454 | static int fts5ExprNearNextRowidMatch( Fts5Expr *pExpr, /* Expression pPhrase belongs to */ Fts5ExprNode *pNode ){ Fts5ExprNearset *pNear = pNode->pNear; int rc = SQLITE_OK; int i, j; /* Phrase and token index, respectively */ | | | > > > > | > | | > > | | 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 | static int fts5ExprNearNextRowidMatch( Fts5Expr *pExpr, /* Expression pPhrase belongs to */ Fts5ExprNode *pNode ){ Fts5ExprNearset *pNear = pNode->pNear; int rc = SQLITE_OK; int i, j; /* Phrase and token index, respectively */ i64 iLast; /* Lastest rowid any iterator points to */ int bMatch; /* True if all terms are at the same rowid */ /* Set iLast, the lastest rowid any iterator points to. If the iterator ** skips through rowids in the default descending order, this means the ** minimum rowid. Or, if the iterator is "ORDER BY rowid ASC", then it ** means the maximum rowid. */ iLast = sqlite3Fts5IterRowid(pNear->apPhrase[0]->aTerm[0].pIter); do { bMatch = 1; for(i=0; i<pNear->nPhrase; i++){ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; for(j=0; j<pPhrase->nTerm; j++){ Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter; i64 iRowid = sqlite3Fts5IterRowid(pIter); if( iRowid!=iLast ) bMatch = 0; if( fts5ExprAdvanceto(pIter, pExpr->bAsc, &iLast, &rc, &pNode->bEof) ){ return rc; } } } }while( bMatch==0 ); pNode->iRowid = iLast; return rc; } /* ** Argument pNode points to a NEAR node. All individual term iterators ** point to valid entries (not EOF). * |
︙ | ︙ | |||
551 552 553 554 555 556 557 | for(j=0; j<pPhrase->nTerm; j++){ pTerm = &pPhrase->aTerm[j]; pTerm->pIter = sqlite3Fts5IndexQuery( pExpr->pIndex, pTerm->zTerm, strlen(pTerm->zTerm), (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) | (pExpr->bAsc ? FTS5INDEX_QUERY_ASC : 0) ); | | | < | > > > > > > > | > > > > > > > > | | > | 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 | for(j=0; j<pPhrase->nTerm; j++){ pTerm = &pPhrase->aTerm[j]; pTerm->pIter = sqlite3Fts5IndexQuery( pExpr->pIndex, pTerm->zTerm, strlen(pTerm->zTerm), (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) | (pExpr->bAsc ? FTS5INDEX_QUERY_ASC : 0) ); if( pTerm->pIter && sqlite3Fts5IterEof(pTerm->pIter) ){ pNode->bEof = 1; return SQLITE_OK; } } } return SQLITE_OK; } /* fts3ExprNodeNext() calls fts5ExprNodeNextMatch(). And vice-versa. */ static int fts5ExprNodeNextMatch(Fts5Expr*, Fts5ExprNode*); /* ** Compare the values currently indicated by the two nodes as follows: ** ** res = (*p1) - (*p2) ** ** Nodes that point to values that come later in the iteration order are ** considered to be larger. Nodes at EOF are the largest of all. ** ** This means that if the iteration order is ASC, then numerically larger ** rowids are considered larger. Or if it is the default DESC, numerically ** smaller rowids are larger. */ static int fts5NodeCompare( Fts5Expr *pExpr, Fts5ExprNode *p1, Fts5ExprNode *p2 ){ if( p2->bEof ) return -1; if( p1->bEof ) return +1; if( pExpr->bAsc ){ if( p1->iRowid<p2->iRowid ) return -1; return (p1->iRowid > p2->iRowid); }else{ if( p1->iRowid>p2->iRowid ) return -1; return (p1->iRowid < p2->iRowid); } } static int fts5ExprNodeNext(Fts5Expr *pExpr, Fts5ExprNode *pNode){ int rc = SQLITE_OK; if( pNode->bEof==0 ){ switch( pNode->eType ){ |
︙ | ︙ | |||
596 597 598 599 600 601 602 | if( rc==SQLITE_OK ) rc = fts5ExprNodeNext(pExpr, pNode->pRight); break; } case FTS5_OR: { Fts5ExprNode *p1 = pNode->pLeft; Fts5ExprNode *p2 = pNode->pRight; | | | 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 | if( rc==SQLITE_OK ) rc = fts5ExprNodeNext(pExpr, pNode->pRight); break; } case FTS5_OR: { Fts5ExprNode *p1 = pNode->pLeft; Fts5ExprNode *p2 = pNode->pRight; int cmp = fts5NodeCompare(pExpr, p1, p2); if( cmp==0 ){ rc = fts5ExprNodeNext(pExpr, p1); if( rc==SQLITE_OK ) rc = fts5ExprNodeNext(pExpr, p2); }else{ rc = fts5ExprNodeNext(pExpr, (cmp < 0) ? p1 : p2); } |
︙ | ︙ | |||
640 641 642 643 644 645 646 | } case FTS5_AND: { Fts5ExprNode *p1 = pNode->pLeft; Fts5ExprNode *p2 = pNode->pRight; while( p1->bEof==0 && p2->bEof==0 && p2->iRowid!=p1->iRowid ){ | > > > > | > | | | 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 | } case FTS5_AND: { Fts5ExprNode *p1 = pNode->pLeft; Fts5ExprNode *p2 = pNode->pRight; while( p1->bEof==0 && p2->bEof==0 && p2->iRowid!=p1->iRowid ){ Fts5ExprNode *pAdv; if( pExpr->bAsc ){ pAdv = (p1->iRowid < p2->iRowid) ? p1 : p2; }else{ pAdv = (p1->iRowid > p2->iRowid) ? p1 : p2; } rc = fts5ExprNodeNext(pExpr, pAdv); if( rc!=SQLITE_OK ) break; } pNode->bEof = p1->bEof || p2->bEof; pNode->iRowid = p1->iRowid; break; } case FTS5_OR: { Fts5ExprNode *p1 = pNode->pLeft; Fts5ExprNode *p2 = pNode->pRight; Fts5ExprNode *pNext = (fts5NodeCompare(pExpr, p1, p2) > 0 ? p2 : p1); pNode->bEof = pNext->bEof; pNode->iRowid = pNext->iRowid; break; } default: assert( pNode->eType==FTS5_NOT ); { Fts5ExprNode *p1 = pNode->pLeft; Fts5ExprNode *p2 = pNode->pRight; while( rc==SQLITE_OK ){ int cmp; while( rc==SQLITE_OK && (cmp = fts5NodeCompare(pExpr, p1, p2))>0 ){ rc = fts5ExprNodeNext(pExpr, p2); } if( rc || cmp ) break; rc = fts5ExprNodeNext(pExpr, p1); } pNode->bEof = p1->bEof; pNode->iRowid = p1->iRowid; |
︙ | ︙ |
Changes to ext/fts5/fts5_index.c.
︙ | ︙ | |||
295 296 297 298 299 300 301 302 303 304 305 306 307 308 | /* State used by the fts5DataXXX() functions. */ sqlite3_blob *pReader; /* RO incr-blob open on %_data table */ sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */ sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */ }; struct Fts5DoclistIter { u8 *a; int n; int i; /* Output variables. aPoslist==0 at EOF */ i64 iRowid; u8 *aPoslist; | > | 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | /* State used by the fts5DataXXX() functions. */ sqlite3_blob *pReader; /* RO incr-blob open on %_data table */ sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */ sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */ }; struct Fts5DoclistIter { int bAsc; u8 *a; int n; int i; /* Output variables. aPoslist==0 at EOF */ i64 iRowid; u8 *aPoslist; |
︙ | ︙ | |||
406 407 408 409 410 411 412 413 414 415 416 417 418 419 | ** considered to be greater than all other iterators. ** ** aFirst[1] contains the index in aSeg[] of the iterator that points to ** the smallest key overall. aFirst[0] is unused. */ struct Fts5MultiSegIter { int nSeg; /* Size of aSeg[] array */ Fts5SegIter *aSeg; /* Array of segment iterators */ u16 *aFirst; /* Current merge state (see above) */ }; /* ** Object for iterating through a single segment, visiting each term/docid ** pair in the segment. | > | 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 | ** considered to be greater than all other iterators. ** ** aFirst[1] contains the index in aSeg[] of the iterator that points to ** the smallest key overall. aFirst[0] is unused. */ struct Fts5MultiSegIter { int nSeg; /* Size of aSeg[] array */ int bRev; /* True to iterate in reverse order */ Fts5SegIter *aSeg; /* Array of segment iterators */ u16 *aFirst; /* Current merge state (see above) */ }; /* ** Object for iterating through a single segment, visiting each term/docid ** pair in the segment. |
︙ | ︙ | |||
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 | ** ** flags: ** Mask of FTS5_SEGITER_XXX values. Interpreted as follows: ** ** FTS5_SEGITER_ONETERM: ** If set, set the iterator to point to EOF after the current doclist ** has been exhausted. Do not proceed to the next term in the segment. */ struct Fts5SegIter { Fts5StructureSegment *pSeg; /* Segment to iterate through */ int iIdx; /* Byte offset within current leaf */ int flags; /* Mask of configuration flags */ int iLeafPgno; /* Current leaf page number */ Fts5Data *pLeaf; /* Current leaf data */ int iLeafOffset; /* Byte offset within current leaf */ int iTermLeafPgno; int iTermLeafOffset; /* Variables populated based on current entry. */ Fts5Buffer term; /* Current term */ i64 iRowid; /* Current rowid */ }; #define FTS5_SEGITER_ONETERM 0x01 /* ** Object for iterating through paginated data. */ struct Fts5ChunkIter { Fts5Data *pLeaf; /* Current leaf data. NULL -> EOF. */ | > > > > > > > > > > > > > > > > > > > > | 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 | ** ** flags: ** Mask of FTS5_SEGITER_XXX values. Interpreted as follows: ** ** FTS5_SEGITER_ONETERM: ** If set, set the iterator to point to EOF after the current doclist ** has been exhausted. Do not proceed to the next term in the segment. ** ** FTS5_SEGITER_REVERSE: ** This flag is only ever set if FTS5_SEGITER_ONETERM is also set. If ** it is set, iterate through docids in ascending order instead of the ** default descending order. ** ** iRowidOffset/nRowidOffset/aRowidOffset: ** These are used if the FTS5_SEGITER_REVERSE flag is set. ** ** Each time a new page is loaded, the iterator is set to point to the ** final rowid. Additionally, the aRowidOffset[] array is populated ** with the byte offsets of all relevant rowid fields on the page. */ struct Fts5SegIter { Fts5StructureSegment *pSeg; /* Segment to iterate through */ int iIdx; /* Byte offset within current leaf */ int flags; /* Mask of configuration flags */ int iLeafPgno; /* Current leaf page number */ Fts5Data *pLeaf; /* Current leaf data */ int iLeafOffset; /* Byte offset within current leaf */ /* The page and offset from which the current term was read. The offset ** is the offset of the first rowid in the current doclist. */ int iTermLeafPgno; int iTermLeafOffset; /* The following are only used if the FTS5_SEGITER_REVERSE flag is set. */ int iRowidOffset; /* Current entry in aRowidOffset[] */ int nRowidOffset; /* Allocated size of aRowidOffset[] array */ int *aRowidOffset; /* Array of offset to rowid fields */ /* Variables populated based on current entry. */ Fts5Buffer term; /* Current term */ i64 iRowid; /* Current rowid */ }; #define FTS5_SEGITER_ONETERM 0x01 #define FTS5_SEGITER_REVERSE 0x02 /* ** Object for iterating through paginated data. */ struct Fts5ChunkIter { Fts5Data *pLeaf; /* Current leaf data. NULL -> EOF. */ |
︙ | ︙ | |||
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 | if( p->rc==SQLITE_OK ){ u8 *a = pIter->pLeaf->p; pIter->iLeafOffset = fts5GetU16(&a[2]); fts5SegIterLoadTerm(p, pIter, 0); } } /* ** Advance iterator pIter to the next entry. ** ** If an error occurs, Fts5Index.rc is set to an appropriate error code. It ** is not considered an error if the iterator reaches EOF. If an error has ** already occurred when this function is called, it is a no-op. */ static void fts5SegIterNext( Fts5Index *p, /* FTS5 backend object */ Fts5SegIter *pIter /* Iterator to advance */ ){ if( p->rc==SQLITE_OK ){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 | if( p->rc==SQLITE_OK ){ u8 *a = pIter->pLeaf->p; pIter->iLeafOffset = fts5GetU16(&a[2]); fts5SegIterLoadTerm(p, pIter, 0); } } static void fts5LeafHeader(Fts5Data *pLeaf, int *piRowid, int *piTerm){ *piRowid = (int)fts5GetU16(&pLeaf->p[0]); *piTerm = (int)fts5GetU16(&pLeaf->p[2]); } /* ** This function is only ever called on iterators created by calls to ** Fts5IndexQuery() with the FTS5INDEX_QUERY_ASC flag set. ** ** When this function is called, iterator pIter points to the first rowid ** on the current leaf associated with the term being queried. This function ** advances it to point to the last such rowid and, if necessary, initializes ** the aRowidOffset[] and iRowidOffset variables. */ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){ int n = pIter->pLeaf->n; int i = pIter->iLeafOffset; u8 *a = pIter->pLeaf->p; int iRowidOffset = 0; while( p->rc==SQLITE_OK && i<n ){ i64 iDelta = 0; int nPos; i += getVarint32(&a[i], nPos); i += nPos; if( i>=n ) break; i += getVarint(&a[i], (u64*)&iDelta); if( iDelta==0 ) break; pIter->iRowid -= iDelta; if( iRowidOffset>=pIter->nRowidOffset ){ int nNew = pIter->nRowidOffset + 8; int *aNew = (int*)sqlite3_realloc(pIter->aRowidOffset, nNew*sizeof(int)); if( aNew==0 ){ p->rc = SQLITE_NOMEM; break; } pIter->aRowidOffset = aNew; pIter->nRowidOffset = nNew; } pIter->aRowidOffset[iRowidOffset++] = pIter->iLeafOffset; pIter->iLeafOffset = i; } pIter->iRowidOffset = iRowidOffset; } /* ** Advance iterator pIter to the next entry. ** ** If an error occurs, Fts5Index.rc is set to an appropriate error code. It ** is not considered an error if the iterator reaches EOF. If an error has ** already occurred when this function is called, it is a no-op. */ static void fts5SegIterNext( Fts5Index *p, /* FTS5 backend object */ Fts5SegIter *pIter /* Iterator to advance */ ){ if( p->rc==SQLITE_OK ){ if( pIter->flags & FTS5_SEGITER_REVERSE ){ if( pIter->iRowidOffset>0 ){ u8 *a = pIter->pLeaf->p; int iOff; int nPos; i64 iDelta; pIter->iRowidOffset--; pIter->iLeafOffset = iOff = pIter->aRowidOffset[pIter->iRowidOffset]; iOff += getVarint32(&a[iOff], nPos); iOff += nPos; getVarint(&a[iOff], (u64*)&iDelta); pIter->iRowid += iDelta; }else{ fts5DataRelease(pIter->pLeaf); pIter->pLeaf = 0; while( p->rc==SQLITE_OK && pIter->iLeafPgno>pIter->iTermLeafPgno ){ Fts5Data *pNew; pIter->iLeafPgno--; pNew = fts5DataRead(p, FTS5_SEGMENT_ROWID( pIter->iIdx, pIter->pSeg->iSegid, 0, pIter->iLeafPgno )); if( pNew ){ if( pIter->iLeafPgno==pIter->iTermLeafPgno ){ if( pIter->iTermLeafOffset<pNew->n ){ pIter->pLeaf = pNew; pIter->iLeafOffset = pIter->iTermLeafOffset; } }else{ int iRowidOff, dummy; fts5LeafHeader(pNew, &iRowidOff, &dummy); if( iRowidOff ){ pIter->pLeaf = pNew; pIter->iLeafOffset = iRowidOff; } } if( pIter->pLeaf ){ u8 *a = &pIter->pLeaf->p[pIter->iLeafOffset]; pIter->iLeafOffset += getVarint(a, (u64*)&pIter->iRowid); break; }else{ fts5DataRelease(pNew); } } } if( pIter->pLeaf ){ fts5SegIterReverseInitPage(p, pIter); } } }else{ Fts5Data *pLeaf = pIter->pLeaf; int iOff; int bNewTerm = 0; int nKeep = 0; /* Search for the end of the position list within the current page. */ u8 *a = pLeaf->p; int n = pLeaf->n; iOff = pIter->iLeafOffset; if( iOff<n ){ int nPoslist; iOff += getVarint32(&a[iOff], nPoslist); iOff += nPoslist; } if( iOff<n ){ /* The next entry is on the current page */ u64 iDelta; iOff += sqlite3GetVarint(&a[iOff], &iDelta); pIter->iLeafOffset = iOff; if( iDelta==0 ){ bNewTerm = 1; if( iOff>=n ){ fts5SegIterNextPage(p, pIter); pIter->iLeafOffset = 4; }else if( iOff!=fts5GetU16(&a[2]) ){ pIter->iLeafOffset += getVarint32(&a[iOff], nKeep); } }else{ pIter->iRowid -= iDelta; } }else{ iOff = 0; /* Next entry is not on the current page */ while( iOff==0 ){ fts5SegIterNextPage(p, pIter); pLeaf = pIter->pLeaf; if( pLeaf==0 ) break; if( (iOff = fts5GetU16(&pLeaf->p[0])) ){ iOff += sqlite3GetVarint(&pLeaf->p[iOff], (u64*)&pIter->iRowid); pIter->iLeafOffset = iOff; } else if( (iOff = fts5GetU16(&pLeaf->p[2])) ){ pIter->iLeafOffset = iOff; bNewTerm = 1; } } } /* Check if the iterator is now at EOF. If so, return early. */ if( pIter->pLeaf && bNewTerm ){ if( pIter->flags & FTS5_SEGITER_ONETERM ){ fts5DataRelease(pIter->pLeaf); pIter->pLeaf = 0; }else{ fts5SegIterLoadTerm(p, pIter, nKeep); } } } } } /* ** Iterator pIter currently points to the first rowid in a doclist. This ** function sets the iterator up so that iterates in reverse order through ** the doclist. */ static void fts5SegIterReverse(Fts5Index *p, int iIdx, Fts5SegIter *pIter){ Fts5Data *pLeaf; /* Current leaf data */ int iOff = pIter->iLeafOffset; /* Byte offset within current leaf */ Fts5Data *pLast = 0; int pgnoLast = 0; /* Move to the page that contains the last rowid in this doclist. */ pLeaf = pIter->pLeaf; while( iOff<pLeaf->n ){ int nPos; i64 iDelta; /* Position list size in bytes */ iOff += getVarint32(&pLeaf->p[iOff], nPos); iOff += nPos; if( iOff>=pLeaf->n ) break; /* Rowid delta. Or, if 0x00, the end of doclist marker. */ nPos = getVarint(&pLeaf->p[iOff], (u64*)&iDelta); if( iDelta==0 ) break; iOff += nPos; } if( iOff>=pLeaf->n ){ Fts5StructureSegment *pSeg = pIter->pSeg; i64 iAbs = FTS5_SEGMENT_ROWID(iIdx, pSeg->iSegid, 0, pIter->iLeafPgno); i64 iLast = FTS5_SEGMENT_ROWID(iIdx, pSeg->iSegid, 0, pSeg->pgnoLast); /* The last rowid in the doclist may not be on the current page. Search ** forward to find the page containing the last rowid. */ for(iAbs++; p->rc==SQLITE_OK && iAbs<=iLast; iAbs++){ Fts5Data *pNew = fts5DataRead(p, iAbs); if( pNew ){ int iRowid, iTerm; fts5LeafHeader(pNew, &iRowid, &iTerm); if( iRowid ){ Fts5Data *pTmp = pLast; pLast = pNew; pNew = pTmp; pgnoLast = iAbs & (((i64)1 << FTS5_DATA_PAGE_B) - 1); } if( iTerm ){ iAbs = iLast; } fts5DataRelease(pNew); } } } /* If pLast is NULL at this point, then the last rowid for this doclist ** lies on the page currently indicated by the iterator. In this case ** iLastOff is set to the value that pIter->iLeafOffset will take when ** the iterator points to that rowid. ** ** Or, if pLast is non-NULL, then it is the page that contains the last ** rowid. */ if( pLast ){ int dummy; fts5DataRelease(pIter->pLeaf); pIter->pLeaf = pLast; pIter->iLeafPgno = pgnoLast; fts5LeafHeader(pLast, &iOff, &dummy); iOff += getVarint(&pLast->p[iOff], (u64*)&pIter->iRowid); pIter->iLeafOffset = iOff; } fts5SegIterReverseInitPage(p, pIter); pIter->flags |= FTS5_SEGITER_REVERSE; } /* ** Initialize the object pIter to point to term pTerm/nTerm within segment ** pSeg, index iIdx. If there is no such term in the index, the iterator ** is set to EOF. ** ** If an error occurs, Fts5Index.rc is set to an appropriate error code. If ** an error has already occurred when this function is called, it is a no-op. */ static void fts5SegIterSeekInit( Fts5Index *p, /* FTS5 backend */ int iIdx, /* Config.aHash[] index of FTS index */ const u8 *pTerm, int nTerm, /* Term to seek to */ int flags, /* Mask of FTS5INDEX_XXX flags */ Fts5StructureSegment *pSeg, /* Description of segment */ Fts5SegIter *pIter /* Object to populate */ ){ int iPg = 1; int h; int bGe = ((flags & FTS5INDEX_QUERY_PREFIX) && iIdx==0); assert( bGe==0 || (flags & FTS5INDEX_QUERY_ASC)==0 ); assert( pTerm && nTerm ); memset(pIter, 0, sizeof(*pIter)); pIter->pSeg = pSeg; pIter->iIdx = iIdx; /* This block sets stack variable iPg to the leaf page number that may ** contain term (pTerm/nTerm), if it is present in the segment. */ |
︙ | ︙ | |||
1216 1217 1218 1219 1220 1221 1222 | if( bGe==0 && res ){ /* Set iterator to point to EOF */ fts5DataRelease(pIter->pLeaf); pIter->pLeaf = 0; } } | > | > > > > > > | 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 | if( bGe==0 && res ){ /* Set iterator to point to EOF */ fts5DataRelease(pIter->pLeaf); pIter->pLeaf = 0; } } if( bGe==0 ){ pIter->flags |= FTS5_SEGITER_ONETERM; if( pIter->pLeaf && (flags & FTS5INDEX_QUERY_ASC) ){ fts5SegIterReverse(p, iIdx, pIter); } } } /* ** Zero the iterator passed as the only argument. */ static void fts5SegIterClear(Fts5SegIter *pIter){ fts5BufferFree(&pIter->term); fts5DataRelease(pIter->pLeaf); sqlite3_free(pIter->aRowidOffset); memset(pIter, 0, sizeof(Fts5SegIter)); } /* ** Do the comparison necessary to populate pIter->aFirst[iOut]. ** ** If the returned value is non-zero, then it is the index of an entry ** in the pIter->aSeg[] array that is (a) not at EOF, and (b) pointing ** to a key that is a duplicate of another, higher priority, ** segment-iterator in the pSeg->aSeg[] array. */ static int fts5MultiIterDoCompare(Fts5MultiSegIter *pIter, int iOut){ int i1; /* Index of left-hand Fts5SegIter */ int i2; /* Index of right-hand Fts5SegIter */ int iRes; Fts5SegIter *p1; /* Left-hand Fts5SegIter */ Fts5SegIter *p2; /* Right-hand Fts5SegIter */ assert( iOut<pIter->nSeg && iOut>0 ); assert( pIter->bRev==0 || pIter->bRev==1 ); if( iOut>=(pIter->nSeg/2) ){ i1 = (iOut - pIter->nSeg/2) * 2; i2 = i1 + 1; }else{ i1 = pIter->aFirst[iOut*2]; i2 = pIter->aFirst[iOut*2+1]; |
︙ | ︙ | |||
1265 1266 1267 1268 1269 1270 1271 | iRes = i1; }else{ int res = fts5BufferCompare(&p1->term, &p2->term); if( res==0 ){ assert( i2>i1 ); assert( i2!=0 ); if( p1->iRowid==p2->iRowid ) return i2; | | | 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 | iRes = i1; }else{ int res = fts5BufferCompare(&p1->term, &p2->term); if( res==0 ){ assert( i2>i1 ); assert( i2!=0 ); if( p1->iRowid==p2->iRowid ) return i2; res = ((p1->iRowid < p2->iRowid)==pIter->bRev) ? -1 : +1; } assert( res!=0 ); if( res<0 ){ iRes = i1; }else{ iRes = i2; } |
︙ | ︙ | |||
1338 1339 1340 1341 1342 1343 1344 | ** The iterator initially points to the first term/rowid entry in the ** iterated data. */ static void fts5MultiIterNew( Fts5Index *p, /* FTS5 backend to iterate within */ Fts5Structure *pStruct, /* Structure of specific index */ int iIdx, /* Config.aHash[] index of FTS index */ | | | 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 | ** The iterator initially points to the first term/rowid entry in the ** iterated data. */ static void fts5MultiIterNew( Fts5Index *p, /* FTS5 backend to iterate within */ Fts5Structure *pStruct, /* Structure of specific index */ int iIdx, /* Config.aHash[] index of FTS index */ int flags, /* True for >= */ const u8 *pTerm, int nTerm, /* Term to seek to (or NULL/0) */ int iLevel, /* Level to iterate (-1 for all) */ int nSegment, /* Number of segments to merge (iLevel>=0) */ Fts5MultiSegIter **ppOut /* New object */ ){ int nSeg; /* Number of segments merged */ int nSlot; /* Power of two >= nSeg */ |
︙ | ︙ | |||
1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 | sizeof(Fts5SegIter) * nSlot + /* pNew->aSeg[] */ sizeof(u16) * nSlot /* pNew->aFirst[] */ ); if( pNew==0 ) return; pNew->nSeg = nSlot; pNew->aSeg = (Fts5SegIter*)&pNew[1]; pNew->aFirst = (u16*)&pNew->aSeg[nSlot]; /* Initialize each of the component segment iterators. */ if( iLevel<0 ){ Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel]; for(pLvl=&pStruct->aLevel[0]; pLvl<pEnd; pLvl++){ for(iSeg=pLvl->nSeg-1; iSeg>=0; iSeg--){ Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; Fts5SegIter *pIter = &pNew->aSeg[iIter++]; if( pTerm==0 ){ fts5SegIterInit(p, iIdx, pSeg, pIter); }else{ | > | | 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 | sizeof(Fts5SegIter) * nSlot + /* pNew->aSeg[] */ sizeof(u16) * nSlot /* pNew->aFirst[] */ ); if( pNew==0 ) return; pNew->nSeg = nSlot; pNew->aSeg = (Fts5SegIter*)&pNew[1]; pNew->aFirst = (u16*)&pNew->aSeg[nSlot]; pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_ASC)); /* Initialize each of the component segment iterators. */ if( iLevel<0 ){ Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel]; for(pLvl=&pStruct->aLevel[0]; pLvl<pEnd; pLvl++){ for(iSeg=pLvl->nSeg-1; iSeg>=0; iSeg--){ Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; Fts5SegIter *pIter = &pNew->aSeg[iIter++]; if( pTerm==0 ){ fts5SegIterInit(p, iIdx, pSeg, pIter); }else{ fts5SegIterSeekInit(p, iIdx, pTerm, nTerm, flags, pSeg, pIter); } } } }else{ pLvl = &pStruct->aLevel[iLevel]; for(iSeg=nSeg-1; iSeg>=0; iSeg--){ fts5SegIterInit(p, iIdx, &pLvl->aSeg[iSeg], &pNew->aSeg[iIter++]); |
︙ | ︙ | |||
3070 3071 3072 3073 3074 3075 3076 | } static void fts5DoclistIterNext(Fts5DoclistIter *pIter){ if( pIter->i<pIter->n ){ if( pIter->i ){ i64 iDelta; pIter->i += getVarint(&pIter->a[pIter->i], (u64*)&iDelta); | > > > | > | > > > > > > > > | > | | | > > | | | | | 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 | } static void fts5DoclistIterNext(Fts5DoclistIter *pIter){ if( pIter->i<pIter->n ){ if( pIter->i ){ i64 iDelta; pIter->i += getVarint(&pIter->a[pIter->i], (u64*)&iDelta); if( pIter->bAsc ){ pIter->iRowid += iDelta; }else{ pIter->iRowid -= iDelta; } }else{ pIter->i += getVarint(&pIter->a[pIter->i], (u64*)&pIter->iRowid); } pIter->i += getVarint32(&pIter->a[pIter->i], pIter->nPoslist); pIter->aPoslist = &pIter->a[pIter->i]; pIter->i += pIter->nPoslist; }else{ pIter->aPoslist = 0; } } static void fts5DoclistIterInit( Fts5Buffer *pBuf, int bAsc, Fts5DoclistIter *pIter ){ memset(pIter, 0, sizeof(*pIter)); pIter->a = pBuf->p; pIter->n = pBuf->n; pIter->bAsc = bAsc; fts5DoclistIterNext(pIter); } /* ** Append a doclist to buffer pBuf. */ static void fts5MergeAppendDocid( int *pRc, /* IN/OUT: Error code */ int bAsc, Fts5Buffer *pBuf, /* Buffer to write to */ i64 *piLastRowid, /* IN/OUT: Previous rowid written (if any) */ i64 iRowid /* Rowid to append */ ){ if( pBuf->n==0 ){ fts5BufferAppendVarint(pRc, pBuf, iRowid); }else if( bAsc==0 ){ fts5BufferAppendVarint(pRc, pBuf, *piLastRowid - iRowid); }else{ fts5BufferAppendVarint(pRc, pBuf, iRowid - *piLastRowid); } *piLastRowid = iRowid; } /* ** Buffers p1 and p2 contain doclists. This function merges the content ** of the two doclists together and sets buffer p1 to the result before ** returning. ** ** If an error occurs, an error code is left in p->rc. If an error has ** already occurred, this function is a no-op. */ static void fts5MergePrefixLists( Fts5Index *p, /* FTS5 backend object */ int bAsc, Fts5Buffer *p1, /* First list to merge */ Fts5Buffer *p2 /* Second list to merge */ ){ if( p2->n ){ i64 iLastRowid = 0; Fts5DoclistIter i1; Fts5DoclistIter i2; Fts5Buffer out; Fts5Buffer tmp; memset(&out, 0, sizeof(out)); memset(&tmp, 0, sizeof(tmp)); fts5DoclistIterInit(p1, bAsc, &i1); fts5DoclistIterInit(p2, bAsc, &i2); while( i1.aPoslist!=0 || i2.aPoslist!=0 ){ if( i2.aPoslist==0 || (i1.aPoslist && ( (!bAsc && i1.iRowid>i2.iRowid) || (bAsc && i1.iRowid<i2.iRowid) ) )){ /* Copy entry from i1 */ fts5MergeAppendDocid(&p->rc, bAsc, &out, &iLastRowid, i1.iRowid); fts5BufferAppendVarint(&p->rc, &out, i1.nPoslist); fts5BufferAppendBlob(&p->rc, &out, i1.nPoslist, i1.aPoslist); fts5DoclistIterNext(&i1); } else if( i1.aPoslist==0 || i2.iRowid!=i1.iRowid ){ /* Copy entry from i2 */ fts5MergeAppendDocid(&p->rc, bAsc, &out, &iLastRowid, i2.iRowid); fts5BufferAppendVarint(&p->rc, &out, i2.nPoslist); fts5BufferAppendBlob(&p->rc, &out, i2.nPoslist, i2.aPoslist); fts5DoclistIterNext(&i2); } else{ Fts5PoslistReader r1; Fts5PoslistReader r2; Fts5PoslistWriter writer; memset(&writer, 0, sizeof(writer)); /* Merge the two position lists. */ fts5MergeAppendDocid(&p->rc, bAsc, &out, &iLastRowid, i2.iRowid); fts5BufferZero(&tmp); sqlite3Fts5PoslistReaderInit(-1, i1.aPoslist, i1.nPoslist, &r1); sqlite3Fts5PoslistReaderInit(-1, i2.aPoslist, i2.nPoslist, &r2); while( p->rc==SQLITE_OK && (r1.bEof==0 || r2.bEof==0) ){ i64 iNew; if( r2.bEof || (r1.bEof==0 && r1.iPos<r2.iPos) ){ iNew = r1.iPos; |
︙ | ︙ | |||
3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 | Fts5Buffer tmp = *p1; *p1 = *p2; *p2 = tmp; } static void fts5SetupPrefixIter( Fts5Index *p, /* Index to read from */ const u8 *pToken, /* Buffer containing prefix to match */ int nToken, /* Size of buffer pToken in bytes */ Fts5IndexIter *pIter /* Populate this object */ ){ Fts5Structure *pStruct; Fts5Buffer *aBuf; const int nBuf = 32; | > < | 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 | Fts5Buffer tmp = *p1; *p1 = *p2; *p2 = tmp; } static void fts5SetupPrefixIter( Fts5Index *p, /* Index to read from */ int bAsc, /* True for "ORDER BY rowid ASC" */ const u8 *pToken, /* Buffer containing prefix to match */ int nToken, /* Size of buffer pToken in bytes */ Fts5IndexIter *pIter /* Populate this object */ ){ Fts5Structure *pStruct; Fts5Buffer *aBuf; const int nBuf = 32; aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); pStruct = fts5StructureRead(p, 0); if( aBuf && pStruct ){ Fts5DoclistIter *pDoclist; int i; |
︙ | ︙ | |||
3221 3222 3223 3224 3225 3226 3227 | ){ i64 iRowid = fts5MultiIterRowid(p1); int nTerm; const u8 *pTerm = fts5MultiIterTerm(p1, &nTerm); assert( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 ); if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break; | | > > > | > > | | | | 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 | ){ i64 iRowid = fts5MultiIterRowid(p1); int nTerm; const u8 *pTerm = fts5MultiIterTerm(p1, &nTerm); assert( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 ); if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break; if( doclist.n>0 && ((!bAsc && iRowid>=iLastRowid) || (bAsc && iRowid<=iLastRowid)) ){ for(i=0; doclist.n && p->rc==SQLITE_OK; i++){ assert( i<nBuf ); if( aBuf[i].n==0 ){ fts5BufferSwap(&doclist, &aBuf[i]); fts5BufferZero(&doclist); }else{ fts5MergePrefixLists(p, bAsc, &doclist, &aBuf[i]); fts5BufferZero(&aBuf[i]); } } } if( doclist.n==0 ){ fts5BufferAppendVarint(&p->rc, &doclist, iRowid); }else if( bAsc==0 ){ fts5BufferAppendVarint(&p->rc, &doclist, iLastRowid - iRowid); }else{ fts5BufferAppendVarint(&p->rc, &doclist, iRowid - iLastRowid); } iLastRowid = iRowid; fts5MultiIterPoslist(p, p1, 1, &doclist); } for(i=0; i<nBuf; i++){ fts5MergePrefixLists(p, bAsc, &doclist, &aBuf[i]); fts5BufferFree(&aBuf[i]); } fts5MultiIterFree(p, p1); pDoclist = (Fts5DoclistIter*)fts5IdxMalloc(p, sizeof(Fts5DoclistIter)); if( !pDoclist ){ fts5BufferFree(&doclist); }else{ pIter->pDoclist = pDoclist; fts5DoclistIterInit(&doclist, bAsc, pIter->pDoclist); } } fts5StructureRelease(pStruct); sqlite3_free(aBuf); } |
︙ | ︙ | |||
3292 3293 3294 3295 3296 3297 3298 | memset(pRet, 0, sizeof(Fts5IndexIter)); pRet->pIndex = p; if( iIdx>=0 ){ pRet->pStruct = fts5StructureRead(p, iIdx); if( pRet->pStruct ){ fts5MultiIterNew(p, pRet->pStruct, | | > | | 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 | memset(pRet, 0, sizeof(Fts5IndexIter)); pRet->pIndex = p; if( iIdx>=0 ){ pRet->pStruct = fts5StructureRead(p, iIdx); if( pRet->pStruct ){ fts5MultiIterNew(p, pRet->pStruct, iIdx, flags, (const u8*)pToken, nToken, -1, 0, &pRet->pMulti ); } }else{ int bAsc = (flags & FTS5INDEX_QUERY_ASC)!=0; fts5SetupPrefixIter(p, bAsc, (const u8*)pToken, nToken, pRet); } } if( p->rc ){ sqlite3Fts5IterClose(pRet); pRet = 0; } |
︙ | ︙ | |||
3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 | if( pIter->pDoclist ){ *pn = pIter->pDoclist->nPoslist; return pIter->pDoclist->aPoslist; }else{ Fts5Index *p = pIter->pIndex; fts5BufferZero(&pIter->poslist); fts5MultiIterPoslist(p, pIter->pMulti, 0, &pIter->poslist); if( p->rc ) return 0; *pn = pIter->poslist.n; return pIter->poslist.p; } } /* | > | 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 | if( pIter->pDoclist ){ *pn = pIter->pDoclist->nPoslist; return pIter->pDoclist->aPoslist; }else{ Fts5Index *p = pIter->pIndex; fts5BufferZero(&pIter->poslist); fts5MultiIterPoslist(p, pIter->pMulti, 0, &pIter->poslist); assert( p->rc==SQLITE_OK ); if( p->rc ) return 0; *pn = pIter->poslist.n; return pIter->poslist.p; } } /* |
︙ | ︙ |
Changes to test/fts5ab.test.
︙ | ︙ | |||
120 121 122 123 124 125 126 | foreach {tn expr res} { 1 {abash} {9 5 3 1} 2 {abase} {9 4 3 1} 3 {abase + abash} {1} 4 {abash + abase} {9} 5 {abaft + abashing} {8 5} | < < > > > > > > > > > > > > > > | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | foreach {tn expr res} { 1 {abash} {9 5 3 1} 2 {abase} {9 4 3 1} 3 {abase + abash} {1} 4 {abash + abase} {9} 5 {abaft + abashing} {8 5} 6 {abandon + abandoning} {10} 7 {"abashing abases abasement abaft abashing"} {8} } { do_execsql_test 3.2.$tn { SELECT rowid FROM t1 WHERE t1 MATCH $expr } $res } do_execsql_test 3.3 { SELECT rowid FROM t1 WHERE t1 MATCH 'NEAR(aback abate, 2)' } {6} foreach {tn expr res} { 1 {abash} {1 3 5 9} 2 {abase} {1 3 4 9} 3 {abase + abash} {1} 4 {abash + abase} {9} 5 {abaft + abashing} {5 8} 6 {abandon + abandoning} {10} 7 {"abashing abases abasement abaft abashing"} {8} } { do_execsql_test 3.4.$tn { SELECT rowid FROM t1 WHERE t1 MATCH $expr ORDER BY rowid ASC } $res } finish_test |
Changes to test/fts5ac.test.
︙ | ︙ | |||
204 205 206 207 208 209 210 | break } } return $bMatch } | | | | | | > > > > > > | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | break } } return $bMatch } proc matchdata {expr {bAsc 0}} { set tclexpr [db one {SELECT fts5_expr_tcl($expr, 'nearset $cols', 'x', 'y')}] set res [list] foreach {id x y} $::data { set cols [list $x $y] if $tclexpr { lappend res $id } } # puts $tclexpr if {$bAsc} { set res [lsort -integer -increasing $res] } else { set res [lsort -integer -decreasing $res] } return $res } foreach {tn phrase} { 1 "o" 2 "b q" 3 "e a e" |
︙ | ︙ | |||
259 260 261 262 263 264 265 | 1 {a b} {[N $x -- {a}] && [N $x -- {b}]} } { do_execsql_test 3.$tn {SELECT fts5_expr_tcl($expr, 'N $x')} [list $tclexpr] } #------------------------------------------------------------------------- # | > > > > | > > | | | | | | | | | | | | | | | | | | | | | | | < < > | 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | 1 {a b} {[N $x -- {a}] && [N $x -- {b}]} } { do_execsql_test 3.$tn {SELECT fts5_expr_tcl($expr, 'N $x')} [list $tclexpr] } #------------------------------------------------------------------------- # foreach {bAsc sql} { 0 {SELECT rowid FROM xx WHERE xx MATCH $expr} 1 {SELECT rowid FROM xx WHERE xx MATCH $expr ORDER BY rowid ASC} } { foreach {tn expr} { 0.1 x 1 { NEAR(r c) } 2 { NEAR(r c, 5) } 3 { NEAR(r c, 3) } 4 { NEAR(r c, 2) } 5 { NEAR(r c, 0) } 6 { NEAR(a b c) } 7 { NEAR(a b c, 8) } 8 { x : NEAR(r c) } 9 { y : NEAR(r c) } 10 { x : "r c" } 11 { y : "r c" } 12 { a AND b } 13 { a AND b AND c } 14a { a } 14b { a OR b } 15 { a OR b AND c } 16 { c AND b OR a } 17 { c AND (b OR a) } 18 { c NOT (b OR a) } 19 { c NOT b OR a AND d } } { set res [matchdata $expr $bAsc] do_execsql_test 4.$bAsc.$tn.[llength $res] $sql $res } } finish_test |
Changes to test/fts5ad.test.
︙ | ︙ | |||
36 37 38 39 40 41 42 43 44 45 46 47 48 49 | 3 {t*} {3 1} 4 {r*} {3 1} } { do_execsql_test 1.$tn { SELECT rowid FROM yy WHERE yy MATCH $match } $res } foreach {T create} { 2 { CREATE VIRTUAL TABLE t1 USING fts5(a, b); INSERT INTO t1(t1) VALUES('pgsz=32'); } | > > > > > > > > > > > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | 3 {t*} {3 1} 4 {r*} {3 1} } { do_execsql_test 1.$tn { SELECT rowid FROM yy WHERE yy MATCH $match } $res } foreach {tn match res} { 5 {c*} {1} 6 {i*} {2 3} 7 {t*} {1 3} 8 {r*} {1 3} } { do_execsql_test 1.$tn { SELECT rowid FROM yy WHERE yy MATCH $match ORDER BY rowid ASC } $res } foreach {T create} { 2 { CREATE VIRTUAL TABLE t1 USING fts5(a, b); INSERT INTO t1(t1) VALUES('pgsz=32'); } |
︙ | ︙ | |||
174 175 176 177 178 179 180 | if {[lsearch -glob $a $prefix]>=0 || [lsearch -glob $b $prefix]>=0} { lappend ret $rowid } } return $ret } | > > > > | | | | | | | | | > > > | | > | 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | if {[lsearch -glob $a $prefix]>=0 || [lsearch -glob $b $prefix]>=0} { lappend ret $rowid } } return $ret } foreach {bAsc sql} { 0 {SELECT rowid FROM t1 WHERE t1 MATCH $prefix} 1 {SELECT rowid FROM t1 WHERE t1 MATCH $prefix ORDER BY rowid ASC} } { foreach {tn prefix} { 1 {a*} 2 {ab*} 3 {abc*} 4 {abcd*} 5 {abcde*} 6 {f*} 7 {fg*} 8 {fgh*} 9 {fghi*} 10 {fghij*} 11 {k*} 12 {kl*} 13 {klm*} 14 {klmn*} 15 {klmno*} 16 {p*} 17 {pq*} 18 {pqr*} 19 {pqrs*} 20 {pqrst*} 21 {u*} 22 {uv*} 23 {uvw*} 24 {uvwx*} 25 {uvwxy*} 26 {uvwxyz*} 27 {x*} } { set res [prefix_query $prefix] if {$bAsc} { set res [lsort -integer -increasing $res] } set n [llength $res] do_execsql_test $T.$bAsc.$tn.$n $sql $res } } } finish_test |
Changes to test/permutations.test.
︙ | ︙ | |||
221 222 223 224 225 226 227 | fts3varint.test fts4growth.test fts4growth2.test } test_suite "fts5" -prefix "" -description { All FTS5 tests. } -files { | | | 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | fts3varint.test fts4growth.test fts4growth2.test } test_suite "fts5" -prefix "" -description { All FTS5 tests. } -files { fts5aa.test fts5ab.test fts5ac.test fts5ad.test fts5ea.test } test_suite "nofaultsim" -prefix "" -description { "Very" quick test suite. Runs in less than 5 minutes on a workstation. This test suite is the same as the "quick" tests, except that some files that test malloc and IO errors are omitted. } -files [ |
︙ | ︙ |