1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 1996,2008 Oracle.  All rights reserved.
4#
5# $Id: lock005.tcl,v 12.6 2008/01/08 20:58:53 bostic Exp $
6#
7# TEST lock005
8# TEST Check that page locks are being released properly.
9
10proc lock005 { } {
11	source ./include.tcl
12
13	puts "Lock005: Page lock release test"
14
15	# Clean up after previous runs
16	env_cleanup $testdir
17
18	# Open/create the lock region
19	set e [berkdb_env -create -lock -home $testdir -txn -log]
20	error_check_good env_open [is_valid_env $e] TRUE
21
22	# Open/create the database
23	set db [berkdb open -create -auto_commit -env $e -len 10 -queue q.db]
24	error_check_good dbopen [is_valid_db $db] TRUE
25
26	# Check that records are locking by trying to
27	# fetch a record on the wrong transaction.
28	puts "\tLock005.a: Verify that we are locking"
29
30	# Start the first transaction
31	set txn1 [$e txn -nowait]
32	error_check_good txn_begin [is_valid_txn $txn1 $e] TRUE
33	set ret [catch {$db put -txn $txn1 -append record1} recno1]
34	error_check_good dbput_txn1 $ret 0
35
36	# Start second txn while the first is still running ...
37	set txn2 [$e txn -nowait]
38	error_check_good txn_begin [is_valid_txn $txn2 $e] TRUE
39
40	# ... and try to get a record from the first txn (should fail)
41	set ret [catch {$db get -txn $txn2 $recno1} res]
42	error_check_good dbget_wrong_record \
43	    [is_substr $res "deadlock"] 1
44
45	# End transactions
46	error_check_good txn1commit [$txn1 commit] 0
47	how_many_locks 1 $e
48	error_check_good txn2commit [$txn2 commit] 0
49	# The number of locks stays the same here because the first
50	# lock is released and the second lock was never granted.
51	how_many_locks 1 $e
52
53	# Test lock behavior for both abort and commit
54	puts "\tLock005.b: Verify locks after abort or commit"
55	foreach endorder {forward reverse} {
56		end_order_test $db $e commit abort $endorder
57		end_order_test $db $e abort commit $endorder
58		end_order_test $db $e commit commit $endorder
59		end_order_test $db $e abort abort $endorder
60	}
61
62	# Clean up
63	error_check_good db_close [$db close] 0
64	error_check_good env_close [$e close] 0
65}
66
67proc end_order_test { db e txn1end txn2end endorder } {
68	# Start one transaction
69	set txn1 [$e txn -nowait]
70	error_check_good txn_begin [is_valid_txn $txn1 $e] TRUE
71	set ret [catch {$db put -txn $txn1 -append record1} recno1]
72	error_check_good dbput_txn1 $ret 0
73
74	# Check number of locks
75	how_many_locks 2 $e
76
77	# Start a second transaction while first is still running
78	set txn2 [$e txn -nowait]
79	error_check_good txn_begin [is_valid_txn $txn2 $e] TRUE
80	set ret [catch {$db put -txn $txn2 -append record2} recno2]
81	error_check_good dbput_txn2 $ret 0
82	how_many_locks 3 $e
83
84	# Now commit or abort one txn and make sure the other is okay
85	if {$endorder == "forward"} {
86		# End transaction 1 first
87		puts "\tLock005.b.1: $txn1end txn1 then $txn2end txn2"
88		error_check_good txn_$txn1end [$txn1 $txn1end] 0
89		how_many_locks 2 $e
90
91		# txn1 is now ended, but txn2 is still running
92		set ret1 [catch {$db get -txn $txn2 $recno1} res1]
93		set ret2 [catch {$db get -txn $txn2 $recno2} res2]
94		if { $txn1end == "commit" } {
95			error_check_good txn2_sees_txn1 $ret1 0
96			error_check_good txn2_sees_txn2 $ret2 0
97		} else {
98			# transaction 1 was aborted
99			error_check_good txn2_cantsee_txn1 [llength $res1] 0
100		}
101
102		# End transaction 2 second
103		error_check_good txn_$txn2end [$txn2 $txn2end] 0
104		how_many_locks 1 $e
105
106		# txn1 and txn2 should both now be invalid
107		# The get no longer needs to be transactional
108		set ret3 [catch {$db get $recno1} res3]
109		set ret4 [catch {$db get $recno2} res4]
110
111		if { $txn2end == "commit" } {
112			error_check_good txn2_sees_txn1 $ret3 0
113			error_check_good txn2_sees_txn2 $ret4 0
114			error_check_good txn2_has_record2 \
115			    [is_substr $res4 "record2"] 1
116		} else {
117			# transaction 2 was aborted
118			error_check_good txn2_cantsee_txn1 $ret3 0
119			error_check_good txn2_aborted [llength $res4] 0
120		}
121
122	} elseif { $endorder == "reverse" } {
123		# End transaction 2 first
124		puts "\tLock005.b.2: $txn2end txn2 then $txn1end txn1"
125		error_check_good txn_$txn2end [$txn2 $txn2end] 0
126		how_many_locks 2 $e
127
128		# txn2 is ended, but txn1 is still running
129		set ret1 [catch {$db get -txn $txn1 $recno1} res1]
130		set ret2 [catch {$db get -txn $txn1 $recno2} res2]
131		if { $txn2end == "commit" } {
132			error_check_good txn1_sees_txn1 $ret1 0
133			error_check_good txn1_sees_txn2 $ret2 0
134		} else {
135			# transaction 2 was aborted
136			error_check_good txn1_cantsee_txn2 [llength $res2] 0
137		}
138
139		# End transaction 1 second
140		error_check_good txn_$txn1end [$txn1 $txn1end] 0
141		how_many_locks 1 $e
142
143		# txn1 and txn2 should both now be invalid
144		# The get no longer needs to be transactional
145		set ret3 [catch {$db get $recno1} res3]
146		set ret4 [catch {$db get $recno2} res4]
147
148		if { $txn1end == "commit" } {
149			error_check_good txn1_sees_txn1 $ret3 0
150			error_check_good txn1_sees_txn2 $ret4 0
151			error_check_good txn1_has_record1 \
152			    [is_substr $res3 "record1"] 1
153		} else {
154			# transaction 1 was aborted
155			error_check_good txn1_cantsee_txn2 $ret4 0
156			error_check_good txn1_aborted [llength $res3] 0
157		}
158	}
159}
160
161proc how_many_locks { expected env } {
162	set stat [$env lock_stat]
163	set str "Current number of locks"
164	set checked 0
165	foreach statpair $stat {
166		if { $checked == 1 } {
167			break
168		}
169		if { [is_substr [lindex $statpair 0] $str] != 0} {
170			set checked 1
171			set nlocks [lindex $statpair 1]
172			error_check_good expected_nlocks $nlocks $expected
173		}
174	}
175	error_check_good checked $checked 1
176}
177