282 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			282 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| # 2009 January 3
 | |
| #
 | |
| # The author disclaims copyright to this source code.  In place of
 | |
| # a legal notice, here is a blessing:
 | |
| #
 | |
| #    May you do good and not evil.
 | |
| #    May you find forgiveness for yourself and forgive others.
 | |
| #    May you share freely, never taking more than you give.
 | |
| #
 | |
| #***********************************************************************
 | |
| #
 | |
| # $Id: savepoint6.test,v 1.4 2009/06/05 17:09:12 drh Exp $
 | |
| 
 | |
| set testdir [file dirname $argv0]
 | |
| source $testdir/tester.tcl
 | |
| 
 | |
| proc sql {zSql} {
 | |
|   uplevel db eval [list $zSql]
 | |
|   #puts stderr "$zSql ;"
 | |
| }
 | |
| 
 | |
| set DATABASE_SCHEMA {
 | |
|     PRAGMA auto_vacuum = incremental;
 | |
|     CREATE TABLE t1(x, y);
 | |
|     CREATE UNIQUE INDEX i1 ON t1(x);
 | |
|     CREATE INDEX i2 ON t1(y);
 | |
| }
 | |
| 
 | |
| if {0==[info exists ::G(savepoint6_iterations)]} {
 | |
|   set ::G(savepoint6_iterations) 1000
 | |
| }
 | |
| 
 | |
| #--------------------------------------------------------------------------
 | |
| # In memory database state.
 | |
| #
 | |
| # ::lSavepoint is a list containing one entry for each active savepoint. The
 | |
| # first entry in the list corresponds to the most recently opened savepoint.
 | |
| # Each entry consists of two elements:
 | |
| #
 | |
| #   1. The savepoint name.
 | |
| #
 | |
| #   2. A serialized Tcl array representing the contents of table t1 at the
 | |
| #      start of the savepoint. The keys of the array are the x values. The
 | |
| #      values are the y values.
 | |
| #  
 | |
| # Array ::aEntry contains the contents of database table t1. Array keys are
 | |
| # x values, the array data values are y values.
 | |
| #
 | |
| set lSavepoint [list]
 | |
| array set aEntry [list]
 | |
| 
 | |
| proc x_to_y {x} {
 | |
|   set nChar [expr int(rand()*250) + 250]
 | |
|   set str " $nChar [string repeat $x. $nChar]"
 | |
|   string range $str 1 $nChar
 | |
| }
 | |
| #--------------------------------------------------------------------------
 | |
| 
 | |
| #-------------------------------------------------------------------------
 | |
| # Procs to operate on database:
 | |
| #
 | |
| #   savepoint NAME
 | |
| #   rollback  NAME
 | |
| #   release   NAME
 | |
| #
 | |
| #   insert_rows XVALUES
 | |
| #   delete_rows XVALUES
 | |
| #
 | |
| proc savepoint {zName} {
 | |
|   catch { sql "SAVEPOINT $zName" }
 | |
|   lappend ::lSavepoint [list $zName [array get ::aEntry]]
 | |
| }
 | |
| 
 | |
| proc rollback {zName} {
 | |
|   catch { sql "ROLLBACK TO $zName" }
 | |
|   for {set i [expr {[llength $::lSavepoint]-1}]} {$i>=0} {incr i -1} {
 | |
|     set zSavepoint [lindex $::lSavepoint $i 0]
 | |
|     if {$zSavepoint eq $zName} {
 | |
|       unset -nocomplain ::aEntry
 | |
|       array set ::aEntry [lindex $::lSavepoint $i 1]
 | |
| 
 | |
| 
 | |
|       if {$i+1 < [llength $::lSavepoint]} {
 | |
|         set ::lSavepoint [lreplace $::lSavepoint [expr $i+1] end]
 | |
|       }
 | |
|       break
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| proc release {zName} {
 | |
|   catch { sql "RELEASE $zName" }
 | |
|   for {set i [expr {[llength $::lSavepoint]-1}]} {$i>=0} {incr i -1} {
 | |
|     set zSavepoint [lindex $::lSavepoint $i 0]
 | |
|     if {$zSavepoint eq $zName} {
 | |
|       set ::lSavepoint [lreplace $::lSavepoint $i end]
 | |
|       break
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if {[llength $::lSavepoint] == 0} {
 | |
|     #puts stderr "-- End of transaction!!!!!!!!!!!!!"
 | |
|   }
 | |
| }
 | |
| 
 | |
| proc insert_rows {lX} {
 | |
|   foreach x $lX {
 | |
|     set y [x_to_y $x]
 | |
| 
 | |
|     # Update database [db]
 | |
|     sql "INSERT OR REPLACE INTO t1 VALUES($x, '$y')"
 | |
| 
 | |
|     # Update the Tcl database.
 | |
|     set ::aEntry($x) $y
 | |
|   }
 | |
| }
 | |
| 
 | |
| proc delete_rows {lX} {
 | |
|   foreach x $lX {
 | |
|     # Update database [db]
 | |
|     sql "DELETE FROM t1 WHERE x = $x"
 | |
| 
 | |
|     # Update the Tcl database.
 | |
|     unset -nocomplain ::aEntry($x)
 | |
|   }
 | |
| }
 | |
| #-------------------------------------------------------------------------
 | |
| 
 | |
| #-------------------------------------------------------------------------
 | |
| # Proc to compare database content with the in-memory representation.
 | |
| #
 | |
| #   checkdb
 | |
| #
 | |
| proc checkdb {} {
 | |
|   set nEntry [db one {SELECT count(*) FROM t1}]
 | |
|   set nEntry2 [array size ::aEntry]
 | |
|   if {$nEntry != $nEntry2} {
 | |
|     error "$nEntry entries in database, $nEntry2 entries in array"
 | |
|   }
 | |
|   db eval {SELECT x, y FROM t1} {
 | |
|     if {![info exists ::aEntry($x)]} {
 | |
|       error "Entry $x exists in database, but not in array"
 | |
|     }
 | |
|     if {$::aEntry($x) ne $y} {
 | |
|       error "Entry $x is set to {$y} in database, {$::aEntry($x)} in array"
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   db eval { PRAGMA integrity_check }
 | |
| }
 | |
| #-------------------------------------------------------------------------
 | |
| 
 | |
| #-------------------------------------------------------------------------
 | |
| # Proc to return random set of x values.
 | |
| #
 | |
| #   random_integers
 | |
| #
 | |
| proc random_integers {nRes nRange} {
 | |
|   set ret [list]
 | |
|   for {set i 0} {$i<$nRes} {incr i} {
 | |
|     lappend ret [expr int(rand()*$nRange)]
 | |
|   }
 | |
|   return $ret
 | |
| } 
 | |
| #-------------------------------------------------------------------------
 | |
| 
 | |
| proc database_op {} {
 | |
|   set i [expr int(rand()*2)] 
 | |
|   if {$i==0} {
 | |
|     insert_rows [random_integers 100 1000]
 | |
|   }
 | |
|   if {$i==1} {
 | |
|     delete_rows [random_integers 100 1000]
 | |
|     set i [expr int(rand()*3)] 
 | |
|     if {$i==0} {
 | |
|       sql {PRAGMA incremental_vacuum}
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| proc savepoint_op {} {
 | |
|   set names {one two three four five}
 | |
|   set cmds  {savepoint savepoint savepoint savepoint release rollback}
 | |
| 
 | |
|   set C [lindex $cmds [expr int(rand()*6)]]
 | |
|   set N [lindex $names [expr int(rand()*5)]]
 | |
| 
 | |
|   #puts stderr "   $C $N ;  "
 | |
|   #flush stderr
 | |
| 
 | |
|   $C $N
 | |
|   return ok
 | |
| }
 | |
| 
 | |
| expr srand(0)
 | |
| 
 | |
| ############################################################################
 | |
| ############################################################################
 | |
| # Start of test cases.
 | |
| 
 | |
| do_test savepoint6-1.1 {
 | |
|   sql $DATABASE_SCHEMA
 | |
| } {}
 | |
| do_test savepoint6-1.2 {
 | |
|   insert_rows {
 | |
|     497 166 230 355 779 588 394 317 290 475 362 193 805 851 564 
 | |
|     763 44 930 389 819 765 760 966 280 538 414 500 18 25 287 320 
 | |
|     30 382 751 87 283 981 429 630 974 421 270 810 405 
 | |
|   }
 | |
| 
 | |
|   savepoint one
 | |
|   insert_rows 858
 | |
|   delete_rows 930
 | |
|   savepoint two
 | |
|     execsql {PRAGMA incremental_vacuum}
 | |
|     savepoint three
 | |
|       insert_rows 144
 | |
|      rollback three
 | |
|     rollback two
 | |
|   release one
 | |
| 
 | |
|   execsql {SELECT count(*) FROM t1}
 | |
| } {44}
 | |
| 
 | |
| foreach zSetup [list {
 | |
|   set testname normal
 | |
|   sqlite3 db test.db
 | |
| } {
 | |
|   if {[wal_is_wal_mode]} continue
 | |
|   set testname tempdb
 | |
|   sqlite3 db ""
 | |
| } {
 | |
|   if {[permutation] eq "journaltest"} {
 | |
|     continue
 | |
|   }
 | |
|   set testname nosync
 | |
|   sqlite3 db test.db
 | |
|   sql { PRAGMA synchronous = off }
 | |
| } {
 | |
|   set testname smallcache
 | |
|   sqlite3 db test.db
 | |
|   sql { PRAGMA cache_size = 10 }
 | |
| }] {
 | |
| 
 | |
|   unset -nocomplain ::lSavepoint
 | |
|   unset -nocomplain ::aEntry
 | |
| 
 | |
|   catch { db close }
 | |
|   forcedelete test.db test.db-wal test.db-journal
 | |
|   eval $zSetup
 | |
|   sql $DATABASE_SCHEMA
 | |
| 
 | |
|   wal_set_journal_mode
 | |
| 
 | |
|   do_test savepoint6-$testname.setup {
 | |
|     savepoint one
 | |
|     insert_rows [random_integers 100 1000]
 | |
|     release one
 | |
|     checkdb
 | |
|   } {ok}
 | |
|   
 | |
|   for {set i 0} {$i < $::G(savepoint6_iterations)} {incr i} {
 | |
|     do_test savepoint6-$testname.$i.1 {
 | |
|       savepoint_op
 | |
|       checkdb
 | |
|     } {ok}
 | |
|   
 | |
|     do_test savepoint6-$testname.$i.2 {
 | |
|       database_op
 | |
|       database_op
 | |
|       checkdb
 | |
|     } {ok}
 | |
|   }
 | |
| 
 | |
|   wal_check_journal_mode savepoint6-$testname.walok
 | |
| }
 | |
| 
 | |
| unset -nocomplain ::lSavepoint
 | |
| unset -nocomplain ::aEntry
 | |
| 
 | |
| finish_test
 | 
