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