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