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