]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix issues with position lists and NEAR constraints.
authordan <dan@noemail.net>
Fri, 18 Jul 2014 19:59:00 +0000 (19:59 +0000)
committerdan <dan@noemail.net>
Fri, 18 Jul 2014 19:59:00 +0000 (19:59 +0000)
FossilOrigin-Name: 16352d3654d5672cd0251db51dbe19f779373feb

ext/fts5/fts5Int.h
ext/fts5/fts5_buffer.c
ext/fts5/fts5_expr.c
manifest
manifest.uuid
test/fts5ac.test
test/fts5ae.test [new file with mode: 0644]

index c7392b06e63cb8575be85ab497412d5d4c5c26ed..d9249b93b1acdc3bd0262e39cfe8903b21115bbc 100644 (file)
@@ -118,8 +118,7 @@ int sqlite3Fts5PoslistReaderNext(Fts5PoslistReader*);
 
 typedef struct Fts5PoslistWriter Fts5PoslistWriter;
 struct Fts5PoslistWriter {
-  int iCol;
-  int iOff;
+  i64 iPrev;
 };
 int sqlite3Fts5PoslistWriterAppend(Fts5Buffer*, Fts5PoslistWriter*, i64);
 
@@ -130,6 +129,12 @@ int sqlite3Fts5PoslistNext(
   int *piOff                      /* IN/OUT: Current token offset */
 );
 
+int sqlite3Fts5PoslistNext64(
+  const u8 *a, int n,             /* Buffer containing poslist */
+  int *pi,                        /* IN/OUT: Offset within a[] */
+  i64 *piOff                      /* IN/OUT: Current offset */
+);
+
 /*
 ** End of interface to code in fts5_buffer.c.
 **************************************************************************/
index 2afce9502068c8459931a28cc97cdf8672a105a0..b69af615b98e35bd9249037e81f03ef9411e8853 100644 (file)
@@ -138,27 +138,40 @@ void sqlite3Fts5BufferSet(
   sqlite3Fts5BufferAppendBlob(pRc, pBuf, nData, pData);
 }
 
+int sqlite3Fts5PoslistNext64(
+  const u8 *a, int n,             /* Buffer containing poslist */
+  int *pi,                        /* IN/OUT: Offset within a[] */
+  i64 *piOff                      /* IN/OUT: Current offset */
+){
+  int i = *pi;
+  if( i>=n ){
+    /* EOF */
+    return 1;  
+  }else{
+    i64 iOff = *piOff;
+    int iVal;
+    i += getVarint32(&a[i], iVal);
+    if( iVal==1 ){
+      i += getVarint32(&a[i], iVal);
+      iOff = ((i64)iVal) << 32;
+      i += getVarint32(&a[i], iVal);
+    }
+    *piOff = iOff + (iVal-2);
+    *pi = i;
+    return 0;
+  }
+}
+
 
 /*
 ** Advance the iterator object passed as the only argument. Return true
 ** if the iterator reaches EOF, or false otherwise.
 */
 int sqlite3Fts5PoslistReaderNext(Fts5PoslistReader *pIter){
-  if( pIter->i>=pIter->n ){
+  if( sqlite3Fts5PoslistNext64(pIter->a, pIter->n, &pIter->i, &pIter->iPos) 
+   || (pIter->iCol>=0 && (pIter->iPos >> 32) > pIter->iCol)
+  ){
     pIter->bEof = 1;
-  }else{
-    int iVal;
-    pIter->i += getVarint32(&pIter->a[pIter->i], iVal);
-    if( iVal==1 ){
-      pIter->i += getVarint32(&pIter->a[pIter->i], iVal);
-      if( pIter->iCol>=0 && iVal>pIter->iCol ){
-        pIter->bEof = 1;
-      }else{
-        pIter->iPos = ((u64)iVal << 32);
-        pIter->i += getVarint32(&pIter->a[pIter->i], iVal);
-      }
-    }
-    pIter->iPos += (iVal-2);
   }
   return pIter->bEof;
 }
@@ -183,18 +196,15 @@ int sqlite3Fts5PoslistWriterAppend(
   Fts5PoslistWriter *pWriter,
   i64 iPos
 ){
+  static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32;
   int rc = SQLITE_OK;
-  int iCol = (int)(iPos >> 32);
-  int iOff = (iPos & 0x7FFFFFFF);
-
-  if( iCol!=pWriter->iCol ){
+  if( (iPos & colmask) != (pWriter->iPrev & colmask) ){
     fts5BufferAppendVarint(&rc, pBuf, 1);
-    fts5BufferAppendVarint(&rc, pBuf, iCol);
-    pWriter->iCol = iCol;
-    pWriter->iOff = 0;
+    fts5BufferAppendVarint(&rc, pBuf, (iPos >> 32));
+    pWriter->iPrev = (iPos & colmask);
   }
-  fts5BufferAppendVarint(&rc, pBuf, (iOff - pWriter->iOff) + 2);
-
+  fts5BufferAppendVarint(&rc, pBuf, (iPos - pWriter->iPrev) + 2);
+  pWriter->iPrev = iPos;
   return rc;
 }
 
index 3ea885e75c43ea323ef6f41c5c002903aafaddfc..06faf7ebff5962ba984f4d21a959ae8511633146 100644 (file)
@@ -52,7 +52,7 @@ struct Fts5ExprNode {
   Fts5ExprNode *pRight;           /* Right hand child node */
   Fts5ExprNearset *pNear;         /* For FTS5_STRING - cluster of phrases */
   int bEof;                       /* True at EOF */
-  i64 iRowid;
+  i64 iRowid;                     /* Current rowid */
 };
 
 /*
@@ -70,6 +70,7 @@ struct Fts5ExprTerm {
 ** within a document for it to match.
 */
 struct Fts5ExprPhrase {
+  Fts5ExprNode *pNode;            /* FTS5_STRING node this phrase is part of */
   Fts5Buffer poslist;             /* Current position list */
   int nTerm;                      /* Number of entries in aTerm[] */
   Fts5ExprTerm aTerm[0];          /* Terms that make up this phrase */
@@ -266,7 +267,7 @@ static int fts5ExprPhraseIsMatch(
   Fts5ExprPhrase *pPhrase,        /* Phrase object to initialize */
   int *pbMatch                    /* OUT: Set to true if really a match */
 ){
-  Fts5PoslistWriter writer = {0, 0};
+  Fts5PoslistWriter writer = {0};
   Fts5PoslistReader aStatic[4];
   Fts5PoslistReader *aIter = aStatic;
   int i;
@@ -322,6 +323,46 @@ static int fts5ExprPhraseIsMatch(
   return rc;
 }
 
+typedef struct Fts5LookaheadReader Fts5LookaheadReader;
+struct Fts5LookaheadReader {
+  const u8 *a;                    /* Buffer containing position list */
+  int n;                          /* Size of buffer a[] in bytes */
+  int i;                          /* Current offset in position list */
+  i64 iPos;                       /* Current position */
+  i64 iLookahead;                 /* Next position */
+};
+
+#define FTS5_LOOKAHEAD_EOF (((i64)1) << 62)
+
+static int fts5LookaheadReaderNext(Fts5LookaheadReader *p){
+  p->iPos = p->iLookahead;
+  if( sqlite3Fts5PoslistNext64(p->a, p->n, &p->i, &p->iLookahead) ){
+    p->iLookahead = FTS5_LOOKAHEAD_EOF;
+  }
+  return (p->iPos==FTS5_LOOKAHEAD_EOF);
+}
+
+static int fts5LookaheadReaderInit(
+  const u8 *a, int n,             /* Buffer to read position list from */
+  Fts5LookaheadReader *p          /* Iterator object to initialize */
+){
+  memset(p, 0, sizeof(Fts5LookaheadReader));
+  p->a = a;
+  p->n = n;
+  fts5LookaheadReaderNext(p);
+  return fts5LookaheadReaderNext(p);
+}
+
+static int fts5LookaheadReaderEof(Fts5LookaheadReader *p){
+  return (p->iPos==FTS5_LOOKAHEAD_EOF);
+}
+
+typedef struct Fts5NearTrimmer Fts5NearTrimmer;
+struct Fts5NearTrimmer {
+  Fts5LookaheadReader reader;     /* Input iterator */
+  Fts5PoslistWriter writer;       /* Writer context */
+  Fts5Buffer *pOut;               /* Output poslist */
+};
 
 /*
 ** The near-set object passed as the first argument contains more than
@@ -340,8 +381,11 @@ static int fts5ExprPhraseIsMatch(
 ** a set of intances that collectively matches the NEAR constraint.
 */
 static int fts5ExprNearIsMatch(Fts5ExprNearset *pNear, int *pbMatch){
-  Fts5PoslistReader aStatic[4];
-  Fts5PoslistReader *aIter = aStatic;
+  Fts5NearTrimmer aStatic[4];
+  Fts5NearTrimmer *a = aStatic;
+
+  Fts5ExprPhrase **apPhrase = pNear->apPhrase;
+
   int i;
   int rc = SQLITE_OK;
   int bMatch;
@@ -352,36 +396,75 @@ static int fts5ExprNearIsMatch(Fts5ExprNearset *pNear, int *pbMatch){
   /* If the aStatic[] array is not large enough, allocate a large array
   ** using sqlite3_malloc(). This approach could be improved upon. */
   if( pNear->nPhrase>(sizeof(aStatic) / sizeof(aStatic[0])) ){
-    int nByte = sizeof(Fts5PoslistReader) * pNear->nPhrase;
-    aIter = (Fts5PoslistReader*)sqlite3_malloc(nByte);
-    if( !aIter ) return SQLITE_NOMEM;
+    int nByte = sizeof(Fts5LookaheadReader) * pNear->nPhrase;
+    a = (Fts5NearTrimmer*)sqlite3_malloc(nByte);
+    if( !a ) return SQLITE_NOMEM;
+    memset(a, 0, nByte);
+  }else{
+    memset(aStatic, 0, sizeof(aStatic));
   }
 
-  /* Initialize a term iterator for each phrase */
+  /* Initialize a lookahead iterator for each phrase. After passing the
+  ** buffer and buffer size to the lookaside-reader init function, zero
+  ** the phrase poslist buffer. The new poslist for the phrase (containing
+  ** the same entries as the original with some entries removed on account 
+  ** of the NEAR constraint) is written over the original even as it is
+  ** being read. This is safe as the entries for the new poslist are a
+  ** subset of the old, so it is not possible for data yet to be read to
+  ** be overwritten.  */
   for(i=0; i<pNear->nPhrase; i++){
-    Fts5Buffer *pPoslist = &pNear->apPhrase[i]->poslist; 
-    sqlite3Fts5PoslistReaderInit(-1, pPoslist->p, pPoslist->n, &aIter[i]);
+    Fts5Buffer *pPoslist = &apPhrase[i]->poslist;
+    fts5LookaheadReaderInit(pPoslist->p, pPoslist->n, &a[i].reader);
+    pPoslist->n = 0;
+    a[i].pOut = pPoslist;
   }
 
-  iMax = aIter[0].iPos;
-  do {
-    bMatch = 1;
-    for(i=0; i<pNear->nPhrase; i++){
-      Fts5PoslistReader *pPos = &aIter[i];
-      i64 iMin = iMax - pNear->apPhrase[i]->nTerm - pNear->nNear;
-      if( pPos->iPos<iMin || pPos->iPos>iMax ){
-        bMatch = 0;
-        while( pPos->iPos<iMin ){
-          if( sqlite3Fts5PoslistReaderNext(pPos) ) goto ismatch_out;
+  while( 1 ){
+    int iAdv;
+    i64 iMin;
+    i64 iMax;
+
+    /* This block advances the phrase iterators until they point to a set of
+    ** entries that together comprise a match.  */
+    iMax = a[0].reader.iPos;
+    do {
+      bMatch = 1;
+      for(i=0; i<pNear->nPhrase; i++){
+        Fts5LookaheadReader *pPos = &a[i].reader;
+        iMin = iMax - pNear->apPhrase[i]->nTerm - pNear->nNear;
+        if( pPos->iPos<iMin || pPos->iPos>iMax ){
+          bMatch = 0;
+          while( pPos->iPos<iMin ){
+            if( fts5LookaheadReaderNext(pPos) ) goto ismatch_out;
+          }
+          if( pPos->iPos>iMax ) iMax = pPos->iPos;
         }
-        if( pPos->iPos>iMax ) iMax = pPos->iPos;
+      }
+    }while( bMatch==0 );
+
+    /* Add an entry to each output position list */
+    for(i=0; i<pNear->nPhrase; i++){
+      i64 iPos = a[i].reader.iPos;
+      Fts5PoslistWriter *pWriter = &a[i].writer;
+      if( a[i].pOut->n==0 || iPos!=pWriter->iPrev ){
+        sqlite3Fts5PoslistWriterAppend(a[i].pOut, pWriter, iPos);
       }
     }
-  }while( bMatch==0 );
+
+    iAdv = 0;
+    iMin = a[0].reader.iLookahead;
+    for(i=0; i<pNear->nPhrase; i++){
+      if( a[i].reader.iLookahead < iMin ){
+        iMin = a[i].reader.iLookahead;
+        iAdv = i;
+      }
+    }
+    if( fts5LookaheadReaderNext(&a[iAdv].reader) ) goto ismatch_out;
+  }
 
  ismatch_out:
-  *pbMatch = bMatch;
-  if( aIter!=aStatic ) sqlite3_free(aIter);
+  *pbMatch = (a[0].pOut->n>0);
+  if( a!=aStatic ) sqlite3_free(a);
   return rc;
 }
 
@@ -519,7 +602,7 @@ static int fts5ExprNearNextMatch(
   while( 1 ){
     int i;
 
-    /* Advance the iterators until they are a match */
+    /* Advance the iterators until they all point to the same rowid */
     rc = fts5ExprNearNextRowidMatch(pExpr, pNode);
     if( pNode->bEof || rc!=SQLITE_OK ) break;
 
@@ -545,6 +628,8 @@ static int fts5ExprNearNextMatch(
       if( rc!=SQLITE_OK || bMatch ) break;
     }
 
+    /* If control flows to here, then the current rowid is not a match.
+    ** Advance all term iterators in all phrases to the next rowid. */
     rc = fts5ExprNearAdvanceAll(pExpr, pNear, &pNode->bEof);
     if( pNode->bEof || rc!=SQLITE_OK ) break;
   }
@@ -661,6 +746,14 @@ static int fts5ExprNodeNext(Fts5Expr *pExpr, Fts5ExprNode *pNode){
   return rc;
 }
 
+static void fts5ExprSetEof(Fts5ExprNode *pNode){
+  if( pNode ){
+    pNode->bEof = 1;
+    fts5ExprSetEof(pNode->pLeft);
+    fts5ExprSetEof(pNode->pRight);
+  }
+}
+
 /*
 **
 */
@@ -688,7 +781,9 @@ static int fts5ExprNodeNextMatch(Fts5Expr *pExpr, Fts5ExprNode *pNode){
           rc = fts5ExprNodeNext(pExpr, pAdv);
           if( rc!=SQLITE_OK ) break;
         }
-        pNode->bEof = p1->bEof || p2->bEof;
+        if( p1->bEof || p2->bEof ){
+          fts5ExprSetEof(pNode);
+        }
         pNode->iRowid = p1->iRowid;
         break;
       }
@@ -1090,6 +1185,12 @@ Fts5ExprNode *sqlite3Fts5ParseNode(
       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;
+        }
+      }
     }
   }
 
@@ -1398,7 +1499,8 @@ int sqlite3Fts5ExprPhraseSize(Fts5Expr *pExpr, int iPhrase){
 int sqlite3Fts5ExprPoslist(Fts5Expr *pExpr, int iPhrase, const u8 **pa){
   if( iPhrase>=0 && iPhrase<pExpr->nPhrase ){
     Fts5ExprPhrase *pPhrase = pExpr->apPhrase[iPhrase];
-    if( sqlite3Fts5IterRowid(pPhrase->aTerm[0].pIter)==pExpr->pRoot->iRowid ){
+    Fts5ExprNode *pNode = pPhrase->pNode;
+    if( pNode->bEof==0 && pNode->iRowid==pExpr->pRoot->iRowid ){
       *pa = pPhrase->poslist.p;
       return pPhrase->poslist.n;
     }
index 7d4537622b4215d8fe96a8be48100ff5989db9a3..c84bbf7a326e0f895a2c923748d6b5d827dacb88 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\sproblem\swith\sposition\slist\sprocessing\sfor\sOR\squeries.
-D 2014-07-17T15:14:07.541
+C Fix\sissues\swith\sposition\slists\sand\sNEAR\sconstraints.
+D 2014-07-18T19:59:00.547
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in b03432313a3aad96c706f8164fb9f5307eaf19f5
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -105,11 +105,11 @@ F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
 F ext/fts3/unicode/mkunicode.tcl dc6f268eb526710e2c6e496c372471d773d0c368
 F ext/fts5/fts5.c 20bcb1e10756c72b550947236960edf96929ca2f
 F ext/fts5/fts5.h cda3b9d73e6ffa6d0cd35b7da6b808bf3a1ada32
-F ext/fts5/fts5Int.h 2d4c1e1ebdf18278776fcd8a64233ff3c04ea51f
+F ext/fts5/fts5Int.h 6cf315d3999c14572012d676fa1baf4f4323587b
 F ext/fts5/fts5_aux.c 27b082732fd76277fd7e9277f52903723d97f99b
-F ext/fts5/fts5_buffer.c b7aa6cdf4a63642fcc12359cedc4be748ca400cc
+F ext/fts5/fts5_buffer.c 71cf2016b2881e7aea39f952995eafa510d96cbd
 F ext/fts5/fts5_config.c 94f1b4cb4de6a7cd5780c14adb0198e289df8cef
-F ext/fts5/fts5_expr.c 52a1b47cfd30feb09c522392b1ba246eda7023f4
+F ext/fts5/fts5_expr.c 288b3e016253eab69ea8cefbff346a4697b44291
 F ext/fts5/fts5_index.c 9ff3008e903aa9077b0a7a7aa76ab6080eb07a36
 F ext/fts5/fts5_storage.c 7848d8f8528d798bba159900ea310a6d4a279da8
 F ext/fts5/fts5parse.y 777da8e5819f75c217982c79c29d014c293acac9
@@ -597,8 +597,9 @@ F test/fts4noti.test 524807f0c36d49deea7920cdd4cd687408b58849
 F test/fts4unicode.test 01ec3fe2a7c3cfff3b4c0581b83caa11b33efa36
 F test/fts5aa.test c8d3b9694f6b2864161c7437408464a535d19343
 F test/fts5ab.test dc04ed48cf93ca957d174406e6c192f2ff4f3397
-F test/fts5ac.test 84599f8253abc7e10b929b8ee0b47c5edd4eafbd
+F test/fts5ac.test 9be418d037763f4cc5d86f4239db41fc86bb4f85
 F test/fts5ad.test 2ed38bbc865678cb2905247120d02ebba7f20e07
+F test/fts5ae.test 5d5ffba68e850d9ade99cdd3f5c6431c82dad81d
 F test/fts5ea.test ff43b40f8879ba50b82def70f2ab67c195d1a1d4
 F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
 F test/func.test ae97561957aba6ca9e3a7b8a13aac41830d701ef
@@ -1194,7 +1195,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 c1f9a4b76c0bbc1ef9f6cdb5d62aa5d536fdf38e
-R c3e4d3fb829636894b73dbf001062a3f
+P 5808f30fae0d844c52a785bf18872be371d4af68
+R c645036fa73431553c03d7990bbe09ec
 U dan
-Z 4c19ad3988f765a5de2b9174d276eba9
+Z 973aba4d2c5c8ae6a3e94f9739fdfe1b
index 0f87023763c60315782ed40199350536d4b2629a..e082d6160aae4eae95c0d4e8c941a1c49f8d8a66 100644 (file)
@@ -1 +1 @@
-5808f30fae0d844c52a785bf18872be371d4af68
\ No newline at end of file
+16352d3654d5672cd0251db51dbe19f779373feb
\ No newline at end of file
index 3257480134a03b43c3e3ae0e9bd54071b3101fac..ae6e56e7e7582afeb15a53072a9676ffbfc9855f 100644 (file)
@@ -309,21 +309,57 @@ foreach {tn expr} {
   2.3 "d+c OR u"
   2.4 "d+c OR u+d"
 
+  3.1 { a AND b AND c }
 } {
   set res [matchdata 1 $expr]
-  do_execsql_test 2.1.$tn.[llength $res] { 
+  do_execsql_test 2.$tn.[llength $res] { 
     SELECT rowid, fts5_test(xx, 'poslist') FROM xx WHERE xx match $expr
   } $res
 }
 
+#-------------------------------------------------------------------------
+# Queries on a specific column.
+#
+foreach {tn expr} {
+  1 "x:a"
+  2 "y:a"
+  3 "x:b"
+  4 "y:b"
+} {
+  set res [matchdata 1 $expr]
+  do_execsql_test 3.$tn.[llength $res] { 
+    SELECT rowid, fts5_test(xx, 'poslist') FROM xx WHERE xx match $expr
+  } $res
+}
+
+#-------------------------------------------------------------------------
+# Some NEAR queries.
+#
+foreach {tn expr} {
+  1 "NEAR(a b)"
+  2 "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) }
+} {
+  set res [matchdata 1 $expr]
+  do_execsql_test 4.1.$tn.[llength $res] { 
+    SELECT rowid, fts5_test(xx, 'poslist') FROM xx WHERE xx match $expr
+  } $res
+}
 
-do_test 2.1  { poslist {{a b c}} -- a } {0.0}
-do_test 2.2  { poslist {{a b c}} -- c } {0.2}
+do_test 4.1  { poslist {{a b c}} -- a } {0.0}
+do_test 4.2  { poslist {{a b c}} -- c } {0.2}
 
 foreach {tn expr tclexpr} {
   1 {a b} {[N $x -- {a}] && [N $x -- {b}]}
 } {
-  do_execsql_test 3.$tn {SELECT fts5_expr_tcl($expr, 'N $x')} [list $tclexpr]
+  do_execsql_test 5.$tn {SELECT fts5_expr_tcl($expr, 'N $x')} [list $tclexpr]
 }
 
 #-------------------------------------------------------------------------
@@ -356,7 +392,7 @@ foreach {bAsc sql} {
     19 { c NOT b OR a AND d }
   } {
     set res [matchdata 0 $expr $bAsc]
-    do_execsql_test 4.$bAsc.$tn.[llength $res] $sql $res
+    do_execsql_test 6.$bAsc.$tn.[llength $res] $sql $res
   }
 }
 
diff --git a/test/fts5ae.test b/test/fts5ae.test
new file mode 100644 (file)
index 0000000..c433d43
--- /dev/null
@@ -0,0 +1,136 @@
+# 2014 June 17
+#
+# 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 FTS5 module.
+#
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix fts5ae
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+  finish_test
+  return
+}
+
+do_execsql_test 1.0 {
+  CREATE VIRTUAL TABLE t1 USING fts5(a, b);
+  INSERT INTO t1(t1) VALUES('pgsz=32');
+}
+
+do_execsql_test 1.1 {
+  INSERT INTO t1 VALUES('hello', 'world');
+  SELECT rowid FROM t1 WHERE t1 MATCH 'hello' ORDER BY rowid ASC;
+} {1}
+
+do_execsql_test 1.2 {
+  INSERT INTO t1 VALUES('world', 'hello');
+  SELECT rowid FROM t1 WHERE t1 MATCH 'hello' ORDER BY rowid ASC;
+} {1 2}
+
+do_execsql_test 1.3 {
+  INSERT INTO t1 VALUES('world', 'world');
+  SELECT rowid FROM t1 WHERE t1 MATCH 'hello' ORDER BY rowid ASC;
+} {1 2}
+
+do_execsql_test 1.4.1 {
+  INSERT INTO t1 VALUES('hello', 'hello');
+}
+
+do_execsql_test 1.4.2 {
+  SELECT rowid FROM t1 WHERE t1 MATCH 'hello' ORDER BY rowid ASC;
+} {1 2 4}
+
+
+#-------------------------------------------------------------------------
+# 
+do_execsql_test 2.0 {
+  CREATE VIRTUAL TABLE t2 USING fts5(x, y);
+  INSERT INTO t2 VALUES('u t l w w m s', 'm f m o l t k o p e');
+  INSERT INTO t2 VALUES('f g q e l n d m z x q', 'z s i i i m f w w f n g p');
+}
+
+do_execsql_test 2.1 {
+  SELECT rowid, fts5_test(t2, 'poslist') FROM t2 
+  WHERE t2 MATCH 'm' ORDER BY rowid;
+} {
+  1 {{0.5 1.0 1.2}} 
+  2 {{0.7 1.5}}
+}
+
+do_execsql_test 2.2 {
+  SELECT rowid, fts5_test(t2, 'poslist') FROM t2 
+  WHERE t2 MATCH 'u OR q' ORDER BY rowid;
+} {
+  1 {0.0 {}}
+  2 {{} {0.2 0.10}}
+}
+
+do_execsql_test 2.3 {
+  SELECT rowid, fts5_test(t2, 'poslist') FROM t2 
+  WHERE t2 MATCH 'y:o' ORDER BY rowid;
+} {
+  1 {{1.3 1.7}}
+}
+
+#-------------------------------------------------------------------------
+# 
+do_execsql_test 3.0 {
+  CREATE VIRTUAL TABLE t3 USING fts5(x, y);
+  INSERT INTO t3 VALUES( 'j f h o x x a z g b a f a m i b', 'j z c z y x w t');
+  INSERT INTO t3 VALUES( 'r c', '');
+}
+
+do_execsql_test 3.1 {
+  SELECT rowid, fts5_test(t3, 'poslist') FROM t3 WHERE t3 MATCH 'NEAR(a b)';
+} {
+  1 {{0.6 0.10 0.12} {0.9 0.15}}
+}
+
+do_execsql_test 3.2 {
+  SELECT rowid, fts5_test(t3, 'poslist') FROM t3 WHERE t3 MATCH 'NEAR(r c)';
+} {
+  2 {0.0 0.1}
+}
+
+do_execsql_test 3.3 {
+  INSERT INTO t3 
+  VALUES('k x j r m a d o i z j', 'r t t t f e b r x i v j v g o');
+  SELECT rowid, fts5_test(t3, 'poslist') 
+  FROM t3 WHERE t3 MATCH 'a OR b AND c';
+} {
+  3 {0.5 {} {}} 
+  1 {{0.6 0.10 0.12} {0.9 0.15} 1.2}
+}
+
+#-------------------------------------------------------------------------
+# 
+do_execsql_test 4.0 {
+  CREATE VIRTUAL TABLE t4 USING fts5(x, y);
+  INSERT INTO t4 
+  VALUES('k x j r m a d o i z j', 'r t t t f e b r x i v j v g o');
+}
+
+breakpoint
+do_execsql_test 4.1 {
+  SELECT rowid, fts5_test(t4, 'poslist') FROM t4 WHERE t4 MATCH 'a OR b AND c';
+} {
+  1 {0.5 {} {}} 
+}
+
+#93 {0.5 1.6 {}}
+
+
+finish_test
+
+