1# See the file LICENSE for redistribution information. 2# 3# Copyright (c) 1999,2008 Oracle. All rights reserved. 4# 5# $Id: sdb012.tcl,v 12.8 2008/01/08 20:58:53 bostic Exp $ 6# 7# TEST sdb012 8# TEST Test subdbs with locking and transactions 9# TEST Tests creating and removing subdbs while handles 10# TEST are open works correctly, and in the face of txns. 11# 12proc sdb012 { method args } { 13 source ./include.tcl 14 15 set args [convert_args $method $args] 16 set omethod [convert_method $method] 17 18 if { [is_queue $method] == 1 } { 19 puts "Subdb012: skipping for method $method" 20 return 21 } 22 23 # If we are using an env, then skip this test. It needs its own. 24 set eindex [lsearch -exact $args "-env"] 25 if { $eindex != -1 } { 26 incr eindex 27 set env [lindex $args $eindex] 28 puts "Subdb012 skipping for env $env" 29 return 30 } 31 set encargs "" 32 set largs [split_encargs $args encargs] 33 34 puts "Subdb012: $method ($largs $encargs) subdb txn/locking tests" 35 36 # 37 # sdb012_body takes a txn list containing 4 elements. 38 # {txn command for first subdb 39 # txn command for second subdb 40 # txn command for first subdb removal 41 # txn command for second subdb removal} 42 # 43 # The allowed commands are 'none' 'one', 'auto', 'abort', 'commit'. 44 # 'none' is a special case meaning run without a txn. In the 45 # case where all 4 items are 'none', we run in a lock-only env. 46 # 'one' is a special case meaning we create the subdbs together 47 # in one single transaction. It is indicated as the value for t1, 48 # and the value in t2 indicates if that single txn should be 49 # aborted or committed. It is not used and has no meaning 50 # in the removal case. 'auto' means use the -auto_commit flag 51 # to the operation, and 'abort' and 'commit' do the obvious. 52 # "-auto" is applied only to the creation of the subdbs, since 53 # it is done by default on database removes in transactional 54 # environments. 55 # 56 # First test locking w/o txns. If any in tlist are 'none', 57 # all must be none. 58 # 59 # Now run through the txn-based operations 60 set count 0 61 set sdb "Subdb012." 62 set teststr "abcdefghijklmnopqrstuvwxyz" 63 set testlet [split $teststr {}] 64 foreach t1 { none one abort auto commit } { 65 foreach t2 { none abort auto commit } { 66 if { $t1 == "one" } { 67 if { $t2 == "none" || $t2 == "auto"} { 68 continue 69 } 70 } 71 set tlet [lindex $testlet $count] 72 foreach r1 { none abort commit } { 73 foreach r2 { none abort commit } { 74 set tlist [list $t1 $t2 $r1 $r2] 75 set nnone [llength \ 76 [lsearch -all $tlist none]] 77 if { $nnone != 0 && $nnone != 4 } { 78 continue 79 } 80 sdb012_body $testdir $omethod $largs \ 81 $encargs $sdb$tlet $tlist 82 } 83 } 84 incr count 85 } 86 } 87 88} 89 90proc s012 { method args } { 91 source ./include.tcl 92 93 set omethod [convert_method $method] 94 95 set encargs "" 96 set largs "" 97 98 puts "Subdb012: $method ($largs $encargs) subdb txn/locking tests" 99 100 set sdb "Subdb012." 101 set tlet X 102 set tlist $args 103 error_check_good tlist [llength $tlist] 4 104 sdb012_body $testdir $omethod $largs $encargs $sdb$tlet $tlist 105} 106 107# 108# This proc checks the tlist values and returns the flags 109# that should be used when opening the env. If we are running 110# with no txns, then just -lock, otherwise -txn. 111# 112proc sdb012_subsys { tlist } { 113 set t1 [lindex $tlist 0] 114 # 115 # If we have no txns, all elements of the list should be none. 116 # In that case we only run with locking turned on. 117 # Otherwise, we use the full txn subsystems. 118 # 119 set allnone {none none none none} 120 if { $allnone == $tlist } { 121 set subsys "-lock" 122 } else { 123 set subsys "-txn" 124 } 125 return $subsys 126} 127 128# 129# This proc parses the tlist and returns a list of 4 items that 130# should be used in operations. I.e. it will begin the txns as 131# needed, or return a -auto_commit flag, etc. 132# 133proc sdb012_tflags { env tlist } { 134 set ret "" 135 set t1 "" 136 foreach t $tlist { 137 switch $t { 138 one { 139 set t1 [$env txn] 140 error_check_good txnbegin [is_valid_txn $t1 $env] TRUE 141 lappend ret "-txn $t1" 142 lappend ret "-txn $t1" 143 } 144 auto { 145 lappend ret "-auto_commit" 146 } 147 abort - 148 commit { 149 # 150 # If the previous command was a "one", skip over 151 # this commit/abort. Otherwise start a new txn 152 # for the removal case. 153 # 154 if { $t1 == "" } { 155 set txn [$env txn] 156 error_check_good txnbegin [is_valid_txn $txn \ 157 $env] TRUE 158 lappend ret "-txn $txn" 159 } else { 160 set t1 "" 161 } 162 } 163 none { 164 lappend ret "" 165 } 166 default { 167 error "Txn command $t not implemented" 168 } 169 } 170 } 171 return $ret 172} 173 174# 175# This proc parses the tlist and returns a list of 4 items that 176# should be used in the txn conclusion operations. I.e. it will 177# give "" if using auto_commit (i.e. no final txn op), or a single 178# abort/commit if both subdb's are in one txn. 179# 180proc sdb012_top { tflags tlist } { 181 set ret "" 182 set t1 "" 183 # 184 # We know both lists have 4 items. Iterate over them 185 # using multiple value lists so we know which txn goes 186 # with each op. 187 # 188 # The tflags list is needed to extract the txn command 189 # out for the operation. The tlist list is needed to 190 # determine what operation we are doing. 191 # 192 foreach t $tlist tf $tflags { 193 switch $t { 194 one { 195 set t1 [lindex $tf 1] 196 } 197 auto { 198 lappend ret "sdb012_nop" 199 } 200 abort - 201 commit { 202 # 203 # If the previous command was a "one" (i.e. t1 204 # is set), append a correct command and then 205 # an empty one. 206 # 207 if { $t1 == "" } { 208 set txn [lindex $tf 1] 209 set top "$txn $t" 210 lappend ret $top 211 } else { 212 set top "$t1 $t" 213 lappend ret "sdb012_nop" 214 lappend ret $top 215 set t1 "" 216 } 217 } 218 none { 219 lappend ret "sdb012_nop" 220 } 221 } 222 } 223 return $ret 224} 225 226proc sdb012_nop { } { 227 return 0 228} 229 230proc sdb012_isabort { tlist item } { 231 set i [lindex $tlist $item] 232 if { $i == "one" } { 233 set i [lindex $tlist [expr $item + 1]] 234 } 235 if { $i == "abort" } { 236 return 1 237 } else { 238 return 0 239 } 240} 241 242proc sdb012_body { testdir omethod largs encargs msg tlist } { 243 244 puts "\t$msg: $tlist" 245 set testfile subdb012.db 246 set subdb1 sub1 247 set subdb2 sub2 248 249 set subsys [sdb012_subsys $tlist] 250 env_cleanup $testdir 251 set env [eval {berkdb_env -create -home} $testdir $subsys $encargs] 252 error_check_good dbenv [is_valid_env $env] TRUE 253 error_check_good test_lock [$env test abort subdb_lock] 0 254 255 # 256 # Convert from our tlist txn commands into real flags we 257 # will pass to commands. Use the multiple values feature 258 # of foreach to do this efficiently. 259 # 260 set tflags [sdb012_tflags $env $tlist] 261 foreach {txn1 txn2 rem1 rem2} $tflags {break} 262 foreach {top1 top2 rop1 rop2} [sdb012_top $tflags $tlist] {break} 263 264# puts "txn1 $txn1, txn2 $txn2, rem1 $rem1, rem2 $rem2" 265# puts "top1 $top1, top2 $top2, rop1 $rop1, rop2 $rop2" 266 puts "\t$msg.0: Create sub databases in env with $subsys" 267 set s1 [eval {berkdb_open -env $env -create -mode 0644} \ 268 $largs $txn1 {$omethod $testfile $subdb1}] 269 error_check_good dbopen [is_valid_db $s1] TRUE 270 271 set ret [eval $top1] 272 error_check_good t1_end $ret 0 273 274 set s2 [eval {berkdb_open -env $env -create -mode 0644} \ 275 $largs $txn2 {$omethod $testfile $subdb2}] 276 error_check_good dbopen [is_valid_db $s2] TRUE 277 278 puts "\t$msg.1: Subdbs are open; resolve txns if necessary" 279 set ret [eval $top2] 280 error_check_good t2_end $ret 0 281 282 set t1_isabort [sdb012_isabort $tlist 0] 283 set t2_isabort [sdb012_isabort $tlist 1] 284 set r1_isabort [sdb012_isabort $tlist 2] 285 set r2_isabort [sdb012_isabort $tlist 3] 286 287# puts "t1_isabort $t1_isabort, t2_isabort $t2_isabort, r1_isabort $r1_isabort, r2_isabort $r2_isabort" 288 289 puts "\t$msg.2: Subdbs are open; verify removal failures" 290 # Verify removes of subdbs with open subdb's fail 291 # 292 # We should fail no matter what. If we aborted, then the 293 # subdb should not exist. If we didn't abort, we should fail 294 # with DB_LOCK_NOTGRANTED. 295 # 296 # XXX - Do we need -auto_commit for all these failing ones? 297 set r [ catch {berkdb dbremove -env $env $testfile $subdb1} result ] 298 error_check_bad dbremove1_open $r 0 299 if { $t1_isabort } { 300 error_check_good dbremove1_open_ab [is_substr \ 301 $result "no such file"] 1 302 } else { 303 error_check_good dbremove1_open [is_substr \ 304 $result DB_LOCK_NOTGRANTED] 1 305 } 306 307 set r [ catch {berkdb dbremove -env $env $testfile $subdb2} result ] 308 error_check_bad dbremove2_open $r 0 309 if { $t2_isabort } { 310 error_check_good dbremove2_open_ab [is_substr \ 311 $result "no such file"] 1 312 } else { 313 error_check_good dbremove2_open [is_substr \ 314 $result DB_LOCK_NOTGRANTED] 1 315 } 316 317 # Verify file remove fails 318 set r [catch {berkdb dbremove -env $env $testfile} result] 319 error_check_bad dbremovef_open $r 0 320 321 # 322 # If both aborted, there should be no file?? 323 # 324 if { $t1_isabort && $t2_isabort } { 325 error_check_good dbremovef_open_ab [is_substr \ 326 $result "no such file"] 1 327 } else { 328 error_check_good dbremovef_open [is_substr \ 329 $result DB_LOCK_NOTGRANTED] 1 330 } 331 332 puts "\t$msg.3: Close subdb2; verify removals" 333 error_check_good close_s2 [$s2 close] 0 334 set r [ catch {eval {berkdb dbremove -env} \ 335 $env $rem2 $testfile $subdb2} result ] 336 if { $t2_isabort } { 337 error_check_bad dbrem2_ab $r 0 338 error_check_good dbrem2_ab [is_substr \ 339 $result "no such file"] 1 340 } else { 341 error_check_good dbrem2 $result 0 342 } 343 # Resolve subdb2 removal txn 344 set r [eval $rop2] 345 error_check_good rop2 $r 0 346 347 set r [ catch {berkdb dbremove -env $env $testfile $subdb1} result ] 348 error_check_bad dbremove1.2_open $r 0 349 if { $t1_isabort } { 350 error_check_good dbremove1.2_open_ab [is_substr \ 351 $result "no such file"] 1 352 } else { 353 error_check_good dbremove1.2_open [is_substr \ 354 $result DB_LOCK_NOTGRANTED] 1 355 } 356 357 # There are three cases here: 358 # 1. if both t1 and t2 aborted, the file shouldn't exist 359 # 2. if only t1 aborted, the file still exists and nothing is open 360 # 3. if neither aborted a remove should fail because the first 361 # subdb is still open 362 # In case 2, don't try the remove, because it should succeed 363 # and we won't be able to test anything else. 364 if { !$t1_isabort || $t2_isabort } { 365 set r [catch {berkdb dbremove -env $env $testfile} result] 366 if { $t1_isabort && $t2_isabort } { 367 error_check_bad dbremovef.2_open $r 0 368 error_check_good dbremove.2_open_ab [is_substr \ 369 $result "no such file"] 1 370 } else { 371 error_check_bad dbremovef.2_open $r 0 372 error_check_good dbremove.2_open [is_substr \ 373 $result DB_LOCK_NOTGRANTED] 1 374 } 375 } 376 377 puts "\t$msg.4: Close subdb1; verify removals" 378 error_check_good close_s1 [$s1 close] 0 379 set r [ catch {eval {berkdb dbremove -env} \ 380 $env $rem1 $testfile $subdb1} result ] 381 if { $t1_isabort } { 382 error_check_bad dbremove1_ab $r 0 383 error_check_good dbremove1_ab [is_substr \ 384 $result "no such file"] 1 385 } else { 386 error_check_good dbremove1 $result 0 387 } 388 # Resolve subdb1 removal txn 389 set r [eval $rop1] 390 error_check_good rop1 $r 0 391 392 # Verify removal of subdb2. All DB handles are closed now. 393 # So we have two scenarios: 394 # 1. The removal of subdb2 above was successful and subdb2 395 # doesn't exist and we should fail that way. 396 # 2. The removal of subdb2 above was aborted, and this 397 # removal should succeed. 398 # 399 set r [ catch {berkdb dbremove -env $env $testfile $subdb2} result ] 400 if { $r2_isabort && !$t2_isabort } { 401 error_check_good dbremove2.1_ab $result 0 402 } else { 403 error_check_bad dbremove2.1 $r 0 404 error_check_good dbremove2.1 [is_substr \ 405 $result "no such file"] 1 406 } 407 408 # Verify removal of subdb1. All DB handles are closed now. 409 # So we have two scenarios: 410 # 1. The removal of subdb1 above was successful and subdb1 411 # doesn't exist and we should fail that way. 412 # 2. The removal of subdb1 above was aborted, and this 413 # removal should succeed. 414 # 415 set r [ catch {berkdb dbremove -env $env $testfile $subdb1} result ] 416 if { $r1_isabort && !$t1_isabort } { 417 error_check_good dbremove1.1 $result 0 418 } else { 419 error_check_bad dbremove_open $r 0 420 error_check_good dbremove.1 [is_substr \ 421 $result "no such file"] 1 422 } 423 424 puts "\t$msg.5: All closed; remove file" 425 set r [catch {berkdb dbremove -env $env $testfile} result] 426 if { $t1_isabort && $t2_isabort } { 427 error_check_bad dbremove_final_ab $r 0 428 error_check_good dbremove_file_abstr [is_substr \ 429 $result "no such file"] 1 430 } else { 431 error_check_good dbremove_final $r 0 432 } 433 error_check_good envclose [$env close] 0 434} 435