]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Improve performance of the fts5 AND operator.
authordan <dan@noemail.net>
Mon, 1 Jun 2015 19:17:06 +0000 (19:17 +0000)
committerdan <dan@noemail.net>
Mon, 1 Jun 2015 19:17:06 +0000 (19:17 +0000)
FossilOrigin-Name: b43e9a5b7a0483ccb102316a4dbc5e32b5bc69ec

ext/fts5/fts5_expr.c
ext/fts5/test/fts5_common.tcl
ext/fts5/test/fts5ea.test
ext/fts5/test/fts5fault4.test
manifest
manifest.uuid

index 9b3e04a74edec79680ed6d0002e34f271fc08436..ab0874bced4b079da52cf9251ce3264b21634afb 100644 (file)
@@ -44,18 +44,21 @@ struct Fts5Expr {
 ** eType:
 **   Expression node type. Always one of:
 **
-**       FTS5_AND                 (pLeft, pRight valid)
-**       FTS5_OR                  (pLeft, pRight valid)
-**       FTS5_NOT                 (pLeft, pRight valid)
+**       FTS5_AND                 (nChild, apChild valid)
+**       FTS5_OR                  (nChild, apChild valid)
+**       FTS5_NOT                 (nChild, apChild valid)
 **       FTS5_STRING              (pNear valid)
 */
 struct Fts5ExprNode {
   int eType;                      /* Node type */
-  Fts5ExprNode *pLeft;            /* Left hand child node */
-  Fts5ExprNode *pRight;           /* Right hand child node */
-  Fts5ExprNearset *pNear;         /* For FTS5_STRING - cluster of phrases */
   int bEof;                       /* True at EOF */
   i64 iRowid;                     /* Current rowid */
+  Fts5ExprNearset *pNear;         /* For FTS5_STRING - cluster of phrases */
+
+  /* Child nodes. For a NOT node, this array always contains 2 entries. For 
+  ** AND or OR nodes, it contains 2 or more entries.  */
+  int nChild;                     /* Number of child nodes */
+  Fts5ExprNode *apChild[0];       /* Array of child nodes */
 };
 
 /*
@@ -315,8 +318,10 @@ int sqlite3Fts5ExprPhraseExpr(
 */
 void sqlite3Fts5ParseNodeFree(Fts5ExprNode *p){
   if( p ){
-    sqlite3Fts5ParseNodeFree(p->pLeft);
-    sqlite3Fts5ParseNodeFree(p->pRight);
+    int i;
+    for(i=0; i<p->nChild; i++){
+      sqlite3Fts5ParseNodeFree(p->apChild[i]);
+    }
     sqlite3Fts5ParseNearsetFree(p->pNear);
     sqlite3_free(p);
   }
@@ -685,6 +690,7 @@ static int fts5ExprNearNextRowidMatch(
   return rc;
 }
 
+
 /*
 ** IN/OUT parameter (*pa) points to a position list n bytes in size. If
 ** the position list contains entries for column iCol, then (*pa) is set
@@ -898,6 +904,16 @@ static int fts5ExprNearInitAll(
 static int fts5ExprNodeNextMatch(Fts5Expr*, Fts5ExprNode*);
 
 
+/*
+** If pExpr is an ASC iterator, this function returns a value with the
+** same sign as:
+**
+**   (iLhs - iRhs)
+**
+** Otherwise, if this is a DESC iterator, the opposite is returned:
+**
+**   (iRhs - iLhs)
+*/
 static int fts5RowidCmp(
   Fts5Expr *pExpr,
   i64 iLhs,
@@ -913,6 +929,68 @@ static int fts5RowidCmp(
   }
 }
 
+static void fts5ExprSetEof(Fts5ExprNode *pNode){
+  if( pNode ){
+    int i;
+    pNode->bEof = 1;
+    for(i=0; i<pNode->nChild; i++){
+      fts5ExprSetEof(pNode->apChild[i]);
+    }
+  }
+}
+
+
+static int fts5ExprNodeNext(Fts5Expr*, Fts5ExprNode*, int, i64);
+
+/*
+** Argument pNode is an FTS5_AND node.
+*/
+static int fts5ExprAndNextRowid(
+  Fts5Expr *pExpr,                /* Expression pPhrase belongs to */
+  Fts5ExprNode *pAnd              /* FTS5_AND node to advance */
+){
+  int iChild;
+  i64 iLast = pAnd->iRowid;
+  int rc = SQLITE_OK;
+  int bMatch;
+
+  assert( pAnd->bEof==0 );
+  do {
+    bMatch = 1;
+    for(iChild=0; iChild<pAnd->nChild; iChild++){
+      Fts5ExprNode *pChild = pAnd->apChild[iChild];
+      if( 0 && pChild->eType==FTS5_STRING ){
+        /* TODO */
+      }else{
+        int cmp = fts5RowidCmp(pExpr, iLast, pChild->iRowid);
+        if( cmp>0 ){
+          /* Advance pChild until it points to iLast or laster */
+          rc = fts5ExprNodeNext(pExpr, pChild, 1, iLast);
+          if( rc!=SQLITE_OK ) return rc;
+        }
+      }
+
+      /* If the child node is now at EOF, so is the parent AND node. Otherwise,
+      ** the child node is guaranteed to have advanced at least as far as
+      ** rowid iLast. So if it is not at exactly iLast, pChild->iRowid is the
+      ** new lastest rowid seen so far.  */
+      assert( pChild->bEof || fts5RowidCmp(pExpr, iLast, pChild->iRowid)<=0 );
+      if( pChild->bEof ){
+        fts5ExprSetEof(pAnd);
+        bMatch = 1;
+        break;
+      }else if( iLast!=pChild->iRowid ){
+        bMatch = 0;
+        iLast = pChild->iRowid;
+      }
+    }
+  }while( bMatch==0 );
+
+  pAnd->iRowid = iLast;
+  return SQLITE_OK;
+}
+
+
 /*
 ** Compare the values currently indicated by the two nodes as follows:
 **
@@ -958,27 +1036,24 @@ static int fts5ExprNodeNext(
       };
 
       case FTS5_AND: {
-        Fts5ExprNode *pLeft = pNode->pLeft;
+        Fts5ExprNode *pLeft = pNode->apChild[0];
         rc = fts5ExprNodeNext(pExpr, pLeft, bFromValid, iFrom);
-        if( rc==SQLITE_OK && pLeft->bEof==0 ){
-          assert( !bFromValid || fts5RowidCmp(pExpr, pLeft->iRowid, iFrom)>=0 );
-          rc = fts5ExprNodeNext(pExpr, pNode->pRight, 1, pLeft->iRowid);
-        }
         break;
       }
 
       case FTS5_OR: {
-        Fts5ExprNode *p1 = pNode->pLeft;
-        Fts5ExprNode *p2 = pNode->pRight;
-        int cmp = fts5NodeCompare(pExpr, p1, p2);
-
-        if( cmp<=0 || (bFromValid && fts5RowidCmp(pExpr,p1->iRowid,iFrom)<0) ){
-          rc = fts5ExprNodeNext(pExpr, p1, bFromValid, iFrom);
-        }
-
-        if( cmp>=0 || (bFromValid && fts5RowidCmp(pExpr,p2->iRowid,iFrom)<0) ){
-          if( rc==SQLITE_OK ){
-            rc = fts5ExprNodeNext(pExpr, p2, bFromValid, iFrom);
+        int i;
+        int iLast = pNode->iRowid;
+
+        for(i=0; rc==SQLITE_OK && i<pNode->nChild; i++){
+          Fts5ExprNode *p1 = pNode->apChild[i];
+          assert( p1->bEof || fts5RowidCmp(pExpr, p1->iRowid, iLast)>=0 );
+          if( p1->bEof==0 ){
+            if( (p1->iRowid==iLast) 
+             || (bFromValid && fts5RowidCmp(pExpr, p1->iRowid, iFrom)<0)
+            ){
+              rc = fts5ExprNodeNext(pExpr, p1, bFromValid, iFrom);
+            }
           }
         }
 
@@ -986,7 +1061,8 @@ static int fts5ExprNodeNext(
       }
 
       default: assert( pNode->eType==FTS5_NOT ); {
-        rc = fts5ExprNodeNext(pExpr, pNode->pLeft, bFromValid, iFrom);
+        assert( pNode->nChild==2 );
+        rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom);
         break;
       }
     }
@@ -1020,8 +1096,10 @@ static void fts5ExprNodeZeroPoslist(Fts5ExprNode *pNode){
       pPhrase->poslist.n = 0;
     }
   }else{
-    fts5ExprNodeZeroPoslist(pNode->pLeft);
-    fts5ExprNodeZeroPoslist(pNode->pRight);
+    int i;
+    for(i=0; i<pNode->nChild; i++){
+      fts5ExprNodeZeroPoslist(pNode->apChild[i]);
+    }
   }
 }
 
@@ -1042,28 +1120,36 @@ static int fts5ExprNodeTest(
         break;
 
       case FTS5_AND: {
-        int bRes1 = fts5ExprNodeTest(pRc, pExpr, iRowid, pNode->pLeft);
-        int bRes2 = fts5ExprNodeTest(pRc, pExpr, iRowid, pNode->pRight);
-        assert( (bRes1==0 || bRes1==1) && (bRes2==0 || bRes2==1) );
-
-        bRes = (bRes1 && bRes2);
-        if( bRes1!=bRes2 ){
-          fts5ExprNodeZeroPoslist(bRes1 ? pNode->pLeft : pNode->pRight);
+        int i;
+        for(i=0; i<pNode->nChild; i++){
+          if( fts5ExprNodeTest(pRc, pExpr, iRowid, pNode->apChild[i])==0 ){
+            break;
+          }
         }
+        bRes = (i==pNode->nChild);
+        if( bRes==0 && i>0 ){
+          for(i=0; i<pNode->nChild; i++){
+            fts5ExprNodeZeroPoslist(pNode->apChild[i]);
+          }
+        }
+
         break;
       }
 
       case FTS5_OR: {
-        int bRes1 = fts5ExprNodeTest(pRc, pExpr, iRowid, pNode->pLeft);
-        int bRes2 = fts5ExprNodeTest(pRc, pExpr, iRowid, pNode->pRight);
-
-        bRes = (bRes1 || bRes2);
+        int i;
+        for(i=0; i<pNode->nChild; i++){
+          if( fts5ExprNodeTest(pRc, pExpr, iRowid, pNode->apChild[i]) ){
+            bRes = 1;
+          }
+        }
         break;
       }
 
       default:
         assert( pNode->eType==FTS5_NOT );
-        bRes = fts5ExprNodeTest(pRc, pExpr, iRowid, pNode->pLeft);
+        assert( pNode->nChild==2 );
+        bRes = fts5ExprNodeTest(pRc, pExpr, iRowid, pNode->apChild[0]);
         break;
     }
   }
@@ -1072,14 +1158,6 @@ static int fts5ExprNodeTest(
 }
 
 
-static void fts5ExprSetEof(Fts5ExprNode *pNode){
-  if( pNode ){
-    pNode->bEof = 1;
-    fts5ExprSetEof(pNode->pLeft);
-    fts5ExprSetEof(pNode->pRight);
-  }
-}
-
 /*
 ** If pNode currently points to a match, this function returns SQLITE_OK
 ** without modifying it. Otherwise, pNode is advanced until it does point
@@ -1102,42 +1180,28 @@ static int fts5ExprNodeNextMatch(
       }
 
       case FTS5_AND: {
-        Fts5ExprNode *p1 = pNode->pLeft;
-        Fts5ExprNode *p2 = pNode->pRight;
-
-        while( p1->bEof==0 && p2->bEof==0 && p2->iRowid!=p1->iRowid ){
-          Fts5ExprNode *pAdv;
-          i64 iFrom;
-          assert( pExpr->bDesc==0 || pExpr->bDesc==1 );
-          if( pExpr->bDesc==(p1->iRowid > p2->iRowid) ){
-            pAdv = p1;
-            iFrom = p2->iRowid;
-          }else{
-            pAdv = p2;
-            iFrom = p1->iRowid;
-          }
-          rc = fts5ExprNodeNext(pExpr, pAdv, 1, iFrom);
-          if( rc!=SQLITE_OK ) break;
-        }
-        if( p1->bEof || p2->bEof ){
-          fts5ExprSetEof(pNode);
-        }
-        pNode->iRowid = p1->iRowid;
+        rc = fts5ExprAndNextRowid(pExpr, pNode);
         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;
+        Fts5ExprNode *pNext = pNode->apChild[0];
+        int i;
+        for(i=1; i<pNode->nChild; i++){
+          Fts5ExprNode *pChild = pNode->apChild[i];
+          if( fts5NodeCompare(pExpr, pNext, pChild)>0 ){
+            pNext = pChild;
+          }
+        }
         pNode->iRowid = pNext->iRowid;
+        pNode->bEof = pNext->bEof;
         break;
       }
 
       default: assert( pNode->eType==FTS5_NOT ); {
-        Fts5ExprNode *p1 = pNode->pLeft;
-        Fts5ExprNode *p2 = pNode->pRight;
+        Fts5ExprNode *p1 = pNode->apChild[0];
+        Fts5ExprNode *p2 = pNode->apChild[1];
+        assert( pNode->nChild==2 );
 
         while( rc==SQLITE_OK && p1->bEof==0 ){
           int cmp = fts5NodeCompare(pExpr, p1, p2);
@@ -1184,10 +1248,12 @@ static int fts5ExprNodeFirst(Fts5Expr *pExpr, Fts5ExprNode *pNode){
     }
 
   }else{
-    rc = fts5ExprNodeFirst(pExpr, pNode->pLeft);
-    if( rc==SQLITE_OK ){
-      rc = fts5ExprNodeFirst(pExpr, pNode->pRight);
+    int i;
+    for(i=0; i<pNode->nChild && rc==SQLITE_OK; i++){
+      rc = fts5ExprNodeFirst(pExpr, pNode->apChild[i]);
     }
+
+    pNode->iRowid = pNode->apChild[0]->iRowid;
     if( rc==SQLITE_OK ){
       rc = fts5ExprNodeNextMatch(pExpr, pNode);
     }
@@ -1230,10 +1296,11 @@ int sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, int bDesc){
 */
 int sqlite3Fts5ExprNext(Fts5Expr *p){
   int rc;
+  Fts5ExprNode *pRoot = p->pRoot;
   do {
-    rc = fts5ExprNodeNext(p, p->pRoot, 0, 0);
-  }while( p->pRoot->bEof==0 
-      && fts5ExprNodeTest(&rc, p, p->pRoot->iRowid, p->pRoot)==0 
+    rc = fts5ExprNodeNext(p, pRoot, 0, 0);
+  }while( pRoot->bEof==0 
+      && fts5ExprNodeTest(&rc, p, pRoot->iRowid, p->pRoot)==0 
       && rc==SQLITE_OK 
   );
   return rc;
@@ -1578,6 +1645,17 @@ void sqlite3Fts5ParseSetColset(
   }
 }
 
+static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
+  if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){
+    int nByte = sizeof(Fts5ExprNode*) * pSub->nChild;
+    memcpy(&p->apChild[p->nChild], pSub->apChild, nByte);
+    p->nChild += pSub->nChild;
+    sqlite3_free(pSub);
+  }else{
+    p->apChild[p->nChild++] = pSub;
+  }
+}
+
 /*
 ** Allocate and return a new expression object. If anything goes wrong (i.e.
 ** OOM error), leave an error code in pParse and return NULL.
@@ -1592,26 +1670,38 @@ Fts5ExprNode *sqlite3Fts5ParseNode(
   Fts5ExprNode *pRet = 0;
 
   if( pParse->rc==SQLITE_OK ){
+    int nChild = 0;               /* Number of children of returned node */
+    int nByte;                    /* Bytes of space to allocate for this node */
     assert( (eType!=FTS5_STRING && !pNear)
-        || (eType==FTS5_STRING && !pLeft && !pRight)
+         || (eType==FTS5_STRING && !pLeft && !pRight)
     );
     if( eType==FTS5_STRING && pNear==0 ) return 0;
     if( eType!=FTS5_STRING && pLeft==0 ) return pRight;
     if( eType!=FTS5_STRING && pRight==0 ) return pLeft;
-    pRet = (Fts5ExprNode*)sqlite3_malloc(sizeof(Fts5ExprNode));
-    if( pRet==0 ){
-      pParse->rc = SQLITE_NOMEM;
-    }else{
-      memset(pRet, 0, sizeof(*pRet));
+
+    if( eType==FTS5_NOT ){
+      nChild = 2;
+    }else if( eType==FTS5_AND || eType==FTS5_OR ){
+      nChild = 2;
+      if( pLeft->eType==eType ) nChild += pLeft->nChild-1;
+      if( pRight->eType==eType ) nChild += pRight->nChild-1;
+    }
+
+    nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*nChild;
+    pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
+
+    if( pRet ){
       pRet->eType = eType;
-      pRet->pLeft = pLeft;
-      pRet->pRight = pRight;
       pRet->pNear = pNear;
       if( eType==FTS5_STRING ){
         int iPhrase;
         for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){
           pNear->apPhrase[iPhrase]->pNode = pRet;
         }
+      }else{
+        fts5ExprAddChildren(pRet, pLeft);
+        fts5ExprAddChildren(pRet, pRight);
       }
     }
   }
@@ -1718,9 +1808,8 @@ static char *fts5ExprPrintTcl(
     if( zRet==0 ) return 0;
 
   }else{
-    char *zOp = 0;
-    char *z1 = 0;
-    char *z2 = 0;
+    char const *zOp = 0;
+    int i;
     switch( pExpr->eType ){
       case FTS5_AND: zOp = "AND"; break;
       case FTS5_NOT: zOp = "NOT"; break;
@@ -1730,13 +1819,16 @@ static char *fts5ExprPrintTcl(
         break;
     }
 
-    z1 = fts5ExprPrintTcl(pConfig, zNearsetCmd, pExpr->pLeft);
-    z2 = fts5ExprPrintTcl(pConfig, zNearsetCmd, pExpr->pRight);
-    if( z1 && z2 ){
-      zRet = sqlite3_mprintf("%s [%s] [%s]", zOp, z1, z2);
+    zRet = sqlite3_mprintf("%s", zOp);
+    for(i=0; zRet && i<pExpr->nChild; i++){
+      char *z = fts5ExprPrintTcl(pConfig, zNearsetCmd, pExpr->apChild[i]);
+      if( !z ){
+        sqlite3_free(zRet);
+        zRet = 0;
+      }else{
+        zRet = fts5PrintfAppend(zRet, " [%z]", z);
+      }
     }
-    sqlite3_free(z1);
-    sqlite3_free(z2);
   }
 
   return zRet;
@@ -1785,31 +1877,32 @@ static char *fts5ExprPrint(Fts5Config *pConfig, Fts5ExprNode *pExpr){
     }
 
   }else{
-    char *zOp = 0;
-    char *z1 = 0;
-    char *z2 = 0;
+    char const *zOp = 0;
+    int i;
+
     switch( pExpr->eType ){
-      case FTS5_AND: zOp = "AND"; break;
-      case FTS5_NOT: zOp = "NOT"; break;
+      case FTS5_AND: zOp = " AND "; break;
+      case FTS5_NOT: zOp = " NOT "; break;
       default:  
         assert( pExpr->eType==FTS5_OR );
-        zOp = "OR"; 
+        zOp = " OR "; 
         break;
     }
 
-    z1 = fts5ExprPrint(pConfig, pExpr->pLeft);
-    z2 = fts5ExprPrint(pConfig, pExpr->pRight);
-    if( z1 && z2 ){
-      int b1 = pExpr->pLeft->eType!=FTS5_STRING;
-      int b2 = pExpr->pRight->eType!=FTS5_STRING;
-      zRet = sqlite3_mprintf("%s%s%s %s %s%s%s", 
-          b1 ? "(" : "", z1, b1 ? ")" : "",
-          zOp, 
-          b2 ? "(" : "", z2, b2 ? ")" : ""
-      );
+    for(i=0; i<pExpr->nChild; i++){
+      char *z = fts5ExprPrint(pConfig, pExpr->apChild[i]);
+      if( z==0 ){
+        sqlite3_free(zRet);
+        zRet = 0;
+      }else{
+        int b = (pExpr->apChild[i]->eType!=FTS5_STRING);
+        zRet = fts5PrintfAppend(zRet, "%s%s%z%s", 
+            (i==0 ? "" : zOp),
+            (b?"(":""), z, (b?")":"")
+        );
+      }
+      if( zRet==0 ) break;
     }
-    sqlite3_free(z1);
-    sqlite3_free(z2);
   }
 
   return zRet;
index deffec5c4a95859855e33a22f442d4f28af4f76e..78e45f80727c18a08bbaf678b2d4a3aaaffc53d9 100644 (file)
@@ -271,12 +271,14 @@ proc instcompare {lhs rhs} {
 #-------------------------------------------------------------------------
 # Logical operators used by the commands returned by fts5_tcl_expr().
 #
-proc AND {a b} {
-  if {[llength $a]==0 || [llength $b]==0} { return [list] }
-  sort_poslist [concat $a $b]
+proc AND {args} {
+  foreach a $args {
+    if {[llength $a]==0} { return [list] }
+  }
+  sort_poslist [concat {*}$args]
 }
-proc OR {a b} {
-  sort_poslist [concat $a $b]
+proc OR {args} {
+  sort_poslist [concat {*}$args]
 }
 proc NOT {a b} {
   if {[llength $b]>0} { return [list] }
index 4b5b14a44108922d4a8bc9a9fc5e4d51f9b465ac..ad05412ba966b6afa8ad3ae2887a0adda077646c 100644 (file)
@@ -44,8 +44,8 @@ foreach {tn expr res} {
   9  {NEAR(one two)}                 {NEAR("one" "two", 10)}
   10 {NEAR("one three"* two, 5)}     {NEAR("one" + "three" * "two", 5)}
   11 {a OR b NOT c}                  {"a" OR ("b" NOT "c")}
-  12 "\x20one\x20two\x20three"       {("one" AND "two") AND "three"}
-  13 "\x09one\x0Atwo\x0Dthree"       {("one" AND "two") AND "three"}
+  12 "\x20one\x20two\x20three"       {"one" AND "two" AND "three"}
+  13 "\x09one\x0Atwo\x0Dthree"       {"one" AND "two" AND "three"}
   14 {"abc""def"}                    {"abc" + "def"}
 } {
   do_execsql_test 1.$tn {SELECT fts5_expr($expr)} [list $res]
index 417d470ed30aa32e8be5d91448b0cef04312ed75..6a37fcffc1e3f5e962f2af3adeabcf092deb9d80 100644 (file)
@@ -306,13 +306,13 @@ do_faultsim_test 9.1 -faults oom-* -body {
 do_faultsim_test 10.1 -faults oom-t* -body {
   db one { SELECT fts5_expr('a AND b NEAR(a b)') }
 } -test {
-  faultsim_test_result {0 {"a" AND ("b" AND NEAR("a" "b", 10))}} 
+  faultsim_test_result {0 {"a" AND "b" AND NEAR("a" "b", 10)}} 
 }
 
 do_faultsim_test 10.2 -faults oom-t* -body {
   db one { SELECT fts5_expr_tcl('x:"a b c" AND b NEAR(a b)', 'ns', 'x') }
 } -test {
-  set res {AND [ns -col 0 -- {a b c}] [AND [ns -- {b}] [ns -near 10 -- {a} {b}]]}
+  set res {AND [ns -col 0 -- {a b c}] [ns -- {b}] [ns -near 10 -- {a} {b}]}
   faultsim_test_result [list 0 $res]
 }
 
index 874c169da6ecd242e5dbde22cc9a749722959e90..cba1461913f0d39cf49f90358245dffe083c8523 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Change\sfts5\sexpression\sprocessing\sto\savoid\slinear\sscans\sof\slong\sdoclists\scaused\sby\sphrases\sthat\smatch\sspecific\scolumns\sonly.
-D 2015-06-01T09:15:20.958
+C Improve\sperformance\sof\sthe\sfts5\sAND\soperator.
+D 2015-06-01T19:17:06.810
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 2c28e557780395095c307a6e5cb539419027eb5e
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -111,7 +111,7 @@ F ext/fts5/fts5Int.h 4c677f3b797acde90ba1b7730eca6a32e7def742
 F ext/fts5/fts5_aux.c d53f00f31ad615ca4f139dd8751f9041afa00971
 F ext/fts5/fts5_buffer.c 9ec57c75c81e81dca118568876b1caead0aadadf
 F ext/fts5/fts5_config.c 11f969ed711a0a8b611d47431d74c372ad78c713
-F ext/fts5/fts5_expr.c e58c9dec148a92e9040abc613eb3c7506d741d4f
+F ext/fts5/fts5_expr.c e68f969e9276d312554195a158240f9705c374c1
 F ext/fts5/fts5_hash.c c1cfdb2cae0fad00b06fae38a40eaf9261563ccc
 F ext/fts5/fts5_index.c 7cea402924cd3d8cd5943a7f9514c9153696571b
 F ext/fts5/fts5_storage.c 04e6717656b78eb230a1c730cac3b935eb94889b
@@ -122,7 +122,7 @@ F ext/fts5/fts5_varint.c 366452037bf9a000c351374b489badc1b3541796
 F ext/fts5/fts5_vocab.c 1f8543b2c1ae4427f127a911bc8e60873fcd7bf9
 F ext/fts5/fts5parse.y 4ee667932d561a150d96483cf563281b95a9e523
 F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
-F ext/fts5/test/fts5_common.tcl 339115b24a57244e792db465c5bad482e0e7db72
+F ext/fts5/test/fts5_common.tcl 0b465b1127adcd1c8131f3454ab4264a6964674c
 F ext/fts5/test/fts5aa.test 5f73afe6a1394fdba9bc18302876ded81021bee6
 F ext/fts5/test/fts5ab.test 6fe3a56731d15978afbb74ae51b355fc9310f2ad
 F ext/fts5/test/fts5ac.test 999fd5f44579f1eb565ed7cf3861c427537ff097
@@ -146,12 +146,12 @@ F ext/fts5/test/fts5corrupt2.test c231f532162de381fa83ec477b51cd8633fd9da7
 F ext/fts5/test/fts5corrupt3.test da4e2adb2308d8587c2eff31b5aa47447b8a2edb
 F ext/fts5/test/fts5dlidx.test 070531bd45685e545e3e6021deb543f730a4011b
 F ext/fts5/test/fts5doclist.test 635b80ac785627841a59c583bac702b55d49fdc5
-F ext/fts5/test/fts5ea.test 6159e66c4fe9466c37cd6a0ed4197354588b4bcb
+F ext/fts5/test/fts5ea.test 451bb37310ee6df8ef72e4354fda5621b3b51448
 F ext/fts5/test/fts5eb.test 728a1f23f263548f5c29b29dfb851b5f2dbe723e
 F ext/fts5/test/fts5fault1.test b42d3296be8a75f557cf2cbce0d8b483fc9db45b
 F ext/fts5/test/fts5fault2.test 28c36c843bb39ae855ba79827417ecc37f114341
 F ext/fts5/test/fts5fault3.test d6e9577d4312e331a913c72931bf131704efc8f3
-F ext/fts5/test/fts5fault4.test b854f9895cb07cec58204d5a7ca82d03ce824e73
+F ext/fts5/test/fts5fault4.test 25306f396d239fd2ef35b2cc273a7f40fab80173
 F ext/fts5/test/fts5fault5.test 54da9fd4c3434a1d4f6abdcb6469299d91cf5875
 F ext/fts5/test/fts5fault6.test 234dc6355f8d3f8b5be2763f30699d770247c215
 F ext/fts5/test/fts5full.test 0924bdca5416a242103239ace79c6f5aa34bab8d
@@ -1333,7 +1333,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P e008c3c8e29c843ec945ddad54b9688bbf2bdb44
-R 99cd144645ddb0146b8edfaf69348c90
+P ec69e09a55b4daf1c40aeaaf9ee95091fe86f5c0
+R acaa89910e5f0d02d970b6387ace6327
 U dan
-Z 9df575eada62ea84610594e2c9d9937b
+Z 115cb3cf0377892edfa5b36bfcc4447b
index 64f51bd23fda946ab76add5fbb50abff0c41dd2b..d1c605efb93493448d9bb3e33a629d94c302c719 100644 (file)
@@ -1 +1 @@
-ec69e09a55b4daf1c40aeaaf9ee95091fe86f5c0
\ No newline at end of file
+b43e9a5b7a0483ccb102316a4dbc5e32b5bc69ec
\ No newline at end of file