1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 2000,2008 Oracle.  All rights reserved.
4#
5# $Id: recd013.tcl,v 12.7 2008/01/08 20:58:53 bostic Exp $
6#
7# TEST	recd013
8# TEST	Test of cursor adjustment on child transaction aborts. [#2373]
9#
10# XXX
11# Other tests that cover more specific variants of the same issue
12# are in the access method tests for now.  This is probably wrong;  we
13# put this one here because they're closely based on and intertwined
14# with other, non-transactional cursor stability tests that are among
15# the access method tests, and because we need at least one test to
16# fit under recd and keep logtrack from complaining.  We'll sort out the mess
17# later;  the important thing, for now, is that everything that needs to gets
18# tested.  (This really shouldn't be under recd at all, since it doesn't
19# run recovery!)
20proc recd013 { method { nitems 100 } args } {
21	source ./include.tcl
22	global alphabet log_log_record_types
23
24	set args [convert_args $method $args]
25	set omethod [convert_method $method]
26	set tnum "013"
27	set pgsz 512
28
29	puts "Recd$tnum $method ($args): Test of aborted cursor adjustments."
30	set pgindex [lsearch -exact $args "-pagesize"]
31	if { $pgindex != -1 } {
32		puts "Recd013: skipping for specific pagesizes"
33		return
34	}
35
36	set testfile recd$tnum.db
37	env_cleanup $testdir
38
39	set i 0
40	if { [is_record_based $method] == 1 } {
41		set keybase ""
42	} else {
43		set keybase "key"
44	}
45
46	puts "\tRecd$tnum.a:\
47	    Create environment, database, and parent transaction."
48	set flags "-create -txn -home $testdir"
49
50	set env_cmd "berkdb_env $flags"
51	set env [eval $env_cmd]
52	error_check_good dbenv [is_valid_env $env] TRUE
53
54	set oflags \
55    "-auto_commit -env $env -create -mode 0644 -pagesize $pgsz $args $omethod"
56	set db [eval {berkdb_open} $oflags $testfile]
57	error_check_good dbopen [is_valid_db $db] TRUE
58
59	# Create a database containing $nitems items, numbered with odds.
60	# We'll then put the even numbers during the body of the test.
61	set txn [$env txn]
62	error_check_good init_txn [is_valid_txn $txn $env] TRUE
63	for { set i 1 } { $i <= 2 * $nitems } { incr i 2 } {
64		set key $keybase$i
65		set data [chop_data $method $i$alphabet]
66
67		# First, try to put the item in a child transaction,
68		# then abort and verify all the cursors we've done up until
69		# now.
70		set ctxn [$env txn -parent $txn]
71		error_check_good child_txn($i) [is_valid_txn $ctxn $env] TRUE
72		error_check_good fake_put($i) [$db put -txn $ctxn $key $data] 0
73		error_check_good ctxn_abort($i) [$ctxn abort] 0
74		for { set j 1 } { $j < $i } { incr j 2 } {
75			error_check_good dbc_get($j):1 [$dbc($j) get -current] \
76			    [list [list $keybase$j \
77			    [pad_data $method $j$alphabet]]]
78		}
79
80		# Then put for real.
81		error_check_good init_put($i) [$db put -txn $txn $key $data] 0
82
83		# Set a cursor of the parent txn to each item.
84		set dbc($i) [$db cursor -txn $txn]
85		error_check_good dbc_getset($i) \
86		    [$dbc($i) get -set $key] \
87		    [list [list $keybase$i [pad_data $method $i$alphabet]]]
88
89		# And verify all the cursors, including the one we just
90		# created.
91		for { set j 1 } { $j <= $i } { incr j 2 } {
92			error_check_good dbc_get($j):2 [$dbc($j) get -current] \
93			    [list [list $keybase$j \
94			    [pad_data $method $j$alphabet]]]
95		}
96	}
97
98	puts "\t\tRecd$tnum.a.1: Verify cursor stability after init."
99	for { set i 1 } { $i <= 2 * $nitems } { incr i 2 } {
100		error_check_good dbc_get($i):3 [$dbc($i) get -current] \
101		    [list [list $keybase$i [pad_data $method $i$alphabet]]]
102	}
103
104	puts "\tRecd$tnum.b: Put test."
105	puts "\t\tRecd$tnum.b.1: Put items."
106	set ctxn [$env txn -parent $txn]
107	error_check_good txn [is_valid_txn $ctxn $env] TRUE
108	for { set i 2 } { $i <= 2 * $nitems } { incr i 2 } {
109		set key $keybase$i
110		set data [chop_data $method $i$alphabet]
111		error_check_good child_put($i) [$db put -txn $ctxn $key $data] 0
112
113		# If we're a renumbering recno, this is uninteresting.
114		# Stir things up by putting a few additional records at
115		# the beginning.
116		if { [is_rrecno $method] == 1 } {
117			set curs [$db cursor -txn $ctxn]
118			error_check_bad llength_get_first \
119			    [llength [$curs get -first]] 0
120			error_check_good cursor [is_valid_cursor $curs $db] TRUE
121			# expect a recno!
122			error_check_good rrecno_put($i) \
123			    [$curs put -before ADDITIONAL.$i] 1
124			error_check_good curs_close [$curs close] 0
125		}
126	}
127
128	puts "\t\tRecd$tnum.b.2: Verify cursor stability after abort."
129	error_check_good ctxn_abort [$ctxn abort] 0
130
131	for { set i 1 } { $i <= 2 * $nitems } { incr i 2 } {
132		error_check_good dbc_get($i):4 [$dbc($i) get -current] \
133		    [list [list $keybase$i [pad_data $method $i$alphabet]]]
134	}
135
136	# Clean up cursors.
137	for { set i 1 } { $i <= 2 * $nitems } { incr i 2 } {
138		error_check_good dbc($i)_close [$dbc($i) close] 0
139	}
140
141	# Sync and verify.
142	error_check_good txn_commit [$txn commit] 0
143	set txn [$env txn]
144	error_check_good txn [is_valid_txn $txn $env] TRUE
145
146	error_check_good db_sync [$db sync] 0
147	error_check_good db_verify \
148	    [verify_dir $testdir "\t\tRecd$tnum.b.3: "] 0
149
150	# Now put back all the even records, this time in the parent.
151	# Commit and re-begin the transaction so we can abort and
152	# get back to a nice full database.
153	for { set i 2 } { $i <= 2 * $nitems } { incr i 2 } {
154		set key $keybase$i
155		set data [chop_data $method $i$alphabet]
156		error_check_good child_put($i) [$db put -txn $txn $key $data] 0
157	}
158	error_check_good txn_commit [$txn commit] 0
159	set txn [$env txn]
160	error_check_good txn [is_valid_txn $txn $env] TRUE
161
162	# Delete test.  Set a cursor to each record.  Delete the even ones
163	# in the parent and check cursor stability.  Then open a child
164	# transaction, and delete the odd ones.  Verify that the database
165	# is empty.
166	puts "\tRecd$tnum.c: Delete test."
167	unset dbc
168
169	# Create cursors pointing at each item.
170	for { set i 1 } { $i <= 2 * $nitems } { incr i } {
171		set dbc($i) [$db cursor -txn $txn]
172		error_check_good dbc($i)_create [is_valid_cursor $dbc($i) $db] \
173		    TRUE
174		error_check_good dbc_getset($i) [$dbc($i) get -set $keybase$i] \
175		    [list [list $keybase$i [pad_data $method $i$alphabet]]]
176	}
177
178	puts "\t\tRecd$tnum.c.1: Delete even items in child txn and abort."
179
180	if { [is_rrecno $method] != 1 } {
181		set init 2
182		set bound [expr 2 * $nitems]
183		set step 2
184	} else {
185		# In rrecno, deletes will renumber the items, so we have
186		# to take that into account when we delete by recno.
187		set init 2
188		set bound [expr $nitems + 1]
189		set step 1
190	}
191
192	set ctxn [$env txn -parent $txn]
193	for { set i $init } { $i <= $bound } { incr i $step } {
194		error_check_good del($i) [$db del -txn $ctxn $keybase$i] 0
195	}
196	error_check_good ctxn_abort [$ctxn abort] 0
197
198	# Verify that no items are deleted.
199	for { set i 1 } { $i <= 2 * $nitems } { incr i } {
200		error_check_good dbc_get($i):5 [$dbc($i) get -current] \
201		    [list [list $keybase$i [pad_data $method $i$alphabet]]]
202	}
203
204	puts "\t\tRecd$tnum.c.2: Delete even items in child txn and commit."
205	set ctxn [$env txn -parent $txn]
206	for { set i $init } { $i <= $bound } { incr i $step } {
207		error_check_good del($i) [$db del -txn $ctxn $keybase$i] 0
208	}
209	error_check_good ctxn_commit [$ctxn commit] 0
210
211	# Verify that even items are deleted and odd items are not.
212	for { set i 1 } { $i <= 2 * $nitems } { incr i 2 } {
213		if { [is_rrecno $method] != 1 } {
214			set j $i
215		} else {
216			set j [expr ($i - 1) / 2 + 1]
217		}
218		error_check_good dbc_get($i):6 [$dbc($i) get -current] \
219		    [list [list $keybase$j [pad_data $method $i$alphabet]]]
220	}
221	for { set i 2 } { $i <= 2 * $nitems } { incr i 2 } {
222		error_check_good dbc_get($i):7 [$dbc($i) get -current] ""
223	}
224
225	puts "\t\tRecd$tnum.c.3: Delete odd items in child txn."
226
227	set ctxn [$env txn -parent $txn]
228
229	for { set i 1 } { $i <= 2 * $nitems } { incr i 2 } {
230		if { [is_rrecno $method] != 1 } {
231			set j $i
232		} else {
233			# If this is an rrecno, just delete the first
234			# item repeatedly--the renumbering will make
235			# that delete everything.
236			set j 1
237		}
238		error_check_good del($i) [$db del -txn $ctxn $keybase$j] 0
239	}
240
241	# Verify that everyone's deleted.
242	for { set i 1 } { $i <= 2 * $nitems } { incr i } {
243		error_check_good get_deleted($i) \
244		    [llength [$db get -txn $ctxn $keybase$i]] 0
245	}
246
247	puts "\t\tRecd$tnum.c.4: Verify cursor stability after abort."
248	error_check_good ctxn_abort [$ctxn abort] 0
249
250	# Verify that even items are deleted and odd items are not.
251	for { set i 1 } { $i <= 2 * $nitems } { incr i 2 } {
252		if { [is_rrecno $method] != 1 } {
253			set j $i
254		} else {
255			set j [expr ($i - 1) / 2 + 1]
256		}
257		error_check_good dbc_get($i):8 [$dbc($i) get -current] \
258		    [list [list $keybase$j [pad_data $method $i$alphabet]]]
259	}
260	for { set i 2 } { $i <= 2 * $nitems } { incr i 2 } {
261		error_check_good dbc_get($i):9 [$dbc($i) get -current] ""
262	}
263
264	# Clean up cursors.
265	for { set i 1 } { $i <= 2 * $nitems } { incr i } {
266		error_check_good dbc($i)_close [$dbc($i) close] 0
267	}
268
269	# Sync and verify.
270	error_check_good db_sync [$db sync] 0
271	error_check_good db_verify \
272	    [verify_dir $testdir "\t\tRecd$tnum.c.5: "] 0
273
274	puts "\tRecd$tnum.d: Clean up."
275	error_check_good txn_commit [$txn commit] 0
276	error_check_good db_close [$db close] 0
277	error_check_good log_flush [$env log_flush] 0
278	error_check_good env_close [$env close] 0
279	error_check_good verify_dir \
280	    [verify_dir $testdir "\t\tRecd$tnum.d.1: "] 0
281
282	if { $log_log_record_types == 1 } {
283		logtrack_read $testdir
284	}
285}
286