• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 2009 January 3
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# $Id: savepoint6.test,v 1.4 2009/06/05 17:09:12 drh Exp $
13
14set testdir [file dirname $argv0]
15source $testdir/tester.tcl
16
17proc sql {zSql} {
18  uplevel db eval [list $zSql]
19  #puts stderr "$zSql ;"
20}
21
22set 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);
27}
28
29if {0==[info exists ::G(savepoint6_iterations)]} {
30  set ::G(savepoint6_iterations) 1000
31}
32
33#--------------------------------------------------------------------------
34# In memory database state.
35#
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:
39#
40#   1. The savepoint name.
41#
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.
48#
49set lSavepoint [list]
50array set aEntry [list]
51
52proc 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
56}
57#--------------------------------------------------------------------------
58
59#-------------------------------------------------------------------------
60# Procs to operate on database:
61#
62#   savepoint NAME
63#   rollback  NAME
64#   release   NAME
65#
66#   insert_rows XVALUES
67#   delete_rows XVALUES
68#
69proc savepoint {zName} {
70  catch { sql "SAVEPOINT $zName" }
71  lappend ::lSavepoint [list $zName [array get ::aEntry]]
72}
73
74proc 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]
81
82
83      if {$i+1 < [llength $::lSavepoint]} {
84        set ::lSavepoint [lreplace $::lSavepoint [expr $i+1] end]
85      }
86      break
87    }
88  }
89}
90
91proc 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  }
100
101  if {[llength $::lSavepoint] == 0} {
102    #puts stderr "-- End of transaction!!!!!!!!!!!!!"
103  }
104}
105
106proc insert_rows {lX} {
107  foreach x $lX {
108    set y [x_to_y $x]
109
110    # Update database [db]
111    sql "INSERT OR REPLACE INTO t1 VALUES($x, '$y')"
112
113    # Update the Tcl database.
114    set ::aEntry($x) $y
115  }
116}
117
118proc delete_rows {lX} {
119  foreach x $lX {
120    # Update database [db]
121    sql "DELETE FROM t1 WHERE x = $x"
122
123    # Update the Tcl database.
124    unset -nocomplain ::aEntry($x)
125  }
126}
127#-------------------------------------------------------------------------
128
129#-------------------------------------------------------------------------
130# Proc to compare database content with the in-memory representation.
131#
132#   checkdb
133#
134proc 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  }
148
149  db eval { PRAGMA integrity_check }
150}
151#-------------------------------------------------------------------------
152
153#-------------------------------------------------------------------------
154# Proc to return random set of x values.
155#
156#   random_integers
157#
158proc 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
164}
165#-------------------------------------------------------------------------
166
167proc 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  }
179}
180
181proc savepoint_op {} {
182  set names {one two three four five}
183  set cmds  {savepoint savepoint savepoint savepoint release rollback}
184
185  set C [lindex $cmds [expr int(rand()*6)]]
186  set N [lindex $names [expr int(rand()*5)]]
187
188  #puts stderr "   $C $N ;  "
189  #flush stderr
190
191  $C $N
192  return ok
193}
194
195expr srand(0)
196
197############################################################################
198############################################################################
199# Start of test cases.
200
201do_test savepoint6-1.1 {
202  sql $DATABASE_SCHEMA
203} {}
204do_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  }
210
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
221
222  execsql {SELECT count(*) FROM t1}
223} {44}
224
225foreach 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}] {
244
245  unset -nocomplain ::lSavepoint
246  unset -nocomplain ::aEntry
247
248  catch { db close }
249  file delete -force test.db test.db-wal test.db-journal
250  eval $zSetup
251  sql $DATABASE_SCHEMA
252
253  wal_set_journal_mode
254
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  }
274
275  wal_check_journal_mode savepoint6-$testname.walok
276}
277
278unset -nocomplain ::lSavepoint
279unset -nocomplain ::aEntry
280
281finish_test
282