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
 |