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