1 # 2011 January 27 2 # 3 # The author disclaims copyright to this source code. In place of 4 # a legal notice, here is a blessing: 5 # 6 # May you do good and not evil. 7 # May you find forgiveness for yourself and forgive others. 8 # May you share freely, never taking more than you give. 9 # 10 #************************************************************************* 11 # This file implements regression tests for SQLite library. The 12 # focus of this script is testing the FTS3 module. 13 # 14 15 set testdir [file dirname $argv0] 16 source $testdir/tester.tcl 17 ifcapable !fts3 { finish_test ; return } 18 set ::testprefix fts3aux1 19 20 do_execsql_test 1.1 { 21 CREATE VIRTUAL TABLE t1 USING fts4; 22 INSERT INTO t1 VALUES('one two three four'); 23 INSERT INTO t1 VALUES('three four five six'); 24 INSERT INTO t1 VALUES('one three five seven'); 25 26 CREATE VIRTUAL TABLE terms USING fts4aux(t1); 27 SELECT term, documents, occurrences FROM terms WHERE col = '*'; 28 } { 29 five 2 2 four 2 2 one 2 2 seven 1 1 30 six 1 1 three 3 3 two 1 1 31 } 32 33 do_execsql_test 1.2 { 34 INSERT INTO t1 VALUES('one one one three three three'); 35 SELECT term, documents, occurrences FROM terms WHERE col = '*'; 36 } { 37 five 2 2 four 2 2 one 3 5 seven 1 1 38 six 1 1 three 4 6 two 1 1 39 } 40 41 do_execsql_test 1.3 { 42 DELETE FROM t1; 43 SELECT term, documents, occurrences FROM terms WHERE col = '*'; 44 } {} 45 46 do_execsql_test 1.4 { 47 INSERT INTO t1 VALUES('a b a b a b a'); 48 INSERT INTO t1 SELECT * FROM t1; 49 INSERT INTO t1 SELECT * FROM t1; 50 INSERT INTO t1 SELECT * FROM t1; 51 INSERT INTO t1 SELECT * FROM t1; 52 INSERT INTO t1 SELECT * FROM t1; 53 INSERT INTO t1 SELECT * FROM t1; 54 INSERT INTO t1 SELECT * FROM t1; 55 INSERT INTO t1 SELECT * FROM t1; 56 SELECT term, documents, occurrences FROM terms WHERE col = '*'; 57 } {a 256 1024 b 256 768} 58 59 #------------------------------------------------------------------------- 60 # The following tests verify that the fts4aux module uses the full-text 61 # index to reduce the number of rows scanned in the following circumstances: 62 # 63 # * when there is equality comparison against the term column using the 64 # BINARY collating sequence. 65 # 66 # * when there is a range constraint on the term column using the BINARY 67 # collating sequence. 68 # 69 # And also uses the full-text index to optimize ORDER BY clauses of the 70 # form "ORDER BY term ASC" or equivalent. 71 # 72 # Test organization is: 73 # 74 # fts3aux1-2.1.*: equality constraints. 75 # fts3aux1-2.2.*: range constraints. 76 # fts3aux1-2.3.*: ORDER BY optimization. 77 # 78 79 do_execsql_test 2.0 { 80 DROP TABLE t1; 81 DROP TABLE terms; 82 83 CREATE VIRTUAL TABLE x1 USING fts4(x); 84 INSERT INTO x1(x1) VALUES('nodesize=24'); 85 CREATE VIRTUAL TABLE terms USING fts4aux(x1); 86 87 CREATE VIEW terms_v AS 88 SELECT term, documents, occurrences FROM terms WHERE col = '*'; 89 90 INSERT INTO x1 VALUES('braes brag bragged bragger bragging'); 91 INSERT INTO x1 VALUES('brags braid braided braiding braids'); 92 INSERT INTO x1 VALUES('brain brainchild brained braining brains'); 93 INSERT INTO x1 VALUES('brainstem brainstems brainstorm brainstorms'); 94 } 95 96 proc rec {varname x} { 97 global $varname 98 incr $varname 99 return 1 100 } 101 db func rec rec 102 103 # Use EQP to show that the WHERE expression "term='braid'" uses a different 104 # index number (1) than "+term='braid'" (0). 105 # 106 do_execsql_test 2.1.1.1 { 107 EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term='braid' 108 } { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)} } 109 do_execsql_test 2.1.1.2 { 110 EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term='braid' 111 } {0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)}} 112 113 # Now show that using "term='braid'" means the virtual table returns 114 # only 1 row to SQLite, but "+term='braid'" means all 19 are returned. 115 # 116 do_test 2.1.2.1 { 117 set cnt 0 118 execsql { SELECT * FROM terms_v WHERE rec('cnt', term) AND term='braid' } 119 set cnt 120 } {2} 121 do_test 2.1.2.2 { 122 set cnt 0 123 execsql { SELECT * FROM terms_v WHERE rec('cnt', term) AND +term='braid' } 124 set cnt 125 } {38} 126 127 # Similar to the test immediately above, but using a term ("breakfast") that 128 # is not featured in the dataset. 129 # 130 do_test 2.1.3.1 { 131 set cnt 0 132 execsql { SELECT * FROM terms_v WHERE rec('cnt', term) AND term='breakfast' } 133 set cnt 134 } {0} 135 do_test 2.1.3.2 { 136 set cnt 0 137 execsql { SELECT * FROM terms_v WHERE rec('cnt', term) AND +term='breakfast' } 138 set cnt 139 } {38} 140 141 do_execsql_test 2.1.4.1 { SELECT * FROM terms_v WHERE term='braid' } {braid 1 1} 142 do_execsql_test 2.1.4.2 { SELECT * FROM terms_v WHERE +term='braid'} {braid 1 1} 143 do_execsql_test 2.1.4.3 { SELECT * FROM terms_v WHERE term='breakfast' } {} 144 do_execsql_test 2.1.4.4 { SELECT * FROM terms_v WHERE +term='breakfast' } {} 145 146 do_execsql_test 2.1.4.5 { SELECT * FROM terms_v WHERE term='cba' } {} 147 do_execsql_test 2.1.4.6 { SELECT * FROM terms_v WHERE +term='cba' } {} 148 do_execsql_test 2.1.4.7 { SELECT * FROM terms_v WHERE term='abc' } {} 149 do_execsql_test 2.1.4.8 { SELECT * FROM terms_v WHERE +term='abc' } {} 150 151 # Special case: term=NULL 152 # 153 do_execsql_test 2.1.5 { SELECT * FROM terms WHERE term=NULL } {} 154 155 do_execsql_test 2.2.1.1 { 156 EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term>'brain' 157 } { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 2: (~0 rows)} } 158 do_execsql_test 2.2.1.2 { 159 EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term>'brain' 160 } { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} } 161 162 do_execsql_test 2.2.1.3 { 163 EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term<'brain' 164 } { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 4: (~0 rows)} } 165 do_execsql_test 2.2.1.4 { 166 EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term<'brain' 167 } { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} } 168 169 do_execsql_test 2.2.1.5 { 170 EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term BETWEEN 'brags' AND 'brain' 171 } { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 6: (~0 rows)} } 172 do_execsql_test 2.2.1.6 { 173 EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term BETWEEN 'brags' AND 'brain' 174 } { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} } 175 176 do_test 2.2.2.1 { 177 set cnt 0 178 execsql { SELECT * FROM terms WHERE rec('cnt', term) AND term>'brain' } 179 set cnt 180 } {18} 181 do_test 2.2.2.2 { 182 set cnt 0 183 execsql { SELECT * FROM terms WHERE rec('cnt', term) AND +term>'brain' } 184 set cnt 185 } {38} 186 do_execsql_test 2.2.2.3 { 187 SELECT term, documents, occurrences FROM terms_v WHERE term>'brain' 188 } { 189 brainchild 1 1 brained 1 1 braining 1 1 brains 1 1 190 brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 191 } 192 do_execsql_test 2.2.2.4 { 193 SELECT term, documents, occurrences FROM terms_v WHERE +term>'brain' 194 } { 195 brainchild 1 1 brained 1 1 braining 1 1 brains 1 1 196 brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 197 } 198 do_execsql_test 2.2.2.5 { 199 SELECT term, documents, occurrences FROM terms_v WHERE term>='brain' 200 } { 201 brain 1 1 202 brainchild 1 1 brained 1 1 braining 1 1 brains 1 1 203 brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 204 } 205 do_execsql_test 2.2.2.6 { 206 SELECT term, documents, occurrences FROM terms_v WHERE +term>='brain' 207 } { 208 brain 1 1 209 brainchild 1 1 brained 1 1 braining 1 1 brains 1 1 210 brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 211 } 212 213 do_execsql_test 2.2.2.7 { 214 SELECT term, documents, occurrences FROM terms_v WHERE term>='abc' 215 } { 216 braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 217 bragging 1 1 brags 1 1 braid 1 1 braided 1 1 218 braiding 1 1 braids 1 1 brain 1 1 brainchild 1 1 219 brained 1 1 braining 1 1 brains 1 1 brainstem 1 1 220 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 221 } 222 do_execsql_test 2.2.2.8 { 223 SELECT term, documents, occurrences FROM terms_v WHERE +term>='abc' 224 } { 225 braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 226 bragging 1 1 brags 1 1 braid 1 1 braided 1 1 227 braiding 1 1 braids 1 1 brain 1 1 brainchild 1 1 228 brained 1 1 braining 1 1 brains 1 1 brainstem 1 1 229 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 230 } 231 232 do_execsql_test 2.2.2.9 { 233 SELECT term, documents, occurrences FROM terms_v WHERE term>='brainstorms' 234 } {brainstorms 1 1} 235 do_execsql_test 2.2.2.10 { 236 SELECT term, documents, occurrences FROM terms_v WHERE term>='brainstorms' 237 } {brainstorms 1 1} 238 do_execsql_test 2.2.2.11 { SELECT * FROM terms_v WHERE term>'brainstorms' } {} 239 do_execsql_test 2.2.2.12 { SELECT * FROM terms_v WHERE term>'brainstorms' } {} 240 241 do_execsql_test 2.2.2.13 { SELECT * FROM terms_v WHERE term>'cba' } {} 242 do_execsql_test 2.2.2.14 { SELECT * FROM terms_v WHERE term>'cba' } {} 243 244 do_test 2.2.3.1 { 245 set cnt 0 246 execsql { SELECT * FROM terms WHERE rec('cnt', term) AND term<'brain' } 247 set cnt 248 } {22} 249 do_test 2.2.3.2 { 250 set cnt 0 251 execsql { SELECT * FROM terms WHERE rec('cnt', term) AND +term<'brain' } 252 set cnt 253 } {38} 254 do_execsql_test 2.2.3.3 { 255 SELECT term, documents, occurrences FROM terms_v WHERE term<'brain' 256 } { 257 braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1 258 brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 259 } 260 do_execsql_test 2.2.3.4 { 261 SELECT term, documents, occurrences FROM terms_v WHERE +term<'brain' 262 } { 263 braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1 264 brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 265 } 266 do_execsql_test 2.2.3.5 { 267 SELECT term, documents, occurrences FROM terms_v WHERE term<='brain' 268 } { 269 braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1 270 brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 271 brain 1 1 272 } 273 do_execsql_test 2.2.3.6 { 274 SELECT term, documents, occurrences FROM terms_v WHERE +term<='brain' 275 } { 276 braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1 277 brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 278 brain 1 1 279 } 280 281 do_test 2.2.4.1 { 282 set cnt 0 283 execsql { 284 SELECT term, documents, occurrences FROM terms 285 WHERE rec('cnt', term) AND term BETWEEN 'brags' AND 'brain' 286 } 287 set cnt 288 } {12} 289 do_test 2.2.4.2 { 290 set cnt 0 291 execsql { 292 SELECT term, documents, occurrences FROM terms 293 WHERE rec('cnt', term) AND +term BETWEEN 'brags' AND 'brain' 294 } 295 set cnt 296 } {38} 297 do_execsql_test 2.2.4.3 { 298 SELECT term, documents, occurrences FROM terms_v 299 WHERE rec('cnt', term) AND term BETWEEN 'brags' AND 'brain' 300 } { 301 brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 brain 1 1 302 } 303 do_execsql_test 2.2.4.4 { 304 SELECT term, documents, occurrences FROM terms_v 305 WHERE rec('cnt', term) AND +term BETWEEN 'brags' AND 'brain' 306 } { 307 brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 brain 1 1 308 } 309 do_execsql_test 2.2.4.5 { 310 SELECT term, documents, occurrences FROM terms_v 311 WHERE rec('cnt', term) AND term > 'brags' AND term < 'brain' 312 } { 313 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 314 } 315 do_execsql_test 2.2.4.6 { 316 SELECT term, documents, occurrences FROM terms_v 317 WHERE rec('cnt', term) AND +term > 'brags' AND +term < 'brain' 318 } { 319 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 320 } 321 322 # Check that "ORDER BY term ASC" and equivalents are sorted by the 323 # virtual table implementation. Any other ORDER BY clause requires 324 # SQLite to sort results using a temporary b-tree. 325 # 326 foreach {tn sort orderby} { 327 1 0 "ORDER BY term ASC" 328 2 0 "ORDER BY term" 329 3 1 "ORDER BY term DESC" 330 4 1 "ORDER BY documents ASC" 331 5 1 "ORDER BY documents" 332 6 1 "ORDER BY documents DESC" 333 7 1 "ORDER BY occurrences ASC" 334 8 1 "ORDER BY occurrences" 335 9 1 "ORDER BY occurrences DESC" 336 } { 337 338 set res [list 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)}] 339 if {$sort} { lappend res 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } 340 341 set sql "SELECT * FROM terms $orderby" 342 do_execsql_test 2.3.1.$tn "EXPLAIN QUERY PLAN $sql" $res 343 } 344 345 #------------------------------------------------------------------------- 346 # The next set of tests, fts3aux1-3.*, test error conditions in the 347 # fts4aux module. Except, fault injection testing (OOM, IO error etc.) is 348 # done in fts3fault2.test 349 # 350 351 do_execsql_test 3.1.1 { 352 CREATE VIRTUAL TABLE t2 USING fts4; 353 } 354 355 do_catchsql_test 3.1.2 { 356 CREATE VIRTUAL TABLE terms2 USING fts4aux; 357 } {1 {wrong number of arguments to fts4aux constructor}} 358 do_catchsql_test 3.1.3 { 359 CREATE VIRTUAL TABLE terms2 USING fts4aux(t2, t2); 360 } {1 {wrong number of arguments to fts4aux constructor}} 361 362 do_execsql_test 3.2.1 { 363 CREATE VIRTUAL TABLE terms3 USING fts4aux(does_not_exist) 364 } 365 do_catchsql_test 3.2.2 { 366 SELECT * FROM terms3 367 } {1 {SQL logic error or missing database}} 368 do_catchsql_test 3.2.3 { 369 SELECT * FROM terms3 WHERE term = 'abc' 370 } {1 {SQL logic error or missing database}} 371 372 do_catchsql_test 3.3.1 { 373 INSERT INTO terms VALUES(1,2,3); 374 } {1 {table terms may not be modified}} 375 do_catchsql_test 3.3.2 { 376 DELETE FROM terms 377 } {1 {table terms may not be modified}} 378 do_catchsql_test 3.3.3 { 379 UPDATE terms set documents = documents+1; 380 } {1 {table terms may not be modified}} 381 382 383 #------------------------------------------------------------------------- 384 # The following tests - fts4aux-4.* - test that joins work with fts4aux 385 # tables. And that fts4aux provides reasonably sane cost information via 386 # xBestIndex to the query planner. 387 # 388 db close 389 forcedelete test.db 390 sqlite3 db test.db 391 do_execsql_test 4.1 { 392 CREATE VIRTUAL TABLE x1 USING fts4(x); 393 CREATE VIRTUAL TABLE terms USING fts4aux(x1); 394 CREATE TABLE x2(y); 395 CREATE TABLE x3(y); 396 CREATE INDEX i1 ON x3(y); 397 398 INSERT INTO x1 VALUES('a b c d e'); 399 INSERT INTO x1 VALUES('f g h i j'); 400 INSERT INTO x1 VALUES('k k l l a'); 401 402 INSERT INTO x2 SELECT term FROM terms WHERE col = '*'; 403 INSERT INTO x3 SELECT term FROM terms WHERE col = '*'; 404 } 405 406 proc do_plansql_test {tn sql r} { 407 uplevel do_execsql_test $tn [list "EXPLAIN QUERY PLAN $sql ; $sql"] [list $r] 408 } 409 410 do_plansql_test 4.2 { 411 SELECT y FROM x2, terms WHERE y = term AND col = '*' 412 } { 413 0 0 0 {SCAN TABLE x2 (~1000000 rows)} 414 0 1 1 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)} 415 a b c d e f g h i j k l 416 } 417 418 do_plansql_test 4.3 { 419 SELECT y FROM terms, x2 WHERE y = term AND col = '*' 420 } { 421 0 0 1 {SCAN TABLE x2 (~1000000 rows)} 422 0 1 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)} 423 a b c d e f g h i j k l 424 } 425 426 do_plansql_test 4.4 { 427 SELECT y FROM x3, terms WHERE y = term AND col = '*' 428 } { 429 0 0 1 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} 430 0 1 0 {SEARCH TABLE x3 USING COVERING INDEX i1 (y=?) (~10 rows)} 431 a b c d e f g h i j k l 432 } 433 434 do_plansql_test 4.5 { 435 SELECT y FROM terms, x3 WHERE y = term AND occurrences>1 AND col = '*' 436 } { 437 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} 438 0 1 1 {SEARCH TABLE x3 USING COVERING INDEX i1 (y=?) (~10 rows)} 439 a k l 440 } 441 442 #------------------------------------------------------------------------- 443 # The following tests check that fts4aux can handle an fts table with an 444 # odd name (one that requires quoting for use in SQL statements). And that 445 # the argument to the fts4aux constructor is properly dequoted before use. 446 # 447 # 448 do_execsql_test 5.1 { 449 CREATE VIRTUAL TABLE "abc '!' def" USING fts4(x, y); 450 INSERT INTO "abc '!' def" VALUES('XX', 'YY'); 451 452 CREATE VIRTUAL TABLE terms3 USING fts4aux("abc '!' def"); 453 SELECT * FROM terms3; 454 } {xx * 1 1 xx 0 1 1 yy * 1 1 yy 1 1 1} 455 456 do_execsql_test 5.2 { 457 CREATE VIRTUAL TABLE "%%^^%%" USING fts4aux('abc ''!'' def'); 458 SELECT * FROM "%%^^%%"; 459 } {xx * 1 1 xx 0 1 1 yy * 1 1 yy 1 1 1} 460 461 462 finish_test 463