modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/session/sessionfault.test (about) 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 15 if {![info exists testdir]} { 16 set testdir [file join [file dirname [info script]] .. .. test] 17 } 18 source [file join [file dirname [info script]] session_common.tcl] 19 source $testdir/tester.tcl 20 ifcapable !session {finish_test; return} 21 22 set testprefix sessionfault 23 24 forcedelete test.db2 25 sqlite3 db2 test.db2 26 do_common_sql { 27 CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b)); 28 INSERT INTO t1 VALUES(1, 2, 3); 29 INSERT INTO t1 VALUES(4, 5, 6); 30 } 31 faultsim_save_and_close 32 db2 close 33 34 #------------------------------------------------------------------------- 35 # Test OOM error handling when collecting and applying a simple changeset. 36 # 37 # Test 1.1 attaches tables individually by name to the session object. 38 # Whereas test 1.2 passes NULL to sqlite3session_attach() to attach all 39 # tables. 40 # 41 do_faultsim_test 1.1 -faults oom-* -prep { 42 catch {db2 close} 43 catch {db close} 44 faultsim_restore_and_reopen 45 sqlite3 db2 test.db2 46 } -body { 47 do_then_apply_sql { 48 INSERT INTO t1 VALUES('a string value', 8, 9); 49 UPDATE t1 SET c = 10 WHERE a = 1; 50 DELETE FROM t1 WHERE a = 4; 51 } 52 } -test { 53 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 54 faultsim_integrity_check 55 if {$testrc==0} { compare_db db db2 } 56 } 57 58 do_faultsim_test 1.2 -faults oom-* -prep { 59 catch {db2 close} 60 catch {db close} 61 faultsim_restore_and_reopen 62 } -body { 63 sqlite3session S db main 64 S attach * 65 execsql { 66 INSERT INTO t1 VALUES('a string value', 8, 9); 67 UPDATE t1 SET c = 10 WHERE a = 1; 68 DELETE FROM t1 WHERE a = 4; 69 } 70 set ::changeset [S changeset] 71 set {} {} 72 } -test { 73 catch { S delete } 74 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 75 faultsim_integrity_check 76 if {$testrc==0} { 77 proc xConflict {args} { return "OMIT" } 78 sqlite3 db2 test.db2 79 sqlite3changeset_apply db2 $::changeset xConflict 80 compare_db db db2 81 } 82 } 83 84 #------------------------------------------------------------------------- 85 # The following block of tests - 2.* - are designed to check 86 # the handling of faults in the sqlite3changeset_apply() function. 87 # 88 catch {db close} 89 catch {db2 close} 90 forcedelete test.db2 test.db 91 sqlite3 db2 test.db2 92 sqlite3 db test.db 93 do_common_sql { 94 CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b)); 95 INSERT INTO t1 VALUES('apple', 'orange', 'pear'); 96 97 CREATE TABLE t2(x PRIMARY KEY, y); 98 } 99 db2 close 100 faultsim_save_and_close 101 102 103 foreach {tn conflict_policy sql sql2} { 104 1 OMIT { INSERT INTO t1 VALUES('one text', 'two text', X'00ff00') } {} 105 2 OMIT { DELETE FROM t1 WHERE a = 'apple' } {} 106 3 OMIT { UPDATE t1 SET c = 'banana' WHERE b = 'orange' } {} 107 4 REPLACE { INSERT INTO t2 VALUES('keyvalue', 'value 1') } { 108 INSERT INTO t2 VALUES('keyvalue', 'value 2'); 109 } 110 } { 111 proc xConflict args [list return $conflict_policy] 112 113 do_faultsim_test 2.$tn -faults oom-transient -prep { 114 catch {db2 close} 115 catch {db close} 116 faultsim_restore_and_reopen 117 set ::changeset [changeset_from_sql $::sql] 118 sqlite3 db2 test.db2 119 sqlite3_db_config_lookaside db2 0 0 0 120 execsql $::sql2 db2 121 } -body { 122 sqlite3changeset_apply db2 $::changeset xConflict 123 } -test { 124 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 125 faultsim_integrity_check 126 if {$testrc==0} { compare_db db db2 } 127 } 128 } 129 130 #------------------------------------------------------------------------- 131 # This test case is designed so that a malloc() failure occurs while 132 # resizing the session object hash-table from 256 to 512 buckets. This 133 # is not an error, just a sub-optimal condition. 134 # 135 do_faultsim_test 3 -faults oom-* -prep { 136 catch {db2 close} 137 catch {db close} 138 faultsim_restore_and_reopen 139 sqlite3 db2 test.db2 140 141 sqlite3session S db main 142 S attach t1 143 execsql { BEGIN } 144 for {set i 0} {$i < 125} {incr i} { 145 execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 10+$i)} 146 } 147 } -body { 148 for {set i 125} {$i < 133} {incr i} { 149 execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 1-+$i)} 150 } 151 S changeset 152 set {} {} 153 } -test { 154 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 155 if {$testrc==0} { 156 sqlite3changeset_apply db2 [S changeset] xConflict 157 compare_db db db2 158 } 159 catch { S delete } 160 faultsim_integrity_check 161 } 162 163 catch { db close } 164 catch { db2 close } 165 forcedelete test.db2 test.db 166 sqlite3 db2 test.db2 167 sqlite3 db test.db 168 169 proc xConflict {op tbl type args} { 170 if { $type=="CONFLICT" || $type=="DATA" } { 171 return "REPLACE" 172 } 173 return "OMIT" 174 } 175 176 do_test 4.0 { 177 execsql { 178 PRAGMA encoding = 'utf16'; 179 CREATE TABLE t1(a PRIMARY KEY, b); 180 INSERT INTO t1 VALUES(5, 32); 181 } 182 execsql { 183 PRAGMA encoding = 'utf16'; 184 CREATE TABLE t1(a PRIMARY KEY, b NOT NULL); 185 INSERT INTO t1 VALUES(1, 2); 186 INSERT INTO t1 VALUES(2, 4); 187 INSERT INTO t1 VALUES(4, 16); 188 } db2 189 } {} 190 191 faultsim_save_and_close 192 db2 close 193 194 do_faultsim_test 4 -faults oom-* -prep { 195 catch {db2 close} 196 catch {db close} 197 faultsim_restore_and_reopen 198 sqlite3 db2 test.db2 199 sqlite3session S db main 200 S attach t1 201 execsql { 202 INSERT INTO t1 VALUES(1, 45); 203 INSERT INTO t1 VALUES(2, 55); 204 INSERT INTO t1 VALUES(3, 55); 205 UPDATE t1 SET a = 4 WHERE a = 5; 206 } 207 } -body { 208 sqlite3changeset_apply db2 [S changeset] xConflict 209 } -test { 210 catch { S delete } 211 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 212 if {$testrc==0} { compare_db db db2 } 213 } 214 215 #------------------------------------------------------------------------- 216 # This block of tests verifies that OOM faults in the 217 # sqlite3changeset_invert() function are handled correctly. 218 # 219 catch {db close} 220 catch {db2 close} 221 forcedelete test.db 222 sqlite3 db test.db 223 execsql { 224 CREATE TABLE t1(a, b, PRIMARY KEY(b)); 225 CREATE TABLE t2(a PRIMARY KEY, b); 226 INSERT INTO t1 VALUES('string', 1); 227 INSERT INTO t1 VALUES(4, 2); 228 INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3); 229 } 230 set changeset [changeset_from_sql { 231 INSERT INTO t1 VALUES('xxx', 'yyy'); 232 DELETE FROM t1 WHERE a = 'string'; 233 UPDATE t1 SET a = 20 WHERE b = 2; 234 }] 235 db close 236 237 do_faultsim_test 5.1 -faults oom* -body { 238 set ::inverse [sqlite3changeset_invert $::changeset] 239 set {} {} 240 } -test { 241 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 242 if {$testrc==0} { 243 set x [list] 244 sqlite3session_foreach c $::inverse { lappend x $c } 245 foreach c { 246 {DELETE t1 0 .X {t xxx t yyy} {}} 247 {INSERT t1 0 .X {} {t string i 1}} 248 {UPDATE t1 0 .X {i 20 i 2} {i 4 {} {}}} 249 } { lappend y $c } 250 if {$x != $y} { error "changeset no good" } 251 } 252 } 253 254 catch {db close} 255 catch {db2 close} 256 forcedelete test.db 257 sqlite3 db test.db 258 execsql { 259 CREATE TABLE t2(a PRIMARY KEY, b); 260 INSERT INTO t2 VALUES(1, 'abc'); 261 INSERT INTO t2 VALUES(2, 'def'); 262 } 263 set changeset [changeset_from_sql { 264 UPDATE t2 SET b = (b || b || b || b); 265 UPDATE t2 SET b = (b || b || b || b); 266 UPDATE t2 SET b = (b || b || b || b); 267 UPDATE t2 SET b = (b || b || b || b); 268 }] 269 db close 270 set abc [string repeat abc 256] 271 set def [string repeat def 256] 272 273 do_faultsim_test 5.2 -faults oom-tra* -body { 274 set ::inverse [sqlite3changeset_invert $::changeset] 275 set {} {} 276 } -test { 277 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 278 if {$testrc==0} { 279 set x [list] 280 sqlite3session_foreach c $::inverse { lappend x $c } 281 foreach c " 282 {UPDATE t2 0 X. {i 1 t $::abc} {{} {} t abc}} 283 {UPDATE t2 0 X. {i 2 t $::def} {{} {} t def}} 284 " { lappend y $c } 285 if {$x != $y} { error "changeset no good" } 286 } 287 } 288 289 catch {db close} 290 catch {db2 close} 291 forcedelete test.db 292 sqlite3 db test.db 293 set abc [string repeat abc 256] 294 set def [string repeat def 256] 295 execsql " 296 CREATE TABLE t2(a PRIMARY KEY, b); 297 INSERT INTO t2 VALUES(1, '$abc'); 298 " 299 set changeset [changeset_from_sql " 300 INSERT INTO t2 VALUES(2, '$def'); 301 DELETE FROM t2 WHERE a = 1; 302 "] 303 db close 304 305 do_faultsim_test 5.3 -faults oom-tra* -body { 306 set ::inverse [sqlite3changeset_invert $::changeset] 307 set {} {} 308 } -test { 309 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 310 if {$testrc==0} { 311 set x [list] 312 sqlite3session_foreach c $::inverse { lappend x $c } 313 foreach c " 314 {INSERT t2 0 X. {} {i 1 t $::abc}} 315 {DELETE t2 0 X. {i 2 t $::def} {}} 316 " { lappend y $c } 317 if {$x != $y} { error "changeset no good" } 318 } 319 } 320 321 #------------------------------------------------------------------------- 322 # Test that OOM errors in sqlite3changeset_concat() are handled correctly. 323 # 324 catch {db close} 325 forcedelete test.db 326 sqlite3 db test.db 327 do_execsql_test 5.prep1 { 328 CREATE TABLE t1(a, b, PRIMARY KEY(b)); 329 CREATE TABLE t2(a PRIMARY KEY, b); 330 INSERT INTO t1 VALUES('string', 1); 331 INSERT INTO t1 VALUES(4, 2); 332 INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3); 333 } 334 335 do_test 6.prep2 { 336 sqlite3session M db main 337 M attach * 338 set ::c2 [changeset_from_sql { 339 INSERT INTO t2 VALUES(randomblob(1000), randomblob(1000)); 340 INSERT INTO t2 VALUES('one', 'two'); 341 INSERT INTO t2 VALUES(1, NULL); 342 UPDATE t1 SET a = 5 WHERE a = 2; 343 }] 344 set ::c1 [changeset_from_sql { 345 DELETE FROM t2 WHERE a = 1; 346 UPDATE t1 SET a = 4 WHERE a = 2; 347 INSERT INTO t2 VALUES('x', 'y'); 348 }] 349 set ::total [changeset_to_list [M changeset]] 350 M delete 351 } {} 352 353 do_faultsim_test 6 -faults oom-* -body { 354 set ::result [sqlite3changeset_concat $::c1 $::c2] 355 set {} {} 356 } -test { 357 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 358 if {$testrc==0} { 359 set v [changeset_to_list $::result] 360 if {$v != $::total} { error "result no good" } 361 } 362 } 363 364 faultsim_delete_and_reopen 365 do_execsql_test 7.prep1 { 366 CREATE TABLE t1(a, b, PRIMARY KEY(a)); 367 } 368 faultsim_save_and_close 369 370 set res [list] 371 for {set ::i 0} {$::i < 480} {incr ::i 4} { 372 lappend res "INSERT t1 0 X. {} {i $::i i $::i}" 373 } 374 set res [lsort $res] 375 do_faultsim_test 7 -faults oom-transient -prep { 376 catch { S delete } 377 faultsim_restore_and_reopen 378 sqlite3session S db main 379 S attach * 380 } -body { 381 for {set ::i 0} {$::i < 480} {incr ::i 4} { 382 execsql {INSERT INTO t1 VALUES($::i, $::i)} 383 } 384 } -test { 385 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 386 if {$testrc==0} { 387 set cres [list [catch {changeset_to_list [S changeset]} msg] $msg] 388 S delete 389 if {$cres != "1 SQLITE_NOMEM" && $cres != "0 {$::res}"} { 390 error "Expected {0 $::res} Got {$cres}" 391 } 392 } else { 393 catch { S changeset } 394 catch { S delete } 395 } 396 } 397 398 faultsim_delete_and_reopen 399 do_test 8.prep { 400 sqlite3session S db main 401 S attach * 402 execsql { 403 CREATE TABLE t1(a, b, PRIMARY KEY(a)); 404 INSERT INTO t1 VALUES(1, 2); 405 INSERT INTO t1 VALUES(3, 4); 406 INSERT INTO t1 VALUES(5, 6); 407 } 408 set ::changeset [S changeset] 409 S delete 410 } {} 411 412 set expected [normalize_list { 413 {INSERT t1 0 X. {} {i 1 i 2}} 414 {INSERT t1 0 X. {} {i 3 i 4}} 415 {INSERT t1 0 X. {} {i 5 i 6}} 416 }] 417 do_faultsim_test 8.1 -faults oom* -body { 418 set ::res [list] 419 sqlite3session_foreach -next v $::changeset { lappend ::res $v } 420 normalize_list $::res 421 } -test { 422 faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM} 423 } 424 do_faultsim_test 8.2 -faults oom* -body { 425 set ::res [list] 426 sqlite3session_foreach v $::changeset { lappend ::res $v } 427 normalize_list $::res 428 } -test { 429 faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM} 430 } 431 432 faultsim_delete_and_reopen 433 do_test 9.1.prep { 434 execsql { 435 PRAGMA encoding = 'utf16'; 436 CREATE TABLE t1(a PRIMARY KEY, b); 437 } 438 } {} 439 faultsim_save_and_close 440 441 set answers [list {0 {}} {1 SQLITE_NOMEM} \ 442 {1 {callback requested query abort}} \ 443 {1 {abort due to ROLLBACK}}] 444 do_faultsim_test 9.1 -faults oom-transient -prep { 445 catch { unset ::c } 446 faultsim_restore_and_reopen 447 sqlite3session S db main 448 S attach * 449 } -body { 450 execsql { 451 INSERT INTO t1 VALUES('abcdefghijklmnopqrstuv', 'ABCDEFGHIJKLMNOPQRSTUV'); 452 } 453 set ::c [S changeset] 454 set {} {} 455 } -test { 456 S delete 457 eval faultsim_test_result $::answers 458 if {[info exists ::c]} { 459 set expected [normalize_list { 460 {INSERT t1 0 X. {} {t abcdefghijklmnopqrstuv t ABCDEFGHIJKLMNOPQRSTUV}} 461 }] 462 if { [changeset_to_list $::c] != $expected } { 463 error "changeset mismatch" 464 } 465 } 466 } 467 468 faultsim_delete_and_reopen 469 do_test 9.2.prep { 470 execsql { 471 PRAGMA encoding = 'utf16'; 472 CREATE TABLE t1(a PRIMARY KEY, b); 473 INSERT INTO t1 VALUES('abcdefghij', 'ABCDEFGHIJKLMNOPQRSTUV'); 474 } 475 } {} 476 faultsim_save_and_close 477 478 set answers [list {0 {}} {1 SQLITE_NOMEM} \ 479 {1 {callback requested query abort}} \ 480 {1 {abort due to ROLLBACK}}] 481 do_faultsim_test 9.2 -faults oom-transient -prep { 482 catch { unset ::c } 483 faultsim_restore_and_reopen 484 sqlite3session S db main 485 S attach * 486 } -body { 487 execsql { 488 UPDATE t1 SET b = 'xyz'; 489 } 490 set ::c [S changeset] 491 set {} {} 492 } -test { 493 S delete 494 eval faultsim_test_result $::answers 495 if {[info exists ::c]} { 496 set expected [normalize_list { 497 {UPDATE t1 0 X. {t abcdefghij t ABCDEFGHIJKLMNOPQRSTUV} {{} {} t xyz}} 498 }] 499 if { [changeset_to_list $::c] != $expected } { 500 error "changeset mismatch" 501 } 502 } 503 } 504 505 #------------------------------------------------------------------------- 506 # Test that if a conflict-handler encounters an OOM in 507 # sqlite3_value_text() but goes on to return SQLITE_CHANGESET_REPLACE 508 # anyway, the OOM is picked up by the sessions module. 509 set bigstr [string repeat abcdefghij 100] 510 faultsim_delete_and_reopen 511 do_test 10.prep.1 { 512 execsql { 513 CREATE TABLE t1(a PRIMARY KEY, b); 514 INSERT INTO t1 VALUES($bigstr, $bigstr); 515 } 516 517 sqlite3session S db main 518 S attach * 519 execsql { UPDATE t1 SET b = b||'x' } 520 set C [S changeset] 521 S delete 522 execsql { UPDATE t1 SET b = b||'xyz' } 523 } {} 524 faultsim_save_and_close 525 526 faultsim_restore_and_reopen 527 do_test 10.prep.2 { 528 proc xConflict {args} { return "ABORT" } 529 list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg 530 } {1 SQLITE_ABORT} 531 do_execsql_test 10.prep.3 { SELECT b=$bigstr||'x' FROM t1 } 0 532 do_test 10.prep.4 { 533 proc xConflict {args} { return "REPLACE" } 534 list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg 535 } {0 {}} 536 do_execsql_test 10.prep.5 { SELECT b=$bigstr||'x' FROM t1 } 1 537 db close 538 539 do_faultsim_test 10 -faults oom-tra* -prep { 540 faultsim_restore_and_reopen 541 } -body { 542 sqlite3changeset_apply_replace_all db $::C 543 } -test { 544 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 545 if {$testrc==0} { 546 if {[db one {SELECT b=$bigstr||'x' FROM t1}]==0} { 547 error "data does not look right" 548 } 549 } 550 } 551 552 #------------------------------------------------------------------------- 553 # Test an OOM with an sqlite3changeset_apply() filter callback. 554 # 555 reset_db 556 do_test 11.prep { 557 execsql { 558 CREATE TABLE t1(a PRIMARY KEY, b); 559 CREATE TABLE t2(x PRIMARY KEY, y); 560 BEGIN; 561 } 562 563 set ::cs [changeset_from_sql { 564 INSERT INTO t1 VALUES(1, 2); 565 INSERT INTO t2 VALUES('x', 'y'); 566 }] 567 568 execsql ROLLBACK 569 set {} {} 570 } {} 571 572 proc filter {x} { return [string equal t1 $x] } 573 faultsim_save_and_close 574 575 do_faultsim_test 11 -faults oom-tra* -prep { 576 faultsim_restore_and_reopen 577 } -body { 578 sqlite3changeset_apply db $::cs {} filter 579 } -test { 580 faultsim_test_result {0 {}} {1 SQLITE_NOMEM} 581 if {$testrc==0} { 582 if {[db eval {SELECT * FROM t1 UNION ALL SELECT * FROM t2}] != "1 2"} { 583 error "data does not look right" 584 } 585 } 586 } 587 588 589 finish_test