• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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#
17set testdir [file dirname $argv0]
18source $testdir/tester.tcl
19
20# A procedure to scramble the elements of list $inlist into a random order.
21#
22proc 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#
37expr srand(1)
38proc 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#
48proc 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}
56proc 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#
67unset -nocomplain data i max_rowid todel n rec max1 id origres newres
68unset -nocomplain inssql modsql s j z
69set data {}
70for {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}
74set 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#
79do_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#
98for {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
230unset -nocomplain data i max_rowid todel n rec max1 id origres newres
231unset -nocomplain inssql modsql s j z
232finish_test
233