1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 2004,2008 Oracle.  All rights reserved.
4#
5# $Id: recd021.tcl,v 12.7 2008/01/08 20:58:53 bostic Exp $
6#
7# TEST	recd021
8# TEST	Test of failed opens in recovery.
9# TEST
10# TEST	If a file was deleted through the file system (and not
11# TEST	within Berkeley DB), an error message should appear.
12# TEST	Test for regular files and subdbs.
13
14proc recd021 { method args } {
15	source ./include.tcl
16	global util_path
17
18	set args [convert_args $method $args]
19	set omethod [convert_method $method]
20	set nentries 100
21
22	puts "\nRecd021: ($method)\
23	    Test failed opens in recovery."
24
25	# The file ops "remove" and "rename" are done within
26	# Berkeley DB.  A "delete" is done directly on the file
27	# system (as if the user deleted the file).
28	#
29	# First test regular files.
30	#
31	foreach op { remove rename delete noop } {
32		env_cleanup $testdir
33		puts "\tRecd021: Test $op of file in recovery."
34
35		# Create transactional environment.
36		set env [berkdb_env -create -home $testdir -txn]
37		error_check_good is_valid_env [is_valid_env $env] TRUE
38
39		# Create database
40		puts "\t\tRecd021.a.1: Create and populate file."
41
42		if { $op == "rename" } {
43			set names {A B}
44		} else {
45			set names {A}
46		}
47		set name [lindex $names 0]
48
49		set db [eval {berkdb_open \
50		    -create} $omethod $args -env $env -auto_commit $name.db]
51		error_check_good dba_open [is_valid_db $db] TRUE
52
53		# Checkpoint.
54		error_check_good txn_checkpoint [$env txn_checkpoint] 0
55		for { set i 1 } { $i <= $nentries } { incr i } {
56			error_check_good dba_put [$db put $i data$i] 0
57		}
58		error_check_good dba_close [$db close] 0
59
60		# Do operation on file.
61		puts "\t\tRecd021.b: Do $op on file."
62		set txn [$env txn]
63		set ret [do_op $omethod $op $names $txn $env]
64		error_check_good do_op $ret 0
65		error_check_good txn_commit [$txn commit] 0
66		error_check_good env_close [$env close] 0
67
68		# Recover.
69		puts "\t\tRecd021.c: Recover."
70		set ret [catch {exec $util_path/db_recover -h $testdir} r]
71		if { $op == "delete" } {
72			error_check_good external_delete \
73			    [is_substr $r "Warning: open failed"] 1
74		} else {
75			error_check_good $op $ret 0
76		}
77
78		# Clean up.
79		error_check_good \
80		    env_remove [berkdb envremove -force -home $testdir] 0
81		fileremove -f $testdir/$name.db
82	}
83
84	# Test subdbs.
85	if { [is_queue $method] == 1 } {
86		puts "Recd021: Skipping test of subdbs for method $method."
87		return
88	}
89
90	# The first subdb test just does the op, and is comparable
91	# to the tests for regular files above.
92	set trunc 0
93	set special {}
94	foreach op { remove rename delete noop } {
95		recd021_testsubdb $method $op $nentries $special $trunc $args
96	}
97
98	# The remainder of the tests are executed first with the log intact,
99	# then with the log truncated at the __db_subdb_name record.
100	foreach trunc { 0 1 } {
101		# Test what happens if subdb2 reuses pages formerly in
102		# subdb1, after removing subdb1.
103		set special "reuse"
104		recd021_testsubdb $method remove $nentries $special $trunc $args
105
106		# Test what happens if a new subdb reuses pages formerly
107		# in subdb1, after removing subdb1.
108		set special "newdb"
109		recd021_testsubdb $method remove $nentries $special $trunc $args
110
111		# Now we test what happens if a new subdb if a different access
112		# method reuses pages formerly in subdb1, after removing subdb1.
113		set special "newtypedb"
114		recd021_testsubdb $method remove $nentries $special $trunc $args
115	}
116}
117
118proc recd021_testsubdb { method op nentries special trunc largs } {
119	source ./include.tcl
120	global util_path
121
122	set omethod [convert_method $method]
123	env_cleanup $testdir
124
125	puts "\tRecd021: \
126	    Test $op of subdb in recovery ($special trunc = $trunc)."
127
128	# Create transactional environment.
129	set env [berkdb_env -create -home $testdir -txn]
130	error_check_good is_valid_env [is_valid_env $env] TRUE
131
132	# Create database with 2 subdbs
133	puts "\t\tRecd021.d: Create and populate subdbs."
134	set sname1 S1
135	set sname2 S2
136	if { $op == "rename" } {
137		set names {A S1 NEW_S1}
138	} elseif { $op == "delete" } {
139		set names {A}
140	} else {
141		set names {A S1}
142	}
143	set name [lindex $names 0]
144
145	set sdb1 [eval {berkdb_open -create} $omethod \
146	    $largs -env $env -auto_commit $name.db $sname1]
147	error_check_good sdb1_open [is_valid_db $sdb1] TRUE
148	set sdb2 [eval {berkdb_open -create} $omethod \
149	    $largs -env $env -auto_commit $name.db $sname2]
150	error_check_good sdb2_open [is_valid_db $sdb2] TRUE
151
152	# Checkpoint.
153	error_check_good txn_checkpoint [$env txn_checkpoint] 0
154	for { set i 1 } { $i <= $nentries } { incr i } {
155		error_check_good sdb1_put [$sdb1 put $i data$i] 0
156	}
157	set dumpfile dump.s1.$trunc
158	set ret [exec $util_path/db_dump -dar -f $dumpfile -h $testdir A.db]
159	for { set i 1 } { $i <= $nentries } { incr i } {
160		error_check_good sdb2_put [$sdb2 put $i data$i] 0
161	}
162	error_check_good sdb1_close [$sdb1 close] 0
163
164	# Do operation on subdb.
165	puts "\t\tRecd021.e: Do $op on file."
166	set txn [$env txn]
167
168	if { $trunc == 1 } {
169		# Create a log cursor to mark where we are before
170		# doing the op.
171		set logc [$env log_cursor]
172		set ret [lindex [$logc get -last] 0]
173		file copy -force $testdir/log.0000000001 $testdir/log.sav
174	}
175
176	set ret [do_subdb_op $omethod $op $names $txn $env]
177	error_check_good do_subdb_op $ret 0
178	error_check_good txn_commit [$txn commit] 0
179
180	if { $trunc == 1 } {
181		# Walk the log and find the __db_subdb_name entry.
182		set found 0
183		while { $found == 0 } {
184			set lsn [lindex [$logc get -next] 0]
185			set lfile [lindex $lsn 0]
186			set loff [lindex $lsn 1]
187			set logrec [exec $util_path/db_printlog -h $testdir \
188			    -b $lfile/$loff -e $lfile/$loff]
189			if { [is_substr $logrec __db_subdb_name] == 1 } {
190				set found 1
191			}
192		}
193		# Create the truncated log, and save it for later.
194		catch [exec dd if=$testdir/log.0000000001 \
195		    of=$testdir/log.sav count=$loff bs=1 >& /dev/null ] res
196	}
197
198	# Here we do the "special" thing, if any.  We always
199	# have to close sdb2, but when we do so varies.
200	switch -exact -- $special {
201		"" {
202			error_check_good sdb2_close [$sdb2 close] 0
203		}
204		reuse {
205			for { set i [expr $nentries + 1] } \
206			    { $i <= [expr $nentries * 2]} { incr i } {
207				error_check_good sdb2_put \
208				    [$sdb2 put $i data$i] 0
209			}
210			error_check_good sdb2_close [$sdb2 close] 0
211			set dumpfile dump.s2.$trunc
212			set ret [exec $util_path/db_dump -dar \
213			    -f $dumpfile -h $testdir A.db]
214		}
215		newdb {
216			error_check_good sdb2_close [$sdb2 close] 0
217			set sname3 S3
218			set sdb3 [eval {berkdb_open -create} $omethod \
219			    $largs -env $env -auto_commit $name.db $sname3]
220			error_check_good sdb3_open [is_valid_db $sdb3] TRUE
221			for { set i 1 } { $i <= $nentries } { incr i } {
222				error_check_good sdb3_put \
223				     [$sdb3 put $i data$i] 0
224			}
225			error_check_good sdb3_close [$sdb3 close] 0
226		}
227		newtypedb {
228			error_check_good sdb2_close [$sdb2 close] 0
229			set sname4 S4
230			set newmethod [different_method $method]
231			set args [convert_args $newmethod]
232			set omethod [convert_method $newmethod]
233			set sdb4 [eval {berkdb_open -create} $omethod \
234			    $args -env $env -auto_commit $name.db $sname4]
235			error_check_good sdb4_open [is_valid_db $sdb4] TRUE
236			for { set i 1 } { $i <= $nentries } { incr i } {
237				error_check_good sdb4_put \
238				     [$sdb4 put $i data$i] 0
239			}
240			error_check_good sdb4_close [$sdb4 close] 0
241		}
242	}
243
244	# Close the env.
245	error_check_good env_close [$env close] 0
246
247	if { $trunc == 1 } {
248		# Swap in the truncated log.
249		file rename -force $testdir/log.sav $testdir/log.0000000001
250	}
251
252	# Recover.
253	puts "\t\tRecd021.f: Recover."
254	set ret [catch {exec $util_path/db_recover -h $testdir} r]
255	if { $op == "delete" || $trunc == 1 && $special != "newdb" } {
256		error_check_good expect_warning \
257		    [is_substr $r "Warning: open failed"] 1
258	} else {
259		error_check_good subdb_$op $ret 0
260	}
261
262	# Clean up.
263	error_check_good env_remove [berkdb envremove -force -home $testdir] 0
264	fileremove -f $testdir/$name.db
265}
266
267proc different_method { method } {
268	# Queue methods are omitted, since this is for subdb testing.
269	set methodlist { -btree -rbtree -recno -frecno -rrecno -hash }
270
271	set method [convert_method $method]
272        set newmethod $method
273        while { $newmethod == $method } {
274                set index [berkdb random_int 0 [expr [llength $methodlist] - 1]]
275                set newmethod [lindex $methodlist $index]
276        }
277        return $newmethod
278}
279