github.com/jdgcs/sqlite3@v1.12.1-0.20210908114423-bc5f96e4dd51/testdata/tcl/fts3auto.test (about)

     1  # 2011 June 10
     2  #
     3  #    May you do good and not evil.
     4  #    May you find forgiveness for yourself and forgive others.
     5  #    May you share freely, never taking more than you give.
     6  #
     7  #***********************************************************************
     8  #
     9  
    10  set testdir [file dirname $argv0]
    11  source $testdir/tester.tcl
    12  
    13  # If this build does not include FTS3, skip the tests in this file.
    14  #
    15  ifcapable !fts3 { finish_test ; return }
    16  source $testdir/fts3_common.tcl
    17  source $testdir/malloc_common.tcl
    18  
    19  set testprefix fts3auto
    20  set sfep $sqlite_fts3_enable_parentheses
    21  set sqlite_fts3_enable_parentheses 1
    22  
    23  #--------------------------------------------------------------------------
    24  # Start of Tcl infrastructure used by tests. The entry points are:
    25  #
    26  #   do_fts3query_test
    27  #   fts3_make_deferrable
    28  #   fts3_zero_long_segments 
    29  #
    30  
    31  #
    32  #    do_fts3query_test TESTNAME ?OPTIONS? TABLE MATCHEXPR
    33  #
    34  # This proc runs several test cases on FTS3/4 table $TABLE using match
    35  # expression $MATCHEXPR. All documents in $TABLE must be formatted so that
    36  # they can be "tokenized" using the Tcl list commands (llength, lindex etc.).
    37  # The name and column names used by $TABLE must not require any quoting or
    38  # escaping when used in SQL statements.
    39  #
    40  # $MATCHINFO may be any expression accepted by the FTS4 MATCH operator, 
    41  # except that the "<column-name>:token" syntax is not supported. Tcl list
    42  # commands are used to tokenize the expression. Any parenthesis must appear
    43  # either as separate list elements, or as the first (for opening) or last
    44  # (for closing) character of a list element. i.e. the expression "(a OR b)c"
    45  # will not be parsed correctly, but "( a OR b) c" will.
    46  #
    47  # Available OPTIONS are:
    48  #
    49  #     -deferred TOKENLIST
    50  #
    51  # If the "deferred" option is supplied, it is passed a list of tokens that
    52  # are deferred by FTS and result in the relevant matchinfo() stats being an
    53  # approximation. 
    54  #
    55  set sqlite_fts3_enable_parentheses 1
    56  proc do_fts3query_test {tn args} {
    57  
    58    set nArg [llength $args]
    59    if {$nArg < 2 || ($nArg % 2)} {
    60      set cmd do_fts3query_test
    61      error "wrong # args: should be \"$cmd ?-deferred LIST? TABLE MATCHEXPR\""
    62    }
    63    set tbl   [lindex $args [expr $nArg-2]]
    64    set match [lindex $args [expr $nArg-1]]
    65    set deferred [list]
    66  
    67    foreach {k v} [lrange $args 0 [expr $nArg-3]] {
    68      switch -- $k {
    69        -deferred {
    70          ifcapable fts4_deferred { set deferred $v }
    71        }
    72        default {
    73          error "bad option \"$k\": must be -deferred"
    74        }
    75      }
    76    }
    77  
    78    get_near_results $tbl $match $deferred aHit
    79    get_near_results $tbl [string map {AND OR} $match] $deferred aMatchinfo
    80  
    81    set matchinfo_asc [list]
    82    foreach docid [lsort -integer -incr [array names aHit]] {
    83      lappend matchinfo_asc $docid $aMatchinfo($docid)
    84    }
    85    set matchinfo_desc [list]
    86    foreach docid [lsort -integer -decr [array names aHit]] {
    87      lappend matchinfo_desc $docid $aMatchinfo($docid)
    88    }
    89  
    90    set title "(\"$match\" -> [llength [array names aHit]] rows)"
    91  
    92    do_execsql_test $tn$title.1 "
    93      SELECT docid FROM $tbl WHERE $tbl MATCH '$match' ORDER BY docid ASC
    94    " [lsort -integer -incr [array names aHit]] 
    95  
    96    do_execsql_test $tn$title.2 "
    97      SELECT docid FROM $tbl WHERE $tbl MATCH '$match' ORDER BY docid DESC
    98    " [lsort -integer -decr [array names aHit]] 
    99  
   100    do_execsql_test $tn$title.3 "
   101      SELECT docid, mit(matchinfo($tbl, 'x')) FROM $tbl 
   102      WHERE $tbl MATCH '$match' ORDER BY docid DESC
   103    " $matchinfo_desc
   104  
   105    do_execsql_test $tn$title.4 "
   106      SELECT docid, mit(matchinfo($tbl, 'x')) FROM $tbl 
   107      WHERE $tbl MATCH '$match' ORDER BY docid ASC
   108    " $matchinfo_asc
   109  }
   110  
   111  #    fts3_make_deferrable TABLE TOKEN ?NROW?
   112  #
   113  proc fts3_make_deferrable {tbl token {nRow 0}} {
   114  
   115    set stmt [sqlite3_prepare db "SELECT * FROM $tbl" -1 dummy]
   116    set name [sqlite3_column_name $stmt 0]
   117    sqlite3_finalize $stmt
   118  
   119    if {$nRow==0} {
   120      set nRow [db one "SELECT count(*) FROM $tbl"]
   121    }
   122    set pgsz [db one "PRAGMA page_size"]
   123    execsql BEGIN
   124    for {set i 0} {$i < ($nRow * $pgsz * 1.2)/100} {incr i} {
   125      set doc [string repeat "$token " 100]
   126      execsql "INSERT INTO $tbl ($name) VALUES(\$doc)"
   127    }
   128    execsql "INSERT INTO $tbl ($name) VALUES('aaaaaaa ${token}aaaaa')"
   129    execsql COMMIT
   130  
   131    return [expr $nRow*$pgsz]
   132  }
   133  
   134  #    fts3_zero_long_segments TABLE ?LIMIT?
   135  #
   136  proc fts3_zero_long_segments {tbl limit} {
   137    sqlite3_db_config db DEFENSIVE 0
   138    execsql " 
   139      UPDATE ${tbl}_segments 
   140      SET block = zeroblob(length(block)) 
   141      WHERE length(block)>$limit
   142    "
   143    return [db changes]
   144  }
   145  
   146  
   147  proc mit {blob} {
   148    set scan(littleEndian) i*
   149    set scan(bigEndian) I*
   150    binary scan $blob $scan($::tcl_platform(byteOrder)) r
   151    return $r
   152  }
   153  db func mit mit
   154  
   155  proc fix_phrase_expr {cols expr colfiltervar} {
   156    upvar $colfiltervar iColFilter
   157  
   158    set out [list]
   159    foreach t $expr {
   160      if {[string match *:* $t]} {
   161        set col [lindex [split $t :] 0]
   162        set t   [lindex [split $t :] 1]
   163        set iCol [lsearch $cols $col]
   164        if {$iCol<0} { error "unknown column: $col" }
   165        if {$iColFilter < 0} {
   166          set iColFilter $iCol
   167        } elseif {$iColFilter != $iCol} {
   168          set iColFilter [llength $cols]
   169        }
   170      }
   171      lappend out $t
   172    }
   173  
   174    return $out
   175  }
   176  
   177  proc fix_near_expr {cols expr colfiltervar} { 
   178    upvar $colfiltervar iColFilter
   179   
   180    set iColFilter -1
   181  
   182    set out [list]
   183    lappend out [fix_phrase_expr $cols [lindex $expr 0] iColFilter]
   184    foreach {a b} [lrange $expr 1 end] {
   185      if {[string match -nocase near $a]}   { set a 10 }
   186      if {[string match -nocase near/* $a]} { set a [string range $a 5 end] }
   187      lappend out $a
   188      lappend out [fix_phrase_expr $cols $b iColFilter]
   189    }
   190    return $out
   191  }
   192  
   193  proc get_single_near_results {tbl expr deferred arrayvar nullvar} {
   194    upvar $arrayvar aMatchinfo
   195    upvar $nullvar nullentry
   196    catch {array unset aMatchinfo}
   197  
   198    set cols [list]
   199    set miss [list]
   200    db eval "PRAGMA table_info($tbl)" A { lappend cols $A(name) ; lappend miss 0 }
   201    set expr [fix_near_expr $cols $expr iColFilter]
   202  
   203    # Calculate the expected results using [fts3_near_match]. The following
   204    # loop populates the "hits" and "counts" arrays as follows:
   205    # 
   206    #   1. For each document in the table that matches the NEAR expression,
   207    #      hits($docid) is set to 1. The set of docids that match the expression
   208    #      can therefore be found using [array names hits].
   209    #
   210    #   2. For each column of each document in the table, counts($docid,$iCol)
   211    #      is set to the -phrasecountvar output.
   212    #
   213    set res [list]
   214    catch { array unset hits }
   215    db eval "SELECT docid, * FROM $tbl" d {
   216      set iCol 0
   217      foreach col [lrange $d(*) 1 end] {
   218        set docid $d(docid)
   219        if {$iColFilter<0 || $iCol==$iColFilter} {
   220          set hit [fts3_near_match $d($col) $expr -p counts($docid,$iCol)]
   221          if {$hit} { set hits($docid) 1 }
   222        } else {
   223          set counts($docid,$iCol) $miss
   224        }
   225        incr iCol
   226      }
   227    }
   228    set nPhrase [expr ([llength $expr]+1)/2]
   229    set nCol $iCol
   230  
   231    # This block populates the nHit and nDoc arrays. For each phrase/column
   232    # in the query/table, array elements are set as follows:
   233    #
   234    #   nHit($iPhrase,$iCol) - Total number of hits for phrase $iPhrase in 
   235    #                          column $iCol.
   236    #
   237    #   nDoc($iPhrase,$iCol) - Number of documents with at least one hit for
   238    #                          phrase $iPhrase in column $iCol.
   239    #
   240    for {set iPhrase 0} {$iPhrase < $nPhrase} {incr iPhrase} {
   241      for {set iCol 0} {$iCol < $nCol} {incr iCol} {
   242        set nHit($iPhrase,$iCol) 0
   243        set nDoc($iPhrase,$iCol) 0
   244      }
   245    }
   246    foreach key [array names counts] {
   247      set iCol [lindex [split $key ,] 1]
   248      set iPhrase 0
   249      foreach c $counts($key) {
   250        if {$c>0} { incr nDoc($iPhrase,$iCol) 1 }
   251        incr nHit($iPhrase,$iCol) $c
   252        incr iPhrase
   253      }
   254    }
   255  
   256    if {[llength $deferred] && [llength $expr]==1} {
   257      set phrase [lindex $expr 0]
   258      set rewritten [list]
   259      set partial 0
   260      foreach tok $phrase {
   261        if {[lsearch $deferred $tok]>=0} {
   262          lappend rewritten *
   263        } else {
   264          lappend rewritten $tok
   265          set partial 1
   266        }
   267      }
   268      if {$partial==0} {
   269        set tblsize [db one "SELECT count(*) FROM $tbl"]
   270        for {set iCol 0} {$iCol < $nCol} {incr iCol} {
   271          set nHit(0,$iCol) $tblsize
   272          set nDoc(0,$iCol) $tblsize
   273        }
   274      } elseif {$rewritten != $phrase} {
   275        while {[lindex $rewritten end] == "*"} {
   276          set rewritten [lrange $rewritten 0 end-1]
   277        }
   278        while {[lindex $rewritten 0] == "*"} {
   279          set rewritten [lrange $rewritten 1 end]
   280        }
   281        get_single_near_results $tbl [list $rewritten] {} aRewrite nullentry
   282        foreach docid [array names hits] {
   283          set aMatchinfo($docid) $aRewrite($docid)
   284        }
   285        return
   286      }
   287    }
   288  
   289    # Set up the aMatchinfo array. For each document, set aMatchinfo($docid) to
   290    # contain the output of matchinfo('x') for the document.
   291    #
   292    foreach docid [array names hits] {
   293      set mi [list]
   294      for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} {
   295        for {set iCol 0} {$iCol<$nCol} {incr iCol} {
   296          lappend mi [lindex $counts($docid,$iCol) $iPhrase]
   297          lappend mi $nHit($iPhrase,$iCol)
   298          lappend mi $nDoc($iPhrase,$iCol)
   299        }
   300      }
   301      set aMatchinfo($docid) $mi
   302    }
   303  
   304    # Set up the nullentry output.
   305    #
   306    set nullentry [list]
   307    for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} {
   308      for {set iCol 0} {$iCol<$nCol} {incr iCol} {
   309        lappend nullentry 0 $nHit($iPhrase,$iCol) $nDoc($iPhrase,$iCol)
   310      }
   311    }
   312  }
   313  
   314  
   315  proc matching_brackets {expr} {
   316    if {[string range $expr 0 0]!="(" || [string range $expr end end] !=")"} { 
   317      return 0 
   318    }
   319  
   320    set iBracket 1
   321    set nExpr [string length $expr]
   322    for {set i 1} {$iBracket && $i < $nExpr} {incr i} {
   323      set c [string range $expr $i $i]
   324      if {$c == "("} {incr iBracket}
   325      if {$c == ")"} {incr iBracket -1}
   326    }
   327  
   328    return [expr ($iBracket==0 && $i==$nExpr)]
   329  }
   330  
   331  proc get_near_results {tbl expr deferred arrayvar {nullvar ""}} {
   332    upvar $arrayvar aMatchinfo
   333    if {$nullvar != ""} { upvar $nullvar nullentry }
   334  
   335    set expr [string trim $expr]
   336    while { [matching_brackets $expr] } {
   337      set expr [string trim [string range $expr 1 end-1]]
   338    }
   339  
   340    set prec(NOT) 1
   341    set prec(AND) 2
   342    set prec(OR)  3
   343  
   344    set currentprec 0
   345    set iBracket 0
   346    set expr_length [llength $expr]
   347    for {set i 0} {$i < $expr_length} {incr i} {
   348      set op [lindex $expr $i]
   349      if {$iBracket==0 && [info exists prec($op)] && $prec($op)>=$currentprec } {
   350        set opidx $i
   351        set currentprec $prec($op)
   352      } else {
   353        for {set j 0} {$j < [string length $op]} {incr j} {
   354          set c [string range $op $j $j]
   355          if {$c == "("} { incr iBracket +1 }
   356          if {$c == ")"} { incr iBracket -1 }
   357        }
   358      }
   359    }
   360    if {$iBracket!=0} { error "mismatched brackets in: $expr" }
   361  
   362    if {[info exists opidx]==0} {
   363      get_single_near_results $tbl $expr $deferred aMatchinfo nullentry
   364    } else {
   365      set eLeft  [lrange $expr 0 [expr $opidx-1]]
   366      set eRight [lrange $expr [expr $opidx+1] end]
   367  
   368      get_near_results $tbl $eLeft  $deferred aLeft  nullleft
   369      get_near_results $tbl $eRight $deferred aRight nullright
   370  
   371      switch -- [lindex $expr $opidx] {
   372        "NOT" {
   373          foreach hit [array names aLeft] {
   374            if {0==[info exists aRight($hit)]} {
   375              set aMatchinfo($hit) $aLeft($hit)
   376            }
   377          }
   378          set nullentry $nullleft
   379        }
   380  
   381        "AND" {
   382          foreach hit [array names aLeft] {
   383            if {[info exists aRight($hit)]} {
   384              set aMatchinfo($hit) [concat $aLeft($hit) $aRight($hit)]
   385            }
   386          }
   387          set nullentry [concat $nullleft $nullright]
   388        }
   389  
   390        "OR" {
   391          foreach hit [array names aLeft] {
   392            if {[info exists aRight($hit)]} {
   393              set aMatchinfo($hit) [concat $aLeft($hit) $aRight($hit)]
   394              unset aRight($hit)
   395            } else {
   396              set aMatchinfo($hit) [concat $aLeft($hit) $nullright]
   397            }
   398          }
   399          foreach hit [array names aRight] {
   400            set aMatchinfo($hit) [concat $nullleft $aRight($hit)]
   401          }
   402  
   403          set nullentry [concat $nullleft $nullright]
   404        }
   405      }
   406    }
   407  }
   408  
   409  
   410  # End of test procs. Actual tests are below this line.
   411  #--------------------------------------------------------------------------
   412  
   413  #--------------------------------------------------------------------------
   414  # The following test cases - fts3auto-1.* - focus on testing the Tcl 
   415  # command [fts3_near_match], which is used by other tests in this file.
   416  #
   417  proc test_fts3_near_match {tn doc expr res} {
   418    fts3_near_match $doc $expr -phrasecountvar p
   419    uplevel do_test [list $tn] [list [list set {} $p]] [list $res]
   420  }
   421  
   422  test_fts3_near_match 1.1.1 {a b c a b} a                   {2}
   423  test_fts3_near_match 1.1.2 {a b c a b} {a 5 b 6 c}         {2 2 1}
   424  test_fts3_near_match 1.1.3 {a b c a b} {"a b"}             {2}
   425  test_fts3_near_match 1.1.4 {a b c a b} {"b c"}             {1}
   426  test_fts3_near_match 1.1.5 {a b c a b} {"c c"}             {0}
   427  
   428  test_fts3_near_match 1.2.1 "a b c d e f g" {b 2 f}         {0 0}
   429  test_fts3_near_match 1.2.2 "a b c d e f g" {b 3 f}         {1 1}
   430  test_fts3_near_match 1.2.3 "a b c d e f g" {f 2 b}         {0 0}
   431  test_fts3_near_match 1.2.4 "a b c d e f g" {f 3 b}         {1 1}
   432  test_fts3_near_match 1.2.5 "a b c d e f g" {"a b" 2 "f g"} {0 0}
   433  test_fts3_near_match 1.2.6 "a b c d e f g" {"a b" 3 "f g"} {1 1}
   434  
   435  set A "a b c d e f g h i j k l m n o p q r s t u v w x y z"
   436  test_fts3_near_match 1.3.1 $A {"c d" 5 "i j" 1 "e f"}      {0 0 0}
   437  test_fts3_near_match 1.3.2 $A {"c d" 5 "i j" 2 "e f"}      {1 1 1}
   438  
   439  #--------------------------------------------------------------------------
   440  # Test cases fts3auto-2.* run some simple tests using the 
   441  # [do_fts3query_test] proc.
   442  #
   443  foreach {tn create} {
   444    1    "fts4(a, b)"
   445    2    "fts4(a, b, order=DESC)"
   446    3    "fts4(a, b, order=ASC)"
   447    4    "fts4(a, b, prefix=1)"
   448    5    "fts4(a, b, order=DESC, prefix=1)"
   449    6    "fts4(a, b, order=ASC, prefix=1)"
   450  } {
   451    do_test 2.$tn.1 {
   452      catchsql { DROP TABLE t1 }
   453      execsql  "CREATE VIRTUAL TABLE t1 USING $create"
   454      for {set i 0} {$i<32} {incr i} {
   455        set doc [list]
   456        if {$i&0x01} {lappend doc one}
   457        if {$i&0x02} {lappend doc two}
   458        if {$i&0x04} {lappend doc three}
   459        if {$i&0x08} {lappend doc four}
   460        if {$i&0x10} {lappend doc five}
   461        execsql { INSERT INTO t1 VALUES($doc, null) }
   462      }
   463    } {}
   464  
   465    foreach {tn2 expr} {
   466      1     {one}
   467      2     {one NEAR/1 five}
   468      3     {t*}
   469      4     {t* NEAR/0 five}
   470      5     {o* NEAR/1 f*}
   471      6     {one NEAR five NEAR two NEAR four NEAR three}
   472      7     {one NEAR xyz}
   473      8     {one OR two}
   474      9     {one AND two}
   475      10    {one NOT two}
   476      11    {one AND two OR three}
   477      12    {three OR one AND two}
   478      13    {(three OR one) AND two}
   479      14    {(three OR one) AND two NOT (five NOT four)}
   480      15    {"one two"}
   481      16    {"one two" NOT "three four"}
   482    } {
   483      do_fts3query_test 2.$tn.2.$tn2 t1 $expr
   484    }
   485  }
   486  
   487  #--------------------------------------------------------------------------
   488  # Some test cases involving deferred tokens.
   489  #
   490  
   491  foreach {tn create} {
   492    1    "fts4(x)"
   493    2    "fts4(x, order=DESC)"
   494  } {
   495    catchsql { DROP TABLE t1 }
   496    execsql  "CREATE VIRTUAL TABLE t1 USING $create"
   497    do_execsql_test 3.$tn.1 {
   498      INSERT INTO t1(docid, x) VALUES(-2, 'a b c d e f g h i j k');
   499      INSERT INTO t1(docid, x) VALUES(-1, 'b c d e f g h i j k a');
   500      INSERT INTO t1(docid, x) VALUES(0, 'c d e f g h i j k a b');
   501      INSERT INTO t1(docid, x) VALUES(1, 'd e f g h i j k a b c');
   502      INSERT INTO t1(docid, x) VALUES(2, 'e f g h i j k a b c d');
   503      INSERT INTO t1(docid, x) VALUES(3, 'f g h i j k a b c d e');
   504      INSERT INTO t1(docid, x) VALUES(4, 'a c e g i k');
   505      INSERT INTO t1(docid, x) VALUES(5, 'a d g j');
   506      INSERT INTO t1(docid, x) VALUES(6, 'c a b');
   507    }
   508  
   509    set limit [fts3_make_deferrable t1 c]
   510  
   511    do_fts3query_test 3.$tn.2.1 t1 {a OR c}
   512  
   513    ifcapable fts4_deferred {
   514      do_test 3.$tn.3 { fts3_zero_long_segments t1 $limit } {1}
   515    }
   516  
   517    foreach {tn2 expr def} {
   518      1     {a NEAR c}            {}
   519      2     {a AND c}             c
   520      3     {"a c"}               c
   521      4     {"c a"}               c
   522      5     {"a c" NEAR/1 g}      {}
   523      6     {"a c" NEAR/0 g}      {}
   524    } {
   525      do_fts3query_test 3.$tn.4.$tn2 -deferred $def t1 $expr
   526    }
   527  }
   528  
   529  #--------------------------------------------------------------------------
   530  # 
   531  foreach {tn create} {
   532    1    "fts4(x, y)"
   533    2    "fts4(x, y, order=DESC)"
   534    3    "fts4(x, y, order=DESC, prefix=2)"
   535  } {
   536  
   537    execsql [subst {
   538      DROP TABLE t1;
   539      CREATE VIRTUAL TABLE t1 USING $create;
   540      INSERT INTO t1 VALUES('one two five four five', '');
   541      INSERT INTO t1 VALUES('', 'one two five four five');
   542      INSERT INTO t1 VALUES('one two', 'five four five');
   543    }]
   544  
   545    do_fts3query_test 4.$tn.1.1 t1 {one AND five}
   546    do_fts3query_test 4.$tn.1.2 t1 {one NEAR five}
   547    do_fts3query_test 4.$tn.1.3 t1 {one NEAR/1 five}
   548    do_fts3query_test 4.$tn.1.4 t1 {one NEAR/2 five}
   549    do_fts3query_test 4.$tn.1.5 t1 {one NEAR/3 five}
   550  
   551    do_test 4.$tn.2 { 
   552      set limit [fts3_make_deferrable t1 five]
   553      execsql { INSERT INTO t1(t1) VALUES('optimize') }
   554      ifcapable fts4_deferred {
   555        expr {[fts3_zero_long_segments t1 $limit]>0}
   556      } else {
   557        expr 1
   558      }
   559    } {1}
   560  
   561    do_fts3query_test 4.$tn.3.1 -deferred five t1 {one AND five}
   562    do_fts3query_test 4.$tn.3.2 -deferred five t1 {one NEAR five}
   563    do_fts3query_test 4.$tn.3.3 -deferred five t1 {one NEAR/1 five}
   564    do_fts3query_test 4.$tn.3.4 -deferred five t1 {one NEAR/2 five}
   565  
   566    do_fts3query_test 4.$tn.3.5 -deferred five t1 {one NEAR/3 five}
   567  
   568    do_fts3query_test 4.$tn.4.1 -deferred fi* t1 {on* AND fi*}
   569    do_fts3query_test 4.$tn.4.2 -deferred fi* t1 {on* NEAR fi*}
   570    do_fts3query_test 4.$tn.4.3 -deferred fi* t1 {on* NEAR/1 fi*}
   571    do_fts3query_test 4.$tn.4.4 -deferred fi* t1 {on* NEAR/2 fi*}
   572    do_fts3query_test 4.$tn.4.5 -deferred fi* t1 {on* NEAR/3 fi*}
   573  
   574    ifcapable fts4_deferred {
   575      db eval {UPDATE t1_stat SET value=x'' WHERE id=0}
   576      do_catchsql_test 4.$tn.4.6 {
   577        SELECT docid FROM t1 WHERE t1 MATCH 'on* NEAR/3 fi*'
   578      } {1 {database disk image is malformed}}
   579    }
   580  }
   581  
   582  #--------------------------------------------------------------------------
   583  # The following test cases - fts3auto-5.* - focus on using prefix indexes.
   584  #
   585  set chunkconfig [fts3_configure_incr_load 1 1]
   586  foreach {tn create pending} {
   587    1    "fts4(a, b)"                                  1
   588    2    "fts4(a, b, order=ASC, prefix=1)"             1
   589    3    "fts4(a, b, order=ASC,  prefix=\"1,3\")"      0
   590    4    "fts4(a, b, order=DESC, prefix=\"2,4\")"      0
   591    5    "fts4(a, b, order=DESC, prefix=\"1\")"        0
   592    6    "fts4(a, b, order=ASC,  prefix=\"1,3\")"      0
   593  } {
   594  
   595    execsql [subst {
   596      DROP TABLE IF EXISTS t1;
   597      CREATE VIRTUAL TABLE t1 USING $create;
   598    }]
   599  
   600    if {$pending} {execsql BEGIN}
   601  
   602    foreach {a b} {
   603      "the song of songs which is solomons"
   604      "let him kiss me with the kisses of his mouth for thy love is better than wine"
   605      "because of the savour of thy good ointments thy name is as ointment poured forth therefore do the virgins love thee"
   606      "draw me we will run after thee the king hath brought me into his chambers we will be glad and rejoice in thee we will remember thy love more than wine the upright love thee"
   607      "i am black but comely o ye daughters of jerusalem as the tents of kedar as the curtains of solomon"
   608      "look not upon me because i am black because the sun hath looked upon me my mothers children were angry with me they made me the keeper of the vineyards but mine own vineyard have i not kept"
   609      "tell me o thou whom my soul loveth where thou feedest where thou makest thy flock to rest at noon for why should i be as one that turneth aside by the flocks of thy companions?"
   610      "if thou know not o thou fairest among women go thy way forth by the footsteps of the flock and feed thy kids beside the shepherds tents"
   611      "i have compared thee o my love to a company of horses in pharaohs chariots"
   612      "thy cheeks are comely with rows of jewels thy neck with chains of gold"
   613      "we will make thee borders of gold with studs of silver"
   614      "while the king sitteth at his table my spikenard sendeth forth the smell thereof"
   615      "a bundle of myrrh is my wellbeloved unto me he shall lie all night betwixt my breasts"
   616      "my beloved is unto me as a cluster of camphire in the vineyards of en gedi"
   617      "behold thou art fair my love behold thou art fair thou hast doves eyes"
   618      "behold thou art fair my beloved yea pleasant also our bed is green"
   619      "the beams of our house are cedar and our rafters of fir"
   620    } {
   621      execsql {INSERT INTO t1(a, b) VALUES($a, $b)}
   622    }
   623  
   624  
   625    do_fts3query_test 5.$tn.1.1 t1 {s*}
   626    do_fts3query_test 5.$tn.1.2 t1 {so*}
   627    do_fts3query_test 5.$tn.1.3 t1 {"s* o*"}
   628    do_fts3query_test 5.$tn.1.4 t1 {b* NEAR/3 a*}
   629    do_fts3query_test 5.$tn.1.5 t1 {a*}
   630    do_fts3query_test 5.$tn.1.6 t1 {th* NEAR/5 a* NEAR/5 w*}
   631    do_fts3query_test 5.$tn.1.7 t1 {"b* th* art* fair*"}
   632  
   633    if {$pending} {execsql COMMIT}
   634  }
   635  eval fts3_configure_incr_load $chunkconfig
   636  
   637  foreach {tn pending create} {
   638    1    0 "fts4(a, b, c, d)"
   639    2    1 "fts4(a, b, c, d)"
   640    3    0 "fts4(a, b, c, d, order=DESC)"
   641    4    1 "fts4(a, b, c, d, order=DESC)"
   642  } {
   643    execsql [subst {
   644      DROP TABLE IF EXISTS t1;
   645      CREATE VIRTUAL TABLE t1 USING $create;
   646    }]
   647  
   648  
   649    if {$pending} { execsql BEGIN }
   650  
   651    foreach {a b c d} {
   652      "A B C" "D E F" "G H I" "J K L"
   653      "B C D" "E F G" "H I J" "K L A"
   654      "C D E" "F G H" "I J K" "L A B"
   655      "D E F" "G H I" "J K L" "A B C"
   656      "E F G" "H I J" "K L A" "B C D"
   657      "F G H" "I J K" "L A B" "C D E"
   658    } {
   659      execsql { INSERT INTO t1 VALUES($a, $b, $c, $d) }
   660    }
   661  
   662    do_fts3query_test 6.$tn.1 t1 {b:G}
   663    do_fts3query_test 6.$tn.2 t1 {b:G AND c:I}
   664    do_fts3query_test 6.$tn.3 t1 {b:G NEAR c:I}
   665    do_fts3query_test 6.$tn.4 t1 {a:C OR b:G OR c:K OR d:C}
   666  
   667    do_fts3query_test 6.$tn.5 t1 {a:G OR b:G}
   668  
   669    catchsql { COMMIT }
   670  }
   671  
   672  foreach {tn create} {
   673    1    "fts4(x)"
   674    2    "fts4(x, order=DESC)"
   675  } {
   676    execsql [subst {
   677      DROP TABLE IF EXISTS t1;
   678      CREATE VIRTUAL TABLE t1 USING $create;
   679    }]
   680  
   681    foreach {x} {
   682      "F E N O T K X V A X I E X A P G Q V H U"
   683      "R V A E T C V Q N I E L O N U G J K L U"
   684      "U Y I G W M V F J L X I D C H F P J Q B"
   685      "S G D Z X R P G S S Y B K A S G A I L L"
   686      "L S I C H T Z S R Q P R N K J X L F M J"
   687      "C C C D P X B Z C M A D A C X S B T X V"
   688      "W Y J M D R G V R K B X S A W R I T N C"
   689      "P K L W T M S P O Y Y V V O E H Q A I R"
   690      "C D Y I C Z F H J C O Y A Q F L S B D K"
   691      "P G S C Y C Y V I M B D S Z D D Y W I E"
   692      "Z K Z U E E S F Y X T U A L W O U J C Q"
   693      "P A T Z S W L P L Q V Y Y I P W U X S S"
   694      "I U I H U O F Z F R H R F T N D X A G M"
   695      "N A B M S H K X S O Y D T X S B R Y H Z"
   696      "L U D A S K I L S V Z J P U B E B Y H M"
   697    } {
   698      execsql { INSERT INTO t1 VALUES($x) }
   699    }
   700  
   701    # Add extra documents to the database such that token "B" will be considered
   702    # deferrable if considering the other tokens means that 2 or fewer documents
   703    # will be loaded into memory.
   704    #
   705    fts3_make_deferrable t1 B 2
   706  
   707    # B is not deferred in either of the first two tests below, since filtering
   708    # on "M" or "D" returns 10 documents or so. But filtering on "M * D" only
   709    # returns 2, so B is deferred in this case.
   710    #
   711    do_fts3query_test 7.$tn.1             t1 {"M B"}
   712    do_fts3query_test 7.$tn.2             t1 {"B D"}
   713    do_fts3query_test 7.$tn.3 -deferred B t1 {"M B D"}
   714  }
   715  
   716  set sqlite_fts3_enable_parentheses $sfep
   717  finish_test