Fix for FETCH/NEXTKEY problem in all *DB*_File modules
[p5sagit/p5-mst-13.2.git] / ext / DB_File / t / db-hash.t
1 #!./perl -w
2
3 BEGIN {
4     chdir 't' if -d 't';
5     @INC = '../lib';
6     require Config; import Config;
7     if ($Config{'extensions'} !~ /\bDB_File\b/) {
8         print "1..0 # Skip: DB_File was not built\n";
9         exit 0;
10     }
11 }
12
13 use strict;
14 use warnings;
15 use DB_File; 
16 use Fcntl;
17
18 print "1..117\n";
19
20 sub ok
21 {
22     my $no = shift ;
23     my $result = shift ;
24  
25     print "not " unless $result ;
26     print "ok $no\n" ;
27 }
28
29 {
30     package Redirect ;
31     use Symbol ;
32
33     sub new
34     {
35         my $class = shift ;
36         my $filename = shift ;
37         my $fh = gensym ;
38         open ($fh, ">$filename") || die "Cannot open $filename: $!" ;
39         my $real_stdout = select($fh) ;
40         return bless [$fh, $real_stdout ] ;
41
42     }
43     sub DESTROY
44     {
45         my $self = shift ;
46         close $self->[0] ;
47         select($self->[1]) ;
48     }
49 }
50
51 sub docat_del
52
53     my $file = shift;
54     local $/ = undef;
55     open(CAT,$file) || die "Cannot open $file: $!";
56     my $result = <CAT>;
57     close(CAT);
58     unlink $file ;
59     return $result;
60 }   
61
62 my $Dfile = "dbhash.tmp";
63 my $null_keys_allowed = ($DB_File::db_ver < 2.004010 
64                                 || $DB_File::db_ver >= 3.1 );
65
66 unlink $Dfile;
67
68 umask(0);
69
70 # Check the interface to HASHINFO
71
72 my $dbh = new DB_File::HASHINFO ;
73
74 ok(1, ! defined $dbh->{bsize}) ;
75 ok(2, ! defined $dbh->{ffactor}) ;
76 ok(3, ! defined $dbh->{nelem}) ;
77 ok(4, ! defined $dbh->{cachesize}) ;
78 ok(5, ! defined $dbh->{hash}) ;
79 ok(6, ! defined $dbh->{lorder}) ;
80
81 $dbh->{bsize} = 3000 ;
82 ok(7, $dbh->{bsize} == 3000 );
83
84 $dbh->{ffactor} = 9000 ;
85 ok(8, $dbh->{ffactor} == 9000 );
86
87 $dbh->{nelem} = 400 ;
88 ok(9, $dbh->{nelem} == 400 );
89
90 $dbh->{cachesize} = 65 ;
91 ok(10, $dbh->{cachesize} == 65 );
92
93 $dbh->{hash} = "abc" ;
94 ok(11, $dbh->{hash} eq "abc" );
95
96 $dbh->{lorder} = 1234 ;
97 ok(12, $dbh->{lorder} == 1234 );
98
99 # Check that an invalid entry is caught both for store & fetch
100 eval '$dbh->{fred} = 1234' ;
101 ok(13, $@ =~ /^DB_File::HASHINFO::STORE - Unknown element 'fred' at/ );
102 eval 'my $q = $dbh->{fred}' ;
103 ok(14, $@ =~ /^DB_File::HASHINFO::FETCH - Unknown element 'fred' at/ );
104
105
106 # Now check the interface to HASH
107 my ($X, %h);
108 ok(15, $X = tie(%h, 'DB_File',$Dfile, O_RDWR|O_CREAT, 0640, $DB_HASH ) );
109
110 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
111    $blksize,$blocks) = stat($Dfile);
112 ok(16, ($mode & 0777) == (($^O eq 'os2' || $^O eq 'MacOS') ? 0666 : 0640) ||
113    $^O eq 'amigaos' || $^O eq 'MSWin32' || $^O eq 'NetWare');
114
115 my ($key, $value, $i);
116 while (($key,$value) = each(%h)) {
117     $i++;
118 }
119 ok(17, !$i );
120
121 $h{'goner1'} = 'snork';
122
123 $h{'abc'} = 'ABC';
124 ok(18, $h{'abc'} eq 'ABC' );
125 ok(19, !defined $h{'jimmy'} );
126 ok(20, !exists $h{'jimmy'} );
127 ok(21, exists $h{'abc'} );
128
129 $h{'def'} = 'DEF';
130 $h{'jkl','mno'} = "JKL\034MNO";
131 $h{'a',2,3,4,5} = join("\034",'A',2,3,4,5);
132 $h{'a'} = 'A';
133
134 #$h{'b'} = 'B';
135 $X->STORE('b', 'B') ;
136
137 $h{'c'} = 'C';
138
139 #$h{'d'} = 'D';
140 $X->put('d', 'D') ;
141
142 $h{'e'} = 'E';
143 $h{'f'} = 'F';
144 $h{'g'} = 'X';
145 $h{'h'} = 'H';
146 $h{'i'} = 'I';
147
148 $h{'goner2'} = 'snork';
149 delete $h{'goner2'};
150
151
152 # IMPORTANT - $X must be undefined before the untie otherwise the
153 #             underlying DB close routine will not get called.
154 undef $X ;
155 untie(%h);
156
157
158 # tie to the same file again, do not supply a type - should default to HASH
159 ok(22, $X = tie(%h,'DB_File',$Dfile, O_RDWR, 0640) );
160
161 # Modify an entry from the previous tie
162 $h{'g'} = 'G';
163
164 $h{'j'} = 'J';
165 $h{'k'} = 'K';
166 $h{'l'} = 'L';
167 $h{'m'} = 'M';
168 $h{'n'} = 'N';
169 $h{'o'} = 'O';
170 $h{'p'} = 'P';
171 $h{'q'} = 'Q';
172 $h{'r'} = 'R';
173 $h{'s'} = 'S';
174 $h{'t'} = 'T';
175 $h{'u'} = 'U';
176 $h{'v'} = 'V';
177 $h{'w'} = 'W';
178 $h{'x'} = 'X';
179 $h{'y'} = 'Y';
180 $h{'z'} = 'Z';
181
182 $h{'goner3'} = 'snork';
183
184 delete $h{'goner1'};
185 $X->DELETE('goner3');
186
187 my @keys = keys(%h);
188 my @values = values(%h);
189
190 ok(23, $#keys == 29 && $#values == 29) ;
191
192 $i = 0 ;
193 while (($key,$value) = each(%h)) {
194     if ($key eq $keys[$i] && $value eq $values[$i] && $key eq lc($value)) {
195         $key =~ y/a-z/A-Z/;
196         $i++ if $key eq $value;
197     }
198 }
199
200 ok(24, $i == 30) ;
201
202 @keys = ('blurfl', keys(%h), 'dyick');
203 ok(25, $#keys == 31) ;
204
205 $h{'foo'} = '';
206 ok(26, $h{'foo'} eq '' );
207
208 # Berkeley DB from version 2.4.10 to 3.0 does not allow null keys.
209 # This feature was reenabled in version 3.1 of Berkeley DB.
210 my $result = 0 ;
211 if ($null_keys_allowed) {
212     $h{''} = 'bar';
213     $result = ( $h{''} eq 'bar' );
214 }
215 else
216   { $result = 1 }
217 ok(27, $result) ;
218
219 # check cache overflow and numeric keys and contents
220 my $ok = 1;
221 for ($i = 1; $i < 200; $i++) { $h{$i + 0} = $i + 0; }
222 for ($i = 1; $i < 200; $i++) { $ok = 0 unless $h{$i} == $i; }
223 ok(28, $ok );
224
225 ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
226    $blksize,$blocks) = stat($Dfile);
227 ok(29, $size > 0 );
228
229 @h{0..200} = 200..400;
230 my @foo = @h{0..200};
231 ok(30, join(':',200..400) eq join(':',@foo) );
232
233
234 # Now check all the non-tie specific stuff
235
236 # Check NOOVERWRITE will make put fail when attempting to overwrite
237 # an existing record.
238  
239 my $status = $X->put( 'x', 'newvalue', R_NOOVERWRITE) ;
240 ok(31, $status == 1 );
241  
242 # check that the value of the key 'x' has not been changed by the 
243 # previous test
244 ok(32, $h{'x'} eq 'X' );
245
246 # standard put
247 $status = $X->put('key', 'value') ;
248 ok(33, $status == 0 );
249
250 #check that previous put can be retrieved
251 $value = 0 ;
252 $status = $X->get('key', $value) ;
253 ok(34, $status == 0 );
254 ok(35, $value eq 'value' );
255
256 # Attempting to delete an existing key should work
257
258 $status = $X->del('q') ;
259 ok(36, $status == 0 );
260
261 # Make sure that the key deleted, cannot be retrieved
262 {
263     no warnings 'uninitialized' ;
264     ok(37, $h{'q'} eq undef );
265 }
266
267 # Attempting to delete a non-existant key should fail
268
269 $status = $X->del('joe') ;
270 ok(38, $status == 1 );
271
272 # Check the get interface
273
274 # First a non-existing key
275 $status = $X->get('aaaa', $value) ;
276 ok(39, $status == 1 );
277
278 # Next an existing key
279 $status = $X->get('a', $value) ;
280 ok(40, $status == 0 );
281 ok(41, $value eq 'A' );
282
283 # seq
284 # ###
285
286 # ditto, but use put to replace the key/value pair.
287
288 # use seq to walk backwards through a file - check that this reversed is
289
290 # check seq FIRST/LAST
291
292 # sync
293 # ####
294
295 $status = $X->sync ;
296 ok(42, $status == 0 );
297
298
299 # fd
300 # ##
301
302 $status = $X->fd ;
303 ok(43, $status != 0 );
304
305 undef $X ;
306 untie %h ;
307
308 unlink $Dfile;
309
310 # clear
311 # #####
312
313 ok(44, tie(%h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_HASH ) );
314 foreach (1 .. 10)
315   { $h{$_} = $_ * 100 }
316
317 # check that there are 10 elements in the hash
318 $i = 0 ;
319 while (($key,$value) = each(%h)) {
320     $i++;
321 }
322 ok(45, $i == 10);
323
324 # now clear the hash
325 %h = () ;
326
327 # check it is empty
328 $i = 0 ;
329 while (($key,$value) = each(%h)) {
330     $i++;
331 }
332 ok(46, $i == 0);
333
334 untie %h ;
335 unlink $Dfile ;
336
337
338 # Now try an in memory file
339 ok(47, $X = tie(%h, 'DB_File',undef, O_RDWR|O_CREAT, 0640, $DB_HASH ) );
340
341 # fd with an in memory file should return fail
342 $status = $X->fd ;
343 ok(48, $status == -1 );
344
345 undef $X ;
346 untie %h ;
347
348 {
349     # check ability to override the default hashing
350     my %x ;
351     my $filename = "xyz" ;
352     my $hi = new DB_File::HASHINFO ;
353     $::count = 0 ;
354     $hi->{hash} = sub { ++$::count ; length $_[0] } ;
355     ok(49, tie %x, 'DB_File', $filename, O_RDWR|O_CREAT, 0640, $hi ) ;
356     $h{"abc"} = 123 ;
357     ok(50, $h{"abc"} == 123) ;
358     untie %x ;
359     unlink $filename ;
360     ok(51, $::count >0) ;
361 }
362
363 {
364     # check that attempting to tie an array to a DB_HASH will fail
365
366     my $filename = "xyz" ;
367     my @x ;
368     eval { tie @x, 'DB_File', $filename, O_RDWR|O_CREAT, 0640, $DB_HASH ; } ;
369     ok(52, $@ =~ /^DB_File can only tie an associative array to a DB_HASH database/) ;
370     unlink $filename ;
371 }
372
373 {
374    # sub-class test
375
376    package Another ;
377
378    use warnings ;
379    use strict ;
380
381    open(FILE, ">SubDB.pm") or die "Cannot open SubDB.pm: $!\n" ;
382    print FILE <<'EOM' ;
383
384    package SubDB ;
385
386    use warnings ;
387    use strict ;
388    use vars qw( @ISA @EXPORT) ;
389
390    require Exporter ;
391    use DB_File;
392    @ISA=qw(DB_File);
393    @EXPORT = @DB_File::EXPORT ;
394
395    sub STORE { 
396         my $self = shift ;
397         my $key = shift ;
398         my $value = shift ;
399         $self->SUPER::STORE($key, $value * 2) ;
400    }
401
402    sub FETCH { 
403         my $self = shift ;
404         my $key = shift ;
405         $self->SUPER::FETCH($key) - 1 ;
406    }
407
408    sub put { 
409         my $self = shift ;
410         my $key = shift ;
411         my $value = shift ;
412         $self->SUPER::put($key, $value * 3) ;
413    }
414
415    sub get { 
416         my $self = shift ;
417         $self->SUPER::get($_[0], $_[1]) ;
418         $_[1] -= 2 ;
419    }
420
421    sub A_new_method
422    {
423         my $self = shift ;
424         my $key = shift ;
425         my $value = $self->FETCH($key) ;
426         return "[[$value]]" ;
427    }
428
429    1 ;
430 EOM
431
432     close FILE ;
433
434     BEGIN { push @INC, '.'; }             
435     eval 'use SubDB ; ';
436     main::ok(53, $@ eq "") ;
437     my %h ;
438     my $X ;
439     eval '
440         $X = tie(%h, "SubDB","dbhash.tmp", O_RDWR|O_CREAT, 0640, $DB_HASH );
441         ' ;
442
443     main::ok(54, $@ eq "") ;
444
445     my $ret = eval '$h{"fred"} = 3 ; return $h{"fred"} ' ;
446     main::ok(55, $@ eq "") ;
447     main::ok(56, $ret == 5) ;
448
449     my $value = 0;
450     $ret = eval '$X->put("joe", 4) ; $X->get("joe", $value) ; return $value' ;
451     main::ok(57, $@ eq "") ;
452     main::ok(58, $ret == 10) ;
453
454     $ret = eval ' R_NEXT eq main::R_NEXT ' ;
455     main::ok(59, $@ eq "" ) ;
456     main::ok(60, $ret == 1) ;
457
458     $ret = eval '$X->A_new_method("joe") ' ;
459     main::ok(61, $@ eq "") ;
460     main::ok(62, $ret eq "[[11]]") ;
461
462     undef $X;
463     untie(%h);
464     unlink "SubDB.pm", "dbhash.tmp" ;
465
466 }
467
468 {
469    # DBM Filter tests
470    use warnings ;
471    use strict ;
472    my (%h, $db) ;
473    my ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
474    unlink $Dfile;
475
476    sub checkOutput
477    {
478        my($fk, $sk, $fv, $sv) = @_ ;
479        return
480            $fetch_key eq $fk && $store_key eq $sk && 
481            $fetch_value eq $fv && $store_value eq $sv &&
482            $_ eq 'original' ;
483    }
484    
485    ok(63, $db = tie(%h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_HASH ) );
486
487    $db->filter_fetch_key   (sub { $fetch_key = $_ }) ;
488    $db->filter_store_key   (sub { $store_key = $_ }) ;
489    $db->filter_fetch_value (sub { $fetch_value = $_}) ;
490    $db->filter_store_value (sub { $store_value = $_ }) ;
491
492    $_ = "original" ;
493
494    $h{"fred"} = "joe" ;
495    #                   fk   sk     fv   sv
496    ok(64, checkOutput( "", "fred", "", "joe")) ;
497
498    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
499    ok(65, $h{"fred"} eq "joe");
500    #                   fk    sk     fv    sv
501    ok(66, checkOutput( "", "fred", "joe", "")) ;
502
503    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
504    ok(67, $db->FIRSTKEY() eq "fred") ;
505    #                    fk     sk  fv  sv
506    ok(68, checkOutput( "fred", "", "", "")) ;
507
508    # replace the filters, but remember the previous set
509    my ($old_fk) = $db->filter_fetch_key   
510                         (sub { $_ = uc $_ ; $fetch_key = $_ }) ;
511    my ($old_sk) = $db->filter_store_key   
512                         (sub { $_ = lc $_ ; $store_key = $_ }) ;
513    my ($old_fv) = $db->filter_fetch_value 
514                         (sub { $_ = "[$_]"; $fetch_value = $_ }) ;
515    my ($old_sv) = $db->filter_store_value 
516                         (sub { s/o/x/g; $store_value = $_ }) ;
517    
518    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
519    $h{"Fred"} = "Joe" ;
520    #                   fk   sk     fv    sv
521    ok(69, checkOutput( "", "fred", "", "Jxe")) ;
522
523    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
524    ok(70, $h{"Fred"} eq "[Jxe]");
525    #                   fk   sk     fv    sv
526    ok(71, checkOutput( "", "fred", "[Jxe]", "")) ;
527
528    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
529    ok(72, $db->FIRSTKEY() eq "FRED") ;
530    #                   fk   sk     fv    sv
531    ok(73, checkOutput( "FRED", "", "", "")) ;
532
533    # put the original filters back
534    $db->filter_fetch_key   ($old_fk);
535    $db->filter_store_key   ($old_sk);
536    $db->filter_fetch_value ($old_fv);
537    $db->filter_store_value ($old_sv);
538
539    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
540    $h{"fred"} = "joe" ;
541    ok(74, checkOutput( "", "fred", "", "joe")) ;
542
543    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
544    ok(75, $h{"fred"} eq "joe");
545    ok(76, checkOutput( "", "fred", "joe", "")) ;
546
547    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
548    ok(77, $db->FIRSTKEY() eq "fred") ;
549    ok(78, checkOutput( "fred", "", "", "")) ;
550
551    # delete the filters
552    $db->filter_fetch_key   (undef);
553    $db->filter_store_key   (undef);
554    $db->filter_fetch_value (undef);
555    $db->filter_store_value (undef);
556
557    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
558    $h{"fred"} = "joe" ;
559    ok(79, checkOutput( "", "", "", "")) ;
560
561    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
562    ok(80, $h{"fred"} eq "joe");
563    ok(81, checkOutput( "", "", "", "")) ;
564
565    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
566    ok(82, $db->FIRSTKEY() eq "fred") ;
567    ok(83, checkOutput( "", "", "", "")) ;
568
569    undef $db ;
570    untie %h;
571    unlink $Dfile;
572 }
573
574 {    
575     # DBM Filter with a closure
576
577     use warnings ;
578     use strict ;
579     my (%h, $db) ;
580
581     unlink $Dfile;
582     ok(84, $db = tie(%h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_HASH ) );
583
584     my %result = () ;
585
586     sub Closure
587     {
588         my ($name) = @_ ;
589         my $count = 0 ;
590         my @kept = () ;
591
592         return sub { ++$count ; 
593                      push @kept, $_ ; 
594                      $result{$name} = "$name - $count: [@kept]" ;
595                    }
596     }
597
598     $db->filter_store_key(Closure("store key")) ;
599     $db->filter_store_value(Closure("store value")) ;
600     $db->filter_fetch_key(Closure("fetch key")) ;
601     $db->filter_fetch_value(Closure("fetch value")) ;
602
603     $_ = "original" ;
604
605     $h{"fred"} = "joe" ;
606     ok(85, $result{"store key"} eq "store key - 1: [fred]");
607     ok(86, $result{"store value"} eq "store value - 1: [joe]");
608     ok(87, ! defined $result{"fetch key"} );
609     ok(88, ! defined $result{"fetch value"} );
610     ok(89, $_ eq "original") ;
611
612     ok(90, $db->FIRSTKEY() eq "fred") ;
613     ok(91, $result{"store key"} eq "store key - 1: [fred]");
614     ok(92, $result{"store value"} eq "store value - 1: [joe]");
615     ok(93, $result{"fetch key"} eq "fetch key - 1: [fred]");
616     ok(94, ! defined $result{"fetch value"} );
617     ok(95, $_ eq "original") ;
618
619     $h{"jim"}  = "john" ;
620     ok(96, $result{"store key"} eq "store key - 2: [fred jim]");
621     ok(97, $result{"store value"} eq "store value - 2: [joe john]");
622     ok(98, $result{"fetch key"} eq "fetch key - 1: [fred]");
623     ok(99, ! defined $result{"fetch value"} );
624     ok(100, $_ eq "original") ;
625
626     ok(101, $h{"fred"} eq "joe");
627     ok(102, $result{"store key"} eq "store key - 3: [fred jim fred]");
628     ok(103, $result{"store value"} eq "store value - 2: [joe john]");
629     ok(104, $result{"fetch key"} eq "fetch key - 1: [fred]");
630     ok(105, $result{"fetch value"} eq "fetch value - 1: [joe]");
631     ok(106, $_ eq "original") ;
632
633     undef $db ;
634     untie %h;
635     unlink $Dfile;
636 }               
637
638 {
639    # DBM Filter recursion detection
640    use warnings ;
641    use strict ;
642    my (%h, $db) ;
643    unlink $Dfile;
644
645    ok(107, $db = tie(%h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_HASH ) );
646
647    $db->filter_store_key (sub { $_ = $h{$_} }) ;
648
649    eval '$h{1} = 1234' ;
650    ok(108, $@ =~ /^recursion detected in filter_store_key at/ );
651    
652    undef $db ;
653    untie %h;
654    unlink $Dfile;
655 }
656
657
658 {
659    # Examples from the POD
660
661   my $file = "xyzt" ;
662   {
663     my $redirect = new Redirect $file ;
664
665     use warnings FATAL => qw(all);
666     use strict ;
667     use DB_File ;
668     use vars qw( %h $k $v ) ;
669
670     unlink "fruit" ;
671     tie %h, "DB_File", "fruit", O_RDWR|O_CREAT, 0640, $DB_HASH 
672         or die "Cannot open file 'fruit': $!\n";
673
674     # Add a few key/value pairs to the file
675     $h{"apple"} = "red" ;
676     $h{"orange"} = "orange" ;
677     $h{"banana"} = "yellow" ;
678     $h{"tomato"} = "red" ;
679
680     # Check for existence of a key
681     print "Banana Exists\n\n" if $h{"banana"} ;
682
683     # Delete a key/value pair.
684     delete $h{"apple"} ;
685
686     # print the contents of the file
687     while (($k, $v) = each %h)
688       { print "$k -> $v\n" }
689
690     untie %h ;
691
692     unlink "fruit" ;
693   }  
694
695   ok(109, docat_del($file) eq <<'EOM') ;
696 Banana Exists
697
698 orange -> orange
699 tomato -> red
700 banana -> yellow
701 EOM
702    
703 }
704
705 {
706     # Bug ID 20001013.009
707     #
708     # test that $hash{KEY} = undef doesn't produce the warning
709     #     Use of uninitialized value in null operation 
710     use warnings ;
711     use strict ;
712     use DB_File ;
713
714     unlink $Dfile;
715     my %h ;
716     my $a = "";
717     local $SIG{__WARN__} = sub {$a = $_[0]} ;
718     
719     tie %h, 'DB_File', $Dfile or die "Can't open file: $!\n" ;
720     $h{ABC} = undef;
721     ok(110, $a eq "") ;
722     untie %h ;
723     unlink $Dfile;
724 }
725
726 {
727     # test that %hash = () doesn't produce the warning
728     #     Argument "" isn't numeric in entersub
729     use warnings ;
730     use strict ;
731     use DB_File ;
732
733     unlink $Dfile;
734     my %h ;
735     my $a = "";
736     local $SIG{__WARN__} = sub {$a = $_[0]} ;
737     
738     tie %h, 'DB_File', $Dfile or die "Can't open file: $!\n" ;
739     %h = (); ;
740     ok(111, $a eq "") ;
741     untie %h ;
742     unlink $Dfile;
743 }
744
745 {
746     # When iterating over a tied hash using "each", the key passed to FETCH
747     # will be recycled and passed to NEXTKEY. If a Source Filter modifies the
748     # key in FETCH via a filter_fetch_key method we need to check that the
749     # modified key doesn't get passed to NEXTKEY.
750     # Also Test "keys" & "values" while we are at it.
751
752     use warnings ;
753     use strict ;
754     use DB_File ;
755
756     unlink $Dfile;
757     my $bad_key = 0 ;
758     my %h = () ;
759     my $db ;
760     ok(112, $db = tie(%h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_HASH ) );
761     $db->filter_fetch_key (sub { $_ =~ s/^Beta_/Alpha_/ if defined $_}) ;
762     $db->filter_store_key (sub { $bad_key = 1 if /^Beta_/ ; $_ =~ s/^Alpha_/Beta_/}) ;
763
764     $h{'Alpha_ABC'} = 2 ;
765     $h{'Alpha_DEF'} = 5 ;
766
767     ok(113, $h{'Alpha_ABC'} == 2);
768     ok(114, $h{'Alpha_DEF'} == 5);
769
770     my ($k, $v) = ("","");
771     while (($k, $v) = each %h) {}
772     ok(115, $bad_key == 0);
773
774     $bad_key = 0 ;
775     foreach $k (keys %h) {}
776     ok(116, $bad_key == 0);
777
778     $bad_key = 0 ;
779     foreach $v (values %h) {}
780     ok(117, $bad_key == 0);
781
782     undef $db ;
783     untie %h ;
784     unlink $Dfile;
785 }
786
787 exit ;