Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add some simple test cases for the OR and NOT logic of the fts1 module. Fix lots of bugs discovered while developing these test cases. (CVS 3400) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
70bcff024b44d1b40afac6eba959fa89 |
User & Date: | drh 2006-09-10 03:34:06.000 |
Context
2006-09-10
| ||
17:08 | Add a new zErrMsg field to the sqlite3_vtab structure to support returning error messages from virtual table constructors. This change means that virtual table implementations compiled as loadable extensions for version 3.3.7 will need to be recompile for version 3.3.8 and will not be usable by both versions at one. The virtual table mechanism is still considered experimental so we feel justified in breaking backwards compatibility in this way. Additional interface changes might occurs in the future. (CVS 3401) (check-in: 36693a5cb7 user: drh tags: trunk) | |
03:34 | Add some simple test cases for the OR and NOT logic of the fts1 module. Fix lots of bugs discovered while developing these test cases. (CVS 3400) (check-in: 70bcff024b user: drh tags: trunk) | |
2006-09-09
| ||
23:11 | Add support for OR and NOT terms in fts1. (CVS 3399) (check-in: ae50265791 user: drh tags: trunk) | |
Changes
Changes to ext/fts1/fts1.c.
︙ | ︙ | |||
279 280 281 282 283 284 285 | sqlite_int64 ret; assert( !atEnd(pReader) ); assert( pReader->iLastPos==-1 ); getVarint(pReader->p, &ret); return ret; } | | > > > < | 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 309 310 311 312 313 314 315 | sqlite_int64 ret; assert( !atEnd(pReader) ); assert( pReader->iLastPos==-1 ); getVarint(pReader->p, &ret); return ret; } /* Read the next docid. See also nextValidDocid(). */ static sqlite_int64 readDocid(DocListReader *pReader){ sqlite_int64 ret; assert( !atEnd(pReader) ); assert( pReader->iLastPos==-1 ); pReader->p += getVarint(pReader->p, &ret); if( pReader->pDoclist->iType>=DL_POSITIONS ){ pReader->iLastPos = 0; } return ret; } /* Read the next position from a position list. * Returns the position, or -1 at the end of the list. */ static int readPosition(DocListReader *pReader){ int i; int iType = pReader->pDoclist->iType; if( pReader->iLastPos==-1 ){ return -1; } assert( !atEnd(pReader) ); if( iType<DL_POSITIONS ){ return -1; } pReader->p += getVarint32(pReader->p, &i); if( i==0 ){ pReader->iLastPos = -1; |
︙ | ︙ | |||
468 469 470 471 472 473 474 475 476 477 478 479 480 | while( !atEnd(&updateReader) ){ char *pSource = updateReader.p; sqlite_int64 iDocid = readDocid(&updateReader); skipPositionList(&updateReader); docListSpliceElement(&accReader, iDocid, pSource, updateReader.p-pSource); } } /* ** pLeft and pRight are two DocListReaders that are pointing to ** positions lists of the same document: iDocid. ** ** If there are no instances in pLeft or pRight where the position | > > > > > > > > > > > > > | | > | > | < | | | > > | < > | > > > > > > > > | | | > > > > | > > > > > > > > > | < > > < | | < > > < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | < < < > > > | > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > | 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 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 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 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 | while( !atEnd(&updateReader) ){ char *pSource = updateReader.p; sqlite_int64 iDocid = readDocid(&updateReader); skipPositionList(&updateReader); docListSpliceElement(&accReader, iDocid, pSource, updateReader.p-pSource); } } /* ** Read the next non-deleted docid off of pIn. Return ** 0 if we reach the end of pDoclist. */ static sqlite_int64 nextValidDocid(DocListReader *pIn){ sqlite_int64 docid = 0; skipPositionList(pIn); while( !atEnd(pIn) && (docid = readDocid(pIn))==0 ){ skipPositionList(pIn); } return docid; } /* ** pLeft and pRight are two DocListReaders that are pointing to ** positions lists of the same document: iDocid. ** ** If there are no instances in pLeft or pRight where the position ** of pLeft is one less than the position of pRight, then this ** routine adds nothing to pOut. ** ** If there are one or more instances where positions from pLeft ** are exactly one less than positions from pRight, then add a new ** document record to pOut. If pOut wants to hold positions, then ** include the positions from pRight that are one more than a ** position in pLeft. In other words: pRight.iPos==pLeft.iPos+1. ** ** pLeft and pRight are left pointing at the next document record. */ static void mergePosList( DocListReader *pLeft, /* Left position list */ DocListReader *pRight, /* Right position list */ sqlite_int64 iDocid, /* The docid from pLeft and pRight */ DocList *pOut /* Write the merged document record here */ ){ int iLeftPos = readPosition(pLeft); int iRightPos = readPosition(pRight); int match = 0; /* Loop until we've reached the end of both position lists. */ while( iLeftPos!=-1 && iRightPos!=-1 ){ if( iLeftPos+1==iRightPos ){ if( !match ){ docListAddDocid(pOut, iDocid); match = 1; } if( pOut->iType>=DL_POSITIONS ){ docListAddPos(pOut, iRightPos); } iLeftPos = readPosition(pLeft); iRightPos = readPosition(pRight); }else if( iRightPos<iLeftPos+1 ){ iRightPos = readPosition(pRight); }else{ iLeftPos = readPosition(pLeft); } } if( iLeftPos>=0 ) skipPositionList(pLeft); if( iRightPos>=0 ) skipPositionList(pRight); } /* We have two doclists: pLeft and pRight. ** Write the phrase intersection of these two doclists into pOut. ** ** A phrase intersection means that two documents only match ** if pLeft.iPos+1==pRight.iPos. ** ** The output pOut may or may not contain positions. If pOut ** does contain positions, they are the positions of pRight. */ static void docListPhraseMerge( DocList *pLeft, /* Doclist resulting from the words on the left */ DocList *pRight, /* Doclist for the next word to the right */ DocList *pOut /* Write the combined doclist here */ ){ DocListReader left, right; sqlite_int64 docidLeft, docidRight; readerInit(&left, pLeft); readerInit(&right, pRight); docidLeft = nextValidDocid(&left); docidRight = nextValidDocid(&right); while( docidLeft>0 && docidRight>0 ){ if( docidLeft<docidRight ){ docidLeft = nextValidDocid(&left); }else if( docidRight<docidLeft ){ docidRight = nextValidDocid(&right); }else{ mergePosList(&left, &right, docidLeft, pOut); docidLeft = nextValidDocid(&left); docidRight = nextValidDocid(&right); } } } /* We have two doclists: pLeft and pRight. ** Write the intersection of these two doclists into pOut. ** Only docids are matched. Position information is ignored. ** ** The output pOut never holds positions. */ static void docListAndMerge( DocList *pLeft, /* Doclist resulting from the words on the left */ DocList *pRight, /* Doclist for the next word to the right */ DocList *pOut /* Write the combined doclist here */ ){ DocListReader left, right; sqlite_int64 docidLeft, docidRight; assert( pOut->iType<DL_POSITIONS ); readerInit(&left, pLeft); readerInit(&right, pRight); docidLeft = nextValidDocid(&left); docidRight = nextValidDocid(&right); while( docidLeft>0 && docidRight>0 ){ if( docidLeft<docidRight ){ docidLeft = nextValidDocid(&left); }else if( docidRight<docidLeft ){ docidRight = nextValidDocid(&right); }else{ docListAddDocid(pOut, docidLeft); docidLeft = nextValidDocid(&left); docidRight = nextValidDocid(&right); } } } /* We have two doclists: pLeft and pRight. ** Write the union of these two doclists into pOut. ** Only docids are matched. Position information is ignored. ** ** The output pOut never holds positions. */ static void docListOrMerge( DocList *pLeft, /* Doclist resulting from the words on the left */ DocList *pRight, /* Doclist for the next word to the right */ DocList *pOut /* Write the combined doclist here */ ){ DocListReader left, right; sqlite_int64 docidLeft, docidRight, priorLeft; readerInit(&left, pLeft); readerInit(&right, pRight); docidLeft = nextValidDocid(&left); docidRight = nextValidDocid(&right); while( docidLeft>0 && docidRight>0 ){ if( docidLeft<=docidRight ){ docListAddDocid(pOut, docidLeft); }else{ docListAddDocid(pOut, docidRight); } priorLeft = docidLeft; if( docidLeft<=docidRight ){ docidLeft = nextValidDocid(&left); } if( docidRight>0 && docidRight<=priorLeft ){ docidRight = nextValidDocid(&right); } } while( docidLeft>0 ){ docListAddDocid(pOut, docidLeft); docidLeft = nextValidDocid(&left); } while( docidRight>0 ){ docListAddDocid(pOut, docidRight); docidRight = nextValidDocid(&right); } } /* We have two doclists: pLeft and pRight. ** Write into pOut all documents that occur in pLeft but not ** in pRight. ** ** Only docids are matched. Position information is ignored. ** ** The output pOut never holds positions. */ static void docListExceptMerge( DocList *pLeft, /* Doclist resulting from the words on the left */ DocList *pRight, /* Doclist for the next word to the right */ DocList *pOut /* Write the combined doclist here */ ){ DocListReader left, right; sqlite_int64 docidLeft, docidRight, priorLeft; readerInit(&left, pLeft); readerInit(&right, pRight); docidLeft = nextValidDocid(&left); docidRight = nextValidDocid(&right); while( docidLeft>0 && docidRight>0 ){ priorLeft = docidLeft; if( docidLeft<docidRight ){ docListAddDocid(pOut, docidLeft); } if( docidLeft<=docidRight ){ docidLeft = nextValidDocid(&left); } if( docidRight>0 && docidRight<=priorLeft ){ docidRight = nextValidDocid(&right); } } while( docidLeft>0 ){ docListAddDocid(pOut, docidLeft); docidLeft = nextValidDocid(&left); } } /* Duplicate a string; the caller must free() the returned string. * (We don't use strdup() since it's not part of the standard C library and * may not be available everywhere.) */ static char *string_dup(const char *s){ int n = strlen(s); |
︙ | ︙ | |||
1259 1260 1261 1262 1263 1264 1265 | c->eof = 1; return rc; } case QUERY_FULLTEXT: rc = sqlite3_reset(c->pStmt); if( rc!=SQLITE_OK ) return rc; | | > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | c->eof = 1; return rc; } case QUERY_FULLTEXT: rc = sqlite3_reset(c->pStmt); if( rc!=SQLITE_OK ) return rc; iDocid = nextValidDocid(&c->result); if( iDocid==0 ){ c->eof = 1; return SQLITE_OK; } rc = sqlite3_bind_int64(c->pStmt, 1, iDocid); if( rc!=SQLITE_OK ) return rc; /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */ rc = sqlite3_step(c->pStmt); if( rc==SQLITE_ROW ){ /* the case we expect */ c->eof = 0; return SQLITE_OK; } /* an error occurred; abort */ return rc==SQLITE_DONE ? SQLITE_ERROR : rc; default: assert( 0 ); return SQLITE_ERROR; /* not reached */ } } /* A single term in a query is represented by an instances of ** the following structure. */ typedef struct QueryTerm { int nPhrase; /* How many following terms are part of the same phrase */ int isOr; /* this term is preceded by "OR" */ int isNot; /* this term is preceded by "-" */ char *pTerm; /* text of the term. '\000' terminated. malloced */ int nTerm; /* Number of bytes in pTerm[] */ } QueryTerm; /* Return a DocList corresponding to the query term *pTerm. If *pTerm ** is the first term of a phrase query, go ahead and evaluate the phrase ** query and return the doclist for the entire phrase query. ** ** The result is stored in pTerm->doclist. */ static int docListOfTerm( fulltext_vtab *v, /* The full text index */ QueryTerm *pQTerm, /* Term we are looking for, or 1st term of a phrase */ DocList **ppResult /* Write the result here */ ){ DocList *pLeft, *pRight, *pNew; int i, rc; pLeft = docListNew(DL_POSITIONS); rc = term_select_all(v, pQTerm->pTerm, pQTerm->nTerm, pLeft); if( rc ) return rc; for(i=1; i<=pQTerm->nPhrase; i++){ pRight = docListNew(DL_POSITIONS); rc = term_select_all(v, pQTerm[i].pTerm, pQTerm[i].nTerm, pRight); if( rc ){ docListDelete(pLeft); return rc; } pNew = docListNew(i<pQTerm->nPhrase ? DL_POSITIONS : DL_DOCIDS); docListPhraseMerge(pLeft, pRight, pNew); docListDelete(pLeft); docListDelete(pRight); pLeft = pNew; } *ppResult = pLeft; return SQLITE_OK; } /* Parse a query string into a Query structure. * * We could, in theory, allow query strings to be complicated * nested expressions with precedence determined by parentheses. * But none of the major search engines do this. (Perhaps the * feeling is that an parenthesized expression is two complex of * an idea for the average user to grasp.) Taking our lead from * the major search engines, we will allow queries to be a list |
︙ | ︙ | |||
1373 1374 1375 1376 1377 1378 1379 1380 1381 | * * [one OR -two] ==> one OR two * */ typedef struct Query { int nTerms; /* Number of terms in the query */ QueryTerm *pTerms; /* Array of terms. Space obtained from malloc() */ } Query; | > | < | < > > | | | | | > | > | > | < | | < < < | | | | | < > > > | 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 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 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 | * * [one OR -two] ==> one OR two * */ typedef struct Query { int nTerms; /* Number of terms in the query */ QueryTerm *pTerms; /* Array of terms. Space obtained from malloc() */ int nextIsOr; /* Set the isOr flag on the next inserted term */ } Query; /* Add a new term pTerm[0..nTerm-1] to the query *q. */ static void queryAdd(Query *q, const char *pTerm, int nTerm){ QueryTerm *t; ++q->nTerms; q->pTerms = realloc(q->pTerms, q->nTerms * sizeof(q->pTerms[0])); if( q->pTerms==0 ){ q->nTerms = 0; return; } t = &q->pTerms[q->nTerms - 1]; memset(t, 0, sizeof(*t)); t->pTerm = malloc(nTerm+1); memcpy(t->pTerm, pTerm, nTerm); t->pTerm[nTerm] = 0; t->nTerm = nTerm; t->isOr = q->nextIsOr; q->nextIsOr = 0; } /* Free all of the memory that was malloced in order to build *q. */ static void queryDestroy(Query *q){ int i; for(i = 0; i < q->nTerms; ++i){ free(q->pTerms[i].pTerm); } free(q->pTerms); } /* ** Parse the text at pSegment[0..nSegment-1]. Add additional terms ** to the query being assemblied in pQuery. ** ** inPhrase is true if pSegment[0..nSegement-1] is contained within ** double-quotes. If inPhrase is true, then the first term ** is marked with the number of terms in the phrase less one and ** OR and "-" syntax is ignored. If inPhrase is false, then every ** term found is marked with nPhrase=0 and OR and "-" syntax is significant. */ static int tokenizeSegment( sqlite3_tokenizer *pTokenizer, /* The tokenizer to use */ const char *pSegment, int nSegment, /* Query expression being parsed */ int inPhrase, /* True if within "..." */ Query *pQuery /* Append results here */ ){ const sqlite3_tokenizer_module *pModule = pTokenizer->pModule; sqlite3_tokenizer_cursor *pCursor; int firstIndex = pQuery->nTerms; int rc = pModule->xOpen(pTokenizer, pSegment, nSegment, &pCursor); if( rc!=SQLITE_OK ) return rc; pCursor->pTokenizer = pTokenizer; while( 1 ){ const char *pToken; int nToken, iBegin, iEnd, iPos; rc = pModule->xNext(pCursor, &pToken, &nToken, &iBegin, &iEnd, &iPos); if( rc!=SQLITE_OK ) break; if( !inPhrase && pQuery->nTerms>0 && nToken==2 && pSegment[iBegin]=='O' && pSegment[iBegin+1]=='R' ){ pQuery->nextIsOr = 1; continue; } queryAdd(pQuery, pToken, nToken); if( !inPhrase && iBegin>0 && pSegment[iBegin-1]=='-' ){ pQuery->pTerms[pQuery->nTerms-1].isNot = 1; } } if( inPhrase && pQuery->nTerms>firstIndex ){ pQuery->pTerms[firstIndex].nPhrase = pQuery->nTerms - firstIndex - 1; } return pModule->xClose(pCursor); } /* Parse a query string, yielding a Query object [pQuery], which the caller * must free. */ static int parseQuery(fulltext_vtab *v, const char *pInput, int nInput, Query *pQuery){ int iInput, inPhrase = 0; if( nInput<0 ) nInput = strlen(pInput); pQuery->nTerms = 0; pQuery->pTerms = NULL; pQuery->nextIsOr = 0; for(iInput=0; iInput<nInput; ++iInput){ int i; for(i=iInput; i<nInput && pInput[i]!='"'; ++i) ; if( i>iInput ){ tokenizeSegment(v->pTokenizer, pInput+iInput, i-iInput, inPhrase, |
︙ | ︙ | |||
1494 1495 1496 1497 1498 1499 1500 | /* Perform a full-text query using the search expression in ** pInput[0..nInput-1]. Return a list of matching documents ** in pResult. */ static int fulltextQuery(fulltext_vtab *v, const char *pInput, int nInput, DocList **pResult){ Query q; | < | | > > | | < < | | > < < | > > > > > > > | | > | > | < < < < | > > < < < < < < < < | > | | < | | < | < < | > | > > > > > | | | 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 | /* Perform a full-text query using the search expression in ** pInput[0..nInput-1]. Return a list of matching documents ** in pResult. */ static int fulltextQuery(fulltext_vtab *v, const char *pInput, int nInput, DocList **pResult){ Query q; int i, rc; DocList *pLeft = NULL; DocList *pRight, *pNew; int nNot = 0; rc = parseQuery(v, pInput, nInput, &q); if( rc!=SQLITE_OK ) return rc; /* Merge AND terms. */ for(i = 0 ; i < q.nTerms; i += q.pTerms[i].nPhrase + 1){ if( q.pTerms[i].isNot ){ /* Handle all NOT terms in a separate pass */ nNot++; continue; } rc = docListOfTerm(v, &q.pTerms[i], &pRight); if( rc ){ queryDestroy(&q); return rc; } if( pLeft==0 ){ pLeft = pRight; }else{ pNew = docListNew(DL_DOCIDS); if( q.pTerms[i].isOr ){ docListOrMerge(pLeft, pRight, pNew); }else{ docListAndMerge(pLeft, pRight, pNew); } docListDelete(pRight); docListDelete(pLeft); pLeft = pNew; } } if( nNot && pLeft==0 ){ /* We do not yet know how to handle a query of only NOT terms */ return SQLITE_ERROR; } /* Do the EXCEPT terms */ for(i=0; i<q.nTerms; i += q.pTerms[i].nPhrase + 1){ if( !q.pTerms[i].isNot ) continue; rc = docListOfTerm(v, &q.pTerms[i], &pRight); if( rc ){ queryDestroy(&q); docListDelete(pLeft); return rc; } pNew = docListNew(DL_DOCIDS); docListExceptMerge(pLeft, pRight, pNew); docListDelete(pRight); docListDelete(pLeft); pLeft = pNew; } queryDestroy(&q); *pResult = pLeft; return rc; } static int fulltextFilter(sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv){ fulltext_cursor *c = (fulltext_cursor *) pCursor; |
︙ | ︙ |
Changes to src/test1.c.
︙ | ︙ | |||
9 10 11 12 13 14 15 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** Code for testing all sorts of SQLite interfaces. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** Code for testing all sorts of SQLite interfaces. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** ** $Id: test1.c,v 1.220 2006/09/10 03:34:06 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include "os.h" #include <stdlib.h> #include <string.h> |
︙ | ︙ | |||
3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 | #endif #ifdef SQLITE_OMIT_FOREIGN_KEY Tcl_SetVar2(interp, "sqlite_options", "foreignkey", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "foreignkey", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_GLOBALRECOVER Tcl_SetVar2(interp, "sqlite_options", "globalrecover", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "globalrecover", "1", TCL_GLOBAL_ONLY); #endif | > > > > > > | 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 | #endif #ifdef SQLITE_OMIT_FOREIGN_KEY Tcl_SetVar2(interp, "sqlite_options", "foreignkey", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "foreignkey", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_ENABLE_FTS1 Tcl_SetVar2(interp, "sqlite_options", "fts1", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "fts1", "0", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_GLOBALRECOVER Tcl_SetVar2(interp, "sqlite_options", "globalrecover", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "globalrecover", "1", TCL_GLOBAL_ONLY); #endif |
︙ | ︙ |
Added test/fts1a.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 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 155 156 157 158 159 | # 2006 September 9 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #************************************************************************* # This file implements regression tests for SQLite library. The # focus of this script is testing the FTS1 module. # # $Id: fts1a.test,v 1.1 2006/09/10 03:34:06 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # If SQLITE_ENABLE_FTS1 is defined, omit this file. ifcapable !fts1 { finish_test return } # Construct a full-text search table containing five keywords: # one, two, three, four, and five, in various combinations. The # rowid for each will be a bitmask for the elements it contains. # db eval { CREATE VIRTUAL TABLE t1 USING fts1; INSERT INTO t1(content) VALUES('one'); INSERT INTO t1(content) VALUES('two'); INSERT INTO t1(content) VALUES('one two'); INSERT INTO t1(content) VALUES('three'); INSERT INTO t1(content) VALUES('one three'); INSERT INTO t1(content) VALUES('two three'); INSERT INTO t1(content) VALUES('one two three'); INSERT INTO t1(content) VALUES('four'); INSERT INTO t1(content) VALUES('one four'); INSERT INTO t1(content) VALUES('two four'); INSERT INTO t1(content) VALUES('one two four'); INSERT INTO t1(content) VALUES('three four'); INSERT INTO t1(content) VALUES('one three four'); INSERT INTO t1(content) VALUES('two three four'); INSERT INTO t1(content) VALUES('one two three four'); INSERT INTO t1(content) VALUES('five'); INSERT INTO t1(content) VALUES('one five'); INSERT INTO t1(content) VALUES('two five'); INSERT INTO t1(content) VALUES('one two five'); INSERT INTO t1(content) VALUES('three five'); INSERT INTO t1(content) VALUES('one three five'); INSERT INTO t1(content) VALUES('two three five'); INSERT INTO t1(content) VALUES('one two three five'); INSERT INTO t1(content) VALUES('four five'); INSERT INTO t1(content) VALUES('one four five'); INSERT INTO t1(content) VALUES('two four five'); INSERT INTO t1(content) VALUES('one two four five'); INSERT INTO t1(content) VALUES('three four five'); INSERT INTO t1(content) VALUES('one three four five'); INSERT INTO t1(content) VALUES('two three four five'); INSERT INTO t1(content) VALUES('one two three four five'); } do_test fts1a-1.1 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'} } {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31} do_test fts1a-1.2 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two'} } {3 7 11 15 19 23 27 31} do_test fts1a-1.3 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'two one'} } {3 7 11 15 19 23 27 31} do_test fts1a-1.4 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two three'} } {7 15 23 31} do_test fts1a-1.5 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'one three two'} } {7 15 23 31} do_test fts1a-1.6 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'two three one'} } {7 15 23 31} do_test fts1a-1.7 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'two one three'} } {7 15 23 31} do_test fts1a-1.8 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'three one two'} } {7 15 23 31} do_test fts1a-1.9 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'three two one'} } {7 15 23 31} do_test fts1a-1.10 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'one two THREE'} } {7 15 23 31} do_test fts1a-1.11 { execsql {SELECT rowid FROM t1 WHERE content MATCH ' ONE Two three '} } {7 15 23 31} do_test fts1a-2.1 { execsql {SELECT rowid FROM t1 WHERE content MATCH '"one"'} } {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31} do_test fts1a-2.2 { execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two"'} } {3 7 11 15 19 23 27 31} do_test fts1a-2.3 { execsql {SELECT rowid FROM t1 WHERE content MATCH '"two one"'} } {} do_test fts1a-2.4 { execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two three"'} } {7 15 23 31} do_test fts1a-2.5 { execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three two"'} } {} do_test fts1a-2.6 { execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two three four"'} } {15 31} do_test fts1a-2.7 { execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three two four"'} } {} do_test fts1a-2.8 { execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three five"'} } {21} do_test fts1a-2.9 { execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three" five'} } {21 29} do_test fts1a-2.10 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'five "one three"'} } {21 29} do_test fts1a-2.11 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'five "one three" four'} } {29} do_test fts1a-2.12 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'five four "one three"'} } {29} do_test fts1a-2.13 { execsql {SELECT rowid FROM t1 WHERE content MATCH '"one three" four five'} } {29} do_test fts1a-3.1 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'one'} } {1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31} do_test fts1a-3.2 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'one -two'} } {1 5 9 13 17 21 25 29} do_test fts1a-3.3 { execsql {SELECT rowid FROM t1 WHERE content MATCH '-two one'} } {1 5 9 13 17 21 25 29} do_test fts1a-4.1 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'one OR two'} } {1 2 3 5 6 7 9 10 11 13 14 15 17 18 19 21 22 23 25 26 27 29 30 31} do_test fts1a-4.2 { execsql {SELECT rowid FROM t1 WHERE content MATCH '"one two" OR three'} } {3 4 5 6 7 11 12 13 14 15 19 20 21 22 23 27 28 29 30 31} do_test fts1a-4.3 { execsql {SELECT rowid FROM t1 WHERE content MATCH 'three OR "one two"'} } {3 4 5 6 7 11 12 13 14 15 19 20 21 22 23 27 28 29 30 31} finish_test |