drh | 255538e | 2008-08-27 18:56:36 +0000 | [diff] [blame] | 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 | } |
dan | 1b4b334 | 2013-11-28 19:28:00 +0000 | [diff] [blame] | 164 | } {1 {NOT NULL constraint failed: t1.z}} |
drh | 255538e | 2008-08-27 18:56:36 +0000 | [diff] [blame] | 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 | } |
dan | 1b4b334 | 2013-11-28 19:28:00 +0000 | [diff] [blame] | 188 | } {1 {NOT NULL constraint failed: t1.z}} |
drh | 255538e | 2008-08-27 18:56:36 +0000 | [diff] [blame] | 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 |