]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add some simple test cases for the OR and NOT logic of the fts1 module.
authordrh <drh@noemail.net>
Sun, 10 Sep 2006 03:34:06 +0000 (03:34 +0000)
committerdrh <drh@noemail.net>
Sun, 10 Sep 2006 03:34:06 +0000 (03:34 +0000)
Fix lots of bugs discovered while developing these test cases. (CVS 3400)

FossilOrigin-Name: 70bcff024b44d1b40afac6eba959fa89fb993147

ext/fts1/fts1.c
manifest
manifest.uuid
src/test1.c
test/fts1a.test [new file with mode: 0644]

index b0b99769e231d21fbd1b15481930f7b21c088761..0c973a6dd1191b6f7fcd473555a06dedd115f4ee 100644 (file)
@@ -283,7 +283,7 @@ static sqlite_int64 peekDocid(DocListReader *pReader){
   return ret;
 }
 
-/* Read the next docid. 
+/* Read the next docid.   See also nextValidDocid().
 */
 static sqlite_int64 readDocid(DocListReader *pReader){
   sqlite_int64 ret;
@@ -302,8 +302,10 @@ static int readPosition(DocListReader *pReader){
   int i;
   int iType = pReader->pDoclist->iType;
 
+  if( pReader->iLastPos==-1 ){
+    return -1;
+  }
   assert( !atEnd(pReader) );
-  assert( pReader->iLastPos!=-1 );
 
   if( iType<DL_POSITIONS ){
     return -1;
@@ -473,25 +475,39 @@ static void docListAccumulate(DocList *pAcc, DocList *pUpdate){
   }
 }
 
+/*
+** 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 iOffset less than the position of pRight, then this
+** 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 iOffset less than positions from pRight, then add a new
-** document record to pOut and include the 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.
+** 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 */
-  int iOffset,             /* Copy if pLeft.pos+iOffset==pRight.iPos */
   DocList *pOut            /* Write the merged document record here */
 ){
   int iLeftPos = readPosition(pLeft);
@@ -500,17 +516,17 @@ static void mergePosList(
 
   /* Loop until we've reached the end of both position lists. */
   while( iLeftPos!=-1 && iRightPos!=-1 ){
-    if( iLeftPos+iOffset==iRightPos ){
+    if( iLeftPos+1==iRightPos ){
       if( !match ){
         docListAddDocid(pOut, iDocid);
         match = 1;
       }
       if( pOut->iType>=DL_POSITIONS ){
-        docListAddPos(pOut, iLeftPos);
+        docListAddPos(pOut, iRightPos);
       }
       iLeftPos = readPosition(pLeft);
       iRightPos = readPosition(pRight);
-    }else if( iRightPos<iLeftPos+iOffset ){
+    }else if( iRightPos<iLeftPos+1 ){
       iRightPos = readPosition(pRight);
     }else{
       iLeftPos = readPosition(pLeft);
@@ -520,28 +536,18 @@ static void mergePosList(
   if( iRightPos>=0 ) skipPositionList(pRight);
 }
 
-/*
-** 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;
-  while( !atEnd(pIn) && (docid = readDocid(pIn))==0 ){
-    skipPositionList(pIn);
-  }
-  return docid;
-}
-
 /* We have two doclists:  pLeft and pRight.
-** Write the intersection of these two doclists into pOut.
+** 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.
 **
-** nLeftPhrase is the number of words of a phrase that have
-** contributed to pLeft.
+** The output pOut may or may not contain positions.  If pOut
+** does contain positions, they are the positions of pRight.
 */
-static void mergeBlockAnd(
+static void docListPhraseMerge(
   DocList *pLeft,    /* Doclist resulting from the words on the left */
   DocList *pRight,   /* Doclist for the next word to the right */
-  int nLeftPhrase,   /* Number of matching phrase terms in pLeft */
   DocList *pOut      /* Write the combined doclist here */
 ){
   DocListReader left, right;
@@ -554,20 +560,11 @@ static void mergeBlockAnd(
 
   while( docidLeft>0 && docidRight>0 ){
     if( docidLeft<docidRight ){
-      skipPositionList(&left);
       docidLeft = nextValidDocid(&left);
     }else if( docidRight<docidLeft ){
-      skipPositionList(&right);
       docidRight = nextValidDocid(&right);
     }else{
-      /* Found a match */
-      if( pLeft->iType>=DL_POSITIONS ){
-        mergePosList(&left, &right, docidLeft, nLeftPhrase, pOut);
-      }else{
-        docListAddDocid(pOut, docidLeft);
-        skipPositionList(&left);
-        skipPositionList(&right);
-      }
+      mergePosList(&left, &right, docidLeft, pOut);
       docidLeft = nextValidDocid(&left);
       docidRight = nextValidDocid(&right);
     }
@@ -575,35 +572,34 @@ static void mergeBlockAnd(
 }
 
 /* We have two doclists:  pLeft and pRight.
-** Write into pOut all documents that occur in pLeft but not
-** in 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 mergeBlockExcept(
+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, priorLeft;
+  sqlite_int64 docidLeft, docidRight;
+
+  assert( pOut->iType<DL_POSITIONS );
 
   readerInit(&left, pLeft);
   readerInit(&right, pRight);
   docidLeft = nextValidDocid(&left);
   docidRight = nextValidDocid(&right);
 
-  while( docidLeft>0 ){
-    priorLeft = docidLeft;
-    if( docidRight==0 || docidLeft<docidRight ){
+  while( docidLeft>0 && docidRight>0 ){
+    if( docidLeft<docidRight ){
+      docidLeft = nextValidDocid(&left);
+    }else if( docidRight<docidLeft ){
+      docidRight = nextValidDocid(&right);
+    }else{
       docListAddDocid(pOut, docidLeft);
-    }
-    if( docidRight==0 || docidLeft<=docidRight ){
-      skipPositionList(&left);
       docidLeft = nextValidDocid(&left);
-    }
-    if( docidRight>0 && docidRight<=priorLeft ){
-      skipPositionList(&right);
       docidRight = nextValidDocid(&right);
     }
   }
@@ -611,10 +607,11 @@ static void mergeBlockExcept(
 
 /* 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 mergeBlockOr(
+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 */
@@ -635,26 +632,61 @@ static void mergeBlockOr(
     }
     priorLeft = docidLeft;
     if( docidLeft<=docidRight ){
-      skipPositionList(&left);
       docidLeft = nextValidDocid(&left);
     }
     if( docidRight>0 && docidRight<=priorLeft ){
-      skipPositionList(&right);
       docidRight = nextValidDocid(&right);
     }
   }
   while( docidLeft>0 ){
     docListAddDocid(pOut, docidLeft);
-    skipPositionList(&left);
     docidLeft = nextValidDocid(&left);
   }
   while( docidRight>0 ){
     docListAddDocid(pOut, docidRight);
-    skipPositionList(&right);
     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.) */
@@ -1263,11 +1295,11 @@ static int fulltextNext(sqlite3_vtab_cursor *pCursor){
       rc = sqlite3_reset(c->pStmt);
       if( rc!=SQLITE_OK ) return rc;
 
-      if( atEnd(&c->result)){
+      iDocid = nextValidDocid(&c->result);
+      if( iDocid==0 ){
         c->eof = 1;
         return SQLITE_OK;
       }
-      iDocid = readDocid(&c->result);
       rc = sqlite3_bind_int64(c->pStmt, 1, iDocid);
       if( rc!=SQLITE_OK ) return rc;
       /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */
@@ -1284,69 +1316,55 @@ static int fulltextNext(sqlite3_vtab_cursor *pCursor){
   }
 }
 
-/*
-** Different kinds of allowed document merge operations.
-*/
-#define MERGE_AND    1     /* Intersection of left and right */
-#define MERGE_OR     2     /* Union of left and right */
-#define MERGE_EXCEPT 3     /* Documents in left but not in right */
-
-/* Read the posting list for [pTerm]; AND it with the doclist [pIn] to
- * produce the doclist [out], using the given phrase position [iPhrasePos].
- * (*pSelect) is used to hold an SQLite statement used inside this function;
- * the caller should initialize *pSelect to NULL before the first call.
- */
-static int mergeQuery(
-  fulltext_vtab *v,              /* The full text index virtual table */
-  const char *pTerm, int nTerm,  /* Term we are searching for */
-  DocList *pIn,                  /* Prior search results. NULL for first term */
-  int iPhrasePos,                /* Offset to first term of phrase search */
-  int eOp,                       /* MERGE_AND, MERGE_OR, or MERGE_EXCEPT */
-  DocList *out                   /* Write results here */
-){
-  int rc;
-  DocList doclist;
-
-  /* If [pIn] is already empty, there's no point in reading the
-   * posting list to AND it in; return immediately. */
-  if( pIn!=NULL && eOp==MERGE_AND && !pIn->nData ) return SQLITE_OK;
-
-  rc = term_select_all(v, pTerm, nTerm, &doclist);
-  if( rc!=SQLITE_OK ) return rc;
-
-  /* If there is no input and the output wants to contain position
-  ** information, then make the result the doclist for pTerm.
-  */
-  if( pIn==0 && out->iType>=DL_POSITIONS ){
-    docListDestroy(out);
-    *out = doclist;
-    return SQLITE_OK;
-  }
-
-  if( eOp==MERGE_AND && pIn!=0 ){
-    mergeBlockAnd(pIn, &doclist, iPhrasePos, out);
-  }else if( eOp==MERGE_OR || pIn==0 ){
-    mergeBlockOr(pIn, &doclist, out);
-  }else if( eOp==MERGE_EXCEPT ){
-    mergeBlockExcept(pIn, &doclist, out);
-  }
-  docListDestroy(&doclist);
-
-  return SQLITE_OK;
-}
-
 /* A single term in a query is represented by an instances of
 ** the following structure.
 */
 typedef struct QueryTerm {
-  int firstInPhrase; /* true if this term begins a new phrase */
+  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;
 
-/* A parsed query.
+
+/* 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.
@@ -1377,12 +1395,12 @@ typedef struct QueryTerm {
 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.  The new term is
-** the beginning of a phrase is firstInPhrase is true.
+/* Add a new term pTerm[0..nTerm-1] to the query *q.
 */
-static void queryAdd(Query *q, int firstInPhrase, const char *pTerm, int nTerm){
+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]));
@@ -1392,11 +1410,12 @@ static void queryAdd(Query *q, int firstInPhrase, const char *pTerm, int nTerm){
   }
   t = &q->pTerms[q->nTerms - 1];
   memset(t, 0, sizeof(*t));
-  t->firstInPhrase = firstInPhrase;
   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.
@@ -1414,18 +1433,20 @@ static void queryDestroy(Query *q){
 ** 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 only the first term
-** is marked with firstInPhrase and OR and "-" syntax is ignored.
-** If inPhrase is false, then every term found is marked with
-** firstInPhrase and OR and "-" syntax is significant.
+** 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,
-                            const char *pSegment, int nSegment, int inPhrase,
-                            Query *pQuery){
+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 is_first = 1;
-  int isOr = 0;
+  int firstIndex = pQuery->nTerms;
   
   int rc = pModule->xOpen(pTokenizer, pSegment, nSegment, &pCursor);
   if( rc!=SQLITE_OK ) return rc;
@@ -1441,19 +1462,17 @@ static int tokenizeSegment(sqlite3_tokenizer *pTokenizer,
     if( rc!=SQLITE_OK ) break;
     if( !inPhrase && pQuery->nTerms>0 && nToken==2
          && pSegment[iBegin]=='O' && pSegment[iBegin+1]=='R' ){
-      isOr = 1;
+      pQuery->nextIsOr = 1;
       continue;
     }
-    queryAdd(pQuery, !inPhrase || is_first, pToken, nToken);
-    if( !inPhrase ){
-      if( isOr ){
-        pQuery->pTerms[pQuery->nTerms-1].isOr = 1;
-      }else if( iBegin>0 && pSegment[iBegin-1]=='-' ){
-        pQuery->pTerms[pQuery->nTerms-1].isNot = 1;
-      }
+    queryAdd(pQuery, pToken, nToken);
+    if( !inPhrase && iBegin>0 && pSegment[iBegin-1]=='-' ){
+      pQuery->pTerms[pQuery->nTerms-1].isNot = 1;
     }
-    is_first = 0;
-    isOr = 0;
+  }
+
+  if( inPhrase && pQuery->nTerms>firstIndex ){
+    pQuery->pTerms[firstIndex].nPhrase = pQuery->nTerms - firstIndex - 1;
   }
 
   return pModule->xClose(pCursor);
@@ -1468,6 +1487,7 @@ static int parseQuery(fulltext_vtab *v, const char *pInput, int nInput,
   if( nInput<0 ) nInput = strlen(pInput);
   pQuery->nTerms = 0;
   pQuery->pTerms = NULL;
+  pQuery->nextIsOr = 0;
 
   for(iInput=0; iInput<nInput; ++iInput){
     int i;
@@ -1498,67 +1518,67 @@ static int parseQuery(fulltext_vtab *v, const char *pInput, int nInput,
 static int fulltextQuery(fulltext_vtab *v, const char *pInput, int nInput,
                           DocList **pResult){
   Query q;
-  int phrase_start = -1;
-  int i;
-  DocList *d = NULL;
+  int i, rc;
+  DocList *pLeft = NULL;
+  DocList *pRight, *pNew;
+  int nNot = 0;
 
-  int rc = parseQuery(v, pInput, nInput, &q);
+  rc = parseQuery(v, pInput, nInput, &q);
   if( rc!=SQLITE_OK ) return rc;
 
   /* Merge AND terms. */
-  for(i = 0 ; i < q.nTerms ; ++i){
-    int needPositions;
-    DocList *next;
+  for(i = 0 ; i < q.nTerms; i += q.pTerms[i].nPhrase + 1){
 
-    if( q.pTerms[i].isNot || q.pTerms[i].isOr ){
-      /* Handle all OR and NOT terms in a separate pass */
+    if( q.pTerms[i].isNot ){
+      /* Handle all NOT terms in a separate pass */
+      nNot++;
       continue;
     }
 
-    /* In each merge step, we need to generate positions whenever we're
-     * processing a phrase which hasn't ended yet. */
-    needPositions = i<q.nTerms-1 && !q.pTerms[i+1].firstInPhrase;
-    next = docListNew(needPositions ? DL_POSITIONS : DL_DOCIDS);
-    if( q.pTerms[i].firstInPhrase ){
-      phrase_start = i;
+    rc = docListOfTerm(v, &q.pTerms[i], &pRight);
+    if( rc ){
+      queryDestroy(&q);
+      return rc;
     }
-    rc = mergeQuery(v, q.pTerms[i].pTerm, q.pTerms[i].nTerm,
-                     d, i-phrase_start, MERGE_AND, next);
-    if( rc!=SQLITE_OK ) break;
-    if( d!=NULL ){
-      docListDelete(d);
+    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;
     }
-    d = next;
   }
 
-  /* Do the OR terms */
-  for(i=0; i<q.nTerms; i++){
-    DocList *next;
-    if( !q.pTerms[i].isOr ) continue;
-    next = docListNew(DL_DOCIDS);
-    rc = mergeQuery(v, q.pTerms[i].pTerm, q.pTerms[i].nTerm,
-                    d, 0, MERGE_OR, next);
-    if( d ){
-      docListDelete(d);
-    }
-    d = next;
+  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++){
-    DocList *next;
+  for(i=0; i<q.nTerms;  i += q.pTerms[i].nPhrase + 1){
     if( !q.pTerms[i].isNot ) continue;
-    next = docListNew(DL_DOCIDS);
-    rc = mergeQuery(v, q.pTerms[i].pTerm, q.pTerms[i].nTerm,
-                    d, 0, MERGE_EXCEPT, next);
-    if( d ){
-      docListDelete(d);
+    rc = docListOfTerm(v, &q.pTerms[i], &pRight);
+    if( rc ){
+      queryDestroy(&q);
+      docListDelete(pLeft);
+      return rc;
     }
-    d = next;
+    pNew = docListNew(DL_DOCIDS);
+    docListExceptMerge(pLeft, pRight, pNew);
+    docListDelete(pRight);
+    docListDelete(pLeft);
+    pLeft = pNew;
   }
 
   queryDestroy(&q);
-  *pResult = d;
+  *pResult = pLeft;
   return rc;
 }
 
index 95f4bbe4f681ee5e6ab3f4915a1792c533dd13a1..b8a242e0e5066e90b308104c96a58d2001e5ce2a 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\ssupport\sfor\sOR\sand\sNOT\sterms\sin\sfts1.\s(CVS\s3399)
-D 2006-09-09T23:11:51
+C Add\ssome\ssimple\stest\scases\sfor\sthe\sOR\sand\sNOT\slogic\sof\sthe\sfts1\smodule.\nFix\slots\sof\sbugs\sdiscovered\swhile\sdeveloping\sthese\stest\scases.\s(CVS\s3400)
+D 2006-09-10T03:34:06
 F Makefile.in cabd42d34340f49260bc2a7668c38eba8d4cfd99
 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -21,7 +21,7 @@ F ext/README.txt 913a7bd3f4837ab14d7e063304181787658b14e1
 F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
 F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
 F ext/fts1/ft_hash.h 1a35e654a235c2c662d3ca0dfc3138ad60b8b7d5
-F ext/fts1/fts1.c c212d1a32d7438359aed98553a0762b9b21cab02
+F ext/fts1/fts1.c 36a33f0dae9b7106737d550e42a8e3974614dc5a
 F ext/fts1/fts1.h fe8e8f38dd6d2d2645b9b0d6972e80985249575f
 F ext/fts1/fts1_hash.c 3196cee866edbebb1c0521e21672e6d599965114
 F ext/fts1/fts1_hash.h 957d378355ed29f672cd5add012ce8b088a5e089
@@ -91,7 +91,7 @@ F src/sqlite3ext.h 11a046b3519c4b9b7709e6d6a95c3a36366f684a
 F src/sqliteInt.h 259adce944cc3b28da1fa3df9beb9ba86017a45d
 F src/table.c d8817f43a6c6bf139487db161760b9e1e02da3f1
 F src/tclsqlite.c e029f739bed90071789fe81a226d53e97a80a4d8
-F src/test1.c c8c3b2fd9e71cda3e6598f84357734cfd4fb93e2
+F src/test1.c e8213bf8024a47b49ee8ad276ea07d8e3d4c3c26
 F src/test2.c ca74a1d8aeb7d9606e8f6b762c5daf85c1a3f92b
 F src/test3.c 85135c09560c48bdb0a23c9b890ab405486b8ec9
 F src/test4.c 8b784cd82de158a2317cb4ac4bc86f91ad315e25
@@ -190,6 +190,7 @@ F test/enc3.test 890508efff6677345e93bf2a8adb0489b30df030
 F test/expr.test c78843f730ccbe973d0c2ad1c99978f936893131
 F test/fkey1.test 153004438d51e6769fb1ce165f6313972d6263ce
 F test/format4.test bf3bed3b13c63abfb3cfec232597a319a31d0bcc
+F test/fts1a.test 78cc3c4630fbc3c64eeafc67464947054dc540d1
 F test/func.test 7f2c91a948a0a177635835dc9afa078413c54ae1
 F test/hook.test 7e7645fd9a033f79cce8fdff151e32715e7ec50a
 F test/in.test 369cb2aa1eab02296b4ec470732fe8c131260b1d
@@ -396,7 +397,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P b6b93a3325d3e728ca36255c0ff6e1f63e03b0ac
-R 17eff1219fcc00b41520f89f3f182cf9
+P ae50265791d1a7500aa3c405a78a9bca8ff0cc08
+R 102c1a21ff09d0f20e46f405978da02e
 U drh
-Z 1588e41141ad25d002ad462a5bfc63c4
+Z f515ac19d2cdc367f4c6838848ae4bb1
index 259a41dc7960f31db3b0c20fab46fe0e737c982e..d8f8de95b4f39cb76371b07766e1cb538077f428 100644 (file)
@@ -1 +1 @@
-ae50265791d1a7500aa3c405a78a9bca8ff0cc08
\ No newline at end of file
+70bcff024b44d1b40afac6eba959fa89fb993147
\ No newline at end of file
index 39de718e9f1d8637e2c0d2821e0dc128553b512a..bd77d51d1c09d34e56c0e4ac558d2932ff02f419 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test1.c,v 1.219 2006/09/04 15:53:53 drh Exp $
+** $Id: test1.c,v 1.220 2006/09/10 03:34:06 drh Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -3576,6 +3576,12 @@ static void set_options(Tcl_Interp *interp){
   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
diff --git a/test/fts1a.test b/test/fts1a.test
new file mode 100644 (file)
index 0000000..28da35d
--- /dev/null
@@ -0,0 +1,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