xref: /sqlite-3.40.0/test/fts3auto.test (revision 7d2a1be8)
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
10set testdir [file dirname $argv0]
11source $testdir/tester.tcl
12
13# If this build does not include FTS3, skip the tests in this file.
14#
15ifcapable !fts3 { finish_test ; return }
16source $testdir/fts3_common.tcl
17source $testdir/malloc_common.tcl
18
19set testprefix fts3auto
20set sfep $sqlite_fts3_enable_parentheses
21set 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#
55set sqlite_fts3_enable_parentheses 1
56proc 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#
113proc 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#
136proc 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
147proc 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}
153db func mit mit
154
155proc 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
177proc 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
193proc 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
315proc 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
331proc 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#
417proc 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
422test_fts3_near_match 1.1.1 {a b c a b} a                   {2}
423test_fts3_near_match 1.1.2 {a b c a b} {a 5 b 6 c}         {2 2 1}
424test_fts3_near_match 1.1.3 {a b c a b} {"a b"}             {2}
425test_fts3_near_match 1.1.4 {a b c a b} {"b c"}             {1}
426test_fts3_near_match 1.1.5 {a b c a b} {"c c"}             {0}
427
428test_fts3_near_match 1.2.1 "a b c d e f g" {b 2 f}         {0 0}
429test_fts3_near_match 1.2.2 "a b c d e f g" {b 3 f}         {1 1}
430test_fts3_near_match 1.2.3 "a b c d e f g" {f 2 b}         {0 0}
431test_fts3_near_match 1.2.4 "a b c d e f g" {f 3 b}         {1 1}
432test_fts3_near_match 1.2.5 "a b c d e f g" {"a b" 2 "f g"} {0 0}
433test_fts3_near_match 1.2.6 "a b c d e f g" {"a b" 3 "f g"} {1 1}
434
435set 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"
436test_fts3_near_match 1.3.1 $A {"c d" 5 "i j" 1 "e f"}      {0 0 0}
437test_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#
443foreach {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
491foreach {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#
531foreach {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#
585set chunkconfig [fts3_configure_incr_load 1 1]
586foreach {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}
635eval fts3_configure_incr_load $chunkconfig
636
637foreach {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
672foreach {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
716set sqlite_fts3_enable_parentheses $sfep
717finish_test
718