Snapshot of upstream SQLite 3.45.3
[sqlcipher.git] / test / fts3auto.test
blob19193973d9eca4637a9aee168b55089e8afe87ac
1 # 2011 June 10
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.
7 #***********************************************************************
10 set testdir [file dirname $argv0]
11 source $testdir/tester.tcl
13 # If this build does not include FTS3, skip the tests in this file.
15 ifcapable !fts3 { finish_test ; return }
16 source $testdir/fts3_common.tcl
17 source $testdir/malloc_common.tcl
19 set testprefix fts3auto
20 set sfep $sqlite_fts3_enable_parentheses
21 set sqlite_fts3_enable_parentheses 1
23 #--------------------------------------------------------------------------
24 # Start of Tcl infrastructure used by tests. The entry points are:
26 #   do_fts3query_test
27 #   fts3_make_deferrable
28 #   fts3_zero_long_segments 
32 #    do_fts3query_test TESTNAME ?OPTIONS? TABLE MATCHEXPR
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.
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.
47 # Available OPTIONS are:
49 #     -deferred TOKENLIST
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. 
55 set sqlite_fts3_enable_parentheses 1
56 proc do_fts3query_test {tn args} {
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]
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   }
78   get_near_results $tbl $match $deferred aHit
79   get_near_results $tbl [string map {AND OR} $match] $deferred aMatchinfo
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   }
90   set title "(\"$match\" -> [llength [array names aHit]] rows)"
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]] 
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]] 
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
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
111 #    fts3_make_deferrable TABLE TOKEN ?NROW?
113 proc fts3_make_deferrable {tbl token {nRow 0}} {
115   set stmt [sqlite3_prepare db "SELECT * FROM $tbl" -1 dummy]
116   set name [sqlite3_column_name $stmt 0]
117   sqlite3_finalize $stmt
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
131   return [expr $nRow*$pgsz]
134 #    fts3_zero_long_segments TABLE ?LIMIT?
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]
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
153 db func mit mit
155 proc fix_phrase_expr {cols expr colfiltervar} {
156   upvar $colfiltervar iColFilter
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   }
174   return $out
177 proc fix_near_expr {cols expr colfiltervar} { 
178   upvar $colfiltervar iColFilter
180   set iColFilter -1
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
193 proc get_single_near_results {tbl expr deferred arrayvar nullvar} {
194   upvar $arrayvar aMatchinfo
195   upvar $nullvar nullentry
196   catch {array unset aMatchinfo}
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]
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
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   }
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   }
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   }
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   }
315 proc matching_brackets {expr} {
316   if {[string range $expr 0 0]!="(" || [string range $expr end end] !=")"} { 
317     return 0 
318   }
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   }
328   return [expr ($iBracket==0 && $i==$nExpr)]
331 proc get_near_results {tbl expr deferred arrayvar {nullvar ""}} {
332   upvar $arrayvar aMatchinfo
333   if {$nullvar != ""} { upvar $nullvar nullentry }
335   set expr [string trim $expr]
336   while { [matching_brackets $expr] } {
337     set expr [string trim [string range $expr 1 end-1]]
338   }
340   set prec(NOT) 1
341   set prec(AND) 2
342   set prec(OR)  3
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" }
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]
368     get_near_results $tbl $eLeft  $deferred aLeft  nullleft
369     get_near_results $tbl $eRight $deferred aRight nullright
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       }
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       }
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         }
403         set nullentry [concat $nullleft $nullright]
404       }
405     }
406   }
410 # End of test procs. Actual tests are below this line.
411 #--------------------------------------------------------------------------
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.
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]
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}
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}
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}
439 #--------------------------------------------------------------------------
440 # Test cases fts3auto-2.* run some simple tests using the 
441 # [do_fts3query_test] proc.
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   } {}
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   }
487 #--------------------------------------------------------------------------
488 # Some test cases involving deferred tokens.
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   }
509   set limit [fts3_make_deferrable t1 c]
511   do_fts3query_test 3.$tn.2.1 t1 {a OR c}
513   ifcapable fts4_deferred {
514     do_test 3.$tn.3 { fts3_zero_long_segments t1 $limit } {1}
515   }
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   }
529 #--------------------------------------------------------------------------
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 } {
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   }]
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}
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}
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}
566   do_fts3query_test 4.$tn.3.5 -deferred five t1 {one NEAR/3 five}
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*}
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   }
582 #--------------------------------------------------------------------------
583 # The following test cases - fts3auto-5.* - focus on using prefix indexes.
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 } {
595   execsql [subst {
596     DROP TABLE IF EXISTS t1;
597     CREATE VIRTUAL TABLE t1 USING $create;
598   }]
600   if {$pending} {execsql BEGIN}
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   }
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*"}
633   if {$pending} {execsql COMMIT}
635 eval fts3_configure_incr_load $chunkconfig
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   }]
649   if {$pending} { execsql BEGIN }
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   }
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}
667   do_fts3query_test 6.$tn.5 t1 {a:G OR b:G}
669   catchsql { COMMIT }
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   }]
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   }
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
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"}
716 set sqlite_fts3_enable_parentheses $sfep
717 finish_test