1# 2011 Mar 21 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# The focus of this file is testing the session module. 13# 14 15if {![info exists testdir]} { 16 set testdir [file join [file dirname [info script]] .. .. test] 17} 18source [file join [file dirname [info script]] session_common.tcl] 19source $testdir/tester.tcl 20 21set testprefix sessionfault 22 23if 1 { 24 25forcedelete test.db2 26sqlite3 db2 test.db2 27do_common_sql { 28 CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b)); 29 INSERT INTO t1 VALUES(1, 2, 3); 30 INSERT INTO t1 VALUES(4, 5, 6); 31} 32faultsim_save_and_close 33db2 close 34 35 36#------------------------------------------------------------------------- 37# Test OOM error handling when collecting and applying a simple changeset. 38# 39# Test 1.1 attaches tables individually by name to the session object. 40# Whereas test 1.2 passes NULL to sqlite3session_attach() to attach all 41# tables. 42# 43do_faultsim_test 1.1 -faults oom-* -prep { 44 catch {db2 close} 45 catch {db close} 46 faultsim_restore_and_reopen 47 sqlite3 db2 test.db2 48} -body { 49 do_then_apply_sql { 50 INSERT INTO t1 VALUES('a string value', 8, 9); 51 UPDATE t1 SET c = 10 WHERE a = 1; 52 DELETE FROM t1 WHERE a = 4; 53 } 54} -test { 55 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 56 faultsim_integrity_check 57 if {$testrc==0} { compare_db db db2 } 58} 59 60do_faultsim_test 1.2 -faults oom-* -prep { 61 catch {db2 close} 62 catch {db close} 63 faultsim_restore_and_reopen 64} -body { 65 sqlite3session S db main 66 S attach * 67 execsql { 68 INSERT INTO t1 VALUES('a string value', 8, 9); 69 UPDATE t1 SET c = 10 WHERE a = 1; 70 DELETE FROM t1 WHERE a = 4; 71 } 72 set ::changeset [S changeset] 73 set {} {} 74} -test { 75 catch { S delete } 76 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 77 faultsim_integrity_check 78 if {$testrc==0} { 79 proc xConflict {args} { return "OMIT" } 80 sqlite3 db2 test.db2 81 sqlite3changeset_apply db2 $::changeset xConflict 82 compare_db db db2 83 } 84} 85 86#------------------------------------------------------------------------- 87# The following block of tests - 2.* - are designed to check 88# the handling of faults in the sqlite3changeset_apply() function. 89# 90catch {db close} 91catch {db2 close} 92forcedelete test.db2 test.db 93sqlite3 db2 test.db2 94sqlite3 db test.db 95do_common_sql { 96 CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b)); 97 INSERT INTO t1 VALUES('apple', 'orange', 'pear'); 98 99 CREATE TABLE t2(x PRIMARY KEY, y); 100} 101db2 close 102faultsim_save_and_close 103 104 105foreach {tn conflict_policy sql sql2} { 106 1 OMIT { INSERT INTO t1 VALUES('one text', 'two text', X'00ff00') } {} 107 2 OMIT { DELETE FROM t1 WHERE a = 'apple' } {} 108 3 OMIT { UPDATE t1 SET c = 'banana' WHERE b = 'orange' } {} 109 4 REPLACE { INSERT INTO t2 VALUES('keyvalue', 'value 1') } { 110 INSERT INTO t2 VALUES('keyvalue', 'value 2'); 111 } 112} { 113 proc xConflict args [list return $conflict_policy] 114 115 do_faultsim_test 2.$tn -faults oom-transient -prep { 116 catch {db2 close} 117 catch {db close} 118 faultsim_restore_and_reopen 119 set ::changeset [changeset_from_sql $::sql] 120 sqlite3 db2 test.db2 121 sqlite3_db_config_lookaside db2 0 0 0 122 execsql $::sql2 db2 123 } -body { 124 sqlite3changeset_apply db2 $::changeset xConflict 125 } -test { 126 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 127 faultsim_integrity_check 128 if {$testrc==0} { compare_db db db2 } 129 } 130} 131 132#------------------------------------------------------------------------- 133# This test case is designed so that a malloc() failure occurs while 134# resizing the session object hash-table from 256 to 512 buckets. This 135# is not an error, just a sub-optimal condition. 136# 137do_faultsim_test 3 -faults oom-* -prep { 138 catch {db2 close} 139 catch {db close} 140 faultsim_restore_and_reopen 141 sqlite3 db2 test.db2 142 143 sqlite3session S db main 144 S attach t1 145 execsql { BEGIN } 146 for {set i 0} {$i < 125} {incr i} { 147 execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 10+$i)} 148 } 149} -body { 150 for {set i 125} {$i < 133} {incr i} { 151 execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 1-+$i)} 152 } 153 S changeset 154 set {} {} 155} -test { 156 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 157 if {$testrc==0} { 158 sqlite3changeset_apply db2 [S changeset] xConflict 159 compare_db db db2 160 } 161 catch { S delete } 162 faultsim_integrity_check 163} 164 165catch { db close } 166catch { db2 close } 167forcedelete test.db2 test.db 168sqlite3 db2 test.db2 169sqlite3 db test.db 170 171proc xConflict {op tbl type args} { 172 if { $type=="CONFLICT" || $type=="DATA" } { 173 return "REPLACE" 174 } 175 return "OMIT" 176} 177 178do_test 4.0 { 179 execsql { 180 PRAGMA encoding = 'utf16'; 181 CREATE TABLE t1(a PRIMARY KEY, b); 182 INSERT INTO t1 VALUES(5, 32); 183 } 184 execsql { 185 PRAGMA encoding = 'utf16'; 186 CREATE TABLE t1(a PRIMARY KEY, b NOT NULL); 187 INSERT INTO t1 VALUES(1, 2); 188 INSERT INTO t1 VALUES(2, 4); 189 INSERT INTO t1 VALUES(4, 16); 190 } db2 191} {} 192 193faultsim_save_and_close 194db2 close 195 196do_faultsim_test 4 -faults oom-* -prep { 197 catch {db2 close} 198 catch {db close} 199 faultsim_restore_and_reopen 200 sqlite3 db2 test.db2 201 sqlite3session S db main 202 S attach t1 203 execsql { 204 INSERT INTO t1 VALUES(1, 45); 205 INSERT INTO t1 VALUES(2, 55); 206 INSERT INTO t1 VALUES(3, 55); 207 UPDATE t1 SET a = 4 WHERE a = 5; 208 } 209} -body { 210 sqlite3changeset_apply db2 [S changeset] xConflict 211} -test { 212 catch { S delete } 213 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 214 if {$testrc==0} { compare_db db db2 } 215} 216 217#------------------------------------------------------------------------- 218# This block of tests verifies that OOM faults in the 219# sqlite3changeset_invert() function are handled correctly. 220# 221catch {db close} 222catch {db2 close} 223forcedelete test.db 224sqlite3 db test.db 225execsql { 226 CREATE TABLE t1(a, b, PRIMARY KEY(b)); 227 CREATE TABLE t2(a PRIMARY KEY, b); 228 INSERT INTO t1 VALUES('string', 1); 229 INSERT INTO t1 VALUES(4, 2); 230 INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3); 231} 232set changeset [changeset_from_sql { 233 INSERT INTO t1 VALUES('xxx', 'yyy'); 234 DELETE FROM t1 WHERE a = 'string'; 235 UPDATE t1 SET a = 20 WHERE b = 2; 236}] 237db close 238 239do_faultsim_test 5 -faults oom* -body { 240 set ::inverse [sqlite3changeset_invert $::changeset] 241 set {} {} 242} -test { 243 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 244 if {$testrc==0} { 245 set x [list] 246 sqlite3session_foreach c $::inverse { lappend x $c } 247 foreach c { 248 {DELETE t1 0 .X {t xxx t yyy} {}} 249 {INSERT t1 0 .X {} {t string i 1}} 250 {UPDATE t1 0 .X {i 20 {} {}} {i 4 i 2}} 251 } { lappend y $c } 252 if {$x != $y} { error "changeset no good" } 253 } 254} 255 256#------------------------------------------------------------------------- 257# Test that OOM errors in sqlite3changeset_concat() are handled correctly. 258# 259catch {db close} 260forcedelete test.db 261sqlite3 db test.db 262do_execsql_test 5.prep1 { 263 CREATE TABLE t1(a, b, PRIMARY KEY(b)); 264 CREATE TABLE t2(a PRIMARY KEY, b); 265 INSERT INTO t1 VALUES('string', 1); 266 INSERT INTO t1 VALUES(4, 2); 267 INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3); 268} 269 270do_test 6.prep2 { 271 sqlite3session M db main 272 M attach * 273 set ::c2 [changeset_from_sql { 274 INSERT INTO t2 VALUES(randomblob(1000), randomblob(1000)); 275 INSERT INTO t2 VALUES('one', 'two'); 276 INSERT INTO t2 VALUES(1, NULL); 277 UPDATE t1 SET a = 5 WHERE a = 2; 278 }] 279 set ::c1 [changeset_from_sql { 280 DELETE FROM t2 WHERE a = 1; 281 UPDATE t1 SET a = 4 WHERE a = 2; 282 INSERT INTO t2 VALUES('x', 'y'); 283 }] 284 set ::total [changeset_to_list [M changeset]] 285 M delete 286} {} 287 288do_faultsim_test 6 -faults oom-* -body { 289 set ::result [sqlite3changeset_concat $::c1 $::c2] 290 set {} {} 291} -test { 292 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 293 if {$testrc==0} { 294 set v [changeset_to_list $::result] 295 if {$v != $::total} { error "result no good" } 296 } 297} 298 299faultsim_delete_and_reopen 300do_execsql_test 5.prep1 { 301 CREATE TABLE t1(a, b, PRIMARY KEY(a)); 302} 303faultsim_save_and_close 304 305set res [list] 306for {set ::i 0} {$::i < 480} {incr ::i 4} { 307 lappend res "INSERT t1 0 X. {} {i $::i i $::i}" 308} 309set res [lsort $res] 310do_faultsim_test 7 -faults oom-transient -prep { 311 faultsim_restore_and_reopen 312 sqlite3session S db main 313 S attach * 314} -body { 315 for {set ::i 0} {$::i < 480} {incr ::i 4} { 316 execsql {INSERT INTO t1 VALUES($::i, $::i)} 317 } 318} -test { 319 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 320 if {$testrc==0} { 321 set cres [list [catch {changeset_to_list [S changeset]} msg] $msg] 322 S delete 323 if {$cres != "1 SQLITE_NOMEM" && $cres != "0 {$::res}"} { 324 error "Expected {0 $::res} Got {$cres}" 325 } 326 } else { 327 S changeset 328 S delete 329 } 330} 331 332faultsim_delete_and_reopen 333do_test 8.prep { 334 sqlite3session S db main 335 S attach * 336 execsql { 337 CREATE TABLE t1(a, b, PRIMARY KEY(a)); 338 INSERT INTO t1 VALUES(1, 2); 339 INSERT INTO t1 VALUES(3, 4); 340 INSERT INTO t1 VALUES(5, 6); 341 } 342 set ::changeset [S changeset] 343 S delete 344} {} 345 346set expected [normalize_list { 347 {INSERT t1 0 X. {} {i 1 i 2}} 348 {INSERT t1 0 X. {} {i 3 i 4}} 349 {INSERT t1 0 X. {} {i 5 i 6}} 350}] 351do_faultsim_test 8.1 -faults oom* -body { 352 set ::res [list] 353 sqlite3session_foreach -next v $::changeset { lappend ::res $v } 354 normalize_list $::res 355} -test { 356 faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM} 357} 358do_faultsim_test 8.2 -faults oom* -body { 359 set ::res [list] 360 sqlite3session_foreach v $::changeset { lappend ::res $v } 361 normalize_list $::res 362} -test { 363 faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM} 364} 365 366faultsim_delete_and_reopen 367do_test 9.1.prep { 368 execsql { 369 PRAGMA encoding = 'utf16'; 370 CREATE TABLE t1(a PRIMARY KEY, b); 371 } 372} {} 373faultsim_save_and_close 374 375set answers [list {0 {}} {1 SQLITE_NOMEM} {1 {callback requested query abort}}] 376do_faultsim_test 9.1 -faults oom-transient -prep { 377 catch { unset ::c } 378 faultsim_restore_and_reopen 379 sqlite3session S db main 380 S attach * 381} -body { 382 execsql { 383 INSERT INTO t1 VALUES('abcdefghijklmnopqrstuv', 'ABCDEFGHIJKLMNOPQRSTUV'); 384 } 385 set ::c [S changeset] 386 set {} {} 387} -test { 388 S delete 389 eval faultsim_test_result $::answers 390 if {[info exists ::c]} { 391 set expected [normalize_list { 392 {INSERT t1 0 X. {} {t abcdefghijklmnopqrstuv t ABCDEFGHIJKLMNOPQRSTUV}} 393 }] 394 if { [changeset_to_list $::c] != $expected } { 395 error "changeset mismatch" 396 } 397 } 398} 399 400} 401 402faultsim_delete_and_reopen 403do_test 9.2.prep { 404 execsql { 405 PRAGMA encoding = 'utf16'; 406 CREATE TABLE t1(a PRIMARY KEY, b); 407 INSERT INTO t1 VALUES('abcdefghij', 'ABCDEFGHIJKLMNOPQRSTUV'); 408 } 409} {} 410faultsim_save_and_close 411 412set answers [list {0 {}} {1 SQLITE_NOMEM} {1 {callback requested query abort}}] 413do_faultsim_test 9.2 -faults oom-transient -prep { 414 catch { unset ::c } 415 faultsim_restore_and_reopen 416 sqlite3session S db main 417 S attach * 418} -body { 419 execsql { 420 UPDATE t1 SET b = 'xyz'; 421 } 422 set ::c [S changeset] 423 set {} {} 424} -test { 425 S delete 426 eval faultsim_test_result $::answers 427 if {[info exists ::c]} { 428 set expected [normalize_list { 429 {UPDATE t1 0 X. {t abcdefghij t ABCDEFGHIJKLMNOPQRSTUV} {{} {} t xyz}} 430 }] 431 if { [changeset_to_list $::c] != $expected } { 432 error "changeset mismatch" 433 } 434 } 435} 436 437 438 439finish_test 440