|
1 # 2008 August 27 |
|
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 # This file implements regression tests for SQLite library. The |
|
13 # focus of this script is transactions |
|
14 # |
|
15 # $Id: trans2.test,v 1.1 2008/08/27 18:56:36 drh Exp $ |
|
16 # |
|
17 set testdir [file dirname $argv0] |
|
18 source $testdir/tester.tcl |
|
19 |
|
20 # A procedure to scramble the elements of list $inlist into a random order. |
|
21 # |
|
22 proc scramble {inlist} { |
|
23 set y {} |
|
24 foreach x $inlist { |
|
25 lappend y [list [expr {rand()}] $x] |
|
26 } |
|
27 set y [lsort $y] |
|
28 set outlist {} |
|
29 foreach x $y { |
|
30 lappend outlist [lindex $x 1] |
|
31 } |
|
32 return $outlist |
|
33 } |
|
34 |
|
35 # Generate a UUID using randomness. |
|
36 # |
|
37 expr srand(1) |
|
38 proc random_uuid {} { |
|
39 set u {} |
|
40 for {set i 0} {$i<5} {incr i} { |
|
41 append u [format %06x [expr {int(rand()*16777216)}]] |
|
42 } |
|
43 return $u |
|
44 } |
|
45 |
|
46 # Compute hashes on the u1 and u2 fields of the sample data. |
|
47 # |
|
48 proc hash1 {} { |
|
49 global data |
|
50 set x "" |
|
51 foreach rec [lsort -integer -index 0 $data] { |
|
52 append x [lindex $rec 1] |
|
53 } |
|
54 return [md5 $x] |
|
55 } |
|
56 proc hash2 {} { |
|
57 global data |
|
58 set x "" |
|
59 foreach rec [lsort -integer -index 0 $data] { |
|
60 append x [lindex $rec 3] |
|
61 } |
|
62 return [md5 $x] |
|
63 } |
|
64 |
|
65 # Create the initial data set |
|
66 # |
|
67 unset -nocomplain data i max_rowid todel n rec max1 id origres newres |
|
68 unset -nocomplain inssql modsql s j z |
|
69 set data {} |
|
70 for {set i 0} {$i<400} {incr i} { |
|
71 set rec [list $i [random_uuid] [expr {int(rand()*5000)+1000}] [random_uuid]] |
|
72 lappend data $rec |
|
73 } |
|
74 set max_rowid [expr {$i-1}] |
|
75 |
|
76 # Create the T1 table used to hold test data. Populate that table with |
|
77 # the initial data set and check hashes to make sure everything is correct. |
|
78 # |
|
79 do_test trans2-1.1 { |
|
80 execsql { |
|
81 PRAGMA cache_size=100; |
|
82 CREATE TABLE t1( |
|
83 id INTEGER PRIMARY KEY, |
|
84 u1 TEXT UNIQUE, |
|
85 z BLOB NOT NULL, |
|
86 u2 TEXT UNIQUE |
|
87 ); |
|
88 } |
|
89 foreach rec [scramble $data] { |
|
90 foreach {id u1 z u2} $rec break |
|
91 db eval {INSERT INTO t1 VALUES($id,$u1,zeroblob($z),$u2)} |
|
92 } |
|
93 db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} |
|
94 } [list [hash1] [hash2]] |
|
95 |
|
96 # Repeat the main test loop multiple times. |
|
97 # |
|
98 for {set i 2} {$i<=30} {incr i} { |
|
99 # Delete one row out of every 10 in the database. This will add |
|
100 # many pages to the freelist. |
|
101 # |
|
102 set todel {} |
|
103 set n [expr {[llength $data]/10}] |
|
104 set data [scramble $data] |
|
105 foreach rec [lrange $data 0 $n] { |
|
106 lappend todel [lindex $rec 0] |
|
107 } |
|
108 set data [lrange $data [expr {$n+1}] end] |
|
109 set max1 [lindex [lindex $data 0] 0] |
|
110 foreach rec $data { |
|
111 set id [lindex $rec 0] |
|
112 if {$id>$max1} {set max1 $id} |
|
113 } |
|
114 set origres [list [hash1] [hash2]] |
|
115 do_test trans2-$i.1 { |
|
116 db eval "DELETE FROM t1 WHERE id IN ([join $todel ,])" |
|
117 db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} |
|
118 } $origres |
|
119 integrity_check trans2-$i.2 |
|
120 |
|
121 # Begin a transaction and insert many new records. |
|
122 # |
|
123 set newdata {} |
|
124 foreach id $todel { |
|
125 set rec [list $id [random_uuid] \ |
|
126 [expr {int(rand()*5000)+1000}] [random_uuid]] |
|
127 lappend newdata $rec |
|
128 lappend data $rec |
|
129 } |
|
130 for {set j 1} {$j<50} {incr j} { |
|
131 set id [expr {$max_rowid+$j}] |
|
132 lappend todel $id |
|
133 set rec [list $id [random_uuid] \ |
|
134 [expr {int(rand()*5000)+1000}] [random_uuid]] |
|
135 lappend newdata $rec |
|
136 lappend data $rec |
|
137 } |
|
138 set max_rowid [expr {$max_rowid+$j-1}] |
|
139 set modsql {} |
|
140 set inssql {} |
|
141 set newres [list [hash1] [hash2]] |
|
142 do_test trans2-$i.3 { |
|
143 db eval BEGIN |
|
144 foreach rec [scramble $newdata] { |
|
145 foreach {id u1 z u2} $rec break |
|
146 set s "INSERT INTO t1 VALUES($id,'$u1',zeroblob($z),'$u2');" |
|
147 append modsql $s\n |
|
148 append inssql $s\n |
|
149 db eval $s |
|
150 } |
|
151 db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} |
|
152 } $newres |
|
153 integrity_check trans2-$i.4 |
|
154 |
|
155 # Do a large update that aborts do to a constraint failure near |
|
156 # the end. This stresses the statement journal mechanism. |
|
157 # |
|
158 do_test trans2-$i.10 { |
|
159 catchsql { |
|
160 UPDATE t1 SET u1=u1||'x', |
|
161 z = CASE WHEN id<$max_rowid |
|
162 THEN zeroblob((random()&65535)%5000 + 1000) END; |
|
163 } |
|
164 } {1 {t1.z may not be NULL}} |
|
165 do_test trans2-$i.11 { |
|
166 db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} |
|
167 } $newres |
|
168 |
|
169 # Delete all of the newly inserted records. Verify that the database |
|
170 # is back to its original state. |
|
171 # |
|
172 do_test trans2-$i.20 { |
|
173 set s "DELETE FROM t1 WHERE id IN ([join $todel ,]);" |
|
174 append modsql $s\n |
|
175 db eval $s |
|
176 db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} |
|
177 } $origres |
|
178 |
|
179 # Do another large update that aborts do to a constraint failure near |
|
180 # the end. This stresses the statement journal mechanism. |
|
181 # |
|
182 do_test trans2-$i.30 { |
|
183 catchsql { |
|
184 UPDATE t1 SET u1=u1||'x', |
|
185 z = CASE WHEN id<$max1 |
|
186 THEN zeroblob((random()&65535)%5000 + 1000) END; |
|
187 } |
|
188 } {1 {t1.z may not be NULL}} |
|
189 do_test trans2-$i.31 { |
|
190 db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} |
|
191 } $origres |
|
192 |
|
193 # Redo the inserts |
|
194 # |
|
195 do_test trans2-$i.40 { |
|
196 db eval $inssql |
|
197 append modsql $inssql |
|
198 db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} |
|
199 } $newres |
|
200 |
|
201 # Rollback the transaction. Verify that the content is restored. |
|
202 # |
|
203 do_test trans2-$i.90 { |
|
204 db eval ROLLBACK |
|
205 db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} |
|
206 } $origres |
|
207 integrity_check trans2-$i.91 |
|
208 |
|
209 # Repeat all the changes, but this time commit. |
|
210 # |
|
211 do_test trans2-$i.92 { |
|
212 db eval BEGIN |
|
213 catchsql { |
|
214 UPDATE t1 SET u1=u1||'x', |
|
215 z = CASE WHEN id<$max1 |
|
216 THEN zeroblob((random()&65535)%5000 + 1000) END; |
|
217 } |
|
218 db eval $modsql |
|
219 catchsql { |
|
220 UPDATE t1 SET u1=u1||'x', |
|
221 z = CASE WHEN id<$max1 |
|
222 THEN zeroblob((random()&65535)%5000 + 1000) END; |
|
223 } |
|
224 db eval COMMIT |
|
225 db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} |
|
226 } $newres |
|
227 integrity_check trans2-$i.93 |
|
228 } |
|
229 |
|
230 unset -nocomplain data i max_rowid todel n rec max1 id origres newres |
|
231 unset -nocomplain inssql modsql s j z |
|
232 finish_test |