199ebad90Sdan# 2011 June 10 299ebad90Sdan# 399ebad90Sdan# May you do good and not evil. 499ebad90Sdan# May you find forgiveness for yourself and forgive others. 599ebad90Sdan# May you share freely, never taking more than you give. 699ebad90Sdan# 799ebad90Sdan#*********************************************************************** 899ebad90Sdan# 999ebad90Sdan 1099ebad90Sdanset testdir [file dirname $argv0] 1199ebad90Sdansource $testdir/tester.tcl 1299ebad90Sdan 1399ebad90Sdan# If this build does not include FTS3, skip the tests in this file. 1499ebad90Sdan# 1599ebad90Sdanifcapable !fts3 { finish_test ; return } 1699ebad90Sdansource $testdir/fts3_common.tcl 1799ebad90Sdansource $testdir/malloc_common.tcl 1899ebad90Sdan 19*3fd65192Sdanset testprefix fts3auto 20*3fd65192Sdanset sfep $sqlite_fts3_enable_parentheses 21*3fd65192Sdanset sqlite_fts3_enable_parentheses 1 2299ebad90Sdan 23*3fd65192Sdan#-------------------------------------------------------------------------- 24*3fd65192Sdan# Start of Tcl procs used by tests. 2599ebad90Sdan# 2699ebad90Sdanproc mit {blob} { 2799ebad90Sdan set scan(littleEndian) i* 2899ebad90Sdan set scan(bigEndian) I* 2999ebad90Sdan binary scan $blob $scan($::tcl_platform(byteOrder)) r 3099ebad90Sdan return $r 3199ebad90Sdan} 3299ebad90Sdandb func mit mit 3399ebad90Sdan 3499ebad90Sdanproc fix_near_expr {expr} { 3599ebad90Sdan set out [list] 3699ebad90Sdan lappend out [lindex $expr 0] 3799ebad90Sdan foreach {a b} [lrange $expr 1 end] { 3899ebad90Sdan if {[string match -nocase near $a]} { set a 10 } 3999ebad90Sdan if {[string match -nocase near/* $a]} { set a [string range $a 5 end] } 4099ebad90Sdan lappend out $a 4199ebad90Sdan lappend out $b 4299ebad90Sdan } 4399ebad90Sdan return $out 4499ebad90Sdan} 4599ebad90Sdan 46*3fd65192Sdanproc get_single_near_results {tbl expr arrayvar nullvar} { 47*3fd65192Sdan upvar $arrayvar aMatchinfo 48*3fd65192Sdan upvar $nullvar nullentry 49*3fd65192Sdan catch {array unset aMatchinfo} 5099ebad90Sdan 5199ebad90Sdan set expr [fix_near_expr $expr] 5299ebad90Sdan 5399ebad90Sdan # Calculate the expected results using [fts3_near_match]. The following 5499ebad90Sdan # loop populates the "hits" and "counts" arrays as follows: 5599ebad90Sdan # 5699ebad90Sdan # 1. For each document in the table that matches the NEAR expression, 5799ebad90Sdan # hits($docid) is set to 1. The set of docids that match the expression 5899ebad90Sdan # can therefore be found using [array names hits]. 5999ebad90Sdan # 6099ebad90Sdan # 2. For each column of each document in the table, counts($docid,$iCol) 6199ebad90Sdan # is set to the -phrasecountvar output. 6299ebad90Sdan # 6399ebad90Sdan set res [list] 6499ebad90Sdan catch { array unset hits } 6599ebad90Sdan db eval "SELECT docid, * FROM $tbl" d { 6699ebad90Sdan set iCol 0 6799ebad90Sdan foreach col [lrange $d(*) 1 end] { 6899ebad90Sdan set docid $d(docid) 6999ebad90Sdan set hit [fts3_near_match $d($col) $expr -p counts($docid,$iCol)] 7099ebad90Sdan if {$hit} { set hits($docid) 1 } 7199ebad90Sdan incr iCol 7299ebad90Sdan } 7399ebad90Sdan } 7499ebad90Sdan set nPhrase [expr ([llength $expr]+1)/2] 7599ebad90Sdan set nCol $iCol 7699ebad90Sdan 7799ebad90Sdan # This block populates the nHit and nDoc arrays. For each phrase/column 7899ebad90Sdan # in the query/table, array elements are set as follows: 7999ebad90Sdan # 8099ebad90Sdan # nHit($iPhrase,$iCol) - Total number of hits for phrase $iPhrase in 8199ebad90Sdan # column $iCol. 8299ebad90Sdan # 8399ebad90Sdan # nDoc($iPhrase,$iCol) - Number of documents with at least one hit for 8499ebad90Sdan # phrase $iPhrase in column $iCol. 8599ebad90Sdan # 8699ebad90Sdan for {set iPhrase 0} {$iPhrase < $nPhrase} {incr iPhrase} { 8799ebad90Sdan for {set iCol 0} {$iCol < $nCol} {incr iCol} { 8899ebad90Sdan set nHit($iPhrase,$iCol) 0 8999ebad90Sdan set nDoc($iPhrase,$iCol) 0 9099ebad90Sdan } 9199ebad90Sdan } 9299ebad90Sdan foreach key [array names counts] { 9399ebad90Sdan set iCol [lindex [split $key ,] 1] 9499ebad90Sdan set iPhrase 0 9599ebad90Sdan foreach c $counts($key) { 9699ebad90Sdan if {$c>0} { incr nHit($iPhrase,$iCol) 1 } 9799ebad90Sdan incr nDoc($iPhrase,$iCol) $c 9899ebad90Sdan incr iPhrase 9999ebad90Sdan } 10099ebad90Sdan } 10199ebad90Sdan 10299ebad90Sdan # Set up the aMatchinfo array. For each document, set aMatchinfo($docid) to 10399ebad90Sdan # contain the output of matchinfo('x') for the document. 10499ebad90Sdan # 10599ebad90Sdan foreach docid [array names hits] { 10699ebad90Sdan set mi [list] 10799ebad90Sdan for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} { 10899ebad90Sdan for {set iCol 0} {$iCol<$nCol} {incr iCol} { 10999ebad90Sdan lappend mi [lindex $counts($docid,$iCol) $iPhrase] 11099ebad90Sdan lappend mi $nDoc($iPhrase,$iCol) 11199ebad90Sdan lappend mi $nHit($iPhrase,$iCol) 11299ebad90Sdan } 11399ebad90Sdan } 11499ebad90Sdan set aMatchinfo($docid) $mi 11599ebad90Sdan } 11699ebad90Sdan 117*3fd65192Sdan # Set up the nullentry output. 118*3fd65192Sdan # 119*3fd65192Sdan set nullentry [list] 120*3fd65192Sdan for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} { 121*3fd65192Sdan for {set iCol 0} {$iCol<$nCol} {incr iCol} { 122*3fd65192Sdan lappend nullentry 0 $nDoc($iPhrase,$iCol) $nHit($iPhrase,$iCol) 123*3fd65192Sdan } 124*3fd65192Sdan } 125*3fd65192Sdan} 126*3fd65192Sdan 127*3fd65192Sdan 128*3fd65192Sdanproc matching_brackets {expr} { 129*3fd65192Sdan if {[string range $expr 0 0]!="(" || [string range $expr end end] !=")"} { 130*3fd65192Sdan return 0 131*3fd65192Sdan } 132*3fd65192Sdan 133*3fd65192Sdan set iBracket 1 134*3fd65192Sdan set nExpr [string length $expr] 135*3fd65192Sdan for {set i 1} {$iBracket && $i < $nExpr} {incr i} { 136*3fd65192Sdan set c [string range $expr $i $i] 137*3fd65192Sdan if {$c == "("} {incr iBracket} 138*3fd65192Sdan if {$c == ")"} {incr iBracket -1} 139*3fd65192Sdan } 140*3fd65192Sdan 141*3fd65192Sdan return [expr ($iBracket==0 && $i==$nExpr)] 142*3fd65192Sdan} 143*3fd65192Sdan 144*3fd65192Sdanproc get_near_results {tbl expr arrayvar {nullvar ""}} { 145*3fd65192Sdan upvar $arrayvar aMatchinfo 146*3fd65192Sdan if {$nullvar != ""} { upvar $nullvar nullentry } 147*3fd65192Sdan 148*3fd65192Sdan set expr [string trim $expr] 149*3fd65192Sdan while { [matching_brackets $expr] } { 150*3fd65192Sdan set expr [string trim [string range $expr 1 end-1]] 151*3fd65192Sdan } 152*3fd65192Sdan 153*3fd65192Sdan set prec(NOT) 1 154*3fd65192Sdan set prec(AND) 2 155*3fd65192Sdan set prec(OR) 3 156*3fd65192Sdan 157*3fd65192Sdan set currentprec 0 158*3fd65192Sdan set iBracket 0 159*3fd65192Sdan set expr_length [llength $expr] 160*3fd65192Sdan for {set i 0} {$i < $expr_length} {incr i} { 161*3fd65192Sdan set op [lindex $expr $i] 162*3fd65192Sdan if {$iBracket==0 && [info exists prec($op)] && $prec($op)>=$currentprec } { 163*3fd65192Sdan set opidx $i 164*3fd65192Sdan set currentprec $prec($op) 165*3fd65192Sdan } else { 166*3fd65192Sdan for {set j 0} {$j < [string length $op]} {incr j} { 167*3fd65192Sdan set c [string range $op $j $j] 168*3fd65192Sdan if {$c == "("} { incr iBracket +1 } 169*3fd65192Sdan if {$c == ")"} { incr iBracket -1 } 170*3fd65192Sdan } 171*3fd65192Sdan } 172*3fd65192Sdan } 173*3fd65192Sdan if {$iBracket!=0} { error "mismatched brackets in: $expr" } 174*3fd65192Sdan 175*3fd65192Sdan if {[info exists opidx]==0} { 176*3fd65192Sdan get_single_near_results $tbl $expr aMatchinfo nullentry 177*3fd65192Sdan } else { 178*3fd65192Sdan set eLeft [lrange $expr 0 [expr $opidx-1]] 179*3fd65192Sdan set eRight [lrange $expr [expr $opidx+1] end] 180*3fd65192Sdan 181*3fd65192Sdan get_near_results $tbl $eLeft aLeft nullleft 182*3fd65192Sdan get_near_results $tbl $eRight aRight nullright 183*3fd65192Sdan 184*3fd65192Sdan switch -- [lindex $expr $opidx] { 185*3fd65192Sdan "NOT" { 186*3fd65192Sdan foreach hit [array names aLeft] { 187*3fd65192Sdan if {0==[info exists aRight($hit)]} { 188*3fd65192Sdan set aMatchinfo($hit) $aLeft($hit) 189*3fd65192Sdan } 190*3fd65192Sdan } 191*3fd65192Sdan set nullentry $nullleft 192*3fd65192Sdan } 193*3fd65192Sdan 194*3fd65192Sdan "AND" { 195*3fd65192Sdan foreach hit [array names aLeft] { 196*3fd65192Sdan if {[info exists aRight($hit)]} { 197*3fd65192Sdan set aMatchinfo($hit) [concat $aLeft($hit) $aRight($hit)] 198*3fd65192Sdan } 199*3fd65192Sdan } 200*3fd65192Sdan set nullentry [concat $nullleft $nullright] 201*3fd65192Sdan } 202*3fd65192Sdan 203*3fd65192Sdan "OR" { 204*3fd65192Sdan foreach hit [array names aLeft] { 205*3fd65192Sdan if {[info exists aRight($hit)]} { 206*3fd65192Sdan set aMatchinfo($hit) [concat $aLeft($hit) $aRight($hit)] 207*3fd65192Sdan unset aRight($hit) 208*3fd65192Sdan } else { 209*3fd65192Sdan set aMatchinfo($hit) [concat $aLeft($hit) $nullright] 210*3fd65192Sdan } 211*3fd65192Sdan } 212*3fd65192Sdan foreach hit [array names aRight] { 213*3fd65192Sdan set aMatchinfo($hit) [concat $nullleft $aRight($hit)] 214*3fd65192Sdan } 215*3fd65192Sdan 216*3fd65192Sdan set nullentry [concat $nullleft $nullright] 217*3fd65192Sdan } 218*3fd65192Sdan } 219*3fd65192Sdan } 220*3fd65192Sdan} 221*3fd65192Sdan 222*3fd65192Sdanproc do_near_test {tn tbl expr} { 223*3fd65192Sdan 224*3fd65192Sdan get_near_results $tbl $expr aMatchinfo 225*3fd65192Sdan set match $expr 226*3fd65192Sdan 22799ebad90Sdan set matchinfo_asc [list] 22899ebad90Sdan foreach docid [lsort -integer -incr [array names aMatchinfo]] { 22999ebad90Sdan lappend matchinfo_asc $docid $aMatchinfo($docid) 23099ebad90Sdan } 23199ebad90Sdan set matchinfo_desc [list] 23299ebad90Sdan foreach docid [lsort -integer -decr [array names aMatchinfo]] { 23399ebad90Sdan lappend matchinfo_desc $docid $aMatchinfo($docid) 23499ebad90Sdan } 23599ebad90Sdan 236*3fd65192Sdan set title "(\"$match\" -> [llength [array names aMatchinfo]] rows)" 23799ebad90Sdan 23899ebad90Sdan do_execsql_test $tn$title.1 " 23999ebad90Sdan SELECT docid FROM $tbl WHERE $tbl MATCH '$match' ORDER BY docid ASC 240*3fd65192Sdan " [lsort -integer -incr [array names aMatchinfo]] 24199ebad90Sdan 24299ebad90Sdan do_execsql_test $tn$title.2 " 24399ebad90Sdan SELECT docid FROM $tbl WHERE $tbl MATCH '$match' ORDER BY docid DESC 244*3fd65192Sdan " [lsort -integer -decr [array names aMatchinfo]] 24599ebad90Sdan 24699ebad90Sdan do_execsql_test $tn$title.3 " 24799ebad90Sdan SELECT docid, mit(matchinfo($tbl, 'x')) FROM $tbl 24899ebad90Sdan WHERE $tbl MATCH '$match' ORDER BY docid DESC 24999ebad90Sdan " $matchinfo_desc 25099ebad90Sdan 25199ebad90Sdan do_execsql_test $tn$title.4 " 25299ebad90Sdan SELECT docid, mit(matchinfo($tbl, 'x')) FROM $tbl 25399ebad90Sdan WHERE $tbl MATCH '$match' ORDER BY docid ASC 25499ebad90Sdan " $matchinfo_asc 25599ebad90Sdan} 25699ebad90Sdan 257*3fd65192Sdan 258*3fd65192Sdan# End of test procs. Actual tests are below this line. 259*3fd65192Sdan#-------------------------------------------------------------------------- 260*3fd65192Sdan 261*3fd65192Sdan#-------------------------------------------------------------------------- 262*3fd65192Sdan# The following test cases - fts3auto-1.* - focus on testing the Tcl 263*3fd65192Sdan# command [fts3_near_match], which is used by other tests in this file. 264*3fd65192Sdan# 265*3fd65192Sdanproc test_fts3_near_match {tn doc expr res} { 266*3fd65192Sdan fts3_near_match $doc $expr -phrasecountvar p 267*3fd65192Sdan uplevel do_test [list $tn] [list [list set {} $p]] [list $res] 268*3fd65192Sdan} 269*3fd65192Sdan 270*3fd65192Sdantest_fts3_near_match 1.1.1 {a b c a b} a {2} 271*3fd65192Sdantest_fts3_near_match 1.1.2 {a b c a b} {a 5 b 6 c} {2 2 1} 272*3fd65192Sdantest_fts3_near_match 1.1.3 {a b c a b} {"a b"} {2} 273*3fd65192Sdantest_fts3_near_match 1.1.4 {a b c a b} {"b c"} {1} 274*3fd65192Sdantest_fts3_near_match 1.1.5 {a b c a b} {"c c"} {0} 275*3fd65192Sdan 276*3fd65192Sdantest_fts3_near_match 1.2.1 "a b c d e f g" {b 2 f} {0 0} 277*3fd65192Sdantest_fts3_near_match 1.2.2 "a b c d e f g" {b 3 f} {1 1} 278*3fd65192Sdantest_fts3_near_match 1.2.3 "a b c d e f g" {f 2 b} {0 0} 279*3fd65192Sdantest_fts3_near_match 1.2.4 "a b c d e f g" {f 3 b} {1 1} 280*3fd65192Sdantest_fts3_near_match 1.2.5 "a b c d e f g" {"a b" 2 "f g"} {0 0} 281*3fd65192Sdantest_fts3_near_match 1.2.6 "a b c d e f g" {"a b" 3 "f g"} {1 1} 282*3fd65192Sdan 283*3fd65192Sdanset 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" 284*3fd65192Sdantest_fts3_near_match 1.3.1 $A {"c d" 5 "i j" 1 "e f"} {0 0 0} 285*3fd65192Sdantest_fts3_near_match 1.3.2 $A {"c d" 5 "i j" 2 "e f"} {1 1 1} 286*3fd65192Sdan 287*3fd65192Sdan#-------------------------------------------------------------------------- 288*3fd65192Sdan# Test cases fts3auto-2.* run some simple tests using the 289*3fd65192Sdan# [do_near_test] proc. 290*3fd65192Sdan# 291*3fd65192Sdanforeach {tn create} { 292*3fd65192Sdan 1 "CREATE VIRTUAL TABLE t1 USING fts4(a, b)" 293*3fd65192Sdan 2 "CREATE VIRTUAL TABLE t1 USING fts4(a, b, order=DESC)" 294*3fd65192Sdan 3 "CREATE VIRTUAL TABLE t1 USING fts4(a, b, order=ASC)" 295*3fd65192Sdan 4 "CREATE VIRTUAL TABLE t1 USING fts4(a, b, prefix=1)" 296*3fd65192Sdan 5 "CREATE VIRTUAL TABLE t1 USING fts4(a, b, order=DESC, prefix=1)" 297*3fd65192Sdan 6 "CREATE VIRTUAL TABLE t1 USING fts4(a, b, order=ASC, prefix=1)" 298*3fd65192Sdan} { 299*3fd65192Sdan do_test 2.$tn.1 { 300*3fd65192Sdan catchsql { DROP TABLE t1 } 301*3fd65192Sdan execsql $create 30299ebad90Sdan for {set i 0} {$i<32} {incr i} { 30399ebad90Sdan set doc [list] 30499ebad90Sdan if {$i&0x01} {lappend doc one} 30599ebad90Sdan if {$i&0x02} {lappend doc two} 30699ebad90Sdan if {$i&0x04} {lappend doc three} 30799ebad90Sdan if {$i&0x08} {lappend doc four} 30899ebad90Sdan if {$i&0x10} {lappend doc five} 30999ebad90Sdan execsql { INSERT INTO t1 VALUES($doc, null) } 31099ebad90Sdan } 31199ebad90Sdan } {} 312*3fd65192Sdan foreach {tn2 expr} { 31399ebad90Sdan 1 {one} 31499ebad90Sdan 2 {one NEAR/1 five} 31599ebad90Sdan 3 {t*} 31699ebad90Sdan 4 {t* NEAR/0 five} 31799ebad90Sdan 5 {o* NEAR/1 f*} 31899ebad90Sdan 6 {one NEAR five NEAR two NEAR four NEAR three} 319*3fd65192Sdan 7 {one NEAR xyz} 320*3fd65192Sdan 8 {one OR two} 321*3fd65192Sdan 9 {one AND two} 322*3fd65192Sdan 10 {one NOT two} 323*3fd65192Sdan 11 {one AND two OR three} 324*3fd65192Sdan 12 {three OR one AND two} 325*3fd65192Sdan 13 {(three OR one) AND two} 326*3fd65192Sdan 14 {(three OR one) AND two NOT (five NOT four)} 327*3fd65192Sdan 15 {"one two"} 328*3fd65192Sdan 16 {"one two" NOT "three four"} 32999ebad90Sdan } { 330*3fd65192Sdan do_near_test 2.$tn.2.$tn2 t1 $expr 331*3fd65192Sdan } 33299ebad90Sdan} 33399ebad90Sdan 334*3fd65192Sdanset sqlite_fts3_enable_parentheses $sfep 33599ebad90Sdanfinish_test 33699ebad90Sdan 337