1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 2000,2008 Oracle.  All rights reserved.
4#
5# $Id: fop001.tcl,v 12.10 2008/01/08 20:58:53 bostic Exp $
6#
7# TEST	fop001.tcl
8# TEST	Test file system operations, combined in a transaction. [#7363]
9proc fop001 { method { inmem 0 } args } {
10	source ./include.tcl
11
12	set args [convert_args $method $args]
13	set omethod [convert_method $method]
14
15	# The variable inmem determines whether the test is being
16	# run with regular named databases or named in-memory databases.
17	if { $inmem == 0 } {
18		set tnum "001"
19		set string "regular named databases"
20		set operator do_op
21	} else {
22		set tnum "007"
23		set string "in-memory named databases"
24		set operator do_inmem_op
25	}
26
27	puts "\nFop$tnum: ($method)\
28	    Two file system ops in one transaction for $string."
29
30	set exists {a b}
31	set noexist {foo bar}
32	set open {}
33	set cases {}
34	set ops {rename remove open open_create open_excl truncate}
35
36	# Set up all sensible two-op cases (op1 succeeds).
37	foreach retval { 0 "file exists" "no such file" } {
38		foreach op1 {rename remove open open_excl \
39		    open_create truncate} {
40			foreach op2 $ops {
41				append cases " " [create_tests $op1 $op2 \
42				    $exists $noexist $open $retval]
43			}
44		}
45	}
46
47	# Set up evil two-op cases (op1 fails).  Omit open_create
48	# and truncate from op1 list -- open_create always succeeds
49	# and truncate requires a successful open.
50	foreach retval { 0 "file exists" "no such file" } {
51		foreach op1 { rename remove open open_excl } {
52			foreach op2 $ops {
53				append cases " " [create_badtests $op1 $op2 \
54					$exists $noexist $open $retval]
55			}
56		}
57	}
58
59	# The structure of each case is:
60	# {{op1 {names1} result end1} {op2 {names2} result}}
61	# A result of "0" indicates no error is expected.
62	# Otherwise, the result is the expected error message.
63	#
64	# The "end1" variable indicates whether the first txn
65	# ended with an abort or a commit, and is not used
66	# in this test.
67	#
68	# Comment this loop out to remove the list of cases.
69#	set i 1
70#	foreach case $cases {
71#		puts "\tFop$tnum:$i: $case"
72#		incr i
73#	}
74
75	set testid 0
76
77	# Run all the cases
78	foreach case $cases {
79		env_cleanup $testdir
80		incr testid
81
82		# Extract elements of the case
83		set op1 [lindex [lindex $case 0] 0]
84		set names1 [lindex [lindex $case 0] 1]
85		set res1 [lindex [lindex $case 0] 2]
86
87		set op2 [lindex [lindex $case 1] 0]
88		set names2 [lindex [lindex $case 1] 1]
89		set res2 [lindex [lindex $case 1] 2]
90
91		puts "\tFop$tnum.$testid: $op1 ($names1), then $op2 ($names2)."
92
93		# The variable 'when' describes when to resolve a txn --
94		# before or after closing any open databases.
95		foreach when { before after } {
96
97			# Create transactional environment.
98			set env [berkdb_env -create -home $testdir -txn]
99			error_check_good is_valid_env [is_valid_env $env] TRUE
100
101			# Create two databases, dba and dbb.
102			if { $inmem == 0 } {
103				set dba [eval {berkdb_open -create} $omethod \
104				    $args -env $env -auto_commit a]
105			} else {
106				set dba [eval {berkdb_open -create} $omethod \
107				    $args -env $env -auto_commit { "" a }]
108			}
109			error_check_good dba_open [is_valid_db $dba] TRUE
110			error_check_good dba_put [$dba put 1 a] 0
111			error_check_good dba_close [$dba close] 0
112
113			if { $inmem == 0 } {
114				set dbb [eval {berkdb_open -create} $omethod \
115				    $args -env $env -auto_commit b]
116			} else {
117				set dbb [eval {berkdb_open -create} $omethod \
118				    $args -env $env -auto_commit { "" b }]
119			}
120			error_check_good dbb_open [is_valid_db $dbb] TRUE
121			error_check_good dbb_put [$dbb put 1 b] 0
122			error_check_good dbb_close [$dbb close] 0
123
124			# The variable 'end' describes how to resolve the txn.
125			# We run the 'abort' first because that leaves the env
126			# properly set up for the 'commit' test.
127			foreach end {abort commit} {
128
129				puts "\t\tFop$tnum.$testid:\
130				    $end $when closing database."
131
132				# Start transaction
133				set txn [$env txn]
134
135				# Execute and check operation 1
136				set result1 [$operator \
137				    $omethod $op1 $names1 $txn $env $args]
138				if { $res1 == 0 } {
139					error_check_good \
140					    op1_should_succeed $result1 $res1
141				} else {
142					set error [extract_error $result1]
143					error_check_good \
144					    op1_wrong_failure $error $res1
145				}
146
147				# Execute and check operation 2
148				set result2 [$operator \
149				    $omethod $op2 $names2 $txn $env $args]
150				if { $res2 == 0 } {
151					error_check_good \
152					    op2_should_succeed $result2 $res2
153				} else {
154					set error [extract_error $result2]
155					error_check_good \
156					    op2_wrong_failure $error $res2
157				}
158
159				if { $when == "before" } {
160					error_check_good txn_$end [$txn $end] 0
161
162					# If the txn was aborted, we still
163					# have the original two databases.
164					if { $end == "abort" } {
165						database_exists \
166						    $inmem $testdir a
167						database_exists \
168						    $inmem $testdir b
169					}
170					close_db_handles
171				} else {
172					close_db_handles
173					error_check_good txn_$end [$txn $end] 0
174
175					if { $end == "abort" } {
176						database_exists \
177						    $inmem $testdir a
178						database_exists \
179						    $inmem $testdir b
180					}
181				}
182			}
183
184			# Clean up for next case
185			error_check_good env_close [$env close] 0
186			error_check_good envremove \
187			    [berkdb envremove -home $testdir] 0
188			env_cleanup $testdir
189		}
190	}
191}
192
193proc database_exists { inmem testdir name } {
194	if { $inmem == 1 } {
195		error_check_good db_exists [inmem_exists $testdir $name] 1
196	} else {
197		error_check_good db_exists [file exists $testdir/$name] 1
198	}
199}
200
201# This is a real hack.  We need to figure out if an in-memory named
202# file exists.  In a perfect world we could use mpool stat.  Unfortunately,
203# mpool_stat returns files that have deadfile set and we need to not consider
204# those files to be meaningful.  So, we are parsing the output of db_stat -MA
205# (I told you this was a hack)  If we ever change the output, this is going
206# to break big time.  Here is what we assume:
207# A file is represented by: File #N name
208# The last field printed for a file is Flags
209# If the file is dead, deadfile will show up in the flags
210proc inmem_exists { dir filename } {
211      set infile 0
212      set islive 0
213      set name ""
214      set s [exec ./db_stat -MA -h $dir]
215      foreach i $s {
216              if { $i == "File" } {
217                      set infile 1
218                      set islive 1
219                      set name ""
220              } elseif { $i == "Flags" } {
221                      set infile 0
222                      if { $name != "" && $islive } {
223                              return 1
224                      }
225              } elseif { $infile != 0 } {
226                      incr infile
227              }
228
229              if { $islive && $i == "deadfile" } {
230                      set islive 0
231              }
232
233              if { $infile == 3 } {
234                      if { $i == $filename } {
235                              set name $filename
236                      }
237              }
238      }
239
240      return 0
241}
242
243