# 2011 Mar 21 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The focus of this file is testing the session module. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] session_common.tcl] source $testdir/tester.tcl set testprefix sessionfault forcedelete test.db2 sqlite3 db2 test.db2 do_common_sql { CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b)); INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(4, 5, 6); } faultsim_save_and_close db2 close #------------------------------------------------------------------------- # Test OOM error handling when collecting and applying a simple changeset. # do_faultsim_test pagerfault-1 -faults oom-* -prep { catch {db2 close} catch {db close} faultsim_restore_and_reopen sqlite3 db2 test.db2 } -body { do_then_apply_sql { INSERT INTO t1 VALUES(7, 8, 9); UPDATE t1 SET c = 10 WHERE a = 1; DELETE FROM t1 WHERE a = 4; } } -test { faultsim_test_result {0 {}} {1 SQLITE_NOMEM} faultsim_integrity_check if {$testrc==0} { compare_db db db2 } } #------------------------------------------------------------------------- # The following block of tests - pagerfault-2.* - are designed to check # the handling of faults in the sqlite3changeset_apply() function. # catch {db close} catch {db2 close} forcedelete test.db2 test.db sqlite3 db2 test.db2 sqlite3 db test.db do_common_sql { CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b)); INSERT INTO t1 VALUES('apple', 'orange', 'pear'); CREATE TABLE t2(x PRIMARY KEY, y); } db2 close faultsim_save_and_close foreach {tn conflict_policy sql sql2} { 1 OMIT { INSERT INTO t1 VALUES('one text', 'two text', X'00ff00') } {} 2 OMIT { DELETE FROM t1 WHERE a = 'apple' } {} 3 OMIT { UPDATE t1 SET c = 'banana' WHERE b = 'orange' } {} 4 REPLACE { INSERT INTO t2 VALUES('keyvalue', 'value 1') } { INSERT INTO t2 VALUES('keyvalue', 'value 2'); } } { proc xConflict args [list return $conflict_policy] do_faultsim_test pagerfault-2.$tn -faults oom-transient -prep { catch {db2 close} catch {db close} faultsim_restore_and_reopen set ::changeset [changeset_from_sql $::sql] sqlite3 db2 test.db2 sqlite3_db_config_lookaside db2 0 0 0 execsql $::sql2 db2 } -body { sqlite3changeset_apply db2 $::changeset xConflict } -test { faultsim_test_result {0 {}} {1 SQLITE_NOMEM} faultsim_integrity_check if {$testrc==0} { compare_db db db2 } } } #------------------------------------------------------------------------- # This test case is designed so that a malloc() failure occurs while # resizing the session object hash-table from 256 to 512 buckets. This # is not an error, just a sub-optimal condition. # do_faultsim_test pagerfault-3 -faults oom-* -prep { catch {db2 close} catch {db close} faultsim_restore_and_reopen sqlite3 db2 test.db2 sqlite3session S db main S attach t1 execsql { BEGIN } for {set i 0} {$i < 125} {incr i} { execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 10+$i)} } } -body { for {set i 125} {$i < 133} {incr i} { execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 1-+$i)} } S changeset set {} {} } -test { faultsim_test_result {0 {}} {1 SQLITE_NOMEM} if {$testrc==0} { sqlite3changeset_apply db2 [S changeset] xConflict compare_db db db2 } catch { S delete } faultsim_integrity_check } catch { db close } catch { db2 close } forcedelete test.db2 test.db sqlite3 db2 test.db2 sqlite3 db test.db proc xConflict {op tbl type args} { if { $type=="CONFLICT" || $type=="DATA" } { return "REPLACE" } return "OMIT" } do_test 4.0 { execsql { PRAGMA encoding = 'utf16'; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(5, 32); } execsql { PRAGMA encoding = 'utf16'; CREATE TABLE t1(a PRIMARY KEY, b NOT NULL); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(2, 4); INSERT INTO t1 VALUES(4, 16); } db2 } {} faultsim_save_and_close db2 close do_faultsim_test pagerfault-4 -faults oom-* -prep { catch {db2 close} catch {db close} faultsim_restore_and_reopen sqlite3 db2 test.db2 sqlite3session S db main S attach t1 execsql { INSERT INTO t1 VALUES(1, 45); INSERT INTO t1 VALUES(2, 55); INSERT INTO t1 VALUES(3, 55); UPDATE t1 SET a = 4 WHERE a = 5; } } -body { sqlite3changeset_apply db2 [S changeset] xConflict } -test { catch { S delete } faultsim_test_result {0 {}} {1 SQLITE_NOMEM} if {$testrc==0} { compare_db db db2 } } #------------------------------------------------------------------------- # This block of tests verifies that OOM faults in the # sqlite3changeset_invert() function are handled correctly. # catch {db close} catch {db2 close} forcedelete test.db sqlite3 db test.db execsql { CREATE TABLE t1(a, b, PRIMARY KEY(b)); CREATE TABLE t2(a PRIMARY KEY, b); INSERT INTO t1 VALUES('string', 1); INSERT INTO t1 VALUES(4, 2); INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3); } set changeset [changeset_from_sql { INSERT INTO t1 VALUES('xxx', 'yyy'); DELETE FROM t1 WHERE a = 'string'; UPDATE t1 SET a = 20 WHERE b = 2; }] db close do_faultsim_test pagerfault-5 -faults oom* -body { set ::inverse [sqlite3changeset_invert $::changeset] set {} {} } -test { faultsim_test_result {0 {}} {1 SQLITE_NOMEM} if {$testrc==0} { set x [list] sqlite3session_foreach c $::inverse { lappend x $c } foreach c { {DELETE t1 {t xxx t yyy} {}} {INSERT t1 {} {t string i 1}} {UPDATE t1 {i 20 {} {}} {i 4 i 2}} } { lappend y $c } if {$x != $y} { error "changeset no good" } } } finish_test