Home | History | Annotate | Download | only in test
      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 
     14 set testdir [file dirname $argv0]
     15 source $testdir/tester.tcl
     16 
     17 proc sql {zSql} {
     18   uplevel db eval [list $zSql]
     19   #puts stderr "$zSql ;"
     20 }
     21 
     22 set 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 
     29 if {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 #
     49 set lSavepoint [list]
     50 array set aEntry [list]
     51 
     52 proc 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 #
     69 proc savepoint {zName} {
     70   catch { sql "SAVEPOINT $zName" }
     71   lappend ::lSavepoint [list $zName [array get ::aEntry]]
     72 }
     73 
     74 proc 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 
     91 proc 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 
    106 proc 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 
    118 proc 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 #
    134 proc 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 #
    158 proc 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 
    167 proc 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 
    181 proc 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 
    195 expr srand(0)
    196 
    197 ############################################################################
    198 ############################################################################
    199 # Start of test cases.
    200 
    201 do_test savepoint6-1.1 {
    202   sql $DATABASE_SCHEMA
    203 } {}
    204 do_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 
    225 foreach 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 
    278 unset -nocomplain ::lSavepoint
    279 unset -nocomplain ::aEntry
    280 
    281 finish_test
    282