1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 1999,2008 Oracle.  All rights reserved.
4#
5# $Id: upgrade.tcl,v 12.14 2008/01/08 20:58:53 bostic Exp $
6
7source ./include.tcl
8
9global upgrade_dir
10# set upgrade_dir "$test_path/upgrade_test"
11set upgrade_dir "$test_path/upgrade/databases"
12
13global gen_upgrade
14set gen_upgrade 0
15global gen_dump
16set gen_dump 0
17global gen_chksum
18set gen_chksum 0
19global gen_upgrade_log
20set gen_upgrade_log 0
21
22global upgrade_dir
23global upgrade_be
24global upgrade_method
25global upgrade_name
26
27proc upgrade { { archived_test_loc "DEFAULT" } } {
28	source ./include.tcl
29	global test_names
30	global upgrade_dir
31	global tcl_platform
32	global saved_logvers
33
34	set saved_upgrade_dir $upgrade_dir
35
36	# Identify endianness of the machine running upgrade.
37	if { [big_endian] == 1 } {
38		set myendianness be
39	} else {
40		set myendianness le
41	}
42	set e $tcl_platform(byteOrder)
43
44	if { [file exists $archived_test_loc/logversion] == 1 } {
45		set fd [open $archived_test_loc/logversion r]
46		set saved_logvers [read $fd]
47		close $fd
48	} else {
49		puts "Old log version number must be available \
50		    in $archived_test_loc/logversion"
51		return
52	}
53
54	fileremove -f UPGRADE.OUT
55	set o [open UPGRADE.OUT a]
56
57	puts -nonewline $o "Upgrade test started at: "
58	puts $o [clock format [clock seconds] -format "%H:%M %D"]
59	puts $o [berkdb version -string]
60	puts $o "Testing $e files"
61
62	puts -nonewline "Upgrade test started at: "
63	puts [clock format [clock seconds] -format "%H:%M %D"]
64	puts [berkdb version -string]
65	puts "Testing $e files"
66
67	if { $archived_test_loc == "DEFAULT" } {
68		puts $o "Using default archived databases in $upgrade_dir."
69		puts "Using default archived databases in $upgrade_dir."
70	} else {
71		set upgrade_dir $archived_test_loc
72		puts $o "Using archived databases in $upgrade_dir."
73		puts "Using archived databases in $upgrade_dir."
74	}
75	close $o
76
77	foreach version [glob $upgrade_dir/*] {
78		if { [string first CVS $version] != -1 } { continue }
79		regexp \[^\/\]*$ $version version
80
81		# Test only files where the endianness of the db matches
82		# the endianness of the test platform.  These are the
83		# meaningful tests:
84		# 1.  File generated on le, tested on le
85		# 2.  File generated on be, tested on be
86		# 3.  Byte-swapped file generated on le, tested on be
87		# 4.  Byte-swapped file generated on be, tested on le
88		#
89		set dbendianness [string range $version end-1 end]
90		if { [string compare $myendianness $dbendianness] != 0 } {
91			puts "Skipping test of $version \
92			    on $myendianness platform."
93		} else {
94			set release [string trim $version -lbe]
95			set o [open UPGRADE.OUT a]
96			puts $o "Files created on release $release"
97			close $o
98			puts "Files created on release $release"
99
100			foreach method [glob $upgrade_dir/$version/*] {
101				regexp \[^\/\]*$ $method method
102				set o [open UPGRADE.OUT a]
103				puts $o "\nTesting $method files"
104				close $o
105				puts "\tTesting $method files"
106
107				foreach file [lsort -dictionary \
108				    [glob -nocomplain \
109				    $upgrade_dir/$version/$method/*]] {
110					regexp (\[^\/\]*)\.tar\.gz$ \
111					    $file dummy name
112
113					cleanup $testdir NULL 1
114					set curdir [pwd]
115					cd $testdir
116					set tarfd [open "|tar xf -" w]
117					cd $curdir
118
119					catch {exec gunzip -c \
120					    "$upgrade_dir/$version/$method/$name.tar.gz" \
121					    >@$tarfd}
122					close $tarfd
123
124					set f [open $testdir/$name.tcldump \
125					    {RDWR CREAT}]
126					close $f
127
128					# We exec a separate tclsh for each
129					# separate subtest to keep the
130					# testing process from consuming a
131					# tremendous amount of memory.
132					#
133					# First we test the .db files.
134					if { [file exists \
135					    $testdir/$name-$myendianness.db] } {
136						if { [catch {exec $tclsh_path \
137						    << "source \
138						    $test_path/test.tcl;\
139						    _upgrade_test $testdir \
140						    $version $method $name \
141						    $myendianness" >>& \
142						    UPGRADE.OUT } message] } {
143							set o [open \
144							    UPGRADE.OUT a]
145							puts $o "FAIL: $message"
146							close $o
147						}
148						if { [catch {exec $tclsh_path\
149						    << "source \
150						    $test_path/test.tcl;\
151						    _db_load_test $testdir \
152						    $version $method $name" >>&\
153						    UPGRADE.OUT } message] } {
154							set o [open \
155							    UPGRADE.OUT a]
156							puts $o "FAIL: $message"
157							close $o
158						}
159					}
160					# Then we test log files.
161					if { [file exists \
162					    $testdir/$name.prlog] } {
163						if { [catch {exec $tclsh_path \
164						    << "source \
165						    $test_path/test.tcl;\
166						    global saved_logvers;\
167						    set saved_logvers \
168						    $saved_logvers;\
169						    _log_test $testdir \
170						    $release $method \
171						    $name" >>& \
172						    UPGRADE.OUT } message] } {
173							set o [open \
174							    UPGRADE.OUT a]
175							puts $o "FAIL: $message"
176							close $o
177						}
178					}
179
180					# Then we test any .dmp files.  Move
181					# the saved file to the current working
182					# directory.  Run the test locally.
183					# Compare the dumps; they should match.
184					if { [file exists $testdir/$name.dmp] } {
185						file rename -force \
186						    $testdir/$name.dmp $name.dmp
187
188						foreach test $test_names(plat) {
189							eval $test $method
190						}
191
192						# Discard lines that can differ.
193						discardline $name.dmp \
194						    TEMPFILE "db_pagesize="
195						file copy -force \
196						    TEMPFILE $name.dmp
197						discardline $testdir/$test.dmp \
198						    TEMPFILE "db_pagesize="
199						file copy -force \
200						    TEMPFILE $testdir/$test.dmp
201
202						error_check_good compare_dump \
203						    [filecmp $name.dmp \
204						    $testdir/$test.dmp] 0
205
206						fileremove $name.dmp
207					}
208				}
209			}
210		}
211	}
212	set upgrade_dir $saved_upgrade_dir
213
214	set o [open UPGRADE.OUT a]
215	puts -nonewline $o "Completed at: "
216	puts $o [clock format [clock seconds] -format "%H:%M %D"]
217	close $o
218
219	puts -nonewline "Completed at: "
220	puts [clock format [clock seconds] -format "%H:%M %D"]
221
222	# Don't provide a return value.
223	return
224}
225
226proc _upgrade_test { temp_dir version method file endianness } {
227	source include.tcl
228	global errorInfo
229	global passwd
230	global encrypt
231
232	puts "Upgrade: $version $method $file $endianness"
233
234	# Check whether we're working with an encrypted file.
235	if { [string match c-* $file] } {
236		set encrypt 1
237	}
238
239	# Open the database prior to upgrading.  If it fails,
240	# it should fail with the DB_OLDVERSION message.
241	set encargs ""
242	set upgradeargs ""
243	if { $encrypt == 1 } {
244		set encargs " -encryptany $passwd "
245		set upgradeargs " -P $passwd "
246	}
247	if { [catch \
248	    { set db [eval {berkdb open} $encargs \
249	    $temp_dir/$file-$endianness.db] } res] } {
250	    	error_check_good old_version [is_substr $res DB_OLDVERSION] 1
251	} else {
252		error_check_good db_close [$db close] 0
253	}
254
255	# Now upgrade the database.
256	set ret [catch {eval exec {$util_path/db_upgrade} $upgradeargs \
257	    "$temp_dir/$file-$endianness.db" } message]
258	error_check_good dbupgrade $ret 0
259
260	error_check_good dbupgrade_verify [verify_dir $temp_dir "" 0 0 1] 0
261
262	upgrade_dump "$temp_dir/$file-$endianness.db" "$temp_dir/temp.dump"
263
264	error_check_good "Upgrade diff.$endianness: $version $method $file" \
265	    [filecmp "$temp_dir/$file.tcldump" "$temp_dir/temp.dump"] 0
266}
267
268proc _db_load_test { temp_dir version method file } {
269	source include.tcl
270	global errorInfo
271
272	puts "Db_load: $version $method $file"
273
274	set ret [catch \
275	    {exec $util_path/db_load -f "$temp_dir/$file.dump" \
276	    "$temp_dir/upgrade.db"} message]
277	error_check_good \
278	    "Upgrade load: $version $method $file $message" $ret 0
279
280	upgrade_dump "$temp_dir/upgrade.db" "$temp_dir/temp.dump"
281
282	error_check_good "Upgrade diff.1.1: $version $method $file" \
283	    [filecmp "$temp_dir/$file.tcldump" "$temp_dir/temp.dump"] 0
284}
285
286proc _log_test { temp_dir release method file } {
287	source ./include.tcl
288	global saved_logvers
289	global passwd
290	puts "Check log file: $temp_dir $release $method $file"
291
292	# Get log version number of current system
293	set env [berkdb_env -create -log -home $testdir]
294	error_check_good is_valid_env [is_valid_env $env] TRUE
295	set current_logvers [get_log_vers $env]
296	error_check_good env_close [$env close] 0
297	error_check_good env_remove [berkdb envremove -home $testdir] 0
298
299	# Rename recd001-x-log.000000000n to log.000000000n.
300	set logfiles [glob -nocomplain $temp_dir/*log.0*]
301	foreach logfile $logfiles {
302		set logname [string replace $logfile 0 \
303		    [string last - $logfile]]
304		file rename -force $logfile $temp_dir/$logname
305	}
306
307	# Use db_printlog to dump the logs.  If the current log file
308	# version is greater than the saved log file version, the log
309	# files are expected to be unreadable.  If the log file is
310	# readable, check that the current printlog dump matches the
311	# archived printlog.
312 	#
313	set ret [catch {exec $util_path/db_printlog -h $temp_dir \
314	    > $temp_dir/logs.prlog} message]
315	if { [is_substr $message "magic number"] } {
316		# The failure is probably due to encryption, try
317		# crypto printlog.
318		set ret [catch {exec $util_path/db_printlog -h $temp_dir \
319		    -P $passwd > $temp_dir/logs.prlog} message]
320		if { $ret == 1 } {
321			# If the failure is because of a historic
322			# log version, that's okay.
323			if { $current_logvers <= $saved_logvers } {
324				puts "db_printlog failed: $message"
325		 	}
326		}
327	}
328
329	# Log versions prior to 8 can only be read by their own version.
330	# Log versions of 8 or greater are readable by Berkeley DB 4.5
331	# or greater, but the output of printlog does not match unless
332	# the versions are identical.
333	set logoldver 8
334	if { $current_logvers > $saved_logvers &&\
335	    $saved_logvers < $logoldver } {
336		error_check_good historic_log_version \
337		    [is_substr $message "historic log version"] 1
338	} elseif { $current_logvers > $saved_logvers } {
339		error_check_good db_printlog:$message $ret 0
340	} elseif { $current_logvers == $saved_logvers  } {
341		error_check_good db_printlog:$message $ret 0
342		# Compare logs.prlog and $file.prlog (should match)
343		error_check_good "Compare printlogs" [filecmp \
344		    "$temp_dir/logs.prlog" "$temp_dir/$file.prlog"] 0
345	} elseif { $current_logvers < $saved_logvers } {
346		puts -nonewline "FAIL: current log version $current_logvers "
347		puts "cannot be less than saved log version $save_logvers."
348	}
349}
350
351proc gen_upgrade { dir { save_crypto 1 } { save_non_crypto 1 } } {
352	global gen_upgrade
353	global gen_upgrade_log
354	global gen_chksum
355	global gen_dump
356	global upgrade_dir
357	global upgrade_be
358	global upgrade_method
359	global upgrade_name
360	global valid_methods
361	global test_names
362	global parms
363	global encrypt
364	global passwd
365	source ./include.tcl
366
367	set upgrade_dir $dir
368	env_cleanup $testdir
369
370	fileremove -f GENERATE.OUT
371	set o [open GENERATE.OUT a]
372
373	puts -nonewline $o "Generating upgrade files.  Started at: "
374	puts $o [clock format [clock seconds] -format "%H:%M %D"]
375	puts $o [berkdb version -string]
376
377	puts -nonewline "Generating upgrade files.  Started at: "
378	puts [clock format [clock seconds] -format "%H:%M %D"]
379	puts [berkdb version -string]
380
381	close $o
382
383	# Create a file that contains the log version number.
384	# If necessary, create the directory to contain the file.
385	set env [berkdb_env -create -log -home $testdir]
386	error_check_good is_valid_env [is_valid_env $env] TRUE
387
388	if { [file exists $dir] == 0 } {
389		file mkdir $dir
390	}
391	set lv [open $dir/logversion w]
392	puts $lv [get_log_vers $env]
393	close $lv
394
395	error_check_good env_close [$env close] 0
396
397	# Generate test databases for each access method and endianness.
398	foreach method $valid_methods {
399		set o [open GENERATE.OUT a]
400		puts $o "\nGenerating $method files"
401		close $o
402		puts "\tGenerating $method files"
403		set upgrade_method $method
404
405		# We piggyback testing of dumped sequence files on upgrade
406		# testing because this is the only place that we ship files
407		# from one machine to another.  Create files for both
408		# endiannesses, because who knows what platform we'll
409		# be testing on.
410
411		set gen_dump 1
412		foreach test $test_names(plat) {
413			set upgrade_name $test
414			foreach upgrade_be { 0 1 } {
415				eval $test $method
416				cleanup $testdir NULL
417			}
418		}
419		set gen_dump 0
420
421#set test_names(test) ""
422		set gen_upgrade 1
423		foreach test $test_names(test) {
424			if { [info exists parms($test)] != 1 } {
425				continue
426			}
427
428			set o [open GENERATE.OUT a]
429			puts $o "\t\tGenerating files for $test"
430			close $o
431			puts "\t\tGenerating files for $test"
432
433			if { $save_non_crypto == 1 } {
434				set encrypt 0
435				foreach upgrade_be { 0 1 } {
436					set upgrade_name $test
437					if [catch {exec $tclsh_path \
438					    << "source $test_path/test.tcl;\
439					    global gen_upgrade upgrade_be;\
440					    global upgrade_method upgrade_name;\
441					    global encrypt;\
442					    set encrypt $encrypt;\
443					    set gen_upgrade 1;\
444					    set upgrade_be $upgrade_be;\
445					    set upgrade_method $upgrade_method;\
446					    set upgrade_name $upgrade_name;\
447					    run_method -$method $test" \
448					    >>& GENERATE.OUT} res] {
449						puts "FAIL: run_method \
450						    $test $method"
451					}
452					cleanup $testdir NULL 1
453				}
454				# Save checksummed files for only one test.
455				# Checksumming should work in all or no cases.
456				set gen_chksum 1
457				foreach upgrade_be { 0 1 } {
458					set upgrade_name $test
459					if { $test == "test001" } {
460						if { [catch {exec $tclsh_path \
461						    << "source $test_path/test.tcl;\
462						    global gen_upgrade;\
463						    global upgrade_be;\
464						    global upgrade_method;\
465						    global upgrade_name;\
466						    global encrypt gen_chksum;\
467						    set encrypt $encrypt;\
468						    set gen_upgrade 1;\
469						    set gen_chksum 1;\
470						    set upgrade_be $upgrade_be;\
471						    set upgrade_method \
472						    $upgrade_method;\
473						    set upgrade_name \
474						    $upgrade_name;\
475						    run_method -$method $test \
476						    0 1 stdout -chksum" \
477						    >>& GENERATE.OUT} res] } {
478							puts "FAIL: run_method \
479							    $test $method \
480							    -chksum: $res"
481						}
482						cleanup $testdir NULL 1
483					}
484				}
485				set gen_chksum 0
486			}
487			# Save encrypted db's only of native endianness.
488			# Encrypted files are not portable across endianness.
489			if { $save_crypto == 1 } {
490				set upgrade_be [big_endian]
491				set encrypt 1
492				set upgrade_name $test
493				if [catch {exec $tclsh_path \
494				    << "source $test_path/test.tcl;\
495				    global gen_upgrade upgrade_be;\
496				    global upgrade_method upgrade_name;\
497				    global encrypt passwd;\
498				    set encrypt $encrypt;\
499				    set passwd $passwd;\
500				    set gen_upgrade 1;\
501				    set upgrade_be $upgrade_be;\
502				    set upgrade_method $upgrade_method;\
503				    set upgrade_name $upgrade_name;\
504				    run_secmethod $method $test" \
505				    >>& GENERATE.OUT} res] {
506					puts "FAIL: run_secmethod \
507					    $test $method"
508				}
509				cleanup $testdir NULL 1
510			}
511		}
512		set gen_upgrade 0
513	}
514
515	# Set upgrade_be to the native value so log files go to the
516	# right place.
517	set upgrade_be [big_endian]
518
519	# Generate log files.
520	set o [open GENERATE.OUT a]
521	puts $o "\tGenerating log files"
522	close $o
523	puts "\tGenerating log files"
524
525	set gen_upgrade_log 1
526	# Pass the global variables and their values to the new tclsh.
527	if { $save_non_crypto == 1 } {
528		set encrypt 0
529		if [catch {exec $tclsh_path  << "source $test_path/test.tcl;\
530		    global gen_upgrade_log upgrade_be upgrade_dir;\
531		    global encrypt;\
532		    set encrypt $encrypt;\
533		    set gen_upgrade_log $gen_upgrade_log; \
534		    set upgrade_be $upgrade_be;\
535		    set upgrade_dir $upgrade_dir;\
536		    run_recds" >>& GENERATE.OUT} res] {
537			puts "FAIL: run_recds: $res"
538		}
539	}
540	if { $save_crypto == 1 } {
541		set encrypt 1
542		if [catch {exec $tclsh_path  << "source $test_path/test.tcl;\
543		    global gen_upgrade_log upgrade_be upgrade_dir;\
544		    global encrypt;\
545		    set encrypt $encrypt;\
546		    set gen_upgrade_log $gen_upgrade_log; \
547		    set upgrade_be $upgrade_be;\
548		    set upgrade_dir $upgrade_dir;\
549		    run_recds "  >>& GENERATE.OUT} res] {
550			puts "FAIL: run_recds with crypto: $res"
551		}
552	}
553	set gen_upgrade_log 0
554
555	set o [open GENERATE.OUT a]
556	puts -nonewline $o "Completed at: "
557	puts $o [clock format [clock seconds] -format "%H:%M %D"]
558	puts -nonewline "Completed at: "
559	puts [clock format [clock seconds] -format "%H:%M %D"]
560	close $o
561}
562
563proc save_upgrade_files { dir } {
564	global upgrade_dir
565	global upgrade_be
566	global upgrade_method
567	global upgrade_name
568	global gen_upgrade
569	global gen_upgrade_log
570	global gen_dump
571	global encrypt
572	global gen_chksum
573	global passwd
574	source ./include.tcl
575
576	set vers [berkdb version]
577	set maj [lindex $vers 0]
578	set min [lindex $vers 1]
579
580	# Is this machine big or little endian?  We want to mark
581	# the test directories appropriately, since testing
582	# little-endian databases generated by a big-endian machine,
583	# and/or vice versa, is interesting.
584	if { [big_endian] } {
585		set myendianness be
586	} else {
587		set myendianness le
588	}
589
590	if { $upgrade_be == 1 } {
591		set version_dir "$myendianness-$maj.${min}be"
592		set en be
593	} else {
594		set version_dir "$myendianness-$maj.${min}le"
595		set en le
596	}
597
598	set dest $upgrade_dir/$version_dir/$upgrade_method
599	exec mkdir -p $dest
600
601	if { $gen_upgrade == 1 } {
602		# Save db files from test001 - testxxx.
603		set dbfiles [glob -nocomplain $dir/*.db]
604		set dumpflag ""
605		# Encrypted files are identified by the prefix "c-".
606		if { $encrypt == 1 } {
607			set upgrade_name c-$upgrade_name
608			set dumpflag " -P $passwd "
609		}
610		# Checksummed files are identified by the prefix "s-".
611		if { $gen_chksum == 1 } {
612			set upgrade_name s-$upgrade_name
613		}
614		foreach dbfile $dbfiles {
615			set basename [string range $dbfile \
616			    [expr [string length $dir] + 1] end-3]
617
618			set newbasename $upgrade_name-$basename
619
620			# db_dump file
621			if { [catch {eval exec $util_path/db_dump -k $dumpflag \
622			    $dbfile > $dir/$newbasename.dump} res] } {
623				puts "FAIL: $res"
624			}
625
626			# tcl_dump file
627			upgrade_dump $dbfile $dir/$newbasename.tcldump
628
629			# Rename dbfile and any dbq files.
630			file rename $dbfile $dir/$newbasename-$en.db
631			foreach dbq \
632			    [glob -nocomplain $dir/__dbq.$basename.db.*] {
633				set s [string length $dir/__dbq.]
634				set newname [string replace $dbq $s \
635				    [expr [string length $basename] + $s - 1] \
636				    $newbasename-$en]
637				file rename $dbq $newname
638			}
639			set cwd [pwd]
640			cd $dir
641			catch {eval exec tar -cvf $dest/$newbasename.tar \
642			    [glob $newbasename* __dbq.$newbasename-$en.db.*]}
643			catch {exec gzip -9v $dest/$newbasename.tar} res
644			cd $cwd
645		}
646	}
647
648	if { $gen_upgrade_log == 1 } {
649		# Save log files from recd tests.
650		set logfiles [glob -nocomplain $dir/log.*]
651		if { [llength $logfiles] > 0 } {
652			# More than one log.0000000001 file may be produced
653			# per recd test, so we generate unique names:
654			# recd001-0-log.0000000001, recd001-1-log.0000000001,
655			# and so on.
656			# We may also have log.0000000001, log.0000000002,
657			# and so on, and they will all be dumped together
658			# by db_printlog.
659			set count 0
660			while { [file exists \
661			    $dest/$upgrade_name-$count-log.tar.gz] \
662			    == 1 } {
663				incr count
664			}
665			set newname $upgrade_name-$count-log
666
667			# Run db_printlog on all the log files
668			if {[catch {exec $util_path/db_printlog -h $dir > \
669			    $dir/$newname.prlog} res] != 0} {
670				puts "Regular printlog failed, try encryption"
671				eval {exec $util_path/db_printlog} -h $dir \
672				    -P $passwd > $dir/$newname.prlog
673			}
674
675			# Rename each log file so we can identify which
676			# recd test created it.
677			foreach logfile $logfiles {
678				set lognum [string range $logfile \
679				    end-9 end]
680				file rename $logfile $dir/$newname.$lognum
681			}
682
683			set cwd [pwd]
684			cd $dir
685
686			catch {eval exec tar -cvf $dest/$newname.tar \
687			    [glob $newname*]}
688			catch {exec gzip -9v $dest/$newname.tar}
689			cd $cwd
690		}
691	}
692
693	if { $gen_dump == 1 } {
694		# Save dump files.  We require that the files have
695		# been created with the extension .dmp.
696		set dumpfiles [glob -nocomplain $dir/*.dmp]
697
698		foreach dumpfile $dumpfiles {
699			set basename [string range $dumpfile \
700			    [expr [string length $dir] + 1] end-4]
701
702			set newbasename $upgrade_name-$basename
703
704			# Rename dumpfile.
705			file rename $dumpfile $dir/$newbasename.dmp
706
707			set cwd [pwd]
708			cd $dir
709			catch {eval exec tar -cvf $dest/$newbasename.tar \
710			    [glob $newbasename.dmp]}
711			catch {exec gzip -9v $dest/$newbasename.tar} res
712			cd $cwd
713		}
714	}
715}
716
717proc upgrade_dump { database file {stripnulls 0} } {
718	global errorInfo
719	global encrypt
720	global passwd
721
722	set encargs ""
723	if { $encrypt == 1 } {
724		set encargs " -encryptany $passwd "
725	}
726	set db [eval {berkdb open} -rdonly $encargs $database]
727	set dbc [$db cursor]
728
729	set f [open $file w+]
730	fconfigure $f -encoding binary -translation binary
731
732	#
733	# Get a sorted list of keys
734	#
735	set key_list ""
736	set pair [$dbc get -first]
737
738	while { 1 } {
739		if { [llength $pair] == 0 } {
740			break
741		}
742		set k [lindex [lindex $pair 0] 0]
743		lappend key_list $k
744		set pair [$dbc get -next]
745	}
746
747	# Discard duplicated keys;  we now have a key for each
748	# duplicate, not each unique key, and we don't want to get each
749	# duplicate multiple times when we iterate over key_list.
750	set uniq_keys ""
751	foreach key $key_list {
752		if { [info exists existence_list($key)] == 0 } {
753			lappend uniq_keys $key
754		}
755		set existence_list($key) 1
756	}
757	set key_list $uniq_keys
758
759	set key_list [lsort -command _comp $key_list]
760
761	#
762	# Get the data for each key
763	#
764	set i 0
765	foreach key $key_list {
766		set pair [$dbc get -set $key]
767		if { $stripnulls != 0 } {
768			# the Tcl interface to db versions before 3.X
769			# added nulls at the end of all keys and data, so
770			# we provide functionality to strip that out.
771			set key [strip_null $key]
772		}
773		set data_list {}
774		catch { while { [llength $pair] != 0 } {
775			set data [lindex [lindex $pair 0] 1]
776			if { $stripnulls != 0 } {
777				set data [strip_null $data]
778			}
779			lappend data_list [list $data]
780			set pair [$dbc get -nextdup]
781		} }
782		#lsort -command _comp data_list
783		set data_list [lsort -command _comp $data_list]
784		puts -nonewline $f [binary format i [string length $key]]
785		puts -nonewline $f $key
786		puts -nonewline $f [binary format i [llength $data_list]]
787		for { set j 0 } { $j < [llength $data_list] } { incr j } {
788			puts -nonewline $f [binary format i [string length \
789			    [concat [lindex $data_list $j]]]]
790			puts -nonewline $f [concat [lindex $data_list $j]]
791		}
792		if { [llength $data_list] == 0 } {
793			puts "WARNING: zero-length data list"
794		}
795		incr i
796	}
797
798	close $f
799	error_check_good upgrade_dump_c_close [$dbc close] 0
800	error_check_good upgrade_dump_db_close [$db close] 0
801}
802
803proc _comp { a b } {
804	if { 0 } {
805	# XXX
806		set a [strip_null [concat $a]]
807		set b [strip_null [concat $b]]
808		#return [expr [concat $a] < [concat $b]]
809	} else {
810		set an [string first "\0" $a]
811		set bn [string first "\0" $b]
812
813		if { $an != -1 } {
814			set a [string range $a 0 [expr $an - 1]]
815		}
816		if { $bn != -1 } {
817			set b [string range $b 0 [expr $bn - 1]]
818		}
819	}
820	#puts "$a $b"
821	return [string compare $a $b]
822}
823
824proc strip_null { str } {
825	set len [string length $str]
826	set last [expr $len - 1]
827
828	set termchar [string range $str $last $last]
829	if { [string compare $termchar \0] == 0 } {
830		set ret [string range $str 0 [expr $last - 1]]
831	} else {
832		set ret $str
833	}
834
835	return $ret
836}
837
838proc get_log_vers { env } {
839	set stat [$env log_stat]
840	foreach pair $stat {
841		set msg [lindex $pair 0]
842		set val [lindex $pair 1]
843		if { $msg == "Log file Version" } {
844			return $val
845		}
846	}
847	puts "FAIL: Log file Version not found in log_stat"
848	return 0
849}
850
851