1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 2003-2009 Oracle.  All rights reserved.
4#
5# $Id$
6#
7# TEST	test119
8# TEST	Test behavior when Berkeley DB returns DB_BUFFER_SMALL on a cursor.
9# TEST
10# TEST	If the user-supplied buffer is not large enough to contain
11# TEST	the returned value, DB returns BUFFER_SMALL.  If it does,
12# TEST	check that the cursor does not move -- if it moves, it will
13# TEST	skip items. [#13815]
14
15proc test119 { method {tnum "119"} args} {
16	source ./include.tcl
17	global alphabet
18	global errorCode
19
20	set args [convert_args $method $args]
21	set omethod [convert_method $method]
22	puts "Test$tnum: $method ($args) Test of DB_BUFFER_SMALL."
23
24	# Skip for queue; it has fixed-length records, so overflowing
25	# the buffer isn't possible with an ordinary get.
26	if { [is_queue $method] == 1 } {
27		puts "Skipping test$tnum for method $method"
28		return
29	}
30
31	# If we are using an env, then testfile should just be the db name.
32	# Otherwise it is the test directory and the name.
33	set txnenv 0
34	set txn ""
35	set eindex [lsearch -exact $args "-env"]
36	if { $eindex == -1 } {
37		set testfile $testdir/test$tnum.db
38		set env NULL
39	} else {
40		set testfile test$tnum.db
41		incr eindex
42		set env [lindex $args $eindex]
43		set txnenv [is_txnenv $env]
44		if { $txnenv == 1 } {
45			append args " -auto_commit "
46		}
47		set testdir [get_home $env]
48	}
49
50	cleanup $testdir $env
51
52	puts "\tTest$tnum.a: Set up database."
53	set db [eval \
54	    {berkdb_open_noerr -create -mode 0644} $args $omethod $testfile]
55	error_check_good dbopen [is_valid_db $db] TRUE
56
57	# Test -data_buf_size with db->get.
58	puts "\tTest$tnum.b: Test db get with -data_buf_size."
59	set datalength 20
60	set data [repeat "a" $datalength]
61	set key 1
62
63	if { $txnenv == 1 } {
64		set t [$env txn]
65		error_check_good txn [is_valid_txn $t $env] TRUE
66		set txn "-txn $t"
67	}
68
69	error_check_good db_put \
70	    [eval {$db put} $txn {$key [chop_data $method $data]}] 0
71
72	# A get with data_buf_size equal to the data size should work.
73	set ret [eval {$db get} $txn -data_buf_size $datalength $key]
74	error_check_good db_get_key [lindex [lindex $ret 0] 0] $key
75	error_check_good db_get_data [lindex [lindex $ret 0] 1] $data
76
77	# A get with a data_buf_size decreased by one should fail.
78	catch {eval {$db get}\
79	    $txn -data_buf_size [expr $datalength - 1] $key} res
80	error_check_good buffer_small_error [is_substr $res DB_BUFFER_SMALL] 1
81
82	# Delete the item so it won't get in the way of the cursor test.
83	error_check_good db_del [eval {$db del} $txn $key] 0
84	if { $txnenv == 1 } {
85		error_check_good txn_commit [$t commit] 0
86	}
87
88	# Test -data_buf_size and -key_buf_size with dbc->get.
89	#
90	# Set up a database that includes large and small keys and
91	# large and small data in various combinations.
92	#
93	# Create small buffer equal to the largest page size.  This will
94	# get DB_BUFFER_SMALL errors.
95	# Create big buffer large enough to never get DB_BUFFER_SMALL
96	# errors with this data set.
97
98	puts "\tTest$tnum.c:\
99	    Test cursor get with -data_buf_size and -key_buf_size."
100	set key $alphabet
101	set data $alphabet
102	set nentries 100
103	set start 100
104	set bigkey [repeat $key 8192]
105	set bigdata [repeat $data 8192]
106	set buffer [expr 64 * 1024]
107	set bigbuf [expr $buffer * 8]
108
109	puts "\tTest$tnum.c1: Populate database."
110	if { $txnenv == 1 } {
111		set t [$env txn]
112		error_check_good txn [is_valid_txn $t $env] TRUE
113		set txn "-txn $t"
114	}
115
116	# Put in a big key every X data items, and big data every
117	# Y data items.  X and Y should be small enough that we
118	# hit the case where both X and Y are big.
119	set x 5
120	set y 7
121	for { set i $start } { $i < [expr $nentries + $start] } { incr i } {
122		# If we have a record-based method, we can't have big keys.
123		# Just use the count.
124		if { [is_record_based $method] == 1 } {
125			set k $i
126		} else {
127			if { [expr $i % $x] == 1 } {
128				set k $i.$bigkey
129			} else {
130				set k $i.$key
131			}
132		}
133
134		# We can have big data on any method.
135		if { [expr $i % $y] == 1 } {
136			set d $i.$bigdata
137		} else {
138			set d $i.$data
139		}
140		error_check_good db_put \
141		    [eval {$db put} $txn {$k [chop_data $method $d]}] 0
142	}
143	if { $txnenv == 1 } {
144		error_check_good txn_commit [$t commit] 0
145	}
146
147	# Walk the database with a cursor.  When we hit DB_BUFFER_SMALL,
148	# make sure DB returns the appropriate key/data pair.
149	puts "\tTest$tnum.c2: Walk the database with a cursor."
150	if { $txnenv == 1 } {
151		set t [$env txn]
152		error_check_good txn [is_valid_txn $t $env] TRUE
153		set txn "-txn $t"
154	}
155	set curs [eval {$db cursor} $txn]
156	error_check_good cursor [is_valid_cursor $curs $db] TRUE
157
158	# Since hash is not sorted, we'll test that no items are
159	# skipped by keeping a list of all items retrieved, and
160	# making sure it is complete and that each item is unique
161	# at the end of the test.
162	set hashitems {}
163
164	set count $start
165	for { set kd [catch {eval $curs get \
166	    -key_buf_size $buffer -data_buf_size $buffer -first} res] } \
167	    { $count < [expr $nentries + $start] } \
168	    { set kd [catch {eval $curs get \
169	    -key_buf_size $buffer -data_buf_size $buffer -next} res] } {
170		if { $kd == 1 } {
171			# Make sure we have the expected error.
172			error_check_good buffer_small_error \
173			    [is_substr $errorCode DB_BUFFER_SMALL] 1
174
175			# Adjust the buffer sizes to fit the big key or data.
176			if { [expr $count % $x] == 1 } {
177				set key_buf $bigbuf
178			} else {
179				set key_buf $buffer
180			}
181			if { [expr $count % $y] == 1 } {
182				set data_buf $bigbuf
183			} else {
184				set data_buf $buffer
185			}
186
187			# Hash is not sorted, so just make sure we can get
188			# the item with a large buffer and check it later.
189			# Likewise for partition callback.
190			if { [is_hash $method] == 1  || \
191			    [is_partition_callback $args] == 1} {
192				set data_buf $bigbuf
193				set key_buf $bigbuf
194			}
195
196			# Retrieve with big buffer; there should be no error.
197			# This also walks the cursor forward.
198			set nextbig [catch {eval $curs get -key_buf_size \
199			    $key_buf -data_buf_size $data_buf -next} res]
200			error_check_good data_big_buffer_get $nextbig 0
201
202			# Extract the item number.
203			set key [lindex [lindex $res 0] 0]
204			set data [lindex [lindex $res 0] 1]
205			if { [string first . $key] != -1 } {
206				set keyindex [string first . $key]
207				set keynumber \
208				    [string range $key 0 [expr $keyindex - 1]]
209			} else {
210				set keynumber $key
211			}
212			set dataindex [string first . $data]
213			set datanumber \
214			    [string range $data 0 [expr $dataindex - 1]]
215
216			# If not hash, check that item number is correct.
217			# If hash, save the number for later verification.
218			if { [is_hash $method] == 0 \
219				&& [is_partition_callback $args] == 0 } {
220				error_check_good key_number $keynumber $count
221				error_check_good data_number $datanumber $count
222			} else {
223				lappend hashitems $keynumber
224			}
225		} else {
226			# For hash, save the item numbers of all items
227			# retrieved, not just those returning DB_BUFFER_SMALL.
228			if { [is_hash $method] == 1  || \
229			    [is_partition_callback $args] == 1} {
230				set key [lindex [lindex $res 0] 0]
231				set keyindex [string first . $key]
232				set keynumber \
233				    [string range $key 0 [expr $keyindex - 1]]
234				lappend hashitems $keynumber
235			}
236		}
237		incr count
238		set errorCode NONE
239	}
240	error_check_good curs_close [$curs close] 0
241	if { $txnenv == 1 } {
242		error_check_good txn [$t commit] 0
243	}
244
245	# Now check the list of items retrieved from hash.
246	if { [is_hash $method] == 1  || \
247	    [is_partition_callback $args] == 1} {
248		set sortedhashitems [lsort $hashitems]
249		for { set i $start } \
250		    { $i < [expr $nentries + $start] } { incr i } {
251			set hashitem \
252			    [lindex $sortedhashitems [expr $i - $start]]
253			error_check_good hash_check $hashitem $i
254		}
255	}
256	error_check_good db_close [$db close] 0
257}
258
259