1# 2009 October 22 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 contains tests to verify that malloc() errors that occur 13# within the FTS3 module code are handled correctly. 14# 15 16set testdir [file dirname $argv0] 17source $testdir/tester.tcl 18ifcapable !fts3 { finish_test ; return } 19source $testdir/malloc_common.tcl 20source $testdir/fts3_common.tcl 21 22# Ensure the lookaside buffer is disabled for these tests. 23# 24sqlite3 db test.db 25sqlite3_db_config_lookaside db 0 0 0 26 27set sqlite_fts3_enable_parentheses 1 28set DO_MALLOC_TEST 1 29 30# Test organization: 31# 32# fts3_malloc-1.*: Test OOM during CREATE and DROP table statements. 33# fts3_malloc-2.*: Test OOM during SELECT operations. 34# fts3_malloc-3.*: Test OOM during SELECT operations with a larger database. 35# fts3_malloc-4.*: Test OOM during database write operations. 36# 37# 38 39#------------------------------------------------------------------------- 40# This proc is used to test a single SELECT statement. Parameter $name is 41# passed a name for the test case (i.e. "fts3_malloc-1.4.1") and parameter 42# $sql is passed the text of the SELECT statement. Parameter $result is 43# set to the expected output if the SELECT statement is successfully 44# executed using [db eval]. 45# 46# Example: 47# 48# do_select_test testcase-1.1 "SELECT 1+1, 1+2" {1 2} 49# 50# If global variable DO_MALLOC_TEST is set to a non-zero value, or if 51# it is not defined at all, then OOM testing is performed on the SELECT 52# statement. Each OOM test case is said to pass if either (a) executing 53# the SELECT statement succeeds and the results match those specified 54# by parameter $result, or (b) TCL throws an "out of memory" error. 55# 56# If DO_MALLOC_TEST is defined and set to zero, then the SELECT statement 57# is executed just once. In this case the test case passes if the results 58# match the expected results passed via parameter $result. 59# 60proc do_select_test {name sql result} { 61 doPassiveTest $name $sql [list 0 $result] 62} 63 64proc do_error_test {name sql error} { 65 doPassiveTest $name $sql [list 1 $error] 66} 67 68proc doPassiveTest {name sql catchres} { 69 if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 } 70 71 if {$::DO_MALLOC_TEST} { 72 set answers [list {1 {out of memory}} $catchres] 73 set modes [list 100000 transient 1 persistent] 74 } else { 75 set answers [list $catchres] 76 set modes [list 0 nofail] 77 } 78 set str [join $answers " OR "] 79 80 foreach {nRepeat zName} $modes { 81 for {set iFail 1} 1 {incr iFail} { 82 if {$::DO_MALLOC_TEST} {sqlite3_memdebug_fail $iFail -repeat $nRepeat} 83 84 set res [catchsql $sql] 85 if {[lsearch $answers $res]>=0} { 86 set res $str 87 } 88 do_test $name.$zName.$iFail [list set {} $res] $str 89 set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign] 90 if {$nFail==0} break 91 } 92 } 93} 94 95 96#------------------------------------------------------------------------- 97# Test a single write to the database. In this case a "write" is a 98# DELETE, UPDATE or INSERT statement. 99# 100# If OOM testing is performed, there are several acceptable outcomes: 101# 102# 1) The write succeeds. No error is returned. 103# 104# 2) An "out of memory" exception is thrown and: 105# 106# a) The statement has no effect, OR 107# b) The current transaction is rolled back, OR 108# c) The statement succeeds. This can only happen if the connection 109# is in auto-commit mode (after the statement is executed, so this 110# includes COMMIT statements). 111# 112# If the write operation eventually succeeds, zero is returned. If a 113# transaction is rolled back, non-zero is returned. 114# 115# Parameter $name is the name to use for the test case (or test cases). 116# The second parameter, $tbl, should be the name of the database table 117# being modified. Parameter $sql contains the SQL statement to test. 118# 119proc do_write_test {name tbl sql} { 120 if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 } 121 122 # Figure out an statement to get a checksum for table $tbl. 123 db eval "SELECT * FROM $tbl" V break 124 set cksumsql "SELECT md5sum([join [concat rowid $V(*)] ,]) FROM $tbl" 125 126 # Calculate the initial table checksum. 127 set cksum1 [db one $cksumsql] 128 129 130 if {$::DO_MALLOC_TEST } { 131 set answers [list {1 {out of memory}} {0 {}}] 132 set modes [list 100000 transient 1 persistent] 133 } else { 134 set answers [list {0 {}}] 135 set modes [list 0 nofail] 136 } 137 set str [join $answers " OR "] 138 139 foreach {nRepeat zName} $modes { 140 for {set iFail 1} 1 {incr iFail} { 141 if {$::DO_MALLOC_TEST} {sqlite3_memdebug_fail $iFail -repeat $nRepeat} 142 143 set res [catchsql $sql] 144 set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign] 145 if {$nFail==0} { 146 do_test $name.$zName.$iFail [list set {} $res] {0 {}} 147 return 148 } else { 149 if {[lsearch $answers $res]>=0} { 150 set res $str 151 } 152 do_test $name.$zName.$iFail [list set {} $res] $str 153 set cksum2 [db one $cksumsql] 154 if {$cksum1 != $cksum2} return 155 } 156 } 157 } 158} 159 160proc normal_list {l} { 161 set ret [list] 162 foreach elem $l {lappend ret $elem} 163 set ret 164} 165 166 167do_write_test fts3_malloc-1.1 sqlite_master { 168 CREATE VIRTUAL TABLE ft1 USING fts3(a, b) 169} 170do_write_test fts3_malloc-1.2 sqlite_master { 171 CREATE VIRTUAL TABLE ft2 USING fts3([a], [b]); 172} 173do_write_test fts3_malloc-1.3 sqlite_master { 174 CREATE VIRTUAL TABLE ft3 USING fts3('a', "b"); 175} 176do_write_test fts3_malloc-1.4 sqlite_master { 177 CREATE VIRTUAL TABLE ft4 USING fts3(`a`, 'fred''s column'); 178} 179do_error_test fts3_malloc-1.5 { 180 CREATE VIRTUAL TABLE ft5 USING fts3(a, b, tokenize unknown) 181} {unknown tokenizer: unknown} 182do_write_test fts3_malloc-1.6 sqlite_master { 183 CREATE VIRTUAL TABLE ft6 USING fts3(a, b, tokenize porter) 184} 185 186# Test the xConnect/xDisconnect methods: 187#db eval { ATTACH 'test2.db' AS aux } 188#do_write_test fts3_malloc-1.6 aux.sqlite_master { 189# CREATE VIRTUAL TABLE aux.ft7 USING fts3(a, b, c); 190#} 191#do_write_test fts3_malloc-1.6 aux.sqlite_master { 192# CREATE VIRTUAL TABLE aux.ft7 USING fts3(a, b, c); 193#} 194 195 196 197do_test fts3_malloc-2.0 { 198 execsql { 199 DROP TABLE ft1; 200 DROP TABLE ft2; 201 DROP TABLE ft3; 202 DROP TABLE ft4; 203 DROP TABLE ft6; 204 } 205 execsql { CREATE VIRTUAL TABLE ft USING fts3(a, b) } 206 for {set ii 1} {$ii < 32} {incr ii} { 207 set a [list] 208 set b [list] 209 if {$ii & 0x01} {lappend a one ; lappend b neung} 210 if {$ii & 0x02} {lappend a two ; lappend b song } 211 if {$ii & 0x04} {lappend a three ; lappend b sahm } 212 if {$ii & 0x08} {lappend a four ; lappend b see } 213 if {$ii & 0x10} {lappend a five ; lappend b hah } 214 execsql { INSERT INTO ft VALUES($a, $b) } 215 } 216} {} 217 218foreach {tn sql result} { 219 1 "SELECT count(*) FROM sqlite_master" {5} 220 2 "SELECT * FROM ft WHERE docid = 1" {one neung} 221 3 "SELECT * FROM ft WHERE docid = 2" {two song} 222 4 "SELECT * FROM ft WHERE docid = 3" {{one two} {neung song}} 223 224 5 "SELECT a FROM ft" { 225 {one} {two} {one two} 226 {three} {one three} {two three} 227 {one two three} {four} {one four} 228 {two four} {one two four} {three four} 229 {one three four} {two three four} {one two three four} 230 {five} {one five} {two five} 231 {one two five} {three five} {one three five} 232 {two three five} {one two three five} {four five} 233 {one four five} {two four five} {one two four five} 234 {three four five} {one three four five} {two three four five} 235 {one two three four five} 236 } 237 238 6 "SELECT a FROM ft WHERE a MATCH 'one'" { 239 {one} {one two} {one three} {one two three} 240 {one four} {one two four} {one three four} {one two three four} 241 {one five} {one two five} {one three five} {one two three five} 242 {one four five} {one two four five} 243 {one three four five} {one two three four five} 244 } 245 246 7 "SELECT a FROM ft WHERE a MATCH 'o*'" { 247 {one} {one two} {one three} {one two three} 248 {one four} {one two four} {one three four} {one two three four} 249 {one five} {one two five} {one three five} {one two three five} 250 {one four five} {one two four five} 251 {one three four five} {one two three four five} 252 } 253 254 8 "SELECT a FROM ft WHERE a MATCH 'o* t*'" { 255 {one two} {one three} {one two three} 256 {one two four} {one three four} {one two three four} 257 {one two five} {one three five} {one two three five} 258 {one two four five} {one three four five} {one two three four five} 259 } 260 261 9 "SELECT a FROM ft WHERE a MATCH '\"o* t*\"'" { 262 {one two} {one three} {one two three} 263 {one two four} {one three four} {one two three four} 264 {one two five} {one three five} {one two three five} 265 {one two four five} {one three four five} {one two three four five} 266 } 267 268 10 {SELECT a FROM ft WHERE a MATCH '"o* f*"'} { 269 {one four} {one five} {one four five} 270 } 271 272 11 {SELECT a FROM ft WHERE a MATCH '"one two three"'} { 273 {one two three} 274 {one two three four} 275 {one two three five} 276 {one two three four five} 277 } 278 279 12 {SELECT a FROM ft WHERE a MATCH '"two three four"'} { 280 {two three four} 281 {one two three four} 282 {two three four five} 283 {one two three four five} 284 } 285 286 12 {SELECT a FROM ft WHERE a MATCH '"two three" five'} { 287 {two three five} {one two three five} 288 {two three four five} {one two three four five} 289 } 290 291 13 {SELECT a FROM ft WHERE ft MATCH '"song sahm" hah'} { 292 {two three five} {one two three five} 293 {two three four five} {one two three four five} 294 } 295 296 14 {SELECT a FROM ft WHERE b MATCH 'neung'} { 297 {one} {one two} 298 {one three} {one two three} 299 {one four} {one two four} 300 {one three four} {one two three four} 301 {one five} {one two five} 302 {one three five} {one two three five} 303 {one four five} {one two four five} 304 {one three four five} {one two three four five} 305 } 306 307 15 {SELECT a FROM ft WHERE b MATCH '"neung song sahm"'} { 308 {one two three} {one two three four} 309 {one two three five} {one two three four five} 310 } 311 312 16 {SELECT a FROM ft WHERE b MATCH 'hah "song sahm"'} { 313 {two three five} {one two three five} 314 {two three four five} {one two three four five} 315 } 316 317 17 {SELECT a FROM ft WHERE b MATCH 'song OR sahm'} { 318 {two} {one two} {three} 319 {one three} {two three} {one two three} 320 {two four} {one two four} {three four} 321 {one three four} {two three four} {one two three four} 322 {two five} {one two five} {three five} 323 {one three five} {two three five} {one two three five} 324 {two four five} {one two four five} {three four five} 325 {one three four five} {two three four five} {one two three four five} 326 } 327 328 18 {SELECT a FROM ft WHERE a MATCH 'three NOT two'} { 329 {three} {one three} {three four} 330 {one three four} {three five} {one three five} 331 {three four five} {one three four five} 332 } 333 334 19 {SELECT a FROM ft WHERE b MATCH 'sahm NOT song'} { 335 {three} {one three} {three four} 336 {one three four} {three five} {one three five} 337 {three four five} {one three four five} 338 } 339 340 20 {SELECT a FROM ft WHERE ft MATCH 'sahm NOT song'} { 341 {three} {one three} {three four} 342 {one three four} {three five} {one three five} 343 {three four five} {one three four five} 344 } 345 346 21 {SELECT a FROM ft WHERE b MATCH 'neung NEAR song NEAR sahm'} { 347 {one two three} {one two three four} 348 {one two three five} {one two three four five} 349 } 350 351} { 352 set result [normal_list $result] 353 do_select_test fts3_malloc-2.$tn $sql $result 354} 355 356do_test fts3_malloc-3.0 { 357 execsql BEGIN 358 for {set ii 32} {$ii < 1024} {incr ii} { 359 set a [list] 360 set b [list] 361 if {$ii & 0x0001} {lappend a one ; lappend b neung } 362 if {$ii & 0x0002} {lappend a two ; lappend b song } 363 if {$ii & 0x0004} {lappend a three ; lappend b sahm } 364 if {$ii & 0x0008} {lappend a four ; lappend b see } 365 if {$ii & 0x0010} {lappend a five ; lappend b hah } 366 if {$ii & 0x0020} {lappend a six ; lappend b hok } 367 if {$ii & 0x0040} {lappend a seven ; lappend b jet } 368 if {$ii & 0x0080} {lappend a eight ; lappend b bairt } 369 if {$ii & 0x0100} {lappend a nine ; lappend b gow } 370 if {$ii & 0x0200} {lappend a ten ; lappend b sip } 371 execsql { INSERT INTO ft VALUES($a, $b) } 372 } 373 execsql COMMIT 374} {} 375foreach {tn sql result} { 376 1 "SELECT count(*) FROM ft" {1023} 377 378 2 "SELECT a FROM ft WHERE a MATCH 'one two three four five six seven eight'" { 379 {one two three four five six seven eight} 380 {one two three four five six seven eight nine} 381 {one two three four five six seven eight ten} 382 {one two three four five six seven eight nine ten} 383 } 384 385 3 {SELECT count(*), sum(docid) FROM ft WHERE a MATCH 'o*'} { 386 512 262144 387 } 388 389 4 {SELECT count(*), sum(docid) FROM ft WHERE a MATCH '"two three four"'} { 390 128 66368 391 } 392} { 393 set result [normal_list $result] 394 do_select_test fts3_malloc-3.$tn $sql $result 395} 396 397do_test fts3_malloc-4.0 { 398 execsql { DELETE FROM ft WHERE docid>=32 } 399} {} 400foreach {tn sql} { 401 1 "DELETE FROM ft WHERE ft MATCH 'one'" 402 2 "DELETE FROM ft WHERE ft MATCH 'three'" 403 3 "DELETE FROM ft WHERE ft MATCH 'five'" 404} { 405 do_write_test fts3_malloc-4.1.$tn ft_content $sql 406} 407do_test fts3_malloc-4.2 { 408 execsql { SELECT a FROM ft } 409} {two four {two four}} 410 411 412finish_test 413 414