1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 1999,2008 Oracle.  All rights reserved.
4#
5# $Id: recd014.tcl,v 12.6 2008/01/08 20:58:53 bostic Exp $
6#
7# TEST	recd014
8# TEST	This is a recovery test for create/delete of queue extents.  We
9# TEST	then need to recover and make sure the file is correctly existing
10# TEST	or not, as the case may be.
11proc recd014 { method args} {
12	global fixed_len
13	source ./include.tcl
14
15	if { ![is_queueext $method] == 1 } {
16		puts "Recd014: Skipping for method $method"
17		return
18	}
19	set pgindex [lsearch -exact $args "-pagesize"]
20	if { $pgindex != -1 } {
21		puts "Recd014: skipping for specific pagesizes"
22		return
23	}
24
25	set orig_fixed_len $fixed_len
26	#
27	# We will use 512-byte pages, to be able to control
28	# when extents get created/removed.
29	#
30	set fixed_len 300
31
32	set opts [convert_args $method $args]
33	set omethod [convert_method $method]
34	#
35	# We want to set -extent 1 instead of what
36	# convert_args gave us.
37	#
38	set exti [lsearch -exact $opts "-extent"]
39	incr exti
40	set opts [lreplace $opts $exti $exti 1]
41
42	puts "Recd014: $method extent creation/deletion tests"
43
44	# Create the database and environment.
45	env_cleanup $testdir
46
47	set testfile recd014.db
48	set flags "-create -txn -home $testdir"
49
50	puts "\tRecd014.a: creating environment"
51	set env_cmd "berkdb_env $flags"
52
53	puts "\tRecd014.b: Create test commit"
54	ext_recover_create $testdir $env_cmd $omethod \
55	    $opts $testfile commit
56	puts "\tRecd014.b: Create test abort"
57	ext_recover_create $testdir $env_cmd $omethod \
58	    $opts $testfile abort
59
60	puts "\tRecd014.c: Consume test commit"
61	ext_recover_consume $testdir $env_cmd $omethod \
62	    $opts $testfile commit
63	puts "\tRecd014.c: Consume test abort"
64	ext_recover_consume $testdir $env_cmd $omethod \
65	    $opts $testfile abort
66
67	set fixed_len $orig_fixed_len
68	puts "\tRecd014.d: Verify db_printlog can read logfile"
69	set tmpfile $testdir/printlog.out
70	set stat [catch {exec $util_path/db_printlog -h $testdir \
71	    > $tmpfile} ret]
72	error_check_good db_printlog $stat 0
73	fileremove $tmpfile
74}
75
76proc ext_recover_create { dir env_cmd method opts dbfile txncmd } {
77	global log_log_record_types
78	global fixed_len
79	global alphabet
80	source ./include.tcl
81
82	# Keep track of the log types we've seen
83	if { $log_log_record_types == 1} {
84		logtrack_read $dir
85	}
86
87	env_cleanup $dir
88	# Open the environment and set the copy/abort locations
89	set env [eval $env_cmd]
90
91	set init_file $dir/$dbfile.init
92	set noenvflags "-create $method -mode 0644 -pagesize 512 $opts $dbfile"
93	set oflags "-env $env $noenvflags"
94
95	set t [$env txn]
96	error_check_good txn_begin [is_valid_txn $t $env] TRUE
97
98	set ret [catch {eval {berkdb_open} -txn $t $oflags} db]
99	error_check_good txn_commit [$t commit] 0
100
101	set t [$env txn]
102	error_check_good txn_begin [is_valid_txn $t $env] TRUE
103
104	#
105	# The command to execute to create an extent is a put.
106	# We are just creating the first one, so our extnum is 0.
107	#
108	set extnum 0
109	set data [chop_data $method [replicate $alphabet 512]]
110	puts "\t\tExecuting command"
111	set putrecno [$db put -txn $t -append $data]
112	error_check_good db_put $putrecno 1
113
114	# Sync the db so any changes to the file that are
115	# in mpool get written to the disk file before the
116	# diff.
117	puts "\t\tSyncing"
118	error_check_good db_sync [$db sync] 0
119
120	catch { file copy -force $dir/$dbfile $dir/$dbfile.afterop } res
121	copy_extent_file $dir $dbfile afterop
122
123	error_check_good txn_$txncmd:$t [$t $txncmd] 0
124	#
125	# If we don't abort, then we expect success.
126	# If we abort, we expect no file created.
127	#
128	set dbq [make_ext_filename $dir $dbfile $extnum]
129	error_check_good extput:exists1 [file exists $dbq] 1
130	set ret [$db get $putrecno]
131	if {$txncmd == "abort"} {
132		#
133		# Operation was aborted.   Verify our entry is not there.
134		#
135		puts "\t\tCommand executed and aborted."
136		error_check_good db_get [llength $ret] 0
137	} else {
138		#
139		# Operation was committed, verify it exists.
140		#
141		puts "\t\tCommand executed and committed."
142		error_check_good db_get [llength $ret] 1
143		catch { file copy -force $dir/$dbfile $init_file } res
144		copy_extent_file $dir $dbfile init
145	}
146	set t [$env txn]
147	error_check_good txn_begin [is_valid_txn $t $env] TRUE
148	error_check_good db_close [$db close] 0
149	error_check_good txn_commit [$t commit] 0
150	error_check_good env_close [$env close] 0
151
152	#
153	# Run recovery here.  Should be a no-op.  Verify that
154	# the file still does/n't exist when we are done.
155	#
156	berkdb debug_check
157	puts -nonewline "\t\tAbout to run recovery (no-op) ... "
158	flush stdout
159
160	set stat [catch {exec $util_path/db_recover -h $dir -c} result]
161	if { $stat == 1 } {
162		error "FAIL: Recovery error: $result."
163		return
164	}
165	puts "complete"
166	#
167	# Verify it did not change.
168	#
169	error_check_good extput:exists2 [file exists $dbq] 1
170	ext_create_check $dir $txncmd $init_file $dbfile $noenvflags $putrecno
171
172	#
173	# Need a new copy to get the right LSN into the file.
174	#
175	catch { file copy -force $dir/$dbfile $init_file } res
176	copy_extent_file $dir $dbfile init
177
178	#
179	# Undo.
180	# Now move the .afterop file to $dbfile.  Run recovery again.
181	#
182	file copy -force $dir/$dbfile.afterop $dir/$dbfile
183	move_file_extent $dir $dbfile afterop copy
184
185	berkdb debug_check
186	puts -nonewline "\t\tAbout to run recovery (afterop) ... "
187	flush stdout
188
189	set stat [catch {exec $util_path/db_recover -h $dir -c} result]
190	if { $stat == 1 } {
191		error "FAIL: Recovery error: $result."
192		return
193	}
194	puts "complete"
195	ext_create_check $dir $txncmd $init_file $dbfile $noenvflags $putrecno
196
197	#
198	# To redo, remove the dbfiles.  Run recovery again.
199	#
200	catch { file rename -force $dir/$dbfile $dir/$dbfile.renamed } res
201	copy_extent_file $dir $dbfile renamed rename
202
203	berkdb debug_check
204	puts -nonewline "\t\tAbout to run recovery (init) ... "
205	flush stdout
206
207	set stat [catch {exec $util_path/db_recover -h $dir -c} result]
208	#
209	# !!!
210	# Even though db_recover exits with status 0, it should print out
211	# a warning because the file didn't exist.  Db_recover writes this
212	# to stderr.  Tcl assumes that ANYTHING written to stderr is an
213	# error, so even though we exit with 0 status, we still get an
214	# error back from 'catch'.  Look for the warning.
215	#
216	if { $stat == 1 && [is_substr $result "warning"] == 0 } {
217		error "FAIL: Recovery error: $result."
218		return
219	}
220	puts "complete"
221
222	#
223	# Verify it was redone.  However, since we removed the files
224	# to begin with, recovery with abort will not recreate the
225	# extent.  Recovery with commit will.
226	#
227	if {$txncmd == "abort"} {
228		error_check_good extput:exists3 [file exists $dbq] 0
229	} else {
230		error_check_good extput:exists3 [file exists $dbq] 1
231	}
232}
233
234proc ext_create_check { dir txncmd init_file dbfile oflags putrecno } {
235	if { $txncmd == "commit" } {
236		#
237		# Operation was committed. Verify it did not change.
238		#
239		error_check_good \
240		    diff(initial,post-recover2):diff($init_file,$dir/$dbfile) \
241		    [dbdump_diff "-dar" $init_file $dir $dbfile] 0
242	} else {
243		#
244		# Operation aborted.  The file is there, but make
245		# sure the item is not.
246		#
247		set xdb [eval {berkdb_open} $oflags]
248		error_check_good db_open [is_valid_db $xdb] TRUE
249		set ret [$xdb get $putrecno]
250		error_check_good db_get [llength $ret] 0
251		error_check_good db_close [$xdb close] 0
252	}
253}
254
255proc ext_recover_consume { dir env_cmd method opts dbfile txncmd} {
256	global log_log_record_types
257	global alphabet
258	source ./include.tcl
259
260	# Keep track of the log types we've seen
261	if { $log_log_record_types == 1} {
262		logtrack_read $dir
263	}
264
265	env_cleanup $dir
266	# Open the environment and set the copy/abort locations
267	set env [eval $env_cmd]
268
269	set oflags "-create -auto_commit $method -mode 0644 -pagesize 512 \
270	   -env $env $opts $dbfile"
271
272	#
273	# Open our db, add some data, close and copy as our
274	# init file.
275	#
276	set db [eval {berkdb_open} $oflags]
277	error_check_good db_open [is_valid_db $db] TRUE
278
279	set extnum 0
280	set data [chop_data $method [replicate $alphabet 512]]
281
282	set txn [$env txn]
283	error_check_good txn_begin [is_valid_txn $txn $env] TRUE
284	set putrecno [$db put -txn $txn -append $data]
285	error_check_good db_put $putrecno 1
286	error_check_good commit [$txn commit] 0
287	error_check_good db_close [$db close] 0
288
289	puts "\t\tExecuting command"
290
291	set init_file $dir/$dbfile.init
292	catch { file copy -force $dir/$dbfile $init_file } res
293	copy_extent_file $dir $dbfile init
294
295	#
296	# If we don't abort, then we expect success.
297	# If we abort, we expect no file removed until recovery is run.
298	#
299	set db [eval {berkdb_open} $oflags]
300	error_check_good db_open [is_valid_db $db] TRUE
301
302	set t [$env txn]
303	error_check_good txn_begin [is_valid_txn $t $env] TRUE
304
305	set dbcmd "$db get -txn $t -consume"
306	set ret [eval $dbcmd]
307	error_check_good db_sync [$db sync] 0
308
309	catch { file copy -force $dir/$dbfile $dir/$dbfile.afterop } res
310	copy_extent_file $dir $dbfile afterop
311
312	error_check_good txn_$txncmd:$t [$t $txncmd] 0
313	error_check_good db_sync [$db sync] 0
314	set dbq [make_ext_filename $dir $dbfile $extnum]
315	if {$txncmd == "abort"} {
316		#
317		# Operation was aborted, verify ext did not change.
318		#
319		puts "\t\tCommand executed and aborted."
320
321		#
322		# Check that the file exists.  Final state.
323		# Since we aborted the txn, we should be able
324		# to get to our original entry.
325		#
326		error_check_good postconsume.1 [file exists $dbq] 1
327		error_check_good \
328		    diff(init,postconsume.2):diff($init_file,$dir/$dbfile)\
329		    [dbdump_diff "-dar" $init_file $dir $dbfile] 0
330	} else {
331		#
332		# Operation was committed, verify it does
333		# not exist.
334		#
335		puts "\t\tCommand executed and committed."
336		#
337		# Check file existence.  Consume operations remove
338		# the extent when we move off, which we should have
339		# done.
340		error_check_good consume_exists [file exists $dbq] 0
341	}
342	error_check_good db_close [$db close] 0
343	error_check_good env_close [$env close] 0
344
345	#
346	# Run recovery here on what we ended up with.  Should be a no-op.
347	#
348	berkdb debug_check
349	puts -nonewline "\t\tAbout to run recovery (no-op) ... "
350	flush stdout
351
352	set stat [catch {exec $util_path/db_recover -h $dir -c} result]
353	if { $stat == 1 } {
354		error "FAIL: Recovery error: $result."
355		return
356	}
357	puts "complete"
358	if { $txncmd == "abort"} {
359		#
360		# Operation was aborted, verify it did not change.
361		#
362		error_check_good \
363		    diff(initial,post-recover1):diff($init_file,$dir/$dbfile) \
364		    [dbdump_diff "-dar" $init_file $dir $dbfile] 0
365	} else {
366		#
367		# Operation was committed, verify it does
368		# not exist.  Both operations should result
369		# in no file existing now that we've run recovery.
370		#
371		error_check_good after_recover1 [file exists $dbq] 0
372	}
373
374	#
375	# Run recovery here. Re-do the operation.
376	# Verify that the file doesn't exist
377	# (if we committed)  or change (if we aborted)
378	# when we are done.
379	#
380	catch { file copy -force $dir/$dbfile $init_file } res
381	copy_extent_file $dir $dbfile init
382	berkdb debug_check
383	puts -nonewline "\t\tAbout to run recovery (init) ... "
384	flush stdout
385
386	set stat [catch {exec $util_path/db_recover -h $dir -c} result]
387	if { $stat == 1 } {
388		error "FAIL: Recovery error: $result."
389		return
390	}
391	puts "complete"
392	if { $txncmd == "abort"} {
393		#
394		# Operation was aborted, verify it did not change.
395		#
396		error_check_good \
397		    diff(initial,post-recover1):diff($init_file,$dir/$dbfile) \
398		    [dbdump_diff "-dar" $init_file $dir $dbfile] 0
399	} else {
400		#
401		# Operation was committed, verify it does
402		# not exist.  Both operations should result
403		# in no file existing now that we've run recovery.
404		#
405		error_check_good after_recover2 [file exists $dbq] 0
406	}
407
408	#
409	# Now move the .afterop file to $dbfile.  Run recovery again.
410	#
411	set filecopy [glob $dir/*.afterop]
412	set afterop [lindex $filecopy 0]
413	file rename -force $afterop $dir/$dbfile
414	set afterop [string range $afterop \
415	    [expr [string last "/" $afterop] + 1]  \
416	    [string last "." $afterop]]
417	move_file_extent $dir $dbfile afterop rename
418
419	berkdb debug_check
420	puts -nonewline "\t\tAbout to run recovery (afterop) ... "
421	flush stdout
422
423	set stat [catch {exec $util_path/db_recover -h $dir -c} result]
424	if { $stat == 1 } {
425		error "FAIL: Recovery error: $result."
426		return
427	}
428	puts "complete"
429
430	if { $txncmd == "abort"} {
431		#
432		# Operation was aborted, verify it did not change.
433		#
434		error_check_good \
435		    diff(initial,post-recover2):diff($init_file,$dir/$dbfile) \
436		    [dbdump_diff "-dar" $init_file $dir $dbfile] 0
437	} else {
438		#
439		# Operation was committed, verify it still does
440		# not exist.
441		#
442		error_check_good after_recover3 [file exists $dbq] 0
443	}
444}
445