summaryrefslogtreecommitdiff
path: root/test/upgrade.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'test/upgrade.tcl')
-rw-r--r--test/upgrade.tcl855
1 files changed, 855 insertions, 0 deletions
diff --git a/test/upgrade.tcl b/test/upgrade.tcl
new file mode 100644
index 00000000..96c5f1db
--- /dev/null
+++ b/test/upgrade.tcl
@@ -0,0 +1,855 @@
+# See the file LICENSE for redistribution information.
+#
+# Copyright (c) 1999-2009 Oracle. All rights reserved.
+#
+# $Id$
+
+source ./include.tcl
+
+global upgrade_dir
+# set upgrade_dir "$test_path/upgrade_test"
+set upgrade_dir "$test_path/upgrade/databases"
+
+global gen_upgrade
+set gen_upgrade 0
+global gen_dump
+set gen_dump 0
+global gen_chksum
+set gen_chksum 0
+global gen_upgrade_log
+set gen_upgrade_log 0
+
+global upgrade_dir
+global upgrade_be
+global upgrade_method
+global upgrade_name
+
+proc upgrade { { archived_test_loc "DEFAULT" } } {
+ source ./include.tcl
+ global test_names
+ global upgrade_dir
+ global tcl_platform
+ global saved_logvers
+
+ set saved_upgrade_dir $upgrade_dir
+
+ # Identify endianness of the machine running upgrade.
+ if { [big_endian] == 1 } {
+ set myendianness be
+ } else {
+ set myendianness le
+ }
+ set e $tcl_platform(byteOrder)
+
+ if { [file exists $archived_test_loc/logversion] == 1 } {
+ set fd [open $archived_test_loc/logversion r]
+ set saved_logvers [read $fd]
+ close $fd
+ } else {
+ puts "Old log version number must be available \
+ in $archived_test_loc/logversion"
+ return
+ }
+
+ fileremove -f UPGRADE.OUT
+ set o [open UPGRADE.OUT a]
+
+ puts -nonewline $o "Upgrade test started at: "
+ puts $o [clock format [clock seconds] -format "%H:%M %D"]
+ puts $o [berkdb version -string]
+ puts $o "Testing $e files"
+
+ puts -nonewline "Upgrade test started at: "
+ puts [clock format [clock seconds] -format "%H:%M %D"]
+ puts [berkdb version -string]
+ puts "Testing $e files"
+
+ if { $archived_test_loc == "DEFAULT" } {
+ puts $o "Using default archived databases in $upgrade_dir."
+ puts "Using default archived databases in $upgrade_dir."
+ } else {
+ set upgrade_dir $archived_test_loc
+ puts $o "Using archived databases in $upgrade_dir."
+ puts "Using archived databases in $upgrade_dir."
+ }
+ close $o
+
+ foreach version [glob $upgrade_dir/*] {
+ if { [string first CVS $version] != -1 } { continue }
+ regexp \[^\/\]*$ $version version
+
+ # Test only files where the endianness of the db matches
+ # the endianness of the test platform. These are the
+ # meaningful tests:
+ # 1. File generated on le, tested on le
+ # 2. File generated on be, tested on be
+ # 3. Byte-swapped file generated on le, tested on be
+ # 4. Byte-swapped file generated on be, tested on le
+ #
+ set dbendianness [string range $version end-1 end]
+ if { [string compare $myendianness $dbendianness] != 0 } {
+ puts "Skipping test of $version \
+ on $myendianness platform."
+ } else {
+ set release [string trim $version -lbe]
+ set o [open UPGRADE.OUT a]
+ puts $o "Files created on release $release"
+ close $o
+ puts "Files created on release $release"
+
+ foreach method [glob $upgrade_dir/$version/*] {
+ regexp \[^\/\]*$ $method method
+ set o [open UPGRADE.OUT a]
+ puts $o "\nTesting $method files"
+ close $o
+ puts "\tTesting $method files"
+
+ foreach file [lsort -dictionary \
+ [glob -nocomplain \
+ $upgrade_dir/$version/$method/*]] {
+ regexp (\[^\/\]*)\.tar\.gz$ \
+ $file dummy name
+
+ cleanup $testdir NULL 1
+ set curdir [pwd]
+ cd $testdir
+ set tarfd [open "|tar xf -" w]
+ cd $curdir
+
+ catch {exec gunzip -c \
+ "$upgrade_dir/$version/$method/$name.tar.gz" \
+ >@$tarfd}
+ close $tarfd
+
+ set f [open $testdir/$name.tcldump \
+ {RDWR CREAT}]
+ close $f
+
+ # We exec a separate tclsh for each
+ # separate subtest to keep the
+ # testing process from consuming a
+ # tremendous amount of memory.
+ #
+ # First we test the .db files.
+ if { [file exists \
+ $testdir/$name-$myendianness.db] } {
+ if { [catch {exec $tclsh_path \
+ << "source \
+ $test_path/test.tcl;\
+ _upgrade_test $testdir \
+ $version $method $name \
+ $myendianness" >>& \
+ UPGRADE.OUT } message] } {
+ set o [open \
+ UPGRADE.OUT a]
+ puts $o "FAIL: $message"
+ close $o
+ }
+ if { [catch {exec $tclsh_path\
+ << "source \
+ $test_path/test.tcl;\
+ _db_load_test $testdir \
+ $version $method $name" >>&\
+ UPGRADE.OUT } message] } {
+ set o [open \
+ UPGRADE.OUT a]
+ puts $o "FAIL: $message"
+ close $o
+ }
+ }
+ # Then we test log files.
+ if { [file exists \
+ $testdir/$name.prlog] } {
+ if { [catch {exec $tclsh_path \
+ << "source \
+ $test_path/test.tcl;\
+ global saved_logvers;\
+ set saved_logvers \
+ $saved_logvers;\
+ _log_test $testdir \
+ $release $method \
+ $name" >>& \
+ UPGRADE.OUT } message] } {
+ set o [open \
+ UPGRADE.OUT a]
+ puts $o "FAIL: $message"
+ close $o
+ }
+ }
+
+ # Then we test any .dmp files. Move
+ # the saved file to the current working
+ # directory. Run the test locally.
+ # Compare the dumps; they should match.
+ if { [file exists $testdir/$name.dmp] } {
+ file rename -force \
+ $testdir/$name.dmp $name.dmp
+
+ foreach test $test_names(plat) {
+ eval $test $method
+ }
+
+ # Discard lines that can differ.
+ discardline $name.dmp \
+ TEMPFILE "db_pagesize="
+ file copy -force \
+ TEMPFILE $name.dmp
+ discardline $testdir/$test.dmp \
+ TEMPFILE "db_pagesize="
+ file copy -force \
+ TEMPFILE $testdir/$test.dmp
+
+ error_check_good compare_dump \
+ [filecmp $name.dmp \
+ $testdir/$test.dmp] 0
+
+ fileremove $name.dmp
+ }
+ }
+ }
+ }
+ }
+ set upgrade_dir $saved_upgrade_dir
+
+ set o [open UPGRADE.OUT a]
+ puts -nonewline $o "Completed at: "
+ puts $o [clock format [clock seconds] -format "%H:%M %D"]
+ close $o
+
+ puts -nonewline "Completed at: "
+ puts [clock format [clock seconds] -format "%H:%M %D"]
+
+ # Don't provide a return value.
+ return
+}
+
+proc _upgrade_test { temp_dir version method file endianness } {
+ source include.tcl
+ global errorInfo
+ global passwd
+ global encrypt
+
+ puts "Upgrade: $version $method $file $endianness"
+
+ # Check whether we're working with an encrypted file.
+ if { [string match c-* $file] } {
+ set encrypt 1
+ }
+
+ # Open the database prior to upgrading. If it fails,
+ # it should fail with the DB_OLDVERSION message.
+ set encargs ""
+ set upgradeargs ""
+ if { $encrypt == 1 } {
+ set encargs " -encryptany $passwd "
+ set upgradeargs " -P $passwd "
+ }
+ if { [catch \
+ { set db [eval {berkdb open} $encargs \
+ $temp_dir/$file-$endianness.db] } res] } {
+ error_check_good old_version [is_substr $res DB_OLDVERSION] 1
+ } else {
+ error_check_good db_close [$db close] 0
+ }
+
+ # Now upgrade the database.
+ set ret [catch {eval exec {$util_path/db_upgrade} $upgradeargs \
+ "$temp_dir/$file-$endianness.db" } message]
+ error_check_good dbupgrade $ret 0
+
+ error_check_good dbupgrade_verify [verify_dir $temp_dir "" 0 0 1] 0
+
+ upgrade_dump "$temp_dir/$file-$endianness.db" "$temp_dir/temp.dump"
+
+ error_check_good "Upgrade diff.$endianness: $version $method $file" \
+ [filecmp "$temp_dir/$file.tcldump" "$temp_dir/temp.dump"] 0
+}
+
+proc _db_load_test { temp_dir version method file } {
+ source include.tcl
+ global errorInfo
+
+ puts "Db_load: $version $method $file"
+
+ set ret [catch \
+ {exec $util_path/db_load -f "$temp_dir/$file.dump" \
+ "$temp_dir/upgrade.db"} message]
+ error_check_good \
+ "Upgrade load: $version $method $file $message" $ret 0
+
+ upgrade_dump "$temp_dir/upgrade.db" "$temp_dir/temp.dump"
+
+ error_check_good "Upgrade diff.1.1: $version $method $file" \
+ [filecmp "$temp_dir/$file.tcldump" "$temp_dir/temp.dump"] 0
+}
+
+proc _log_test { temp_dir release method file } {
+ source ./include.tcl
+ global saved_logvers
+ global passwd
+ puts "Check log file: $temp_dir $release $method $file"
+
+ # Get log version number of current system
+ set env [berkdb_env -create -log -home $testdir]
+ error_check_good is_valid_env [is_valid_env $env] TRUE
+ set current_logvers [get_log_vers $env]
+ error_check_good env_close [$env close] 0
+ error_check_good env_remove [berkdb envremove -home $testdir] 0
+
+ # Rename recd001-x-log.000000000n to log.000000000n.
+ set logfiles [glob -nocomplain $temp_dir/*log.0*]
+ foreach logfile $logfiles {
+ set logname [string replace $logfile 0 \
+ [string last - $logfile]]
+ file rename -force $logfile $temp_dir/$logname
+ }
+
+ # Use db_printlog to dump the logs. If the current log file
+ # version is greater than the saved log file version, the log
+ # files are expected to be unreadable. If the log file is
+ # readable, check that the current printlog dump matches the
+ # archived printlog.
+ #
+ set ret [catch {exec $util_path/db_printlog -h $temp_dir \
+ > $temp_dir/logs.prlog} message]
+ if { [is_substr $message "magic number"] } {
+ # The failure is probably due to encryption, try
+ # crypto printlog.
+ set ret [catch {exec $util_path/db_printlog -h $temp_dir \
+ -P $passwd > $temp_dir/logs.prlog} message]
+ if { $ret == 1 } {
+ # If the failure is because of a historic
+ # log version, that's okay.
+ if { $current_logvers <= $saved_logvers } {
+ puts "db_printlog failed: $message"
+ }
+ }
+ }
+
+ # Log versions prior to 8 can only be read by their own version.
+ # Log versions of 8 or greater are readable by Berkeley DB 4.5
+ # or greater, but the output of printlog does not match unless
+ # the versions are identical.
+ #
+ # As of Berkeley DB 4.8, we'll only try to read back to log
+ # version 11, which came out with 4.4. Backwards compatibility
+ # now only extends back to 4.4 because of page changes.
+ #
+ set logoldver 11
+ if { $current_logvers > $saved_logvers &&\
+ $saved_logvers < $logoldver } {
+ error_check_good historic_log_version \
+ [is_substr $message "historic log version"] 1
+ } elseif { $current_logvers > $saved_logvers } {
+ error_check_good db_printlog:$message $ret 0
+ } elseif { $current_logvers == $saved_logvers } {
+ error_check_good db_printlog:$message $ret 0
+ # Compare logs.prlog and $file.prlog (should match)
+ error_check_good "Compare printlogs" [filecmp \
+ "$temp_dir/logs.prlog" "$temp_dir/$file.prlog"] 0
+ } elseif { $current_logvers < $saved_logvers } {
+ puts -nonewline "FAIL: current log version $current_logvers "
+ puts "cannot be less than saved log version $save_logvers."
+ }
+}
+
+proc gen_upgrade { dir { save_crypto 1 } { save_non_crypto 1 } } {
+ global gen_upgrade
+ global gen_upgrade_log
+ global gen_chksum
+ global gen_dump
+ global upgrade_dir
+ global upgrade_be
+ global upgrade_method
+ global upgrade_name
+ global valid_methods
+ global test_names
+ global parms
+ global encrypt
+ global passwd
+ source ./include.tcl
+
+ set upgrade_dir $dir
+ env_cleanup $testdir
+
+ fileremove -f GENERATE.OUT
+ set o [open GENERATE.OUT a]
+
+ puts -nonewline $o "Generating upgrade files. Started at: "
+ puts $o [clock format [clock seconds] -format "%H:%M %D"]
+ puts $o [berkdb version -string]
+
+ puts -nonewline "Generating upgrade files. Started at: "
+ puts [clock format [clock seconds] -format "%H:%M %D"]
+ puts [berkdb version -string]
+
+ close $o
+
+ # Create a file that contains the log version number.
+ # If necessary, create the directory to contain the file.
+ set env [berkdb_env -create -log -home $testdir]
+ error_check_good is_valid_env [is_valid_env $env] TRUE
+
+ if { [file exists $dir] == 0 } {
+ file mkdir $dir
+ }
+ set lv [open $dir/logversion w]
+ puts $lv [get_log_vers $env]
+ close $lv
+
+ error_check_good env_close [$env close] 0
+
+ # Generate test databases for each access method and endianness.
+ foreach method $valid_methods {
+ set o [open GENERATE.OUT a]
+ puts $o "\nGenerating $method files"
+ close $o
+ puts "\tGenerating $method files"
+ set upgrade_method $method
+
+ # We piggyback testing of dumped sequence files on upgrade
+ # testing because this is the only place that we ship files
+ # from one machine to another. Create files for both
+ # endiannesses, because who knows what platform we'll
+ # be testing on.
+
+ set gen_dump 1
+ foreach test $test_names(plat) {
+ set upgrade_name $test
+ foreach upgrade_be { 0 1 } {
+ eval $test $method
+ cleanup $testdir NULL
+ }
+ }
+ set gen_dump 0
+
+#set test_names(test) ""
+ set gen_upgrade 1
+ foreach test $test_names(test) {
+ if { [info exists parms($test)] != 1 } {
+ continue
+ }
+
+ set o [open GENERATE.OUT a]
+ puts $o "\t\tGenerating files for $test"
+ close $o
+ puts "\t\tGenerating files for $test"
+
+ if { $save_non_crypto == 1 } {
+ set encrypt 0
+ foreach upgrade_be { 0 1 } {
+ set upgrade_name $test
+ if [catch {exec $tclsh_path \
+ << "source $test_path/test.tcl;\
+ global gen_upgrade upgrade_be;\
+ global upgrade_method upgrade_name;\
+ global encrypt;\
+ set encrypt $encrypt;\
+ set gen_upgrade 1;\
+ set upgrade_be $upgrade_be;\
+ set upgrade_method $upgrade_method;\
+ set upgrade_name $upgrade_name;\
+ run_method -$method $test" \
+ >>& GENERATE.OUT} res] {
+ puts "FAIL: run_method \
+ $test $method"
+ }
+ cleanup $testdir NULL 1
+ }
+ # Save checksummed files for only one test.
+ # Checksumming should work in all or no cases.
+ set gen_chksum 1
+ foreach upgrade_be { 0 1 } {
+ set upgrade_name $test
+ if { $test == "test001" } {
+ if { [catch {exec $tclsh_path \
+ << "source $test_path/test.tcl;\
+ global gen_upgrade;\
+ global upgrade_be;\
+ global upgrade_method;\
+ global upgrade_name;\
+ global encrypt gen_chksum;\
+ set encrypt $encrypt;\
+ set gen_upgrade 1;\
+ set gen_chksum 1;\
+ set upgrade_be $upgrade_be;\
+ set upgrade_method \
+ $upgrade_method;\
+ set upgrade_name \
+ $upgrade_name;\
+ run_method -$method $test \
+ 0 1 stdout -chksum" \
+ >>& GENERATE.OUT} res] } {
+ puts "FAIL: run_method \
+ $test $method \
+ -chksum: $res"
+ }
+ cleanup $testdir NULL 1
+ }
+ }
+ set gen_chksum 0
+ }
+ # Save encrypted db's only of native endianness.
+ # Encrypted files are not portable across endianness.
+ if { $save_crypto == 1 } {
+ set upgrade_be [big_endian]
+ set encrypt 1
+ set upgrade_name $test
+ if [catch {exec $tclsh_path \
+ << "source $test_path/test.tcl;\
+ global gen_upgrade upgrade_be;\
+ global upgrade_method upgrade_name;\
+ global encrypt passwd;\
+ set encrypt $encrypt;\
+ set passwd $passwd;\
+ set gen_upgrade 1;\
+ set upgrade_be $upgrade_be;\
+ set upgrade_method $upgrade_method;\
+ set upgrade_name $upgrade_name;\
+ run_secmethod $method $test" \
+ >>& GENERATE.OUT} res] {
+ puts "FAIL: run_secmethod \
+ $test $method"
+ }
+ cleanup $testdir NULL 1
+ }
+ }
+ set gen_upgrade 0
+ }
+
+ # Set upgrade_be to the native value so log files go to the
+ # right place.
+ set upgrade_be [big_endian]
+
+ # Generate log files.
+ set o [open GENERATE.OUT a]
+ puts $o "\tGenerating log files"
+ close $o
+ puts "\tGenerating log files"
+
+ set gen_upgrade_log 1
+ # Pass the global variables and their values to the new tclsh.
+ if { $save_non_crypto == 1 } {
+ set encrypt 0
+ if [catch {exec $tclsh_path << "source $test_path/test.tcl;\
+ global gen_upgrade_log upgrade_be upgrade_dir;\
+ global encrypt;\
+ set encrypt $encrypt;\
+ set gen_upgrade_log $gen_upgrade_log; \
+ set upgrade_be $upgrade_be;\
+ set upgrade_dir $upgrade_dir;\
+ run_recds" >>& GENERATE.OUT} res] {
+ puts "FAIL: run_recds: $res"
+ }
+ }
+ if { $save_crypto == 1 } {
+ set encrypt 1
+ if [catch {exec $tclsh_path << "source $test_path/test.tcl;\
+ global gen_upgrade_log upgrade_be upgrade_dir;\
+ global encrypt;\
+ set encrypt $encrypt;\
+ set gen_upgrade_log $gen_upgrade_log; \
+ set upgrade_be $upgrade_be;\
+ set upgrade_dir $upgrade_dir;\
+ run_recds " >>& GENERATE.OUT} res] {
+ puts "FAIL: run_recds with crypto: $res"
+ }
+ }
+ set gen_upgrade_log 0
+
+ set o [open GENERATE.OUT a]
+ puts -nonewline $o "Completed at: "
+ puts $o [clock format [clock seconds] -format "%H:%M %D"]
+ puts -nonewline "Completed at: "
+ puts [clock format [clock seconds] -format "%H:%M %D"]
+ close $o
+}
+
+proc save_upgrade_files { dir } {
+ global upgrade_dir
+ global upgrade_be
+ global upgrade_method
+ global upgrade_name
+ global gen_upgrade
+ global gen_upgrade_log
+ global gen_dump
+ global encrypt
+ global gen_chksum
+ global passwd
+ source ./include.tcl
+
+ set vers [berkdb version]
+ set maj [lindex $vers 0]
+ set min [lindex $vers 1]
+
+ # Is this machine big or little endian? We want to mark
+ # the test directories appropriately, since testing
+ # little-endian databases generated by a big-endian machine,
+ # and/or vice versa, is interesting.
+ if { [big_endian] } {
+ set myendianness be
+ } else {
+ set myendianness le
+ }
+
+ if { $upgrade_be == 1 } {
+ set version_dir "$myendianness-$maj.${min}be"
+ set en be
+ } else {
+ set version_dir "$myendianness-$maj.${min}le"
+ set en le
+ }
+
+ set dest $upgrade_dir/$version_dir/$upgrade_method
+ exec mkdir -p $dest
+
+ if { $gen_upgrade == 1 } {
+ # Save db files from test001 - testxxx.
+ set dbfiles [glob -nocomplain $dir/*.db]
+ set dumpflag ""
+ # Encrypted files are identified by the prefix "c-".
+ if { $encrypt == 1 } {
+ set upgrade_name c-$upgrade_name
+ set dumpflag " -P $passwd "
+ }
+ # Checksummed files are identified by the prefix "s-".
+ if { $gen_chksum == 1 } {
+ set upgrade_name s-$upgrade_name
+ }
+ foreach dbfile $dbfiles {
+ set basename [string range $dbfile \
+ [expr [string length $dir] + 1] end-3]
+
+ set newbasename $upgrade_name-$basename
+
+ # db_dump file
+ if { [catch {eval exec $util_path/db_dump -k $dumpflag \
+ $dbfile > $dir/$newbasename.dump} res] } {
+ puts "FAIL: $res"
+ }
+
+ # tcl_dump file
+ upgrade_dump $dbfile $dir/$newbasename.tcldump
+
+ # Rename dbfile and any dbq files.
+ file rename $dbfile $dir/$newbasename-$en.db
+ foreach dbq \
+ [glob -nocomplain $dir/__dbq.$basename.db.*] {
+ set s [string length $dir/__dbq.]
+ set newname [string replace $dbq $s \
+ [expr [string length $basename] + $s - 1] \
+ $newbasename-$en]
+ file rename $dbq $newname
+ }
+ set cwd [pwd]
+ cd $dir
+ catch {eval exec tar -cvf $dest/$newbasename.tar \
+ [glob $newbasename* __dbq.$newbasename-$en.db.*]}
+ catch {exec gzip -9v $dest/$newbasename.tar} res
+ cd $cwd
+ }
+ }
+
+ if { $gen_upgrade_log == 1 } {
+ # Save log files from recd tests.
+ set logfiles [glob -nocomplain $dir/log.*]
+ if { [llength $logfiles] > 0 } {
+ # More than one log.0000000001 file may be produced
+ # per recd test, so we generate unique names:
+ # recd001-0-log.0000000001, recd001-1-log.0000000001,
+ # and so on.
+ # We may also have log.0000000001, log.0000000002,
+ # and so on, and they will all be dumped together
+ # by db_printlog.
+ set count 0
+ while { [file exists \
+ $dest/$upgrade_name-$count-log.tar.gz] \
+ == 1 } {
+ incr count
+ }
+ set newname $upgrade_name-$count-log
+
+ # Run db_printlog on all the log files
+ if {[catch {exec $util_path/db_printlog -h $dir > \
+ $dir/$newname.prlog} res] != 0} {
+ puts "Regular printlog failed, try encryption"
+ eval {exec $util_path/db_printlog} -h $dir \
+ -P $passwd > $dir/$newname.prlog
+ }
+
+ # Rename each log file so we can identify which
+ # recd test created it.
+ foreach logfile $logfiles {
+ set lognum [string range $logfile \
+ end-9 end]
+ file rename $logfile $dir/$newname.$lognum
+ }
+
+ set cwd [pwd]
+ cd $dir
+
+ catch {eval exec tar -cvf $dest/$newname.tar \
+ [glob $newname*]}
+ catch {exec gzip -9v $dest/$newname.tar}
+ cd $cwd
+ }
+ }
+
+ if { $gen_dump == 1 } {
+ # Save dump files. We require that the files have
+ # been created with the extension .dmp.
+ set dumpfiles [glob -nocomplain $dir/*.dmp]
+
+ foreach dumpfile $dumpfiles {
+ set basename [string range $dumpfile \
+ [expr [string length $dir] + 1] end-4]
+
+ set newbasename $upgrade_name-$basename
+
+ # Rename dumpfile.
+ file rename $dumpfile $dir/$newbasename.dmp
+
+ set cwd [pwd]
+ cd $dir
+ catch {eval exec tar -cvf $dest/$newbasename.tar \
+ [glob $newbasename.dmp]}
+ catch {exec gzip -9v $dest/$newbasename.tar} res
+ cd $cwd
+ }
+ }
+}
+
+proc upgrade_dump { database file {stripnulls 0} } {
+ global errorInfo
+ global encrypt
+ global passwd
+
+ set encargs ""
+ if { $encrypt == 1 } {
+ set encargs " -encryptany $passwd "
+ }
+ set db [eval {berkdb open} -rdonly $encargs $database]
+ set dbc [$db cursor]
+
+ set f [open $file w+]
+ fconfigure $f -encoding binary -translation binary
+
+ #
+ # Get a sorted list of keys
+ #
+ set key_list ""
+ set pair [$dbc get -first]
+
+ while { 1 } {
+ if { [llength $pair] == 0 } {
+ break
+ }
+ set k [lindex [lindex $pair 0] 0]
+ lappend key_list $k
+ set pair [$dbc get -next]
+ }
+
+ # Discard duplicated keys; we now have a key for each
+ # duplicate, not each unique key, and we don't want to get each
+ # duplicate multiple times when we iterate over key_list.
+ set uniq_keys ""
+ foreach key $key_list {
+ if { [info exists existence_list($key)] == 0 } {
+ lappend uniq_keys $key
+ }
+ set existence_list($key) 1
+ }
+ set key_list $uniq_keys
+
+ set key_list [lsort -command _comp $key_list]
+
+ #
+ # Get the data for each key
+ #
+ set i 0
+ foreach key $key_list {
+ set pair [$dbc get -set $key]
+ if { $stripnulls != 0 } {
+ # the Tcl interface to db versions before 3.X
+ # added nulls at the end of all keys and data, so
+ # we provide functionality to strip that out.
+ set key [strip_null $key]
+ }
+ set data_list {}
+ catch { while { [llength $pair] != 0 } {
+ set data [lindex [lindex $pair 0] 1]
+ if { $stripnulls != 0 } {
+ set data [strip_null $data]
+ }
+ lappend data_list [list $data]
+ set pair [$dbc get -nextdup]
+ } }
+ #lsort -command _comp data_list
+ set data_list [lsort -command _comp $data_list]
+ puts -nonewline $f [binary format i [string length $key]]
+ puts -nonewline $f $key
+ puts -nonewline $f [binary format i [llength $data_list]]
+ for { set j 0 } { $j < [llength $data_list] } { incr j } {
+ puts -nonewline $f [binary format i [string length \
+ [concat [lindex $data_list $j]]]]
+ puts -nonewline $f [concat [lindex $data_list $j]]
+ }
+ if { [llength $data_list] == 0 } {
+ puts "WARNING: zero-length data list"
+ }
+ incr i
+ }
+
+ close $f
+ error_check_good upgrade_dump_c_close [$dbc close] 0
+ error_check_good upgrade_dump_db_close [$db close] 0
+}
+
+proc _comp { a b } {
+ if { 0 } {
+ # XXX
+ set a [strip_null [concat $a]]
+ set b [strip_null [concat $b]]
+ #return [expr [concat $a] < [concat $b]]
+ } else {
+ set an [string first "\0" $a]
+ set bn [string first "\0" $b]
+
+ if { $an != -1 } {
+ set a [string range $a 0 [expr $an - 1]]
+ }
+ if { $bn != -1 } {
+ set b [string range $b 0 [expr $bn - 1]]
+ }
+ }
+ #puts "$a $b"
+ return [string compare $a $b]
+}
+
+proc strip_null { str } {
+ set len [string length $str]
+ set last [expr $len - 1]
+
+ set termchar [string range $str $last $last]
+ if { [string compare $termchar \0] == 0 } {
+ set ret [string range $str 0 [expr $last - 1]]
+ } else {
+ set ret $str
+ }
+
+ return $ret
+}
+
+proc get_log_vers { env } {
+ set stat [$env log_stat]
+ foreach pair $stat {
+ set msg [lindex $pair 0]
+ set val [lindex $pair 1]
+ if { $msg == "Log file Version" } {
+ return $val
+ }
+ }
+ puts "FAIL: Log file Version not found in log_stat"
+ return 0
+}
+