1255538e9Sdrh# 2008 August 27 2255538e9Sdrh# 3255538e9Sdrh# The author disclaims copyright to this source code. In place of 4255538e9Sdrh# a legal notice, here is a blessing: 5255538e9Sdrh# 6255538e9Sdrh# May you do good and not evil. 7255538e9Sdrh# May you find forgiveness for yourself and forgive others. 8255538e9Sdrh# May you share freely, never taking more than you give. 9255538e9Sdrh# 10255538e9Sdrh#*********************************************************************** 11255538e9Sdrh# 12255538e9Sdrh# This file implements regression tests for SQLite library. The 13255538e9Sdrh# focus of this script is transactions 14255538e9Sdrh# 15255538e9Sdrh# $Id: trans2.test,v 1.1 2008/08/27 18:56:36 drh Exp $ 16255538e9Sdrh# 17255538e9Sdrhset testdir [file dirname $argv0] 18255538e9Sdrhsource $testdir/tester.tcl 19255538e9Sdrh 20255538e9Sdrh# A procedure to scramble the elements of list $inlist into a random order. 21255538e9Sdrh# 22255538e9Sdrhproc scramble {inlist} { 23255538e9Sdrh set y {} 24255538e9Sdrh foreach x $inlist { 25255538e9Sdrh lappend y [list [expr {rand()}] $x] 26255538e9Sdrh } 27255538e9Sdrh set y [lsort $y] 28255538e9Sdrh set outlist {} 29255538e9Sdrh foreach x $y { 30255538e9Sdrh lappend outlist [lindex $x 1] 31255538e9Sdrh } 32255538e9Sdrh return $outlist 33255538e9Sdrh} 34255538e9Sdrh 35255538e9Sdrh# Generate a UUID using randomness. 36255538e9Sdrh# 37255538e9Sdrhexpr srand(1) 38255538e9Sdrhproc random_uuid {} { 39255538e9Sdrh set u {} 40255538e9Sdrh for {set i 0} {$i<5} {incr i} { 41255538e9Sdrh append u [format %06x [expr {int(rand()*16777216)}]] 42255538e9Sdrh } 43255538e9Sdrh return $u 44255538e9Sdrh} 45255538e9Sdrh 46255538e9Sdrh# Compute hashes on the u1 and u2 fields of the sample data. 47255538e9Sdrh# 48255538e9Sdrhproc hash1 {} { 49255538e9Sdrh global data 50255538e9Sdrh set x "" 51255538e9Sdrh foreach rec [lsort -integer -index 0 $data] { 52255538e9Sdrh append x [lindex $rec 1] 53255538e9Sdrh } 54255538e9Sdrh return [md5 $x] 55255538e9Sdrh} 56255538e9Sdrhproc hash2 {} { 57255538e9Sdrh global data 58255538e9Sdrh set x "" 59255538e9Sdrh foreach rec [lsort -integer -index 0 $data] { 60255538e9Sdrh append x [lindex $rec 3] 61255538e9Sdrh } 62255538e9Sdrh return [md5 $x] 63255538e9Sdrh} 64255538e9Sdrh 65255538e9Sdrh# Create the initial data set 66255538e9Sdrh# 67255538e9Sdrhunset -nocomplain data i max_rowid todel n rec max1 id origres newres 68255538e9Sdrhunset -nocomplain inssql modsql s j z 69255538e9Sdrhset data {} 70255538e9Sdrhfor {set i 0} {$i<400} {incr i} { 71255538e9Sdrh set rec [list $i [random_uuid] [expr {int(rand()*5000)+1000}] [random_uuid]] 72255538e9Sdrh lappend data $rec 73255538e9Sdrh} 74255538e9Sdrhset max_rowid [expr {$i-1}] 75255538e9Sdrh 76255538e9Sdrh# Create the T1 table used to hold test data. Populate that table with 77255538e9Sdrh# the initial data set and check hashes to make sure everything is correct. 78255538e9Sdrh# 79255538e9Sdrhdo_test trans2-1.1 { 80255538e9Sdrh execsql { 81255538e9Sdrh PRAGMA cache_size=100; 82255538e9Sdrh CREATE TABLE t1( 83255538e9Sdrh id INTEGER PRIMARY KEY, 84255538e9Sdrh u1 TEXT UNIQUE, 85255538e9Sdrh z BLOB NOT NULL, 86255538e9Sdrh u2 TEXT UNIQUE 87255538e9Sdrh ); 88255538e9Sdrh } 89255538e9Sdrh foreach rec [scramble $data] { 90255538e9Sdrh foreach {id u1 z u2} $rec break 91255538e9Sdrh db eval {INSERT INTO t1 VALUES($id,$u1,zeroblob($z),$u2)} 92255538e9Sdrh } 93255538e9Sdrh db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} 94255538e9Sdrh} [list [hash1] [hash2]] 95255538e9Sdrh 96255538e9Sdrh# Repeat the main test loop multiple times. 97255538e9Sdrh# 98255538e9Sdrhfor {set i 2} {$i<=30} {incr i} { 99255538e9Sdrh # Delete one row out of every 10 in the database. This will add 100255538e9Sdrh # many pages to the freelist. 101255538e9Sdrh # 102255538e9Sdrh set todel {} 103255538e9Sdrh set n [expr {[llength $data]/10}] 104255538e9Sdrh set data [scramble $data] 105255538e9Sdrh foreach rec [lrange $data 0 $n] { 106255538e9Sdrh lappend todel [lindex $rec 0] 107255538e9Sdrh } 108255538e9Sdrh set data [lrange $data [expr {$n+1}] end] 109255538e9Sdrh set max1 [lindex [lindex $data 0] 0] 110255538e9Sdrh foreach rec $data { 111255538e9Sdrh set id [lindex $rec 0] 112255538e9Sdrh if {$id>$max1} {set max1 $id} 113255538e9Sdrh } 114255538e9Sdrh set origres [list [hash1] [hash2]] 115255538e9Sdrh do_test trans2-$i.1 { 116255538e9Sdrh db eval "DELETE FROM t1 WHERE id IN ([join $todel ,])" 117255538e9Sdrh db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} 118255538e9Sdrh } $origres 119255538e9Sdrh integrity_check trans2-$i.2 120255538e9Sdrh 121255538e9Sdrh # Begin a transaction and insert many new records. 122255538e9Sdrh # 123255538e9Sdrh set newdata {} 124255538e9Sdrh foreach id $todel { 125255538e9Sdrh set rec [list $id [random_uuid] \ 126255538e9Sdrh [expr {int(rand()*5000)+1000}] [random_uuid]] 127255538e9Sdrh lappend newdata $rec 128255538e9Sdrh lappend data $rec 129255538e9Sdrh } 130255538e9Sdrh for {set j 1} {$j<50} {incr j} { 131255538e9Sdrh set id [expr {$max_rowid+$j}] 132255538e9Sdrh lappend todel $id 133255538e9Sdrh set rec [list $id [random_uuid] \ 134255538e9Sdrh [expr {int(rand()*5000)+1000}] [random_uuid]] 135255538e9Sdrh lappend newdata $rec 136255538e9Sdrh lappend data $rec 137255538e9Sdrh } 138255538e9Sdrh set max_rowid [expr {$max_rowid+$j-1}] 139255538e9Sdrh set modsql {} 140255538e9Sdrh set inssql {} 141255538e9Sdrh set newres [list [hash1] [hash2]] 142255538e9Sdrh do_test trans2-$i.3 { 143255538e9Sdrh db eval BEGIN 144255538e9Sdrh foreach rec [scramble $newdata] { 145255538e9Sdrh foreach {id u1 z u2} $rec break 146255538e9Sdrh set s "INSERT INTO t1 VALUES($id,'$u1',zeroblob($z),'$u2');" 147255538e9Sdrh append modsql $s\n 148255538e9Sdrh append inssql $s\n 149255538e9Sdrh db eval $s 150255538e9Sdrh } 151255538e9Sdrh db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} 152255538e9Sdrh } $newres 153255538e9Sdrh integrity_check trans2-$i.4 154255538e9Sdrh 155255538e9Sdrh # Do a large update that aborts do to a constraint failure near 156255538e9Sdrh # the end. This stresses the statement journal mechanism. 157255538e9Sdrh # 158255538e9Sdrh do_test trans2-$i.10 { 159255538e9Sdrh catchsql { 160255538e9Sdrh UPDATE t1 SET u1=u1||'x', 161255538e9Sdrh z = CASE WHEN id<$max_rowid 162255538e9Sdrh THEN zeroblob((random()&65535)%5000 + 1000) END; 163255538e9Sdrh } 164*1b4b334aSdan } {1 {NOT NULL constraint failed: t1.z}} 165255538e9Sdrh do_test trans2-$i.11 { 166255538e9Sdrh db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} 167255538e9Sdrh } $newres 168255538e9Sdrh 169255538e9Sdrh # Delete all of the newly inserted records. Verify that the database 170255538e9Sdrh # is back to its original state. 171255538e9Sdrh # 172255538e9Sdrh do_test trans2-$i.20 { 173255538e9Sdrh set s "DELETE FROM t1 WHERE id IN ([join $todel ,]);" 174255538e9Sdrh append modsql $s\n 175255538e9Sdrh db eval $s 176255538e9Sdrh db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} 177255538e9Sdrh } $origres 178255538e9Sdrh 179255538e9Sdrh # Do another large update that aborts do to a constraint failure near 180255538e9Sdrh # the end. This stresses the statement journal mechanism. 181255538e9Sdrh # 182255538e9Sdrh do_test trans2-$i.30 { 183255538e9Sdrh catchsql { 184255538e9Sdrh UPDATE t1 SET u1=u1||'x', 185255538e9Sdrh z = CASE WHEN id<$max1 186255538e9Sdrh THEN zeroblob((random()&65535)%5000 + 1000) END; 187255538e9Sdrh } 188*1b4b334aSdan } {1 {NOT NULL constraint failed: t1.z}} 189255538e9Sdrh do_test trans2-$i.31 { 190255538e9Sdrh db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} 191255538e9Sdrh } $origres 192255538e9Sdrh 193255538e9Sdrh # Redo the inserts 194255538e9Sdrh # 195255538e9Sdrh do_test trans2-$i.40 { 196255538e9Sdrh db eval $inssql 197255538e9Sdrh append modsql $inssql 198255538e9Sdrh db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} 199255538e9Sdrh } $newres 200255538e9Sdrh 201255538e9Sdrh # Rollback the transaction. Verify that the content is restored. 202255538e9Sdrh # 203255538e9Sdrh do_test trans2-$i.90 { 204255538e9Sdrh db eval ROLLBACK 205255538e9Sdrh db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} 206255538e9Sdrh } $origres 207255538e9Sdrh integrity_check trans2-$i.91 208255538e9Sdrh 209255538e9Sdrh # Repeat all the changes, but this time commit. 210255538e9Sdrh # 211255538e9Sdrh do_test trans2-$i.92 { 212255538e9Sdrh db eval BEGIN 213255538e9Sdrh catchsql { 214255538e9Sdrh UPDATE t1 SET u1=u1||'x', 215255538e9Sdrh z = CASE WHEN id<$max1 216255538e9Sdrh THEN zeroblob((random()&65535)%5000 + 1000) END; 217255538e9Sdrh } 218255538e9Sdrh db eval $modsql 219255538e9Sdrh catchsql { 220255538e9Sdrh UPDATE t1 SET u1=u1||'x', 221255538e9Sdrh z = CASE WHEN id<$max1 222255538e9Sdrh THEN zeroblob((random()&65535)%5000 + 1000) END; 223255538e9Sdrh } 224255538e9Sdrh db eval COMMIT 225255538e9Sdrh db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id} 226255538e9Sdrh } $newres 227255538e9Sdrh integrity_check trans2-$i.93 228255538e9Sdrh} 229255538e9Sdrh 230255538e9Sdrhunset -nocomplain data i max_rowid todel n rec max1 id origres newres 231255538e9Sdrhunset -nocomplain inssql modsql s j z 232255538e9Sdrhfinish_test 233