1# See the file LICENSE for redistribution information. 2# 3# Copyright (c) 1996-2009 Oracle. All rights reserved. 4# 5# $Id$ 6# 7# TEST test054 8# TEST Cursor maintenance during key/data deletion. 9# TEST 10# TEST This test checks for cursor maintenance in the presence of deletes. 11# TEST There are N different scenarios to tests: 12# TEST 1. No duplicates. Cursor A deletes a key, do a GET for the key. 13# TEST 2. No duplicates. Cursor is positioned right before key K, Delete K, 14# TEST do a next on the cursor. 15# TEST 3. No duplicates. Cursor is positioned on key K, do a regular delete 16# TEST of K, do a current get on K. 17# TEST 4. Repeat 3 but do a next instead of current. 18# TEST 5. Duplicates. Cursor A is on the first item of a duplicate set, A 19# TEST does a delete. Then we do a non-cursor get. 20# TEST 6. Duplicates. Cursor A is in a duplicate set and deletes the item. 21# TEST do a delete of the entire Key. Test cursor current. 22# TEST 7. Continue last test and try cursor next. 23# TEST 8. Duplicates. Cursor A is in a duplicate set and deletes the item. 24# TEST Cursor B is in the same duplicate set and deletes a different item. 25# TEST Verify that the cursor is in the right place. 26# TEST 9. Cursors A and B are in the place in the same duplicate set. A 27# TEST deletes its item. Do current on B. 28# TEST 10. Continue 8 and do a next on B. 29proc test054 { method args } { 30 global errorInfo 31 source ./include.tcl 32 33 set args [convert_args $method $args] 34 set omethod [convert_method $method] 35 36 append args " -create -mode 0644" 37 puts "Test054 ($method $args):\ 38 interspersed cursor and normal operations" 39 if { [is_record_based $method] == 1 } { 40 puts "Test054 skipping for method $method" 41 return 42 } 43 44 # Find the environment in the argument list, we'll need it 45 # later. 46 set txnenv 0 47 set eindex [lsearch -exact $args "-env"] 48 if { $eindex != -1 } { 49 incr eindex 50 } 51 52 # Create the database and open the dictionary 53 # 54 # If we are using an env, then testfile should just be the db name. 55 # Otherwise it is the test directory and the name. 56 if { $eindex == -1 } { 57 set testfile $testdir/test054-nodup.db 58 set env NULL 59 } else { 60 set testfile test054-nodup.db 61 set env [lindex $args $eindex] 62 set txnenv [is_txnenv $env] 63 if { $txnenv == 1 } { 64 append args " -auto_commit " 65 } 66 set testdir [get_home $env] 67 } 68 cleanup $testdir $env 69 70 set flags "" 71 set txn "" 72 73 puts "\tTest054.a: No Duplicate Tests" 74 set db [eval {berkdb_open} $args {$omethod $testfile}] 75 error_check_good db_open:nodup [is_valid_db $db] TRUE 76 77 # Put three keys in the database 78 for { set key 1 } { $key <= 3 } {incr key} { 79 if { $txnenv == 1 } { 80 set t [$env txn] 81 error_check_good txn [is_valid_txn $t $env] TRUE 82 set txn "-txn $t" 83 } 84 set r [eval {$db put} $txn $flags {$key datum$key}] 85 error_check_good put $r 0 86 if { $txnenv == 1 } { 87 error_check_good txn [$t commit] 0 88 } 89 } 90 91 if { $txnenv == 1 } { 92 set t [$env txn] 93 error_check_good txn [is_valid_txn $t $env] TRUE 94 set txn "-txn $t" 95 } 96 set curs [eval {$db cursor} $txn] 97 error_check_good curs_open:nodup [is_valid_cursor $curs $db] TRUE 98 99 # Retrieve keys sequentially so we can figure out their order 100 set i 1 101 for {set d [$curs get -first] } \ 102 {[llength $d] != 0 } \ 103 {set d [$curs get -next] } { 104 set key_set($i) [lindex [lindex $d 0] 0] 105 incr i 106 } 107 108 # Test case #1. 109 puts "\tTest054.a1: Delete w/cursor, regular get" 110 111 # Now set the cursor on the middle on. 112 set r [$curs get -set $key_set(2)] 113 error_check_bad cursor_get:DB_SET [llength $r] 0 114 set k [lindex [lindex $r 0] 0] 115 set d [lindex [lindex $r 0] 1] 116 error_check_good curs_get:DB_SET:key $k $key_set(2) 117 error_check_good curs_get:DB_SET:data $d datum$key_set(2) 118 119 # Now do the delete 120 set r [$curs del] 121 error_check_good curs_del $r 0 122 123 # Now do the get 124 set r [eval {$db get} $txn {$key_set(2)}] 125 error_check_good get_after_del [llength $r] 0 126 127 # Free up the cursor. 128 error_check_good cursor_close [eval {$curs close}] 0 129 if { $txnenv == 1 } { 130 error_check_good txn [$t commit] 0 131 } 132 133 # Test case #2. 134 puts "\tTest054.a2: Cursor before K, delete K, cursor next" 135 136 # Replace key 2 137 if { $txnenv == 1 } { 138 set t [$env txn] 139 error_check_good txn [is_valid_txn $t $env] TRUE 140 set txn "-txn $t" 141 } 142 set r [eval {$db put} $txn {$key_set(2) datum$key_set(2)}] 143 error_check_good put $r 0 144 if { $txnenv == 1 } { 145 error_check_good txn [$t commit] 0 146 } 147 148 # Open and position cursor on first item. 149 if { $txnenv == 1 } { 150 set t [$env txn] 151 error_check_good txn [is_valid_txn $t $env] TRUE 152 set txn "-txn $t" 153 } 154 set curs [eval {$db cursor} $txn] 155 error_check_good curs_open:nodup [is_valid_cursor $curs $db] TRUE 156 157 # Retrieve keys sequentially so we can figure out their order 158 set i 1 159 for {set d [eval {$curs get} -first] } \ 160 {[llength $d] != 0 } \ 161 {set d [$curs get -nextdup] } { 162 set key_set($i) [lindex [lindex $d 0] 0] 163 incr i 164 } 165 166 set r [eval {$curs get} -set {$key_set(1)} ] 167 error_check_bad cursor_get:DB_SET [llength $r] 0 168 set k [lindex [lindex $r 0] 0] 169 set d [lindex [lindex $r 0] 1] 170 error_check_good curs_get:DB_SET:key $k $key_set(1) 171 error_check_good curs_get:DB_SET:data $d datum$key_set(1) 172 173 # Now delete (next item) $key_set(2) 174 error_check_good \ 175 db_del:$key_set(2) [eval {$db del} $txn {$key_set(2)}] 0 176 177 # Now do next on cursor 178 set r [$curs get -next] 179 error_check_bad cursor_get:DB_NEXT [llength $r] 0 180 set k [lindex [lindex $r 0] 0] 181 set d [lindex [lindex $r 0] 1] 182 error_check_good curs_get:DB_NEXT:key $k $key_set(3) 183 error_check_good curs_get:DB_NEXT:data $d datum$key_set(3) 184 185 # Test case #3. 186 puts "\tTest054.a3: Cursor on K, delete K, cursor current" 187 188 # delete item 3 189 error_check_good \ 190 db_del:$key_set(3) [eval {$db del} $txn {$key_set(3)}] 0 191 # NEEDS TO COME BACK IN, BUG CHECK 192 set ret [$curs get -current] 193 error_check_good current_after_del $ret "" 194 error_check_good cursor_close [$curs close] 0 195 if { $txnenv == 1 } { 196 error_check_good txn [$t commit] 0 197 } 198 199 puts "\tTest054.a4: Cursor on K, delete K, cursor next" 200 201 # Restore keys 2 and 3 202 if { $txnenv == 1 } { 203 set t [$env txn] 204 error_check_good txn [is_valid_txn $t $env] TRUE 205 set txn "-txn $t" 206 } 207 set r [eval {$db put} $txn {$key_set(2) datum$key_set(2)}] 208 error_check_good put $r 0 209 set r [eval {$db put} $txn {$key_set(3) datum$key_set(3)}] 210 error_check_good put $r 0 211 if { $txnenv == 1 } { 212 error_check_good txn [$t commit] 0 213 } 214 215 if { $txnenv == 1 } { 216 set t [$env txn] 217 error_check_good txn [is_valid_txn $t $env] TRUE 218 set txn "-txn $t" 219 } 220 # Create the new cursor and put it on 1 221 set curs [eval {$db cursor} $txn] 222 error_check_good curs_open:nodup [is_valid_cursor $curs $db] TRUE 223 set r [$curs get -set $key_set(1)] 224 error_check_bad cursor_get:DB_SET [llength $r] 0 225 set k [lindex [lindex $r 0] 0] 226 set d [lindex [lindex $r 0] 1] 227 error_check_good curs_get:DB_SET:key $k $key_set(1) 228 error_check_good curs_get:DB_SET:data $d datum$key_set(1) 229 230 # Delete 2 231 error_check_good \ 232 db_del:$key_set(2) [eval {$db del} $txn {$key_set(2)}] 0 233 234 # Now do next on cursor 235 set r [$curs get -next] 236 error_check_bad cursor_get:DB_NEXT [llength $r] 0 237 set k [lindex [lindex $r 0] 0] 238 set d [lindex [lindex $r 0] 1] 239 error_check_good curs_get:DB_NEXT:key $k $key_set(3) 240 error_check_good curs_get:DB_NEXT:data $d datum$key_set(3) 241 242 # Close cursor 243 error_check_good curs_close [$curs close] 0 244 if { $txnenv == 1 } { 245 error_check_good txn [$t commit] 0 246 } 247 error_check_good db_close [$db close] 0 248 249 # Now get ready for duplicate tests 250 251 if { [is_rbtree $method] == 1 || [is_compressed $args] == 1 } { 252 puts "Test054: skipping remainder of test for method $method." 253 return 254 } 255 256 puts "\tTest054.b: Duplicate Tests" 257 append args " -dup" 258 259 # Open a new database for the dup tests so -truncate is not needed. 260 # If we are using an env, then testfile should just be the db name. 261 # Otherwise it is the test directory and the name. 262 if { $eindex == -1 } { 263 set testfile $testdir/test054-dup.db 264 set env NULL 265 } else { 266 set testfile test054-dup.db 267 set env [lindex $args $eindex] 268 set testdir [get_home $env] 269 } 270 cleanup $testdir $env 271 272 set flags "" 273 set txn "" 274 275 set db [eval {berkdb_open} $args {$omethod $testfile}] 276 error_check_good db_open:dup [is_valid_db $db] TRUE 277 278 # Put three keys in the database 279 for { set key 1 } { $key <= 3 } {incr key} { 280 if { $txnenv == 1 } { 281 set t [$env txn] 282 error_check_good txn [is_valid_txn $t $env] TRUE 283 set txn "-txn $t" 284 } 285 set r [eval {$db put} $txn $flags {$key datum$key}] 286 error_check_good put $r 0 287 if { $txnenv == 1 } { 288 error_check_good txn [$t commit] 0 289 } 290 } 291 292 # Retrieve keys sequentially so we can figure out their order 293 if { $txnenv == 1 } { 294 set t [$env txn] 295 error_check_good txn [is_valid_txn $t $env] TRUE 296 set txn "-txn $t" 297 } 298 set curs [eval {$db cursor} $txn] 299 error_check_good curs_open:dup [is_valid_cursor $curs $db] TRUE 300 301 set i 1 302 for {set d [$curs get -first] } \ 303 {[llength $d] != 0 } \ 304 {set d [$curs get -nextdup] } { 305 set key_set($i) [lindex [lindex $d 0] 0] 306 incr i 307 } 308 309 # Now put in a bunch of duplicates for key 2 310 for { set d 1 } { $d <= 5 } {incr d} { 311 set r [eval {$db put} $txn $flags {$key_set(2) dup_$d}] 312 error_check_good dup:put $r 0 313 } 314 315 # Test case #5. 316 puts "\tTest054.b1: Delete dup w/cursor on first item. Get on key." 317 318 # Now set the cursor on the first of the duplicate set. 319 set r [eval {$curs get} -set {$key_set(2)}] 320 error_check_bad cursor_get:DB_SET [llength $r] 0 321 set k [lindex [lindex $r 0] 0] 322 set d [lindex [lindex $r 0] 1] 323 error_check_good curs_get:DB_SET:key $k $key_set(2) 324 error_check_good curs_get:DB_SET:data $d datum$key_set(2) 325 326 # Now do the delete 327 set r [$curs del] 328 error_check_good curs_del $r 0 329 330 # Now do the get 331 set r [eval {$db get} $txn {$key_set(2)}] 332 error_check_good get_after_del [lindex [lindex $r 0] 1] dup_1 333 334 # Test case #6. 335 puts "\tTest054.b2: Now get the next duplicate from the cursor." 336 337 # Now do next on cursor 338 set r [$curs get -nextdup] 339 error_check_bad cursor_get:DB_NEXT [llength $r] 0 340 set k [lindex [lindex $r 0] 0] 341 set d [lindex [lindex $r 0] 1] 342 error_check_good curs_get:DB_NEXT:key $k $key_set(2) 343 error_check_good curs_get:DB_NEXT:data $d dup_1 344 345 # Test case #3. 346 puts "\tTest054.b3: Two cursors in set; each delete different items" 347 348 # Open a new cursor. 349 set curs2 [eval {$db cursor} $txn] 350 error_check_good curs_open [is_valid_cursor $curs2 $db] TRUE 351 352 # Set on last of duplicate set. 353 set r [$curs2 get -set $key_set(3)] 354 error_check_bad cursor_get:DB_SET [llength $r] 0 355 set k [lindex [lindex $r 0] 0] 356 set d [lindex [lindex $r 0] 1] 357 error_check_good curs_get:DB_SET:key $k $key_set(3) 358 error_check_good curs_get:DB_SET:data $d datum$key_set(3) 359 360 set r [$curs2 get -prev] 361 error_check_bad cursor_get:DB_PREV [llength $r] 0 362 set k [lindex [lindex $r 0] 0] 363 set d [lindex [lindex $r 0] 1] 364 error_check_good curs_get:DB_PREV:key $k $key_set(2) 365 error_check_good curs_get:DB_PREV:data $d dup_5 366 367 # Delete the item at cursor 1 (dup_1) 368 error_check_good curs1_del [$curs del] 0 369 370 # Verify curs1 and curs2 371 # current should fail 372 set ret [$curs get -current] 373 error_check_good curs1_get_after_del $ret "" 374 375 set r [$curs2 get -current] 376 error_check_bad curs2_get [llength $r] 0 377 set k [lindex [lindex $r 0] 0] 378 set d [lindex [lindex $r 0] 1] 379 error_check_good curs_get:DB_CURRENT:key $k $key_set(2) 380 error_check_good curs_get:DB_CURRENT:data $d dup_5 381 382 # Now delete the item at cursor 2 (dup_5) 383 error_check_good curs2_del [$curs2 del] 0 384 385 # Verify curs1 and curs2 386 set ret [$curs get -current] 387 error_check_good curs1_get:del2 $ret "" 388 389 set ret [$curs2 get -current] 390 error_check_good curs2_get:del2 $ret "" 391 392 # Now verify that next and prev work. 393 394 set r [$curs2 get -prev] 395 error_check_bad cursor_get:DB_PREV [llength $r] 0 396 set k [lindex [lindex $r 0] 0] 397 set d [lindex [lindex $r 0] 1] 398 error_check_good curs_get:DB_PREV:key $k $key_set(2) 399 error_check_good curs_get:DB_PREV:data $d dup_4 400 401 set r [$curs get -next] 402 error_check_bad cursor_get:DB_NEXT [llength $r] 0 403 set k [lindex [lindex $r 0] 0] 404 set d [lindex [lindex $r 0] 1] 405 error_check_good curs_get:DB_NEXT:key $k $key_set(2) 406 error_check_good curs_get:DB_NEXT:data $d dup_2 407 408 puts "\tTest054.b4: Two cursors same item, one delete, one get" 409 410 # Move curs2 onto dup_2 411 set r [$curs2 get -prev] 412 error_check_bad cursor_get:DB_PREV [llength $r] 0 413 set k [lindex [lindex $r 0] 0] 414 set d [lindex [lindex $r 0] 1] 415 error_check_good curs_get:DB_PREV:key $k $key_set(2) 416 error_check_good curs_get:DB_PREV:data $d dup_3 417 418 set r [$curs2 get -prev] 419 error_check_bad cursor_get:DB_PREV [llength $r] 0 420 set k [lindex [lindex $r 0] 0] 421 set d [lindex [lindex $r 0] 1] 422 error_check_good curs_get:DB_PREV:key $k $key_set(2) 423 error_check_good curs_get:DB_PREV:data $d dup_2 424 425 # delete on curs 1 426 error_check_good curs1_del [$curs del] 0 427 428 # Verify gets on both 1 and 2 429 set ret [$curs get -current] 430 error_check_good \ 431 curs1_get:deleted $ret "" 432 set ret [$curs2 get -current] 433 error_check_good \ 434 curs2_get:deleted $ret "" 435 436 puts "\tTest054.b5: Now do a next on both cursors" 437 438 set r [$curs get -next] 439 error_check_bad cursor_get:DB_NEXT [llength $r] 0 440 set k [lindex [lindex $r 0] 0] 441 set d [lindex [lindex $r 0] 1] 442 error_check_good curs_get:DB_NEXT:key $k $key_set(2) 443 error_check_good curs_get:DB_NEXT:data $d dup_3 444 445 set r [$curs2 get -next] 446 error_check_bad cursor_get:DB_NEXT [llength $r] 0 447 set k [lindex [lindex $r 0] 0] 448 set d [lindex [lindex $r 0] 1] 449 error_check_good curs_get:DB_NEXT:key $k $key_set(2) 450 error_check_good curs_get:DB_NEXT:data $d dup_3 451 452 # Close cursor 453 error_check_good curs_close [$curs close] 0 454 error_check_good curs2_close [$curs2 close] 0 455 if { $txnenv == 1 } { 456 error_check_good txn [$t commit] 0 457 } 458 error_check_good db_close [$db close] 0 459} 460