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
23forcedelete test.db2
24sqlite3 db2 test.db2
25do_common_sql {
26  CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b));
27  INSERT INTO t1 VALUES(1, 2, 3);
28  INSERT INTO t1 VALUES(4, 5, 6);
29}
30faultsim_save_and_close
31db2 close
32
33
34#-------------------------------------------------------------------------
35# Test OOM error handling when collecting and applying a simple changeset.
36#
37do_faultsim_test pagerfault-1 -faults oom-* -prep {
38  catch {db2 close}
39  catch {db close}
40  faultsim_restore_and_reopen
41  sqlite3 db2 test.db2
42} -body {
43  do_then_apply_sql {
44    INSERT INTO t1 VALUES(7, 8, 9);
45    UPDATE t1 SET c = 10 WHERE a = 1;
46    DELETE FROM t1 WHERE a = 4;
47  }
48} -test {
49  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
50  faultsim_integrity_check
51  if {$testrc==0} { compare_db db db2 }
52}
53
54#-------------------------------------------------------------------------
55# The following block of tests - pagerfault-2.* - are designed to check
56# the handling of faults in the sqlite3changeset_apply() function.
57#
58catch {db close}
59catch {db2 close}
60forcedelete test.db2 test.db
61sqlite3 db2 test.db2
62sqlite3 db test.db
63do_common_sql {
64  CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b));
65  INSERT INTO t1 VALUES('apple', 'orange', 'pear');
66
67  CREATE TABLE t2(x PRIMARY KEY, y);
68}
69db2 close
70faultsim_save_and_close
71
72
73foreach {tn conflict_policy sql sql2} {
74  1 OMIT { INSERT INTO t1 VALUES('one text', 'two text', X'00ff00') } {}
75  2 OMIT { DELETE FROM t1 WHERE a = 'apple' }                         {}
76  3 OMIT { UPDATE t1 SET c = 'banana' WHERE b = 'orange' }            {}
77  4 REPLACE { INSERT INTO t2 VALUES('keyvalue', 'value 1') } {
78    INSERT INTO t2 VALUES('keyvalue', 'value 2');
79  }
80} {
81  proc xConflict args [list return $conflict_policy]
82
83  do_faultsim_test pagerfault-2.$tn -faults oom-transient -prep {
84    catch {db2 close}
85    catch {db close}
86    faultsim_restore_and_reopen
87    set ::changeset [changeset_from_sql $::sql]
88    sqlite3 db2 test.db2
89    sqlite3_db_config_lookaside db2 0 0 0
90    execsql $::sql2 db2
91  } -body {
92    sqlite3changeset_apply db2 $::changeset xConflict
93  } -test {
94    faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
95    faultsim_integrity_check
96    if {$testrc==0} { compare_db db db2 }
97  }
98}
99
100#-------------------------------------------------------------------------
101# This test case is designed so that a malloc() failure occurs while
102# resizing the session object hash-table from 256 to 512 buckets. This
103# is not an error, just a sub-optimal condition.
104#
105do_faultsim_test pagerfault-3 -faults oom-* -prep {
106  catch {db2 close}
107  catch {db close}
108  faultsim_restore_and_reopen
109  sqlite3 db2 test.db2
110
111  sqlite3session S db main
112  S attach t1
113  execsql { BEGIN }
114  for {set i 0} {$i < 125} {incr i} {
115    execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 10+$i)}
116  }
117} -body {
118  for {set i 125} {$i < 133} {incr i} {
119    execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 1-+$i)}
120  }
121  S changeset
122  set {} {}
123} -test {
124  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
125  if {$testrc==0} {
126    sqlite3changeset_apply db2 [S changeset] xConflict
127    compare_db db db2
128  }
129  catch { S delete }
130  faultsim_integrity_check
131}
132
133catch { db close }
134catch { db2 close }
135forcedelete test.db2 test.db
136sqlite3 db2 test.db2
137sqlite3 db test.db
138
139proc xConflict {op tbl type args} {
140  if { $type=="CONFLICT" || $type=="DATA" } {
141    return "REPLACE"
142  }
143  return "OMIT"
144}
145
146do_test 4.0 {
147  execsql {
148    PRAGMA encoding = 'utf16';
149    CREATE TABLE t1(a PRIMARY KEY, b);
150    INSERT INTO t1 VALUES(5, 32);
151  }
152  execsql {
153    PRAGMA encoding = 'utf16';
154    CREATE TABLE t1(a PRIMARY KEY, b NOT NULL);
155    INSERT INTO t1 VALUES(1, 2);
156    INSERT INTO t1 VALUES(2, 4);
157    INSERT INTO t1 VALUES(4, 16);
158  } db2
159} {}
160
161faultsim_save_and_close
162db2 close
163
164do_faultsim_test pagerfault-4 -faults oom-* -prep {
165  catch {db2 close}
166  catch {db close}
167  faultsim_restore_and_reopen
168  sqlite3 db2 test.db2
169  sqlite3session S db main
170  S attach t1
171  execsql {
172    INSERT INTO t1 VALUES(1, 45);
173    INSERT INTO t1 VALUES(2, 55);
174    INSERT INTO t1 VALUES(3, 55);
175    UPDATE t1 SET a = 4 WHERE a = 5;
176  }
177} -body {
178  sqlite3changeset_apply db2 [S changeset] xConflict
179} -test {
180  catch { S delete }
181  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
182  if {$testrc==0} { compare_db db db2 }
183}
184
185#-------------------------------------------------------------------------
186# This block of tests verifies that OOM faults in the
187# sqlite3changeset_invert() function are handled correctly.
188#
189catch {db close}
190catch {db2 close}
191forcedelete test.db
192sqlite3 db test.db
193execsql {
194  CREATE TABLE t1(a, b, PRIMARY KEY(b));
195  CREATE TABLE t2(a PRIMARY KEY, b);
196  INSERT INTO t1 VALUES('string', 1);
197  INSERT INTO t1 VALUES(4, 2);
198  INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3);
199}
200set changeset [changeset_from_sql {
201  INSERT INTO t1 VALUES('xxx', 'yyy');
202  DELETE FROM t1 WHERE a = 'string';
203  UPDATE t1 SET a = 20 WHERE b = 2;
204}]
205db close
206
207do_faultsim_test pagerfault-5 -faults oom* -body {
208  set ::inverse [sqlite3changeset_invert $::changeset]
209  set {} {}
210} -test {
211  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
212  if {$testrc==0} {
213    set x [list]
214    sqlite3session_foreach c $::inverse { lappend x $c }
215    foreach c {
216        {DELETE t1 {t xxx t yyy} {}}
217        {INSERT t1 {} {t string i 1}}
218        {UPDATE t1 {i 20 {} {}} {i 4 i 2}}
219    } { lappend y $c }
220    if {$x != $y} { error "changeset no good" }
221  }
222}
223
224finish_test
225