1# 2014 June 17 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# 12# This file is focused on OOM errors. 13# 14 15source [file join [file dirname [info script]] fts5_common.tcl] 16source $testdir/malloc_common.tcl 17set testprefix fts5fault4 18 19# If SQLITE_ENABLE_FTS5 is not defined, omit this file. 20ifcapable !fts5 { 21 finish_test 22 return 23} 24 25set ::TMPDBERROR [list 1 \ 26 {unable to open a temporary database file for storing temporary tables} 27] 28 29#------------------------------------------------------------------------- 30# An OOM while dropping an fts5 table. 31# 32db func rnddoc fts5_rnddoc 33do_test 1.0 { 34 execsql { CREATE VIRTUAL TABLE xx USING fts5(x) } 35} {} 36faultsim_save_and_close 37 38do_faultsim_test 1 -faults oom-* -prep { 39 faultsim_restore_and_reopen 40 execsql { SELECT * FROM xx } 41} -body { 42 execsql { DROP TABLE xx } 43} -test { 44 faultsim_test_result [list 0 {}] 45} 46 47#------------------------------------------------------------------------- 48# An OOM while "reseeking" an FTS cursor. 49# 50do_execsql_test 3.0 { 51 CREATE VIRTUAL TABLE jj USING fts5(j); 52 INSERT INTO jj(rowid, j) VALUES(101, 'm t w t f s s'); 53 INSERT INTO jj(rowid, j) VALUES(202, 't w t f s'); 54 INSERT INTO jj(rowid, j) VALUES(303, 'w t f'); 55 INSERT INTO jj(rowid, j) VALUES(404, 't'); 56} 57faultsim_save_and_close 58 59do_faultsim_test 3 -faults oom-* -prep { 60 faultsim_restore_and_reopen 61 execsql { SELECT * FROM jj } 62} -body { 63 set res [list] 64 db eval { SELECT rowid FROM jj WHERE jj MATCH 't' } { 65 lappend res $rowid 66 if {$rowid==303} { 67 execsql { DELETE FROM jj WHERE rowid=404 } 68 } 69 } 70 set res 71} -test { 72 faultsim_test_result [list 0 {101 202 303}] 73} 74 75#------------------------------------------------------------------------- 76# An OOM within a special "*reads" query. 77# 78reset_db 79db func rnddoc fts5_rnddoc 80do_execsql_test 4.0 { 81 CREATE VIRTUAL TABLE x1 USING fts5(x); 82 INSERT INTO x1(x1, rank) VALUES('pgsz', 32); 83 84 WITH ii(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<10 ) 85 INSERT INTO x1 SELECT rnddoc(5) FROM ii; 86} 87 88set ::res [db eval {SELECT rowid, x1 FROM x1 WHERE x1 MATCH '*reads'}] 89 90do_faultsim_test 4 -faults oom-* -body { 91 db eval {SELECT rowid, x, x1 FROM x1 WHERE x1 MATCH '*reads'} 92} -test { 93 faultsim_test_result {0 {0 {} 3}} 94} 95 96#------------------------------------------------------------------------- 97# An OOM within a query that uses a custom rank function. 98# 99reset_db 100do_execsql_test 5.0 { 101 PRAGMA encoding='utf16'; 102 CREATE VIRTUAL TABLE x2 USING fts5(x); 103 INSERT INTO x2(rowid, x) VALUES(10, 'a b c'); -- 3 104 INSERT INTO x2(rowid, x) VALUES(20, 'a b c'); -- 6 105 INSERT INTO x2(rowid, x) VALUES(30, 'a b c'); -- 2 106 INSERT INTO x2(rowid, x) VALUES(40, 'a b c'); -- 5 107 INSERT INTO x2(rowid, x) VALUES(50, 'a b c'); -- 1 108} 109 110proc rowidmod {cmd mod} { 111 set row [$cmd xRowid] 112 expr {$row % $mod} 113} 114sqlite3_fts5_create_function db rowidmod rowidmod 115 116do_faultsim_test 5.1 -faults oom-* -body { 117 db eval { 118 SELECT rowid || '-' || rank FROM x2 WHERE x2 MATCH 'b' AND 119 rank MATCH "rowidmod('7')" ORDER BY rank 120 } 121} -test { 122 faultsim_test_result {0 {50-1 30-2 10-3 40-5 20-6}} 123} 124 125proc rowidprefix {cmd prefix} { 126 set row [$cmd xRowid] 127 set {} "${row}-${prefix}" 128} 129sqlite3_fts5_create_function db rowidprefix rowidprefix 130 131set str [string repeat abcdefghijklmnopqrstuvwxyz 10] 132do_faultsim_test 5.2 -faults oom-* -body { 133 db eval " 134 SELECT rank, x FROM x2 WHERE x2 MATCH 'b' AND 135 rank MATCH 'rowidprefix(''$::str'')' 136 LIMIT 1 137 " 138} -test { 139 faultsim_test_result "0 {10-$::str {a b c}}" 140} 141 142 143#------------------------------------------------------------------------- 144# OOM errors within auxiliary functions. 145# 146reset_db 147do_execsql_test 6.0 { 148 CREATE VIRTUAL TABLE x3 USING fts5(xxx); 149 INSERT INTO x3 VALUES('a b c d c b a'); 150 INSERT INTO x3 VALUES('a a a a a a a'); 151 INSERT INTO x3 VALUES('a a a a a a a'); 152} 153 154do_faultsim_test 6.1 -faults oom-t* -body { 155 db eval { SELECT highlight(x3, 0, '*', '*') FROM x3 WHERE x3 MATCH 'c' } 156} -test { 157 faultsim_test_result {0 {{a b *c* d *c* b a}}} 158} 159 160proc firstinst {cmd} { 161 foreach {p c o} [$cmd xInst 0] {} 162 expr $c*100 + $o 163} 164sqlite3_fts5_create_function db firstinst firstinst 165 166do_faultsim_test 6.2 -faults oom-t* -body { 167 db eval { SELECT firstinst(x3) FROM x3 WHERE x3 MATCH 'c' } 168} -test { 169 faultsim_test_result {0 2} {1 SQLITE_NOMEM} 170} 171 172proc previc {cmd} { 173 set res [$cmd xGetAuxdataInt 0] 174 $cmd xSetAuxdataInt [$cmd xInstCount] 175 return $res 176} 177sqlite3_fts5_create_function db previc previc 178 179do_faultsim_test 6.2 -faults oom-t* -body { 180 db eval { SELECT previc(x3) FROM x3 WHERE x3 MATCH 'a' } 181} -test { 182 faultsim_test_result {0 {0 2 7}} {1 SQLITE_NOMEM} 183} 184 185#------------------------------------------------------------------------- 186# OOM error when querying for a phrase with many tokens. 187# 188reset_db 189do_execsql_test 7.0 { 190 CREATE VIRTUAL TABLE tt USING fts5(x, y); 191 INSERT INTO tt VALUES('f b g b c b', 'f a d c c b'); -- 1 192 INSERT INTO tt VALUES('d a e f e d', 'f b b d e e'); -- 2 193 INSERT INTO tt VALUES('f b g a d c', 'e f c f a d'); -- 3 194 INSERT INTO tt VALUES('f f c d g f', 'f a e b g b'); -- 4 195 INSERT INTO tt VALUES('a g b d a g', 'e g a e a c'); -- 5 196 INSERT INTO tt VALUES('c d b d e f', 'f g e g e e'); -- 6 197 INSERT INTO tt VALUES('e g f f b c', 'f c e f g f'); -- 7 198 INSERT INTO tt VALUES('e g c f c e', 'f e e a f g'); -- 8 199 INSERT INTO tt VALUES('e a e b e e', 'd c c f f f'); -- 9 200 INSERT INTO tt VALUES('f a g g c c', 'e g d g c e'); -- 10 201 INSERT INTO tt VALUES('c d b a e f', 'f g e h e e'); -- 11 202 203 CREATE VIRTUAL TABLE tt2 USING fts5(o); 204 INSERT INTO tt2(rowid, o) SELECT rowid, x||' '||y FROM tt; 205 INSERT INTO tt2(rowid, o) VALUES(12, 'a b c d e f g h i j k l'); 206} 207 208do_faultsim_test 7.2 -faults oom-* -body { 209 db eval { SELECT rowid FROM tt WHERE tt MATCH 'f+g+e+g+e+e' } 210} -test { 211 faultsim_test_result {0 6} {1 SQLITE_NOMEM} 212} 213 214do_faultsim_test 7.3 -faults oom-* -body { 215 db eval { SELECT rowid FROM tt WHERE tt MATCH 'NEAR(a b c d e f)' } 216} -test { 217 faultsim_test_result {0 11} {1 SQLITE_NOMEM} 218} 219 220do_faultsim_test 7.4 -faults oom-t* -body { 221 db eval { SELECT rowid FROM tt2 WHERE tt2 MATCH '"g c f c e f e e a f"' } 222} -test { 223 faultsim_test_result {0 8} {1 SQLITE_NOMEM} 224} 225 226do_faultsim_test 7.5 -faults oom-* -body { 227 db eval {SELECT rowid FROM tt2 WHERE tt2 MATCH 'NEAR(a b c d e f g h i j k)'} 228} -test { 229 faultsim_test_result {0 12} {1 SQLITE_NOMEM} 230} 231 232do_faultsim_test 7.6 -faults oom-* -body { 233 db eval {SELECT rowid FROM tt WHERE tt MATCH 'y: "c c"'} 234} -test { 235 faultsim_test_result {0 {1 9}} {1 SQLITE_NOMEM} 236} 237 238#------------------------------------------------------------------------- 239# 240reset_db 241do_execsql_test 8.0 { 242 CREATE VIRTUAL TABLE tt USING fts5(x); 243 INSERT INTO tt(tt, rank) VALUES('pgsz', 32); 244 BEGIN; 245 INSERT INTO tt(rowid, x) VALUES(1, 'a b c d x x'); 246 WITH ii(i) AS (SELECT 2 UNION ALL SELECT i+1 FROM ii WHERE i<99) 247 INSERT INTO tt(rowid, x) SELECT i, 'a b c x x d' FROM ii; 248 INSERT INTO tt(rowid, x) VALUES(100, 'a b c d x x'); 249 COMMIT; 250} 251 252do_faultsim_test 8.1 -faults oom-t* -body { 253 db eval { SELECT rowid FROM tt WHERE tt MATCH 'NEAR(a b c d, 2)' } 254} -test { 255 faultsim_test_result {0 {1 100}} {1 SQLITE_NOMEM} 256} 257 258do_faultsim_test 8.2 -faults oom-t* -body { 259 db eval { SELECT count(*) FROM tt WHERE tt MATCH 'a OR d' } 260} -test { 261 faultsim_test_result {0 100} {1 SQLITE_NOMEM} 262} 263 264 265#------------------------------------------------------------------------- 266# Fault in NOT query. 267# 268reset_db 269do_execsql_test 9.0 { 270 CREATE VIRTUAL TABLE tt USING fts5(x); 271 INSERT INTO tt(tt, rank) VALUES('pgsz', 32); 272 BEGIN; 273 WITH ii(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<200) 274 INSERT INTO tt(rowid, x) 275 SELECT i, CASE WHEN (i%50)==0 THEN 'a a a a a a' ELSE 'a x a x a x' END 276 FROM ii; 277 COMMIT; 278} 279 280do_faultsim_test 9.1 -faults oom-* -body { 281 db eval { SELECT rowid FROM tt WHERE tt MATCH 'a NOT x' } 282} -test { 283 faultsim_test_result {0 {50 100 150 200}} {1 SQLITE_NOMEM} 284} 285 286#------------------------------------------------------------------------- 287# OOM in fts5_expr() SQL function. 288# 289do_faultsim_test 10.1 -faults oom-t* -body { 290 db one { SELECT fts5_expr('a AND b NEAR(a b)') } 291} -test { 292 faultsim_test_result {0 {"a" AND "b" AND NEAR("a" "b", 10)}} 293} 294 295do_faultsim_test 10.2 -faults oom-t* -body { 296 db one { SELECT fts5_expr_tcl('x:"a b c" AND b NEAR(a b)', 'ns', 'x') } 297} -test { 298 set res {AND [ns -col 0 -- {a b c}] [ns -- {b}] [ns -near 10 -- {a} {b}]} 299 faultsim_test_result [list 0 $res] 300} 301 302do_faultsim_test 10.3 -faults oom-t* -body { 303 db one { SELECT fts5_expr('x:a', 'x') } 304} -test { 305 faultsim_test_result {0 {x : "a"}} 306} 307 308#------------------------------------------------------------------------- 309# OOM while configuring 'rank' option. 310# 311reset_db 312do_execsql_test 11.0 { 313 CREATE VIRTUAL TABLE ft USING fts5(x); 314} 315do_faultsim_test 11.1 -faults oom-t* -body { 316 db eval { INSERT INTO ft(ft, rank) VALUES('rank', 'bm25(10.0, 5.0)') } 317} -test { 318 faultsim_test_result {0 {}} {1 {disk I/O error}} 319} 320 321#------------------------------------------------------------------------- 322# OOM while creating an fts5vocab table. 323# 324reset_db 325do_execsql_test 12.0 { 326 CREATE VIRTUAL TABLE ft USING fts5(x); 327} 328faultsim_save_and_close 329do_faultsim_test 12.1 -faults oom-t* -prep { 330 faultsim_restore_and_reopen 331 db eval { SELECT * FROM sqlite_master } 332} -body { 333 db eval { CREATE VIRTUAL TABLE vv USING fts5vocab(ft, 'row') } 334} -test { 335 faultsim_test_result {0 {}} 336} 337 338#------------------------------------------------------------------------- 339# OOM while querying an fts5vocab table. 340# 341reset_db 342do_execsql_test 13.0 { 343 CREATE VIRTUAL TABLE ft USING fts5(x); 344 INSERT INTO ft VALUES('a b'); 345 CREATE VIRTUAL TABLE vv USING fts5vocab(ft, 'row'); 346} 347faultsim_save_and_close 348do_faultsim_test 13.1 -faults oom-t* -prep { 349 faultsim_restore_and_reopen 350 db eval { SELECT * FROM vv } 351} -body { 352 db eval { SELECT * FROM vv } 353} -test { 354 faultsim_test_result {0 {a 1 1 b 1 1}} 355} 356 357#------------------------------------------------------------------------- 358# OOM in multi-column token query. 359# 360reset_db 361do_execsql_test 13.0 { 362 CREATE VIRTUAL TABLE ft USING fts5(x, y, z); 363 INSERT INTO ft(ft, rank) VALUES('pgsz', 32); 364 INSERT INTO ft VALUES( 365 'x x x x x x x x x x x x x x x x', 366 'y y y y y y y y y y y y y y y y', 367 'z z z z z z z z x x x x x x x x' 368 ); 369 INSERT INTO ft SELECT * FROM ft; 370 INSERT INTO ft SELECT * FROM ft; 371 INSERT INTO ft SELECT * FROM ft; 372 INSERT INTO ft SELECT * FROM ft; 373} 374faultsim_save_and_close 375do_faultsim_test 13.1 -faults oom-t* -prep { 376 faultsim_restore_and_reopen 377 db eval { SELECT * FROM ft } 378} -body { 379 db eval { SELECT rowid FROM ft WHERE ft MATCH '{x z}: x' } 380} -test { 381 faultsim_test_result {0 {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16}} 382} 383 384#------------------------------------------------------------------------- 385# OOM in an "ALTER TABLE RENAME TO" 386# 387reset_db 388do_execsql_test 14.0 { 389 CREATE VIRTUAL TABLE "tbl one" USING fts5(x, y, z); 390} 391faultsim_save_and_close 392do_faultsim_test 14.1 -faults oom-t* -prep { 393 faultsim_restore_and_reopen 394 db eval { SELECT * FROM "tbl one" } 395} -body { 396 db eval { ALTER TABLE "tbl one" RENAME TO "tbl two" } 397} -test { 398 faultsim_test_result {0 {}} $::TMPDBERROR 399} 400 401finish_test 402