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