mirror of
				https://github.com/sqlite/sqlite.git
				synced 2025-11-03 16:53:36 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			300 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Tcl
		
	
	
	
	
	
			
		
		
	
	
			300 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Tcl
		
	
	
	
	
	
 | 
						|
proc do_changeset_test {tn session res} {
 | 
						|
  set r [list]
 | 
						|
  foreach x $res {lappend r $x}
 | 
						|
  uplevel do_test $tn [list [subst -nocommands {
 | 
						|
    set x [list]
 | 
						|
    sqlite3session_foreach c [$session changeset] { lappend x [set c] }
 | 
						|
    set x
 | 
						|
  }]] [list $r]
 | 
						|
}
 | 
						|
 | 
						|
proc do_patchset_test {tn session res} {
 | 
						|
  set r [list]
 | 
						|
  foreach x $res {lappend r $x}
 | 
						|
  uplevel do_test $tn [list [subst -nocommands {
 | 
						|
    set x [list]
 | 
						|
    sqlite3session_foreach c [$session patchset] { lappend x [set c] }
 | 
						|
    set x
 | 
						|
  }]] [list $r]
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
proc do_changeset_invert_test {tn session res} {
 | 
						|
  set r [list]
 | 
						|
  foreach x $res {lappend r $x}
 | 
						|
  uplevel do_test $tn [list [subst -nocommands {
 | 
						|
    set x [list]
 | 
						|
    set changeset [sqlite3changeset_invert [$session changeset]]
 | 
						|
    sqlite3session_foreach c [set changeset] { lappend x [set c] }
 | 
						|
    set x
 | 
						|
  }]] [list $r]
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
proc do_conflict_test {tn args} {
 | 
						|
 | 
						|
  set O(-tables)    [list]
 | 
						|
  set O(-sql)       [list]
 | 
						|
  set O(-conflicts) [list]
 | 
						|
  set O(-policy)    "OMIT"
 | 
						|
 | 
						|
  array set V $args
 | 
						|
  foreach key [array names V] {
 | 
						|
    if {![info exists O($key)]} {error "no such option: $key"}
 | 
						|
  }
 | 
						|
  array set O $args
 | 
						|
 | 
						|
  proc xConflict {args} [subst -nocommands { 
 | 
						|
    lappend ::xConflict [set args]
 | 
						|
    return $O(-policy) 
 | 
						|
  }]
 | 
						|
  proc bgerror {args} { set ::background_error $args }
 | 
						|
 | 
						|
  sqlite3session S db main
 | 
						|
  S object_config rowid 1
 | 
						|
  foreach t $O(-tables) { S attach $t }
 | 
						|
  execsql $O(-sql)
 | 
						|
 | 
						|
  set ::xConflict [list]
 | 
						|
  sqlite3changeset_apply db2 [S changeset] xConflict
 | 
						|
 | 
						|
  set conflicts [list]
 | 
						|
  foreach c $O(-conflicts) {
 | 
						|
    lappend conflicts $c
 | 
						|
  }
 | 
						|
 | 
						|
  after 1 {set go 1}
 | 
						|
  vwait go
 | 
						|
 | 
						|
  uplevel do_test $tn [list { set ::xConflict }] [list $conflicts]
 | 
						|
  S delete
 | 
						|
}
 | 
						|
 | 
						|
proc do_common_sql {sql} {
 | 
						|
  execsql $sql db
 | 
						|
  execsql $sql db2
 | 
						|
}
 | 
						|
 | 
						|
proc changeset_from_sql {sql {dbname main}} {
 | 
						|
  if {$dbname == "main"} {
 | 
						|
    return [sql_exec_changeset db $sql]
 | 
						|
  }
 | 
						|
  set rc [catch {
 | 
						|
    sqlite3session S db $dbname
 | 
						|
    S object_config rowid 1
 | 
						|
    db eval "SELECT name FROM $dbname.sqlite_master WHERE type = 'table'" {
 | 
						|
      S attach $name
 | 
						|
    }
 | 
						|
    db eval $sql
 | 
						|
    S changeset
 | 
						|
  } changeset]
 | 
						|
  catch { S delete }
 | 
						|
 | 
						|
  if {$rc} {
 | 
						|
    error $changeset
 | 
						|
  }
 | 
						|
  return $changeset
 | 
						|
}
 | 
						|
 | 
						|
proc patchset_from_sql {sql {dbname main}} {
 | 
						|
  set rc [catch {
 | 
						|
    sqlite3session S db $dbname
 | 
						|
    db eval "SELECT name FROM $dbname.sqlite_master WHERE type = 'table'" {
 | 
						|
      S attach $name
 | 
						|
    }
 | 
						|
    db eval $sql
 | 
						|
    S patchset
 | 
						|
  } patchset]
 | 
						|
  catch { S delete }
 | 
						|
 | 
						|
  if {$rc} {
 | 
						|
    error $patchset
 | 
						|
  }
 | 
						|
  return $patchset
 | 
						|
}
 | 
						|
 | 
						|
# Usage: do_then_apply_sql ?-ignorenoop? SQL ?DBNAME? 
 | 
						|
#
 | 
						|
proc do_then_apply_sql {args} {
 | 
						|
  
 | 
						|
  set bIgnoreNoop 0
 | 
						|
  set a1 [lindex $args 0]
 | 
						|
  if {[string length $a1]>1 && [string first $a1 -ignorenoop]==0} {
 | 
						|
    set bIgnoreNoop 1
 | 
						|
    set args [lrange $args 1 end]
 | 
						|
  }
 | 
						|
 | 
						|
  if {[llength $args]!=1 && [llength $args]!=2} {
 | 
						|
    error "usage: do_then_apply_sql ?-ignorenoop? SQL ?DBNAME?"
 | 
						|
  }
 | 
						|
 | 
						|
  set sql [lindex $args 0]
 | 
						|
  if {[llength $args]==1} {
 | 
						|
    set dbname main
 | 
						|
  } else {
 | 
						|
    set dbname [lindex $args 1]
 | 
						|
  }
 | 
						|
 | 
						|
  set ::n_conflict 0
 | 
						|
  proc xConflict args { incr ::n_conflict ; return "OMIT" }
 | 
						|
  set rc [catch {
 | 
						|
    sqlite3session S db $dbname
 | 
						|
    S object_config rowid 1
 | 
						|
    db eval "SELECT name FROM $dbname.sqlite_master WHERE type = 'table'" {
 | 
						|
      S attach $name
 | 
						|
    }
 | 
						|
    db eval $sql
 | 
						|
    set ::changeset [S changeset]
 | 
						|
    sqlite3changeset_apply db2 $::changeset xConflict
 | 
						|
  } msg]
 | 
						|
 | 
						|
  catch { S delete }
 | 
						|
  if {$rc} {error $msg}
 | 
						|
 | 
						|
  if {$bIgnoreNoop} {
 | 
						|
    set nSave $::n_conflict
 | 
						|
    set ::n_conflict 0
 | 
						|
    proc xConflict args { incr ::n_conflict ; return "OMIT" }
 | 
						|
    sqlite3changeset_apply_v2 -ignorenoop db2 $::changeset xConflict
 | 
						|
    if {$::n_conflict!=$nSave} {
 | 
						|
      error "-ignorenoop problem ($::n_conflict $nSave)..."
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
proc do_iterator_test {tn tbl_list sql res} {
 | 
						|
  sqlite3session S db main
 | 
						|
  S object_config rowid 1
 | 
						|
 | 
						|
  if {[llength $tbl_list]==0} { S attach * }
 | 
						|
  foreach t $tbl_list {S attach $t}
 | 
						|
 | 
						|
  execsql $sql
 | 
						|
 | 
						|
  set r [list]
 | 
						|
  foreach v $res { lappend r $v }
 | 
						|
 | 
						|
  set x [list]
 | 
						|
# set ::c [S changeset] ; execsql_pp { SELECT quote($::c) }
 | 
						|
  sqlite3session_foreach c [S changeset] { lappend x $c }
 | 
						|
  uplevel do_test $tn [list [list set {} $x]] [list $r]
 | 
						|
 | 
						|
  S delete
 | 
						|
}
 | 
						|
 | 
						|
# Compare the contents of all tables in [db1] and [db2]. Throw an error if 
 | 
						|
# they are not identical, or return an empty string if they are.
 | 
						|
#
 | 
						|
proc compare_db {db1 db2} {
 | 
						|
 | 
						|
  set sql {SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name}
 | 
						|
  set lot1 [$db1 eval $sql]
 | 
						|
  set lot2 [$db2 eval $sql]
 | 
						|
 | 
						|
  if {$lot1 != $lot2} { 
 | 
						|
    puts $lot1
 | 
						|
    puts $lot2
 | 
						|
    error "databases contain different tables" 
 | 
						|
  }
 | 
						|
 | 
						|
  foreach tbl $lot1 {
 | 
						|
    set col1 [list]
 | 
						|
    set col2 [list]
 | 
						|
 | 
						|
    $db1 eval "PRAGMA table_info = $tbl" { lappend col1 $name }
 | 
						|
    $db2 eval "PRAGMA table_info = $tbl" { lappend col2 $name }
 | 
						|
    if {$col1 != $col2} { error "table $tbl schema mismatch" }
 | 
						|
 | 
						|
    set sql "SELECT * FROM $tbl ORDER BY [join $col1 ,]"
 | 
						|
    set data1 [$db1 eval $sql]
 | 
						|
    set data2 [$db2 eval $sql]
 | 
						|
    if {$data1 != $data2} { 
 | 
						|
      puts "$db1: $data1"
 | 
						|
      puts "$db2: $data2"
 | 
						|
      error "table $tbl data mismatch" 
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return ""
 | 
						|
}
 | 
						|
 | 
						|
proc changeset_to_list {c} {
 | 
						|
  set list [list]
 | 
						|
  sqlite3session_foreach elem $c { lappend list $elem }
 | 
						|
  lsort $list
 | 
						|
}
 | 
						|
 | 
						|
set ones {zero one two three four five six seven eight nine
 | 
						|
          ten eleven twelve thirteen fourteen fifteen sixteen seventeen
 | 
						|
          eighteen nineteen}
 | 
						|
set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety}
 | 
						|
proc number_name {n} {
 | 
						|
  if {$n>=1000} {
 | 
						|
    set txt "[number_name [expr {$n/1000}]] thousand"
 | 
						|
    set n [expr {$n%1000}]
 | 
						|
  } else {
 | 
						|
    set txt {}
 | 
						|
  }
 | 
						|
  if {$n>=100} {
 | 
						|
    append txt " [lindex $::ones [expr {$n/100}]] hundred"
 | 
						|
    set n [expr {$n%100}]
 | 
						|
  }
 | 
						|
  if {$n>=20} {
 | 
						|
    append txt " [lindex $::tens [expr {$n/10}]]"
 | 
						|
    set n [expr {$n%10}]
 | 
						|
  }
 | 
						|
  if {$n>0} {
 | 
						|
    append txt " [lindex $::ones $n]"
 | 
						|
  }
 | 
						|
  set txt [string trim $txt]
 | 
						|
  if {$txt==""} {set txt zero}
 | 
						|
  return $txt
 | 
						|
}
 | 
						|
 | 
						|
proc scksum {db dbname} {
 | 
						|
 | 
						|
  if {$dbname=="temp"} {
 | 
						|
    set master sqlite_temp_master
 | 
						|
  } else {
 | 
						|
    set master $dbname.sqlite_master
 | 
						|
  }
 | 
						|
 | 
						|
  set alltab [$db eval "SELECT name FROM $master WHERE type='table'"]
 | 
						|
  set txt [$db eval "SELECT * FROM $master ORDER BY type,name,sql"]
 | 
						|
  foreach tab $alltab {
 | 
						|
    set cols [list]
 | 
						|
    db eval "PRAGMA $dbname.table_info = $tab" x { 
 | 
						|
      lappend cols "quote($x(name))" 
 | 
						|
    }
 | 
						|
    set cols [join $cols ,]
 | 
						|
    append txt [db eval "SELECT $cols FROM $dbname.$tab ORDER BY $cols"]
 | 
						|
  }
 | 
						|
  return [md5 $txt]
 | 
						|
}
 | 
						|
 | 
						|
proc do_diff_test {tn setup} {
 | 
						|
  reset_db
 | 
						|
  forcedelete test.db2
 | 
						|
  execsql { ATTACH 'test.db2' AS aux }
 | 
						|
  execsql $setup
 | 
						|
 | 
						|
  sqlite3session S db main
 | 
						|
  S object_config rowid 1
 | 
						|
  foreach tbl [db eval {SELECT name FROM sqlite_master WHERE type='table'}] {
 | 
						|
    S attach $tbl
 | 
						|
    S diff aux $tbl
 | 
						|
  }
 | 
						|
 | 
						|
  set C [S changeset]
 | 
						|
  S delete
 | 
						|
 | 
						|
  sqlite3 db2 test.db2
 | 
						|
  sqlite3changeset_apply db2 $C ""
 | 
						|
  uplevel do_test $tn.1 [list {execsql { PRAGMA integrity_check } db2}] ok
 | 
						|
  db2 close
 | 
						|
 | 
						|
  set cksum [scksum db main]
 | 
						|
  uplevel do_test $tn.2 [list {scksum db aux}] [list $cksum]
 | 
						|
}
 |