xref: /openbsd-src/gnu/usr.bin/perl/cpan/DB_File/t/db-btree.t (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1#!./perl -w
2
3use warnings;
4use strict;
5use Config;
6
7BEGIN {
8    if(-d "lib" && -f "TEST") {
9        if ($Config{'extensions'} !~ /\bDB_File\b/ ) {
10            print "1..0 # Skip: DB_File was not built\n";
11            exit 0;
12        }
13    }
14}
15
16BEGIN
17{
18    if ($^O eq 'darwin'
19	&& (split(/\./, $Config{osvers}))[0] < 7 # Mac OS X 10.3 == Darwin 7
20	&& $Config{db_version_major} == 1
21	&& $Config{db_version_minor} == 0
22	&& $Config{db_version_patch} == 0) {
23	warn <<EOM;
24#
25# This test is known to crash in Mac OS X versions 10.2 (or earlier)
26# because of the buggy Berkeley DB version included with the OS.
27#
28EOM
29    }
30}
31
32use DB_File;
33use Fcntl;
34
35print "1..197\n";
36
37unlink glob "__db.*";
38
39sub ok
40{
41    my $no = shift ;
42    my $result = shift ;
43
44    print "not " unless $result ;
45    print "ok $no\n" ;
46}
47
48sub lexical
49{
50    my(@a) = unpack ("C*", $a) ;
51    my(@b) = unpack ("C*", $b) ;
52
53    my $len = (@a > @b ? @b : @a) ;
54    my $i = 0 ;
55
56    foreach $i ( 0 .. $len -1) {
57        return $a[$i] - $b[$i] if $a[$i] != $b[$i] ;
58    }
59
60    return @a - @b ;
61}
62
63{
64    package Redirect ;
65    use Symbol ;
66
67    sub new
68    {
69        my $class = shift ;
70        my $filename = shift ;
71	my $fh = gensym ;
72	open ($fh, ">$filename") || die "Cannot open $filename: $!" ;
73	my $real_stdout = select($fh) ;
74	return bless [$fh, $real_stdout ] ;
75
76    }
77    sub DESTROY
78    {
79        my $self = shift ;
80	close $self->[0] ;
81	select($self->[1]) ;
82    }
83}
84
85sub docat
86{
87    my $file = shift;
88    local $/ = undef ;
89    open(CAT,$file) || die "Cannot open $file: $!";
90    my $result = <CAT>;
91    close(CAT);
92    $result = normalise($result) ;
93    return $result ;
94}
95
96sub docat_del
97{
98    my $file = shift;
99    my $result = docat($file);
100    unlink $file ;
101    return $result ;
102}
103
104sub normalise
105{
106    my $data = shift ;
107    $data =~ s#\r\n#\n#g
108        if $^O eq 'cygwin' ;
109
110    return $data ;
111}
112
113sub safeUntie
114{
115    my $hashref = shift ;
116    my $no_inner = 1;
117    local $SIG{__WARN__} = sub {-- $no_inner } ;
118    untie %$hashref;
119    return $no_inner;
120}
121
122
123
124my $db185mode =  ($DB_File::db_version == 1 && ! $DB_File::db_185_compat) ;
125my $null_keys_allowed = ($DB_File::db_ver < 2.004010
126				|| $DB_File::db_ver >= 3.1 );
127
128my $Dfile = "dbbtree.tmp";
129unlink $Dfile;
130
131umask(0);
132
133# Check the interface to BTREEINFO
134
135my $dbh = new DB_File::BTREEINFO ;
136ok(1, ! defined $dbh->{flags}) ;
137ok(2, ! defined $dbh->{cachesize}) ;
138ok(3, ! defined $dbh->{psize}) ;
139ok(4, ! defined $dbh->{lorder}) ;
140ok(5, ! defined $dbh->{minkeypage}) ;
141ok(6, ! defined $dbh->{maxkeypage}) ;
142ok(7, ! defined $dbh->{compare}) ;
143ok(8, ! defined $dbh->{prefix}) ;
144
145$dbh->{flags} = 3000 ;
146ok(9, $dbh->{flags} == 3000) ;
147
148$dbh->{cachesize} = 9000 ;
149ok(10, $dbh->{cachesize} == 9000);
150
151$dbh->{psize} = 400 ;
152ok(11, $dbh->{psize} == 400) ;
153
154$dbh->{lorder} = 65 ;
155ok(12, $dbh->{lorder} == 65) ;
156
157$dbh->{minkeypage} = 123 ;
158ok(13, $dbh->{minkeypage} == 123) ;
159
160$dbh->{maxkeypage} = 1234 ;
161ok(14, $dbh->{maxkeypage} == 1234 );
162
163# Check that an invalid entry is caught both for store & fetch
164eval '$dbh->{fred} = 1234' ;
165ok(15, $@ =~ /^DB_File::BTREEINFO::STORE - Unknown element 'fred' at/ ) ;
166eval 'my $q = $dbh->{fred}' ;
167ok(16, $@ =~ /^DB_File::BTREEINFO::FETCH - Unknown element 'fred' at/ ) ;
168
169# Now check the interface to BTREE
170
171my ($X, %h) ;
172ok(17, $X = tie(%h, 'DB_File',$Dfile, O_RDWR|O_CREAT, 0640, $DB_BTREE )) ;
173die "Could not tie: $!" unless $X;
174
175my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
176   $blksize,$blocks) = stat($Dfile);
177
178my %noMode = map { $_, 1} qw( amigaos MSWin32 NetWare cygwin ) ;
179
180ok(18, ($mode & 0777) == (($^O eq 'os2' || $^O eq 'MacOS') ? 0666 : 0640)
181   || $noMode{$^O} );
182
183my ($key, $value, $i);
184while (($key,$value) = each(%h)) {
185    $i++;
186}
187ok(19, !$i ) ;
188
189$h{'goner1'} = 'snork';
190
191$h{'abc'} = 'ABC';
192ok(20, $h{'abc'} eq 'ABC' );
193ok(21, ! defined $h{'jimmy'} ) ;
194ok(22, ! exists $h{'jimmy'} ) ;
195ok(23,  defined $h{'abc'} ) ;
196
197$h{'def'} = 'DEF';
198$h{'jkl','mno'} = "JKL\034MNO";
199$h{'a',2,3,4,5} = join("\034",'A',2,3,4,5);
200$h{'a'} = 'A';
201
202#$h{'b'} = 'B';
203$X->STORE('b', 'B') ;
204
205$h{'c'} = 'C';
206
207#$h{'d'} = 'D';
208$X->put('d', 'D') ;
209
210$h{'e'} = 'E';
211$h{'f'} = 'F';
212$h{'g'} = 'X';
213$h{'h'} = 'H';
214$h{'i'} = 'I';
215
216$h{'goner2'} = 'snork';
217delete $h{'goner2'};
218
219
220# IMPORTANT - $X must be undefined before the untie otherwise the
221#             underlying DB close routine will not get called.
222undef $X ;
223untie(%h);
224
225# tie to the same file again
226ok(24, $X = tie(%h,'DB_File',$Dfile, O_RDWR, 0640, $DB_BTREE)) ;
227
228# Modify an entry from the previous tie
229$h{'g'} = 'G';
230
231$h{'j'} = 'J';
232$h{'k'} = 'K';
233$h{'l'} = 'L';
234$h{'m'} = 'M';
235$h{'n'} = 'N';
236$h{'o'} = 'O';
237$h{'p'} = 'P';
238$h{'q'} = 'Q';
239$h{'r'} = 'R';
240$h{'s'} = 'S';
241$h{'t'} = 'T';
242$h{'u'} = 'U';
243$h{'v'} = 'V';
244$h{'w'} = 'W';
245$h{'x'} = 'X';
246$h{'y'} = 'Y';
247$h{'z'} = 'Z';
248
249$h{'goner3'} = 'snork';
250
251delete $h{'goner1'};
252$X->DELETE('goner3');
253
254my @keys = keys(%h);
255my @values = values(%h);
256
257ok(25, $#keys == 29 && $#values == 29) ;
258
259$i = 0 ;
260while (($key,$value) = each(%h)) {
261    if ($key eq $keys[$i] && $value eq $values[$i] && $key eq lc($value)) {
262	$key =~ y/a-z/A-Z/;
263	$i++ if $key eq $value;
264    }
265}
266
267ok(26, $i == 30) ;
268
269@keys = ('blurfl', keys(%h), 'dyick');
270ok(27, $#keys == 31) ;
271
272#Check that the keys can be retrieved in order
273my @b = keys %h ;
274my @c = sort lexical @b ;
275ok(28, ArrayCompare(\@b, \@c)) ;
276
277$h{'foo'} = '';
278ok(29, $h{'foo'} eq '' ) ;
279
280# Berkeley DB from version 2.4.10 to 3.0 does not allow null keys.
281# This feature was reenabled in version 3.1 of Berkeley DB.
282my $result = 0 ;
283if ($null_keys_allowed) {
284    $h{''} = 'bar';
285    $result = ( $h{''} eq 'bar' );
286}
287else
288  { $result = 1 }
289ok(30, $result) ;
290
291# check cache overflow and numeric keys and contents
292my $ok = 1;
293for ($i = 1; $i < 200; $i++) { $h{$i + 0} = $i + 0; }
294for ($i = 1; $i < 200; $i++) { $ok = 0 unless $h{$i} == $i; }
295ok(31, $ok);
296
297($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
298   $blksize,$blocks) = stat($Dfile);
299ok(32, $size > 0 );
300
301@h{0..200} = 200..400;
302my @foo = @h{0..200};
303ok(33, join(':',200..400) eq join(':',@foo) );
304
305# Now check all the non-tie specific stuff
306
307
308# Check R_NOOVERWRITE flag will make put fail when attempting to overwrite
309# an existing record.
310
311my $status = $X->put( 'x', 'newvalue', R_NOOVERWRITE) ;
312ok(34, $status == 1 );
313
314# check that the value of the key 'x' has not been changed by the
315# previous test
316ok(35, $h{'x'} eq 'X' );
317
318# standard put
319$status = $X->put('key', 'value') ;
320ok(36, $status == 0 );
321
322#check that previous put can be retrieved
323$value = 0 ;
324$status = $X->get('key', $value) ;
325ok(37, $status == 0 );
326ok(38, $value eq 'value' );
327
328# Attempting to delete an existing key should work
329
330$status = $X->del('q') ;
331ok(39, $status == 0 );
332if ($null_keys_allowed) {
333    $status = $X->del('') ;
334} else {
335    $status = 0 ;
336}
337ok(40, $status == 0 );
338
339# Make sure that the key deleted, cannot be retrieved
340ok(41, ! defined $h{'q'}) ;
341ok(42, ! defined $h{''}) ;
342
343undef $X ;
344untie %h ;
345
346ok(43, $X = tie(%h, 'DB_File',$Dfile, O_RDWR, 0640, $DB_BTREE ));
347
348# Attempting to delete a non-existent key should fail
349
350$status = $X->del('joe') ;
351ok(44, $status == 1 );
352
353# Check the get interface
354
355# First a non-existing key
356$status = $X->get('aaaa', $value) ;
357ok(45, $status == 1 );
358
359# Next an existing key
360$status = $X->get('a', $value) ;
361ok(46, $status == 0 );
362ok(47, $value eq 'A' );
363
364# seq
365# ###
366
367# use seq to find an approximate match
368$key = 'ke' ;
369$value = '' ;
370$status = $X->seq($key, $value, R_CURSOR) ;
371ok(48, $status == 0 );
372ok(49, $key eq 'key' );
373ok(50, $value eq 'value' );
374
375# seq when the key does not match
376$key = 'zzz' ;
377$value = '' ;
378$status = $X->seq($key, $value, R_CURSOR) ;
379ok(51, $status == 1 );
380
381
382# use seq to set the cursor, then delete the record @ the cursor.
383
384$key = 'x' ;
385$value = '' ;
386$status = $X->seq($key, $value, R_CURSOR) ;
387ok(52, $status == 0 );
388ok(53, $key eq 'x' );
389ok(54, $value eq 'X' );
390$status = $X->del(0, R_CURSOR) ;
391ok(55, $status == 0 );
392$status = $X->get('x', $value) ;
393ok(56, $status == 1 );
394
395# ditto, but use put to replace the key/value pair.
396$key = 'y' ;
397$value = '' ;
398$status = $X->seq($key, $value, R_CURSOR) ;
399ok(57, $status == 0 );
400ok(58, $key eq 'y' );
401ok(59, $value eq 'Y' );
402
403$key = "replace key" ;
404$value = "replace value" ;
405$status = $X->put($key, $value, R_CURSOR) ;
406ok(60, $status == 0 );
407ok(61, $key eq 'replace key' );
408ok(62, $value eq 'replace value' );
409$status = $X->get('y', $value) ;
410ok(63, 1) ; # hard-wire to always pass. the previous test ($status == 1)
411	    # only worked because of a bug in 1.85/6
412
413# use seq to walk forwards through a file
414
415$status = $X->seq($key, $value, R_FIRST) ;
416ok(64, $status == 0 );
417my $previous = $key ;
418
419$ok = 1 ;
420while (($status = $X->seq($key, $value, R_NEXT)) == 0)
421{
422    ($ok = 0), last if ($previous cmp $key) == 1 ;
423}
424
425ok(65, $status == 1 );
426ok(66, $ok == 1 );
427
428# use seq to walk backwards through a file
429$status = $X->seq($key, $value, R_LAST) ;
430ok(67, $status == 0 );
431$previous = $key ;
432
433$ok = 1 ;
434while (($status = $X->seq($key, $value, R_PREV)) == 0)
435{
436    ($ok = 0), last if ($previous cmp $key) == -1 ;
437    #print "key = [$key] value = [$value]\n" ;
438}
439
440ok(68, $status == 1 );
441ok(69, $ok == 1 );
442
443
444# check seq FIRST/LAST
445
446# sync
447# ####
448
449$status = $X->sync ;
450ok(70, $status == 0 );
451
452
453# fd
454# ##
455
456$status = $X->fd ;
457ok(71, 1 );
458#ok(71, $status != 0 );
459
460
461undef $X ;
462untie %h ;
463
464unlink $Dfile;
465
466# Now try an in memory file
467my $Y;
468ok(72, $Y = tie(%h, 'DB_File',undef, O_RDWR|O_CREAT, 0640, $DB_BTREE ));
469
470# fd with an in memory file should return failure
471$status = $Y->fd ;
472ok(73, $status == -1 );
473
474
475undef $Y ;
476untie %h ;
477
478# Duplicate keys
479my $bt = new DB_File::BTREEINFO ;
480$bt->{flags} = R_DUP ;
481my ($YY, %hh);
482ok(74, $YY = tie(%hh, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $bt )) ;
483
484$hh{'Wall'} = 'Larry' ;
485$hh{'Wall'} = 'Stone' ; # Note the duplicate key
486$hh{'Wall'} = 'Brick' ; # Note the duplicate key
487$hh{'Wall'} = 'Brick' ; # Note the duplicate key and value
488$hh{'Smith'} = 'John' ;
489$hh{'mouse'} = 'mickey' ;
490
491# first work in scalar context
492ok(75, scalar $YY->get_dup('Unknown') == 0 );
493ok(76, scalar $YY->get_dup('Smith') == 1 );
494ok(77, scalar $YY->get_dup('Wall') == 4 );
495
496# now in list context
497my @unknown = $YY->get_dup('Unknown') ;
498ok(78, "@unknown" eq "" );
499
500my @smith = $YY->get_dup('Smith') ;
501ok(79, "@smith" eq "John" );
502
503{
504my @wall = $YY->get_dup('Wall') ;
505my %wall ;
506@wall{@wall} = @wall ;
507ok(80, (@wall == 4 && $wall{'Larry'} && $wall{'Stone'} && $wall{'Brick'}) );
508}
509
510# hash
511my %unknown = $YY->get_dup('Unknown', 1) ;
512ok(81, keys %unknown == 0 );
513
514my %smith = $YY->get_dup('Smith', 1) ;
515ok(82, keys %smith == 1 && $smith{'John'}) ;
516
517my %wall = $YY->get_dup('Wall', 1) ;
518ok(83, keys %wall == 3 && $wall{'Larry'} == 1 && $wall{'Stone'} == 1
519		&& $wall{'Brick'} == 2);
520
521undef $YY ;
522untie %hh ;
523unlink $Dfile;
524
525
526# test multiple callbacks
527my $Dfile1 = "btree1" ;
528my $Dfile2 = "btree2" ;
529my $Dfile3 = "btree3" ;
530
531my $dbh1 = new DB_File::BTREEINFO ;
532$dbh1->{compare} = sub {
533	no warnings 'numeric' ;
534	$_[0] <=> $_[1] } ;
535
536my $dbh2 = new DB_File::BTREEINFO ;
537$dbh2->{compare} = sub { $_[0] cmp $_[1] } ;
538
539my $dbh3 = new DB_File::BTREEINFO ;
540$dbh3->{compare} = sub { length $_[0] <=> length $_[1] } ;
541
542
543my (%g, %k);
544tie(%h, 'DB_File',$Dfile1, O_RDWR|O_CREAT, 0640, $dbh1 ) or die $!;
545tie(%g, 'DB_File',$Dfile2, O_RDWR|O_CREAT, 0640, $dbh2 ) or die $!;
546tie(%k, 'DB_File',$Dfile3, O_RDWR|O_CREAT, 0640, $dbh3 ) or die $!;
547
548my @Keys = qw( 0123 12 -1234 9 987654321 def  ) ;
549my (@srt_1, @srt_2, @srt_3);
550{
551  no warnings 'numeric' ;
552  @srt_1 = sort { $a <=> $b } @Keys ;
553}
554@srt_2 = sort { $a cmp $b } @Keys ;
555@srt_3 = sort { length $a <=> length $b } @Keys ;
556
557foreach (@Keys) {
558    $h{$_} = 1 ;
559    $g{$_} = 1 ;
560    $k{$_} = 1 ;
561}
562
563sub ArrayCompare
564{
565    my($a, $b) = @_ ;
566
567    return 0 if @$a != @$b ;
568
569    foreach (0 .. @$a - 1)
570    {
571        return 0 unless $$a[$_] eq $$b[$_];
572    }
573
574    1 ;
575}
576
577ok(84, ArrayCompare (\@srt_1, [keys %h]) );
578ok(85, ArrayCompare (\@srt_2, [keys %g]) );
579ok(86, ArrayCompare (\@srt_3, [keys %k]) );
580
581untie %h ;
582untie %g ;
583untie %k ;
584unlink $Dfile1, $Dfile2, $Dfile3 ;
585
586# clear
587# #####
588
589ok(87, tie(%h, 'DB_File', $Dfile1, O_RDWR|O_CREAT, 0640, $DB_BTREE ) );
590foreach (1 .. 10)
591  { $h{$_} = $_ * 100 }
592
593# check that there are 10 elements in the hash
594$i = 0 ;
595while (($key,$value) = each(%h)) {
596    $i++;
597}
598ok(88, $i == 10);
599
600# now clear the hash
601%h = () ;
602
603# check it is empty
604$i = 0 ;
605while (($key,$value) = each(%h)) {
606    $i++;
607}
608ok(89, $i == 0);
609
610untie %h ;
611unlink $Dfile1 ;
612
613{
614    # check that attempting to tie an array to a DB_BTREE will fail
615
616    my $filename = "xyz" ;
617    my @x ;
618    eval { tie @x, 'DB_File', $filename, O_RDWR|O_CREAT, 0640, $DB_BTREE ; } ;
619    ok(90, $@ =~ /^DB_File can only tie an associative array to a DB_BTREE database/) ;
620    unlink $filename ;
621}
622
623{
624   # sub-class test
625
626   package Another ;
627
628   use warnings ;
629   use strict ;
630
631   open(FILE, ">SubDB.pm") or die "Cannot open SubDB.pm: $!\n" ;
632   print FILE <<'EOM' ;
633
634   package SubDB ;
635
636   use warnings ;
637   use strict ;
638   our (@ISA, @EXPORT);
639
640   require Exporter ;
641   use DB_File;
642   @ISA=qw(DB_File);
643   @EXPORT = @DB_File::EXPORT ;
644
645   sub STORE {
646	my $self = shift ;
647        my $key = shift ;
648        my $value = shift ;
649        $self->SUPER::STORE($key, $value * 2) ;
650   }
651
652   sub FETCH {
653	my $self = shift ;
654        my $key = shift ;
655        $self->SUPER::FETCH($key) - 1 ;
656   }
657
658   sub put {
659	my $self = shift ;
660        my $key = shift ;
661        my $value = shift ;
662        $self->SUPER::put($key, $value * 3) ;
663   }
664
665   sub get {
666	my $self = shift ;
667        $self->SUPER::get($_[0], $_[1]) ;
668	$_[1] -= 2 ;
669   }
670
671   sub A_new_method
672   {
673	my $self = shift ;
674        my $key = shift ;
675        my $value = $self->FETCH($key) ;
676	return "[[$value]]" ;
677   }
678
679   1 ;
680EOM
681
682    close FILE ;
683
684    BEGIN { push @INC, '.'; }
685    eval 'use SubDB ; ';
686    main::ok(91, $@ eq "") ;
687    my %h ;
688    my $X ;
689    eval '
690	$X = tie(%h, "SubDB","dbbtree.tmp", O_RDWR|O_CREAT, 0640, $DB_BTREE );
691	' ;
692
693    main::ok(92, $@ eq "") ;
694
695    my $ret = eval '$h{"fred"} = 3 ; return $h{"fred"} ' ;
696    main::ok(93, $@ eq "") ;
697    main::ok(94, $ret == 5) ;
698
699    my $value = 0;
700    $ret = eval '$X->put("joe", 4) ; $X->get("joe", $value) ; return $value' ;
701    main::ok(95, $@ eq "") ;
702    main::ok(96, $ret == 10) ;
703
704    $ret = eval ' R_NEXT eq main::R_NEXT ' ;
705    main::ok(97, $@ eq "" ) ;
706    main::ok(98, $ret == 1) ;
707
708    $ret = eval '$X->A_new_method("joe") ' ;
709    main::ok(99, $@ eq "") ;
710    main::ok(100, $ret eq "[[11]]") ;
711
712    undef $X;
713    untie(%h);
714    unlink "SubDB.pm", "dbbtree.tmp" ;
715
716}
717
718{
719   # DBM Filter tests
720   use warnings ;
721   use strict ;
722   my (%h, $db) ;
723   my ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
724   unlink $Dfile;
725
726   sub checkOutput
727   {
728       my($fk, $sk, $fv, $sv) = @_ ;
729       return
730           $fetch_key eq $fk && $store_key eq $sk &&
731	   $fetch_value eq $fv && $store_value eq $sv &&
732	   $_ eq 'original' ;
733   }
734
735   ok(101, $db = tie(%h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_BTREE ) );
736
737   $db->filter_fetch_key   (sub { $fetch_key = $_ }) ;
738   $db->filter_store_key   (sub { $store_key = $_ }) ;
739   $db->filter_fetch_value (sub { $fetch_value = $_}) ;
740   $db->filter_store_value (sub { $store_value = $_ }) ;
741
742   $_ = "original" ;
743
744   $h{"fred"} = "joe" ;
745   #                   fk   sk     fv   sv
746   ok(102, checkOutput( "", "fred", "", "joe")) ;
747
748   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
749   ok(103, $h{"fred"} eq "joe");
750   #                   fk    sk     fv    sv
751   ok(104, checkOutput( "", "fred", "joe", "")) ;
752
753   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
754   ok(105, $db->FIRSTKEY() eq "fred") ;
755   #                    fk     sk  fv  sv
756   ok(106, checkOutput( "fred", "", "", "")) ;
757
758   # replace the filters, but remember the previous set
759   my ($old_fk) = $db->filter_fetch_key
760   			(sub { $_ = uc $_ ; $fetch_key = $_ }) ;
761   my ($old_sk) = $db->filter_store_key
762   			(sub { $_ = lc $_ ; $store_key = $_ }) ;
763   my ($old_fv) = $db->filter_fetch_value
764   			(sub { $_ = "[$_]"; $fetch_value = $_ }) ;
765   my ($old_sv) = $db->filter_store_value
766   			(sub { s/o/x/g; $store_value = $_ }) ;
767
768   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
769   $h{"Fred"} = "Joe" ;
770   #                   fk   sk     fv    sv
771   ok(107, checkOutput( "", "fred", "", "Jxe")) ;
772
773   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
774   ok(108, $h{"Fred"} eq "[Jxe]");
775   #                   fk   sk     fv    sv
776   ok(109, checkOutput( "", "fred", "[Jxe]", "")) ;
777
778   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
779   ok(110, $db->FIRSTKEY() eq "FRED") ;
780   #                   fk   sk     fv    sv
781   ok(111, checkOutput( "FRED", "", "", "")) ;
782
783   # put the original filters back
784   $db->filter_fetch_key   ($old_fk);
785   $db->filter_store_key   ($old_sk);
786   $db->filter_fetch_value ($old_fv);
787   $db->filter_store_value ($old_sv);
788
789   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
790   $h{"fred"} = "joe" ;
791   ok(112, checkOutput( "", "fred", "", "joe")) ;
792
793   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
794   ok(113, $h{"fred"} eq "joe");
795   ok(114, checkOutput( "", "fred", "joe", "")) ;
796
797   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
798   ok(115, $db->FIRSTKEY() eq "fred") ;
799   ok(116, checkOutput( "fred", "", "", "")) ;
800
801   # delete the filters
802   $db->filter_fetch_key   (undef);
803   $db->filter_store_key   (undef);
804   $db->filter_fetch_value (undef);
805   $db->filter_store_value (undef);
806
807   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
808   $h{"fred"} = "joe" ;
809   ok(117, checkOutput( "", "", "", "")) ;
810
811   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
812   ok(118, $h{"fred"} eq "joe");
813   ok(119, checkOutput( "", "", "", "")) ;
814
815   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
816   ok(120, $db->FIRSTKEY() eq "fred") ;
817   ok(121, checkOutput( "", "", "", "")) ;
818
819   undef $db ;
820   untie %h;
821   unlink $Dfile;
822}
823
824{
825    # DBM Filter with a closure
826
827    use warnings ;
828    use strict ;
829    my (%h, $db) ;
830
831    unlink $Dfile;
832    ok(122, $db = tie(%h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_BTREE ) );
833
834    my %result = () ;
835
836    sub Closure
837    {
838        my ($name) = @_ ;
839	my $count = 0 ;
840	my @kept = () ;
841
842	return sub { ++$count ;
843		     push @kept, $_ ;
844		     $result{$name} = "$name - $count: [@kept]" ;
845		   }
846    }
847
848    $db->filter_store_key(Closure("store key")) ;
849    $db->filter_store_value(Closure("store value")) ;
850    $db->filter_fetch_key(Closure("fetch key")) ;
851    $db->filter_fetch_value(Closure("fetch value")) ;
852
853    $_ = "original" ;
854
855    $h{"fred"} = "joe" ;
856    ok(123, $result{"store key"} eq "store key - 1: [fred]");
857    ok(124, $result{"store value"} eq "store value - 1: [joe]");
858    ok(125, ! defined $result{"fetch key"} );
859    ok(126, ! defined $result{"fetch value"} );
860    ok(127, $_ eq "original") ;
861
862    ok(128, $db->FIRSTKEY() eq "fred") ;
863    ok(129, $result{"store key"} eq "store key - 1: [fred]");
864    ok(130, $result{"store value"} eq "store value - 1: [joe]");
865    ok(131, $result{"fetch key"} eq "fetch key - 1: [fred]");
866    ok(132, ! defined $result{"fetch value"} );
867    ok(133, $_ eq "original") ;
868
869    $h{"jim"}  = "john" ;
870    ok(134, $result{"store key"} eq "store key - 2: [fred jim]");
871    ok(135, $result{"store value"} eq "store value - 2: [joe john]");
872    ok(136, $result{"fetch key"} eq "fetch key - 1: [fred]");
873    ok(137, ! defined $result{"fetch value"} );
874    ok(138, $_ eq "original") ;
875
876    ok(139, $h{"fred"} eq "joe");
877    ok(140, $result{"store key"} eq "store key - 3: [fred jim fred]");
878    ok(141, $result{"store value"} eq "store value - 2: [joe john]");
879    ok(142, $result{"fetch key"} eq "fetch key - 1: [fred]");
880    ok(143, $result{"fetch value"} eq "fetch value - 1: [joe]");
881    ok(144, $_ eq "original") ;
882
883    undef $db ;
884    untie %h;
885    unlink $Dfile;
886}
887
888{
889   # DBM Filter recursion detection
890   use warnings ;
891   use strict ;
892   my (%h, $db) ;
893   unlink $Dfile;
894
895   ok(145, $db = tie(%h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_BTREE ) );
896
897   $db->filter_store_key (sub { $_ = $h{$_} }) ;
898
899   eval '$h{1} = 1234' ;
900   ok(146, $@ =~ /^recursion detected in filter_store_key at/ );
901
902   undef $db ;
903   untie %h;
904   unlink $Dfile;
905}
906
907
908{
909   # Examples from the POD
910
911
912  my $file = "xyzt" ;
913  {
914    my $redirect = new Redirect $file ;
915
916    # BTREE example 1
917    ###
918
919    use warnings FATAL => qw(all) ;
920    use strict ;
921    use DB_File ;
922
923    my %h ;
924
925    sub Compare
926    {
927        my ($key1, $key2) = @_ ;
928        "\L$key1" cmp "\L$key2" ;
929    }
930
931    # specify the Perl sub that will do the comparison
932    $DB_BTREE->{'compare'} = \&Compare ;
933
934    unlink "tree" ;
935    tie %h, "DB_File", "tree", O_RDWR|O_CREAT, 0640, $DB_BTREE
936        or die "Cannot open file 'tree': $!\n" ;
937
938    # Add a key/value pair to the file
939    $h{'Wall'} = 'Larry' ;
940    $h{'Smith'} = 'John' ;
941    $h{'mouse'} = 'mickey' ;
942    $h{'duck'}  = 'donald' ;
943
944    # Delete
945    delete $h{"duck"} ;
946
947    # Cycle through the keys printing them in order.
948    # Note it is not necessary to sort the keys as
949    # the btree will have kept them in order automatically.
950    foreach (keys %h)
951      { print "$_\n" }
952
953    untie %h ;
954
955    unlink "tree" ;
956  }
957
958  delete $DB_BTREE->{'compare'} ;
959
960  ok(147, docat_del($file) eq <<'EOM') ;
961mouse
962Smith
963Wall
964EOM
965
966  {
967    my $redirect = new Redirect $file ;
968
969    # BTREE example 2
970    ###
971
972    use warnings FATAL => qw(all) ;
973    use strict ;
974    use DB_File ;
975
976    my ($filename, %h);
977
978    $filename = "tree" ;
979    unlink $filename ;
980
981    # Enable duplicate records
982    $DB_BTREE->{'flags'} = R_DUP ;
983
984    tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0640, $DB_BTREE
985	or die "Cannot open $filename: $!\n";
986
987    # Add some key/value pairs to the file
988    $h{'Wall'} = 'Larry' ;
989    $h{'Wall'} = 'Brick' ; # Note the duplicate key
990    $h{'Wall'} = 'Brick' ; # Note the duplicate key and value
991    $h{'Smith'} = 'John' ;
992    $h{'mouse'} = 'mickey' ;
993
994    # iterate through the associative array
995    # and print each key/value pair.
996    foreach (keys %h)
997      { print "$_	-> $h{$_}\n" }
998
999    untie %h ;
1000
1001    unlink $filename ;
1002  }
1003
1004  ok(148, docat_del($file) eq ($db185mode ? <<'EOM' : <<'EOM') ) ;
1005Smith	-> John
1006Wall	-> Brick
1007Wall	-> Brick
1008Wall	-> Brick
1009mouse	-> mickey
1010EOM
1011Smith	-> John
1012Wall	-> Larry
1013Wall	-> Larry
1014Wall	-> Larry
1015mouse	-> mickey
1016EOM
1017
1018  {
1019    my $redirect = new Redirect $file ;
1020
1021    # BTREE example 3
1022    ###
1023
1024    use warnings FATAL => qw(all) ;
1025    use strict ;
1026    use DB_File ;
1027
1028    my ($filename, $x, %h, $status, $key, $value);
1029
1030    $filename = "tree" ;
1031    unlink $filename ;
1032
1033    # Enable duplicate records
1034    $DB_BTREE->{'flags'} = R_DUP ;
1035
1036    $x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0640, $DB_BTREE
1037	or die "Cannot open $filename: $!\n";
1038
1039    # Add some key/value pairs to the file
1040    $h{'Wall'} = 'Larry' ;
1041    $h{'Wall'} = 'Brick' ; # Note the duplicate key
1042    $h{'Wall'} = 'Brick' ; # Note the duplicate key and value
1043    $h{'Smith'} = 'John' ;
1044    $h{'mouse'} = 'mickey' ;
1045
1046    # iterate through the btree using seq
1047    # and print each key/value pair.
1048    $key = $value = 0 ;
1049    for ($status = $x->seq($key, $value, R_FIRST) ;
1050         $status == 0 ;
1051         $status = $x->seq($key, $value, R_NEXT) )
1052      {  print "$key	-> $value\n" }
1053
1054
1055    undef $x ;
1056    untie %h ;
1057  }
1058
1059  ok(149, docat_del($file) eq ($db185mode == 1 ? <<'EOM' : <<'EOM') ) ;
1060Smith	-> John
1061Wall	-> Brick
1062Wall	-> Brick
1063Wall	-> Larry
1064mouse	-> mickey
1065EOM
1066Smith	-> John
1067Wall	-> Larry
1068Wall	-> Brick
1069Wall	-> Brick
1070mouse	-> mickey
1071EOM
1072
1073
1074  {
1075    my $redirect = new Redirect $file ;
1076
1077    # BTREE example 4
1078    ###
1079
1080    use warnings FATAL => qw(all) ;
1081    use strict ;
1082    use DB_File ;
1083
1084    my ($filename, $x, %h);
1085
1086    $filename = "tree" ;
1087
1088    # Enable duplicate records
1089    $DB_BTREE->{'flags'} = R_DUP ;
1090
1091    $x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0640, $DB_BTREE
1092	or die "Cannot open $filename: $!\n";
1093
1094    my $cnt  = $x->get_dup("Wall") ;
1095    print "Wall occurred $cnt times\n" ;
1096
1097    my %hash = $x->get_dup("Wall", 1) ;
1098    print "Larry is there\n" if $hash{'Larry'} ;
1099    print "There are $hash{'Brick'} Brick Walls\n" ;
1100
1101    my @list = sort $x->get_dup("Wall") ;
1102    print "Wall =>	[@list]\n" ;
1103
1104    @list = $x->get_dup("Smith") ;
1105    print "Smith =>	[@list]\n" ;
1106
1107    @list = $x->get_dup("Dog") ;
1108    print "Dog =>	[@list]\n" ;
1109
1110    undef $x ;
1111    untie %h ;
1112  }
1113
1114  ok(150, docat_del($file) eq <<'EOM') ;
1115Wall occurred 3 times
1116Larry is there
1117There are 2 Brick Walls
1118Wall =>	[Brick Brick Larry]
1119Smith =>	[John]
1120Dog =>	[]
1121EOM
1122
1123  {
1124    my $redirect = new Redirect $file ;
1125
1126    # BTREE example 5
1127    ###
1128
1129    use warnings FATAL => qw(all) ;
1130    use strict ;
1131    use DB_File ;
1132
1133    my ($filename, $x, %h, $found);
1134
1135    $filename = "tree" ;
1136
1137    # Enable duplicate records
1138    $DB_BTREE->{'flags'} = R_DUP ;
1139
1140    $x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0640, $DB_BTREE
1141	or die "Cannot open $filename: $!\n";
1142
1143    $found = ( $x->find_dup("Wall", "Larry") == 0 ? "" : "not") ;
1144    print "Larry Wall is $found there\n" ;
1145
1146    $found = ( $x->find_dup("Wall", "Harry") == 0 ? "" : "not") ;
1147    print "Harry Wall is $found there\n" ;
1148
1149    undef $x ;
1150    untie %h ;
1151  }
1152
1153  ok(151, docat_del($file) eq <<'EOM') ;
1154Larry Wall is  there
1155Harry Wall is not there
1156EOM
1157
1158  {
1159    my $redirect = new Redirect $file ;
1160
1161    # BTREE example 6
1162    ###
1163
1164    use warnings FATAL => qw(all) ;
1165    use strict ;
1166    use DB_File ;
1167
1168    my ($filename, $x, %h, $found);
1169
1170    $filename = "tree" ;
1171
1172    # Enable duplicate records
1173    $DB_BTREE->{'flags'} = R_DUP ;
1174
1175    $x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0640, $DB_BTREE
1176	or die "Cannot open $filename: $!\n";
1177
1178    $x->del_dup("Wall", "Larry") ;
1179
1180    $found = ( $x->find_dup("Wall", "Larry") == 0 ? "" : "not") ;
1181    print "Larry Wall is $found there\n" ;
1182
1183    undef $x ;
1184    untie %h ;
1185
1186    unlink $filename ;
1187  }
1188
1189  ok(152, docat_del($file) eq <<'EOM') ;
1190Larry Wall is not there
1191EOM
1192
1193  {
1194    my $redirect = new Redirect $file ;
1195
1196    # BTREE example 7
1197    ###
1198
1199    use warnings FATAL => qw(all) ;
1200    use strict ;
1201    use DB_File ;
1202    use Fcntl ;
1203
1204    my ($filename, $x, %h, $st, $key, $value);
1205
1206    sub match
1207    {
1208        my $key = shift ;
1209        my $value = 0;
1210        my $orig_key = $key ;
1211        $x->seq($key, $value, R_CURSOR) ;
1212        print "$orig_key\t-> $key\t-> $value\n" ;
1213    }
1214
1215    $filename = "tree" ;
1216    unlink $filename ;
1217
1218    $x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0640, $DB_BTREE
1219        or die "Cannot open $filename: $!\n";
1220
1221    # Add some key/value pairs to the file
1222    $h{'mouse'} = 'mickey' ;
1223    $h{'Wall'} = 'Larry' ;
1224    $h{'Walls'} = 'Brick' ;
1225    $h{'Smith'} = 'John' ;
1226
1227
1228    $key = $value = 0 ;
1229    print "IN ORDER\n" ;
1230    for ($st = $x->seq($key, $value, R_FIRST) ;
1231	 $st == 0 ;
1232         $st = $x->seq($key, $value, R_NEXT) )
1233
1234      {  print "$key	-> $value\n" }
1235
1236    print "\nPARTIAL MATCH\n" ;
1237
1238    match "Wa" ;
1239    match "A" ;
1240    match "a" ;
1241
1242    undef $x ;
1243    untie %h ;
1244
1245    unlink $filename ;
1246
1247  }
1248
1249  ok(153, docat_del($file) eq <<'EOM') ;
1250IN ORDER
1251Smith	-> John
1252Wall	-> Larry
1253Walls	-> Brick
1254mouse	-> mickey
1255
1256PARTIAL MATCH
1257Wa	-> Wall	-> Larry
1258A	-> Smith	-> John
1259a	-> mouse	-> mickey
1260EOM
1261
1262}
1263
1264#{
1265#   # R_SETCURSOR
1266#   use strict ;
1267#   my (%h, $db) ;
1268#   unlink $Dfile;
1269#
1270#   ok(156, $db = tie(%h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_BTREE ) );
1271#
1272#   $h{abc} = 33 ;
1273#   my $k = "newest" ;
1274#   my $v = 44 ;
1275#   my $status = $db->put($k, $v, R_SETCURSOR) ;
1276#   print "status = [$status]\n" ;
1277#   ok(157, $status == 0) ;
1278#   $status = $db->del($k, R_CURSOR) ;
1279#   print "status = [$status]\n" ;
1280#   ok(158, $status == 0) ;
1281#   $k = "newest" ;
1282#   ok(159, $db->get($k, $v, R_CURSOR)) ;
1283#
1284#   ok(160, keys %h == 1) ;
1285#
1286#   undef $db ;
1287#   untie %h;
1288#   unlink $Dfile;
1289#}
1290
1291{
1292    # Bug ID 20001013.009
1293    #
1294    # test that $hash{KEY} = undef doesn't produce the warning
1295    #     Use of uninitialized value in null operation
1296    use warnings ;
1297    use strict ;
1298    use DB_File ;
1299
1300    unlink $Dfile;
1301    my %h ;
1302    my $a = "";
1303    local $SIG{__WARN__} = sub {$a = $_[0]} ;
1304
1305    tie %h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0664, $DB_BTREE
1306	or die "Can't open file: $!\n" ;
1307    $h{ABC} = undef;
1308    ok(154, $a eq "") ;
1309    untie %h ;
1310    unlink $Dfile;
1311}
1312
1313{
1314    # test that %hash = () doesn't produce the warning
1315    #     Argument "" isn't numeric in entersub
1316    use warnings ;
1317    use strict ;
1318    use DB_File ;
1319
1320    unlink $Dfile;
1321    my %h ;
1322    my $a = "";
1323    local $SIG{__WARN__} = sub {$a = $_[0]} ;
1324
1325    tie %h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0664, $DB_BTREE
1326	or die "Can't open file: $!\n" ;
1327    %h = (); ;
1328    ok(155, $a eq "") ;
1329    untie %h ;
1330    unlink $Dfile;
1331}
1332
1333{
1334    # When iterating over a tied hash using "each", the key passed to FETCH
1335    # will be recycled and passed to NEXTKEY. If a Source Filter modifies the
1336    # key in FETCH via a filter_fetch_key method we need to check that the
1337    # modified key doesn't get passed to NEXTKEY.
1338    # Also Test "keys" & "values" while we are at it.
1339
1340    use warnings ;
1341    use strict ;
1342    use DB_File ;
1343
1344    unlink $Dfile;
1345    my $bad_key = 0 ;
1346    my %h = () ;
1347    my $db ;
1348    ok(156, $db = tie(%h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_BTREE ) );
1349    $db->filter_fetch_key (sub { $_ =~ s/^Beta_/Alpha_/ if defined $_}) ;
1350    $db->filter_store_key (sub { $bad_key = 1 if /^Beta_/ ; $_ =~ s/^Alpha_/Beta_/}) ;
1351
1352    $h{'Alpha_ABC'} = 2 ;
1353    $h{'Alpha_DEF'} = 5 ;
1354
1355    ok(157, $h{'Alpha_ABC'} == 2);
1356    ok(158, $h{'Alpha_DEF'} == 5);
1357
1358    my ($k, $v) = ("","");
1359    while (($k, $v) = each %h) {}
1360    ok(159, $bad_key == 0);
1361
1362    $bad_key = 0 ;
1363    foreach $k (keys %h) {}
1364    ok(160, $bad_key == 0);
1365
1366    $bad_key = 0 ;
1367    foreach $v (values %h) {}
1368    ok(161, $bad_key == 0);
1369
1370    undef $db ;
1371    untie %h ;
1372    unlink $Dfile;
1373}
1374
1375{
1376    # now an error to pass 'compare' a non-code reference
1377    my $dbh = new DB_File::BTREEINFO ;
1378
1379    eval { $dbh->{compare} = 2 };
1380    ok(162, $@ =~ /^Key 'compare' not associated with a code reference at/);
1381
1382    eval { $dbh->{prefix} = 2 };
1383    ok(163, $@ =~ /^Key 'prefix' not associated with a code reference at/);
1384
1385}
1386
1387
1388#{
1389#    # recursion detection in btree
1390#    my %hash ;
1391#    unlink $Dfile;
1392#    my $dbh = new DB_File::BTREEINFO ;
1393#    $dbh->{compare} = sub { $hash{3} = 4 ; length $_[0] } ;
1394#
1395#
1396#    my (%h);
1397#    ok(164, tie(%hash, 'DB_File',$Dfile, O_RDWR|O_CREAT, 0640, $dbh ) );
1398#
1399#    eval {	$hash{1} = 2;
1400#    		$hash{4} = 5;
1401#	 };
1402#
1403#    ok(165, $@ =~ /^DB_File btree_compare: recursion detected/);
1404#    {
1405#        no warnings;
1406#        untie %hash;
1407#    }
1408#    unlink $Dfile;
1409#}
1410ok(164,1);
1411ok(165,1);
1412
1413{
1414    # Check that two callbacks don't interact
1415    my %hash1 ;
1416    my %hash2 ;
1417    my $h1_count = 0;
1418    my $h2_count = 0;
1419    unlink $Dfile, $Dfile2;
1420    my $dbh1 = new DB_File::BTREEINFO ;
1421    $dbh1->{compare} = sub { ++ $h1_count ; $_[0] cmp $_[1] } ;
1422
1423    my $dbh2 = new DB_File::BTREEINFO ;
1424    $dbh2->{compare} = sub { ;++ $h2_count ; $_[0] cmp $_[1] } ;
1425
1426
1427
1428    my (%h);
1429    ok(166, tie(%hash1, 'DB_File',$Dfile, O_RDWR|O_CREAT, 0640, $dbh1 ) );
1430    ok(167, tie(%hash2, 'DB_File',$Dfile2, O_RDWR|O_CREAT, 0640, $dbh2 ) );
1431
1432    $hash1{DEFG} = 5;
1433    $hash1{XYZ} = 2;
1434    $hash1{ABCDE} = 5;
1435
1436    $hash2{defg} = 5;
1437    $hash2{xyz} = 2;
1438    $hash2{abcde} = 5;
1439
1440    ok(168, $h1_count > 0);
1441    ok(169, $h1_count == $h2_count);
1442
1443    ok(170, safeUntie \%hash1);
1444    ok(171, safeUntie \%hash2);
1445    unlink $Dfile, $Dfile2;
1446}
1447
1448{
1449   # Check that DBM Filter can cope with read-only $_
1450
1451   use warnings ;
1452   use strict ;
1453   my (%h, $db) ;
1454   unlink $Dfile;
1455
1456   ok(172, $db = tie(%h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_BTREE ) );
1457
1458   $db->filter_fetch_key   (sub { }) ;
1459   $db->filter_store_key   (sub { }) ;
1460   $db->filter_fetch_value (sub { }) ;
1461   $db->filter_store_value (sub { }) ;
1462
1463   $_ = "original" ;
1464
1465   $h{"fred"} = "joe" ;
1466   ok(173, $h{"fred"} eq "joe");
1467
1468   eval { my @r= grep { $h{$_} } (1, 2, 3) };
1469   ok (174, ! $@);
1470
1471
1472   # delete the filters
1473   $db->filter_fetch_key   (undef);
1474   $db->filter_store_key   (undef);
1475   $db->filter_fetch_value (undef);
1476   $db->filter_store_value (undef);
1477
1478   $h{"fred"} = "joe" ;
1479
1480   ok(175, $h{"fred"} eq "joe");
1481
1482   ok(176, $db->FIRSTKEY() eq "fred") ;
1483
1484   eval { my @r= grep { $h{$_} } (1, 2, 3) };
1485   ok (177, ! $@);
1486
1487   undef $db ;
1488   untie %h;
1489   unlink $Dfile;
1490}
1491
1492{
1493   # Check low-level API works with filter
1494
1495   use warnings ;
1496   use strict ;
1497   my (%h, $db) ;
1498   my $Dfile = "xxy.db";
1499   unlink $Dfile;
1500
1501   ok(178, $db = tie(%h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_BTREE ) );
1502
1503
1504   $db->filter_fetch_key   (sub { $_ = unpack("i", $_) } );
1505   $db->filter_store_key   (sub { $_ = pack("i", $_) } );
1506   $db->filter_fetch_value (sub { $_ = unpack("i", $_) } );
1507   $db->filter_store_value (sub { $_ = pack("i", $_) } );
1508
1509   $_ = 'fred';
1510
1511   my $key = 22 ;
1512   my $value = 34 ;
1513
1514   $db->put($key, $value) ;
1515   ok 179, $key == 22;
1516   ok 180, $value == 34 ;
1517   ok 181, $_ eq 'fred';
1518   #print "k [$key][$value]\n" ;
1519
1520   my $val ;
1521   $db->get($key, $val) ;
1522   ok 182, $key == 22;
1523   ok 183, $val == 34 ;
1524   ok 184, $_ eq 'fred';
1525
1526   $key = 51 ;
1527   $value = 454;
1528   $h{$key} = $value ;
1529   ok 185, $key == 51;
1530   ok 186, $value == 454 ;
1531   ok 187, $_ eq 'fred';
1532
1533   undef $db ;
1534   untie %h;
1535   unlink $Dfile;
1536}
1537
1538
1539
1540{
1541    # Regression Test for bug 30237
1542    # Check that substr can be used in the key to db_put
1543    # and that db_put does not trigger the warning
1544    #
1545    #     Use of uninitialized value in subroutine entry
1546
1547
1548    use warnings ;
1549    use strict ;
1550    my (%h, $db) ;
1551    my $Dfile = "xxy.db";
1552    unlink $Dfile;
1553
1554    ok(188, $db = tie(%h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_BTREE ));
1555
1556    my $warned = '';
1557    local $SIG{__WARN__} = sub {$warned = $_[0]} ;
1558
1559    # db-put with substr of key
1560    my %remember = () ;
1561    for my $ix ( 10 .. 12 )
1562    {
1563        my $key = $ix . "data" ;
1564        my $value = "value$ix" ;
1565        $remember{$key} = $value ;
1566        $db->put(substr($key,0), $value) ;
1567    }
1568
1569    ok 189, $warned eq ''
1570      or print "# Caught warning [$warned]\n" ;
1571
1572    # db-put with substr of value
1573    $warned = '';
1574    for my $ix ( 20 .. 22 )
1575    {
1576        my $key = $ix . "data" ;
1577        my $value = "value$ix" ;
1578        $remember{$key} = $value ;
1579        $db->put($key, substr($value,0)) ;
1580    }
1581
1582    ok 190, $warned eq ''
1583      or print "# Caught warning [$warned]\n" ;
1584
1585    # via the tied hash is not a problem, but check anyway
1586    # substr of key
1587    $warned = '';
1588    for my $ix ( 30 .. 32 )
1589    {
1590        my $key = $ix . "data" ;
1591        my $value = "value$ix" ;
1592        $remember{$key} = $value ;
1593        $h{substr($key,0)} = $value ;
1594    }
1595
1596    ok 191, $warned eq ''
1597      or print "# Caught warning [$warned]\n" ;
1598
1599    # via the tied hash is not a problem, but check anyway
1600    # substr of value
1601    $warned = '';
1602    for my $ix ( 40 .. 42 )
1603    {
1604        my $key = $ix . "data" ;
1605        my $value = "value$ix" ;
1606        $remember{$key} = $value ;
1607        $h{$key} = substr($value,0) ;
1608    }
1609
1610    ok 192, $warned eq ''
1611      or print "# Caught warning [$warned]\n" ;
1612
1613    my %bad = () ;
1614    $key = '';
1615    for ($status = $db->seq($key, $value, R_FIRST ) ;
1616         $status == 0 ;
1617         $status = $db->seq($key, $value, R_NEXT ) ) {
1618
1619        #print "# key [$key] value [$value]\n" ;
1620        if (defined $remember{$key} && defined $value &&
1621             $remember{$key} eq $value) {
1622            delete $remember{$key} ;
1623        }
1624        else {
1625            $bad{$key} = $value ;
1626        }
1627    }
1628
1629    ok 193, keys %bad == 0 ;
1630    ok 194, keys %remember == 0 ;
1631
1632    print "# missing -- $key $value\n" while ($key, $value) = each %remember;
1633    print "# bad     -- $key $value\n" while ($key, $value) = each %bad;
1634
1635    # Make sure this fix does not break code to handle an undef key
1636    # Berkeley DB undef key is bron between versions 2.3.16 and
1637    my $value = 'fred';
1638    $warned = '';
1639    $db->put(undef, $value) ;
1640    ok 195, $warned eq ''
1641      or print "# Caught warning [$warned]\n" ;
1642    $warned = '';
1643
1644    my $no_NULL = ($DB_File::db_ver >= 2.003016 && $DB_File::db_ver < 3.001) ;
1645    print "# db_ver $DB_File::db_ver\n";
1646    $value = '' ;
1647    $db->get(undef, $value) ;
1648    ok 196, $no_NULL || $value eq 'fred' or print "# got [$value]\n" ;
1649    ok 197, $warned eq ''
1650      or print "# Caught warning [$warned]\n" ;
1651    $warned = '';
1652
1653    undef $db ;
1654    untie %h;
1655    unlink $Dfile;
1656}
1657exit ;
1658