Fix a case where a corrupt stat4 record could go unrecognized due to integer overflow.
[sqlite.git] / test / savepoint6.test
blobb1d0d46f5c9f3beef77133eb0e620059828e6871
1 # 2009 January 3
3 # The author disclaims copyright to this source code.  In place of
4 # a legal notice, here is a blessing:
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.
10 #***********************************************************************
12 # $Id: savepoint6.test,v 1.4 2009/06/05 17:09:12 drh Exp $
14 set testdir [file dirname $argv0]
15 source $testdir/tester.tcl
17 proc sql {zSql} {
18   uplevel db eval [list $zSql]
19   #puts stderr "$zSql ;"
22 set DATABASE_SCHEMA {
23     PRAGMA auto_vacuum = incremental;
24     CREATE TABLE t1(x, y);
25     CREATE UNIQUE INDEX i1 ON t1(x);
26     CREATE INDEX i2 ON t1(y);
29 if {0==[info exists ::G(savepoint6_iterations)]} {
30   set ::G(savepoint6_iterations) 1000
33 #--------------------------------------------------------------------------
34 # In memory database state.
36 # ::lSavepoint is a list containing one entry for each active savepoint. The
37 # first entry in the list corresponds to the most recently opened savepoint.
38 # Each entry consists of two elements:
40 #   1. The savepoint name.
42 #   2. A serialized Tcl array representing the contents of table t1 at the
43 #      start of the savepoint. The keys of the array are the x values. The
44 #      values are the y values.
45 #  
46 # Array ::aEntry contains the contents of database table t1. Array keys are
47 # x values, the array data values are y values.
49 set lSavepoint [list]
50 array set aEntry [list]
52 proc x_to_y {x} {
53   set nChar [expr int(rand()*250) + 250]
54   set str " $nChar [string repeat $x. $nChar]"
55   string range $str 1 $nChar
57 #--------------------------------------------------------------------------
59 #-------------------------------------------------------------------------
60 # Procs to operate on database:
62 #   savepoint NAME
63 #   rollback  NAME
64 #   release   NAME
66 #   insert_rows XVALUES
67 #   delete_rows XVALUES
69 proc savepoint {zName} {
70   catch { sql "SAVEPOINT $zName" }
71   lappend ::lSavepoint [list $zName [array get ::aEntry]]
74 proc rollback {zName} {
75   catch { sql "ROLLBACK TO $zName" }
76   for {set i [expr {[llength $::lSavepoint]-1}]} {$i>=0} {incr i -1} {
77     set zSavepoint [lindex $::lSavepoint $i 0]
78     if {$zSavepoint eq $zName} {
79       unset -nocomplain ::aEntry
80       array set ::aEntry [lindex $::lSavepoint $i 1]
83       if {$i+1 < [llength $::lSavepoint]} {
84         set ::lSavepoint [lreplace $::lSavepoint [expr $i+1] end]
85       }
86       break
87     }
88   }
91 proc release {zName} {
92   catch { sql "RELEASE $zName" }
93   for {set i [expr {[llength $::lSavepoint]-1}]} {$i>=0} {incr i -1} {
94     set zSavepoint [lindex $::lSavepoint $i 0]
95     if {$zSavepoint eq $zName} {
96       set ::lSavepoint [lreplace $::lSavepoint $i end]
97       break
98     }
99   }
101   if {[llength $::lSavepoint] == 0} {
102     #puts stderr "-- End of transaction!!!!!!!!!!!!!"
103   }
106 proc insert_rows {lX} {
107   foreach x $lX {
108     set y [x_to_y $x]
110     # Update database [db]
111     sql "INSERT OR REPLACE INTO t1 VALUES($x, '$y')"
113     # Update the Tcl database.
114     set ::aEntry($x) $y
115   }
118 proc delete_rows {lX} {
119   foreach x $lX {
120     # Update database [db]
121     sql "DELETE FROM t1 WHERE x = $x"
123     # Update the Tcl database.
124     unset -nocomplain ::aEntry($x)
125   }
127 #-------------------------------------------------------------------------
129 #-------------------------------------------------------------------------
130 # Proc to compare database content with the in-memory representation.
132 #   checkdb
134 proc checkdb {} {
135   set nEntry [db one {SELECT count(*) FROM t1}]
136   set nEntry2 [array size ::aEntry]
137   if {$nEntry != $nEntry2} {
138     error "$nEntry entries in database, $nEntry2 entries in array"
139   }
140   db eval {SELECT x, y FROM t1} {
141     if {![info exists ::aEntry($x)]} {
142       error "Entry $x exists in database, but not in array"
143     }
144     if {$::aEntry($x) ne $y} {
145       error "Entry $x is set to {$y} in database, {$::aEntry($x)} in array"
146     }
147   }
149   db eval { PRAGMA integrity_check }
151 #-------------------------------------------------------------------------
153 #-------------------------------------------------------------------------
154 # Proc to return random set of x values.
156 #   random_integers
158 proc random_integers {nRes nRange} {
159   set ret [list]
160   for {set i 0} {$i<$nRes} {incr i} {
161     lappend ret [expr int(rand()*$nRange)]
162   }
163   return $ret
165 #-------------------------------------------------------------------------
167 proc database_op {} {
168   set i [expr int(rand()*2)] 
169   if {$i==0} {
170     insert_rows [random_integers 100 1000]
171   }
172   if {$i==1} {
173     delete_rows [random_integers 100 1000]
174     set i [expr int(rand()*3)] 
175     if {$i==0} {
176       sql {PRAGMA incremental_vacuum}
177     }
178   }
181 proc savepoint_op {} {
182   set names {one two three four five}
183   set cmds  {savepoint savepoint savepoint savepoint release rollback}
185   set C [lindex $cmds [expr int(rand()*6)]]
186   set N [lindex $names [expr int(rand()*5)]]
188   #puts stderr "   $C $N ;  "
189   #flush stderr
191   $C $N
192   return ok
195 expr srand(0)
197 ############################################################################
198 ############################################################################
199 # Start of test cases.
201 do_test savepoint6-1.1 {
202   sql $DATABASE_SCHEMA
203 } {}
204 do_test savepoint6-1.2 {
205   insert_rows {
206     497 166 230 355 779 588 394 317 290 475 362 193 805 851 564 
207     763 44 930 389 819 765 760 966 280 538 414 500 18 25 287 320 
208     30 382 751 87 283 981 429 630 974 421 270 810 405 
209   }
211   savepoint one
212   insert_rows 858
213   delete_rows 930
214   savepoint two
215     execsql {PRAGMA incremental_vacuum}
216     savepoint three
217       insert_rows 144
218      rollback three
219     rollback two
220   release one
222   execsql {SELECT count(*) FROM t1}
223 } {44}
225 foreach zSetup [list {
226   set testname normal
227   sqlite3 db test.db
228 } {
229   if {[wal_is_wal_mode]} continue
230   set testname tempdb
231   sqlite3 db ""
232 } {
233   if {[permutation] eq "journaltest"} {
234     continue
235   }
236   set testname nosync
237   sqlite3 db test.db
238   sql { PRAGMA synchronous = off }
239 } {
240   set testname smallcache
241   sqlite3 db test.db
242   sql { PRAGMA cache_size = 10 }
243 }] {
245   unset -nocomplain ::lSavepoint
246   unset -nocomplain ::aEntry
248   catch { db close }
249   forcedelete test.db test.db-wal test.db-journal
250   eval $zSetup
251   sql $DATABASE_SCHEMA
253   wal_set_journal_mode
255   do_test savepoint6-$testname.setup {
256     savepoint one
257     insert_rows [random_integers 100 1000]
258     release one
259     checkdb
260   } {ok}
261   
262   for {set i 0} {$i < $::G(savepoint6_iterations)} {incr i} {
263     do_test savepoint6-$testname.$i.1 {
264       savepoint_op
265       checkdb
266     } {ok}
267   
268     do_test savepoint6-$testname.$i.2 {
269       database_op
270       database_op
271       checkdb
272     } {ok}
273   }
275   wal_check_journal_mode savepoint6-$testname.walok
278 unset -nocomplain ::lSavepoint
279 unset -nocomplain ::aEntry
281 finish_test