# See the file LICENSE for redistribution information.
#
# Copyright (c) 1996-2002
#	Sleepycat Software.  All rights reserved.
#
# $Id: recd020.tcl,v 11.8 2002/08/08 15:38:08 bostic Exp $
#
# TEST	recd020
# TEST	Test recovery after checksum error.
proc recd020 { method args} {
	global fixed_len
	global log_log_record_types
	global datastr
	source ./include.tcl

	set pgindex [lsearch -exact $args "-pagesize"]
	if { $pgindex != -1 } {
		puts "Recd020: skipping for specific pagesizes"
		return
	}
	if { [is_queueext $method] == 1 } {
		puts "Recd020: skipping for method $method"
		return
	}

	puts "Recd020: $method recovery after checksum error"

	# Create the database and environment.
	env_cleanup $testdir

	set testfile recd020.db
	set flags "-create -txn -home $testdir"

	puts "\tRecd020.a: creating environment"
	set env_cmd "berkdb_env $flags"
	set dbenv [eval $env_cmd]
	error_check_good dbenv [is_valid_env $dbenv] TRUE

	set pgsize 512
	set orig_fixed_len $fixed_len
	set fixed_len [expr $pgsize / 4]
	set opts [convert_args $method $args]
	set omethod [convert_method $method]
	set oflags "-create $omethod -mode 0644 \
	    -auto_commit -chksum -pagesize $pgsize $opts $testfile"
	set db [eval {berkdb_open} -env $dbenv $oflags]

	#
	# Put some data.
	#
	set nument 50
	puts "\tRecd020.b: Put some data"
	for { set i 1 } { $i <= $nument } { incr i } {
		# Use 'i' as key so method doesn't matter
		set key $i
		set data $i$datastr

		# Put, in a txn.
		set txn [$dbenv txn]
		error_check_good txn_begin [is_valid_txn $txn $dbenv] TRUE
		error_check_good db_put \
		    [$db put -txn $txn $key [chop_data $method $data]] 0
		error_check_good txn_commit [$txn commit] 0
	}
	error_check_good db_close [$db close] 0
	error_check_good env_close [$dbenv close] 0
	#
	# We need to remove the env so that we don't get cached
	# pages.
	#
	error_check_good env_remove [berkdb envremove -home $testdir] 0

	puts "\tRecd020.c: Overwrite part of database"
	#
	# First just touch some bits in the file.  We want to go
	# through the paging system, so touch some data pages,
	# like the middle of page 2.
	# We should get a checksum error for the checksummed file.
	#
	set pg 2
	set fid [open $testdir/$testfile r+]
	fconfigure $fid -translation binary
	set seeklen [expr $pgsize * $pg + 200]
	seek $fid $seeklen start
	set byte [read $fid 1]
	binary scan $byte c val
	set newval [expr ~$val]
	set newbyte [binary format c $newval]
	seek $fid $seeklen start
	puts -nonewline $fid $newbyte
	close $fid

	#
	# Verify we get the checksum error.  When we get it, it should
	# log the error as well, so when we run recovery we'll need to
	# do catastrophic recovery.  We do this in a sub-process so that
	# the files are closed after the panic.
	#
	set f1 [open |$tclsh_path r+]
	puts $f1 "source $test_path/test.tcl"

	set env_cmd "berkdb_env_noerr $flags"
	set dbenv [send_cmd $f1 $env_cmd]
	error_check_good dbenv [is_valid_env $dbenv] TRUE

	set db [send_cmd $f1  "{berkdb_open_noerr} -env $dbenv $oflags"]
	error_check_good db [is_valid_db $db] TRUE

	# We need to set non-blocking mode so that after each command
	# we can read all the remaining output from that command and
	# we can know what the output from one command is.
	fconfigure $f1 -blocking 0
	set ret [read $f1]
	set got_err 0
	for { set i 1 } { $i <= $nument } { incr i } {
		set stat [send_cmd $f1  "catch {$db get $i} r"]
		set getret [send_cmd $f1  "puts \$r"]
		set ret [read $f1]
		if { $stat == 1 } {
			error_check_good dbget:fail [is_substr $getret \
			    "checksum error: catastrophic recovery required"] 1
			set got_err 1
			# Now verify that it was an error on the page we set.
			error_check_good dbget:pg$pg [is_substr $ret \
			    "failed for page $pg"] 1
			break
		} else {
			set key [lindex [lindex $getret 0] 0]
			set data [lindex [lindex $getret 0] 1]
			error_check_good keychk $key $i
			error_check_good datachk $data \
			    [pad_data $method $i$datastr]
		}
	}
	error_check_good got_chksum $got_err 1
	set ret [send_cmd $f1  "$db close"]
	set extra [read $f1]
	error_check_good db:fail [is_substr $ret "run recovery"] 1

	set ret [send_cmd $f1  "$dbenv close"]
	error_check_good env_close:fail [is_substr $ret "run recovery"] 1
	close $f1

        # Keep track of the log types we've seen
	if { $log_log_record_types == 1} {
		logtrack_read $testdir
	}

	puts "\tRecd020.d: Run normal recovery"
	set ret [catch {exec $util_path/db_recover -h $testdir} r]
	error_check_good db_recover $ret 1
	error_check_good dbrec:fail \
	    [is_substr $r "checksum error: catastrophic recovery required"] 1

	catch {fileremove $testdir/$testfile} ret
	puts "\tRecd020.e: Run catastrophic recovery"
	set ret [catch {exec $util_path/db_recover -c -h $testdir} r]
	error_check_good db_recover $ret 0

	#
	# Now verify the data was reconstructed correctly.
	#
	set env_cmd "berkdb_env_noerr $flags"
	set dbenv [eval $env_cmd]
	error_check_good dbenv [is_valid_env $dbenv] TRUE

	set db [eval {berkdb_open} -env $dbenv $oflags]
	error_check_good db [is_valid_db $db] TRUE

	for { set i 1 } { $i <= $nument } { incr i } {
		set stat [catch {$db get $i} ret]
		error_check_good stat $stat 0
		set key [lindex [lindex $ret 0] 0]
		set data [lindex [lindex $ret 0] 1]
		error_check_good keychk $key $i
		error_check_good datachk $data [pad_data $method $i$datastr]
	}
	error_check_good db_close [$db close] 0
	error_check_good env_close [$dbenv close] 0
}
