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