From f6b705ef90786c91033644c26163b35ad705ea2e Mon Sep 17 00:00:00 2001 From: Perl 5 Porters Date: Sat, 7 Sep 1996 03:18:27 +0000 Subject: [PATCH] Update to DB_File 1.03. --- ext/DB_File/DB_File.pm | 870 ++++++++++++++++++++++++++++++++++++++++--------- ext/DB_File/DB_File.xs | 39 ++- t/lib/db-btree.t | 245 ++++++++------ t/lib/db-hash.t | 145 ++++++--- t/lib/db-recno.t | 78 +++-- 5 files changed, 1035 insertions(+), 342 deletions(-) diff --git a/ext/DB_File/DB_File.pm b/ext/DB_File/DB_File.pm index 61cd138..8f3dd96 100644 --- a/ext/DB_File/DB_File.pm +++ b/ext/DB_File/DB_File.pm @@ -1,8 +1,8 @@ # DB_File.pm -- Perl 5 interface to Berkeley DB # # written by Paul Marquess (pmarquess@bfsec.bt.co.uk) -# last modified 28th June 1996 -# version 1.02 +# last modified 4th Sept 1996 +# version 1.03 package DB_File::HASHINFO ; @@ -126,7 +126,7 @@ sub TIEHASH { my $pkg = shift ; - bless { 'flags' => undef, + bless { 'flags' => undef, 'cachesize' => undef, 'maxkeypage' => undef, 'minkeypage' => undef, @@ -145,7 +145,7 @@ use vars qw($VERSION @ISA @EXPORT $AUTOLOAD $DB_BTREE $DB_HASH $DB_RECNO) ; use Carp; -$VERSION = "1.02" ; +$VERSION = "1.03" ; #typedef enum { DB_BTREE, DB_HASH, DB_RECNO } DBTYPE; #$DB_BTREE = TIEHASH DB_File::BTREEINFO ; @@ -214,6 +214,19 @@ sub AUTOLOAD { goto &$AUTOLOAD; } + +# import borrowed from IO::File +# exports Fcntl constants if available. +sub import { + my $pkg = shift; + my $callpkg = caller; + Exporter::export $pkg, $callpkg; + eval { + require Fcntl; + Exporter::export 'Fcntl', $callpkg; + }; +} + bootstrap DB_File $VERSION; # Preloaded methods go here. Autoload methods go after __END__, and are @@ -228,31 +241,36 @@ sub get_dup my $db = shift ; my $key = shift ; my $flag = shift ; - my $value ; + my $value = 0 ; my $origkey = $key ; my $wantarray = wantarray ; + my %values = () ; my @values = () ; my $counter = 0 ; + my $status = 0 ; # get the first value associated with the key, $key - $db->seq($key, $value, R_CURSOR()) ; + #$db->seq($key, $value, R_CURSOR()) ; - if ( $key eq $origkey) { + # iterate through the database until either EOF ($status == 0) + # or a different key is encountered ($key ne $origkey). + for ($status = $db->seq($key, $value, R_CURSOR()) ; + $status == 0 and $key eq $origkey ; + $status = $db->seq($key, $value, R_NEXT()) ) { - while (1) { - # save the value or count matches - if ($wantarray) - { push (@values, $value) ; push(@values, 1) if $flag } - else - { ++ $counter } + # save the value or count number of matches + if ($wantarray) { + if ($flag) + { ++ $values{$value} } + else + { push (@values, $value) } + } + else + { ++ $counter } - # iterate through the database until either EOF - # or a different key is encountered. - last if $db->seq($key, $value, R_NEXT()) != 0 or $key ne $origkey ; - } } - $wantarray ? @values : $counter ; + return ($wantarray ? ($flag ? %values : @values) : $counter) ; } @@ -268,7 +286,7 @@ DB_File - Perl5 access to Berkeley DB =head1 SYNOPSIS use DB_File ; - use Fcntl ; + use strict 'untie' ; [$X =] tie %hash, 'DB_File', [$filename, $flags, $mode, $DB_HASH] ; [$X =] tie %hash, 'DB_File', $filename, $flags, $mode, $DB_BTREE ; @@ -281,10 +299,18 @@ DB_File - Perl5 access to Berkeley DB $status = $X->sync([$flags]) ; $status = $X->fd ; + # BTREE only $count = $X->get_dup($key) ; @list = $X->get_dup($key) ; %list = $X->get_dup($key, 1) ; + # RECNO only + $a = $X->length; + $a = $X->pop ; + $X->push(list); + $a = $X->shift; + $X->unshift(list); + untie %hash ; untie @array ; @@ -292,7 +318,7 @@ DB_File - Perl5 access to Berkeley DB B is a module which allows Perl programs to make use of the facilities provided by Berkeley DB. If you intend to use this -module you should really have a copy of the Berkeley DB manual page at +module you should really have a copy of the Berkeley DB manual pages at hand. The interface defined here mirrors the Berkeley DB interface closely. @@ -345,12 +371,12 @@ array (for the DB_RECNO file type). In addition to the tie() interface, it is also possible to access most of the functions provided in the Berkeley DB API directly. -See L<"Using the Berkeley DB API Directly">. +See L. =head2 Opening a Berkeley DB Database File Berkeley DB uses the function dbopen() to open or create a database. -Below is the C prototype for dbopen(). +Here is the C prototype for dbopen(): DB* dbopen (const char * file, int flags, int mode, @@ -422,7 +448,7 @@ for DB_HASH, DB_BTREE and DB_RECNO respectively. The values stored in the hashes above are mostly the direct equivalent of their C counterpart. Like their C counterparts, all are set to a -default set of values - that means you don't have to set I of the +default values - that means you don't have to set I of the values when you only want to change one. Here is an example: $a = new DB_File::HASHINFO ; @@ -461,7 +487,8 @@ to Perl subs. Below are templates for each of the subs: return $bytes ; } -See L<"Using BTREE"> for an example of using the C +See L for an example of using the +C template. =head2 Default Parameters @@ -484,22 +511,142 @@ is equivalent to: tie %A, "DB_File", undef, O_CREAT|O_RDWR, 0640, $DB_HASH ; -See L<"In Memory Databases"> for a discussion on the use of C +See L for a discussion on the use of C in place of a filename. -=head2 Handling duplicate keys in BTREE databases +=head2 In Memory Databases + +Berkeley DB allows the creation of in-memory databases by using NULL +(that is, a C<(char *)0> in C) in place of the filename. B +uses C instead of NULL to provide this functionality. + +=head1 DB_HASH + +The DB_HASH file format is probably the most commonly used of the three +file formats that B supports. It is also very straightforward +to use. + +=head2 A Simple Example. + +This example shows how to create a database, add key/value pairs to the +database, delete keys/value pairs and finally how to enumerate the +contents of the database. + + use DB_File ; + use strict 'untie' ; + + tie %h, "DB_File", "fruit", O_RDWR|O_CREAT, 0640, $DB_HASH + or die "Cannot open file 'fruit': $!\n"; + + # Add a few key/value pairs to the file + $h{"apple"} = "red" ; + $h{"orange"} = "orange" ; + $h{"banana"} = "yellow" ; + $h{"tomato"} = "red" ; + + # Check for existence of a key + print "Banana Exists\n\n" if $h{"banana"} ; + + # Delete a key/value pair. + delete $h{"apple"} ; + + # print the contents of the file + while (($k, $v) = each %h) + { print "$k -> $v\n" } + + untie %h ; + +here is the output: + + Banana Exists + + orange -> orange + tomato -> red + banana -> yellow + +Note that the like ordinary associative arrays, the order of the keys +retrieved is in an apparently random order. + +=head1 DB_BTREE + +The DB_BTREE format is useful when you want to store data in a given +order. By default the keys will be stored in lexical order, but as you +will see from the example shown in the next section, it is very easy to +define your own sorting function. + +=head2 Changing the BTREE sort order + +This script shows how to override the default sorting algorithm that +BTREE uses. Instead of using the normal lexical ordering, a case +insensitive compare function will be used. -The BTREE file type in Berkeley DB optionally allows a single key to be -associated with an arbitrary number of values. This option is enabled by -setting the flags element of C<$DB_BTREE> to R_DUP when creating the + use DB_File ; + use strict 'untie' ; + + sub Compare + { + my ($key1, $key2) = @_ ; + "\L$key1" cmp "\L$key2" ; + } + + # specify the Perl sub that will do the comparison + $DB_BTREE->{'compare'} = \&Compare ; + + tie %h, "DB_File", "tree", O_RDWR|O_CREAT, 0640, $DB_BTREE + or die "Cannot open file 'tree': $!\n" ; + + # Add a key/value pair to the file + $h{'Wall'} = 'Larry' ; + $h{'Smith'} = 'John' ; + $h{'mouse'} = 'mickey' ; + $h{'duck'} = 'donald' ; + + # Delete + delete $h{"duck"} ; + + # Cycle through the keys printing them in order. + # Note it is not necessary to sort the keys as + # the btree will have kept them in order automatically. + foreach (keys %h) + { print "$_\n" } + + untie %h ; + +Here is the output from the code above. + + mouse + Smith + Wall + +There are a few point to bear in mind if you want to change the +ordering in a BTREE database: + +=over 5 + +=item 1. + +The new compare function must be specified when you create the database. + +=item 2. + +You cannot change the ordering once the database has been created. Thus +you must use the same compare function every time you access the database. +=back + +=head2 Handling duplicate keys + +The BTREE file type optionally allows a single key to be associated +with an arbitrary number of values. This option is enabled by setting +the flags element of C<$DB_BTREE> to R_DUP when creating the database. + There are some difficulties in using the tied hash interface if you want to manipulate a BTREE database with duplicate keys. Consider this code: use DB_File ; - use Fcntl ; + use strict 'untie' ; $filename = "tree" ; unlink $filename ; @@ -513,6 +660,7 @@ code: # Add some key/value pairs to the file $h{'Wall'} = 'Larry' ; $h{'Wall'} = 'Brick' ; # Note the duplicate key + $h{'Wall'} = 'Brick' ; # Note the duplicate key and value $h{'Smith'} = 'John' ; $h{'mouse'} = 'mickey' ; @@ -521,20 +669,22 @@ code: foreach (keys %h) { print "$_ -> $h{$_}\n" } + untie %h ; + Here is the output: Smith -> John Wall -> Larry Wall -> Larry + Wall -> Larry mouse -> mickey -As you can see 2 records have been successfully created with key C +As you can see 3 records have been successfully created with key C - the only thing is, when they are retrieved from the database they -both I to have the same value, namely C. The problem is -caused by the way that the associative array interface works. -Basically, when the associative array interface is used to fetch the -value associated with a given key, it will only ever retrieve the first -value. +I to have the same value, namely C. The problem is caused +by the way that the associative array interface works. Basically, when +the associative array interface is used to fetch the value associated +with a given key, it will only ever retrieve the first value. Although it may not be immediately obvious from the code above, the associative array interface can be used to write values with duplicate @@ -542,13 +692,13 @@ keys, but it cannot be used to read them back from the database. The way to get around this problem is to use the Berkeley DB API method called C. This method allows sequential access to key/value -pairs. See L<"Using the Berkeley DB API Directly"> for details of both -the C method and the API in general. +pairs. See L for details of both the C method +and the API in general. Here is the script above rewritten using the C API method. use DB_File ; - use Fcntl ; + use strict 'untie' ; $filename = "tree" ; unlink $filename ; @@ -562,16 +712,15 @@ Here is the script above rewritten using the C API method. # Add some key/value pairs to the file $h{'Wall'} = 'Larry' ; $h{'Wall'} = 'Brick' ; # Note the duplicate key + $h{'Wall'} = 'Brick' ; # Note the duplicate key and value $h{'Smith'} = 'John' ; $h{'mouse'} = 'mickey' ; - # Point to the first record in the btree - $x->seq($key, $value, R_FIRST) ; - - # now iterate through the rest of the btree + # iterate through the btree using seq # and print each key/value pair. - print "$key -> $value\n" ; - while ( $x->seq($key, $value, R_NEXT) == 0) + for ($status = $x->seq($key, $value, R_FIRST) ; + $status == 0 ; + $status = $x->seq($key, $value, R_NEXT) ) { print "$key -> $value\n" } undef $x ; @@ -581,13 +730,16 @@ that prints: Smith -> John Wall -> Brick + Wall -> Brick Wall -> Larry mouse -> mickey -This time we have got all the key/value pairs, including both the +This time we have got all the key/value pairs, including the multiple values associated with the key C. -C comes with a utility method, called C, to assist in +=head2 The get_dup method. + +B comes with a utility method, called C, to assist in reading duplicate values from BTREE databases. The method can take the following forms: @@ -599,40 +751,123 @@ In a scalar context the method returns the number of values associated with the key, C<$key>. In list context, it returns all the values which match C<$key>. Note -that the values returned will be in an apparently random order. +that the values will be returned in an apparently random order. -If the second parameter is present and evaluates TRUE, the method -returns an associative array whose keys correspond to the the values -from the BTREE and whose values are all C<1>. +In list context, if the second parameter is present and evaluates TRUE, +the method returns an associative array. The keys of the associative +array correspond to the the values that matched in the BTREE and the +values of the array are a count of the number of times that particular +value occurred in the BTREE. -So assuming the database created above, we can use C like +So assuming the database created above, we can use C like this: - $cnt = $x->get_dups("Wall") ; + $cnt = $x->get_dup("Wall") ; print "Wall occurred $cnt times\n" ; - %hash = $x->get_dups("Wall", 1) ; + %hash = $x->get_dup("Wall", 1) ; print "Larry is there\n" if $hash{'Larry'} ; + print "There are $hash{'Brick'} Brick Walls\n" ; - @list = $x->get_dups("Wall") ; + @list = $x->get_dup("Wall") ; print "Wall => [@list]\n" ; - @list = $x->get_dups("Smith") ; + @list = $x->get_dup("Smith") ; print "Smith => [@list]\n" ; - @list = $x->get_dups("Dog") ; + @list = $x->get_dup("Dog") ; print "Dog => [@list]\n" ; and it will print: - Wall occurred 2 times + Wall occurred 3 times Larry is there - Wall => [Brick Larry] + There are 2 Brick Walls + Wall => [Brick Brick Larry] Smith => [John] Dog => [] -=head2 RECNO +=head2 Matching Partial Keys + +The BTREE interface has a feature which allows partial keys to be +matched. This functionality is I available when the C method +is used along with the R_CURSOR flag. + + $x->seq($key, $value, R_CURSOR) ; + +Here is the relevant quote from the dbopen man page where it defines +the use of the R_CURSOR flag with seq: + + + Note, for the DB_BTREE access method, the returned key is not + necessarily an exact match for the specified key. The returned key + is the smallest key greater than or equal to the specified key, + permitting partial key matches and range searches. + + +In the example script below, the C sub uses this feature to find +and print the first matching key/value pair given a partial key. + + use DB_File ; + use Fcntl ; + use strict 'untie' ; + + sub match + { + my $key = shift ; + my $value ; + my $orig_key = $key ; + $x->seq($key, $value, R_CURSOR) ; + print "$orig_key\t-> $key\t-> $value\n" ; + } + + $filename = "tree" ; + unlink $filename ; + + $x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0640, $DB_BTREE + or die "Cannot open $filename: $!\n"; + + # Add some key/value pairs to the file + $h{'mouse'} = 'mickey' ; + $h{'Wall'} = 'Larry' ; + $h{'Walls'} = 'Brick' ; + $h{'Smith'} = 'John' ; + + + print "IN ORDER\n" ; + for ($st = $x->seq($key, $value, R_FIRST) ; + $st == 0 ; + $st = $x->seq($key, $value, R_NEXT) ) + + { print "$key -> $value\n" } + + print "\nPARTIAL MATCH\n" ; + + match "Wa" ; + match "A" ; + match "a" ; + + undef $x ; + untie %h ; + +Here is the output: + + IN ORDER + Smith -> John + Wall -> Larry + Walls -> Brick + mouse -> mickey + + PARTIAL MATCH + Wa -> Wall -> Larry + A -> Smith -> John + a -> mouse -> mickey + +=head1 DB_RECNO + +DB_RECNO provides an interface to flat text files. Both variable and +fixed length records are supported. In order to make RECNO more compatible with Perl the array offset for all RECNO arrays begins at 0 rather than 1 as in Berkeley DB. @@ -642,14 +877,203 @@ negative indexes. The index -1 refers to the last element of the array, -2 the second last, and so on. Attempting to access an element before the start of the array will raise a fatal run-time error. -=head2 In Memory Databases +=head2 A Simple Example -Berkeley DB allows the creation of in-memory databases by using NULL -(that is, a C<(char *)0> in C) in place of the filename. B -uses C instead of NULL to provide this functionality. +Here is a simple example that uses RECNO. + + use DB_File ; + use strict 'untie' ; + + tie @h, "DB_File", "text", O_RDWR|O_CREAT, 0640, $DB_RECNO + or die "Cannot open file 'text': $!\n" ; + + # Add a few key/value pairs to the file + $h[0] = "orange" ; + $h[1] = "blue" ; + $h[2] = "yellow" ; + + # Check for existence of a key + print "Element 1 Exists with value $h[1]\n" if $h[1] ; + + # use a negative index + print "The last element is $h[-1]\n" ; + print "The 2nd last element is $h[-2]\n" ; + + untie @h ; +Here is the output from the script: + + + Element 1 Exists with value blue + The last element is yellow + The 2nd last element is blue + +=head2 Extra Methods + +As you can see from the example above, the tied array interface is +quite limited. To make the interface more useful, a number of methods +are supplied with B to simulate the standard array operations +that are not currently implemented in Perl's tied array interface. All +these methods are accessed via the object returned from the tie call. + +Here are the methods: + +=over 5 -=head2 Using the Berkeley DB API Directly +=item B<$X-Epush(list) ;> + +Pushes the elements of C to the end of the array. + +=item B<$value = $X-Epop ;> + +Removes and returns the last element of the array. + +=item B<$X-Eshift> + +Removes and returns the first element of the array. + +=item B<$X-Eunshift(list) ;> + +Pushes the elements of C to the start of the array. + +=item B<$X-Elength> + +Returns the number of elements in the array. + +=back + +=head2 Another Example + +Here is a more complete example that makes use of some of the methods +described above. It also makes use of the API interface directly (see +L). + + use strict ; + use vars qw(@h $H $file $i) ; + use DB_File ; + use Fcntl ; + + $file = "text" ; + + unlink $file ; + + $H = tie @h, "DB_File", $file, O_RDWR|O_CREAT, 0640, $DB_RECNO + or die "Cannot open file $file: $!\n" ; + + # first create a text file to play with + $h[0] = "zero" ; + $h[1] = "one" ; + $h[2] = "two" ; + $h[3] = "three" ; + $h[4] = "four" ; + + + # Print the records in order. + # + # The length method is needed here because evaluating a tied + # array in a scalar context does not return the number of + # elements in the array. + + print "\nORIGINAL\n" ; + foreach $i (0 .. $H->length - 1) { + print "$i: $h[$i]\n" ; + } + + # use the push & pop methods + $a = $H->pop ; + $H->push("last") ; + print "\nThe last record was [$a]\n" ; + + # and the shift & unshift methods + $a = $H->shift ; + $H->unshift("first") ; + print "The first record was [$a]\n" ; + + # Use the API to add a new record after record 2. + $i = 2 ; + $H->put($i, "Newbie", R_IAFTER) ; + + # and a new record before record 1. + $i = 1 ; + $H->put($i, "New One", R_IBEFORE) ; + + # delete record 3 + $H->del(3) ; + + # now print the records in reverse order + print "\nREVERSE\n" ; + for ($i = $H->length - 1 ; $i >= 0 ; -- $i) + { print "$i: $h[$i]\n" } + + # same again, but use the API functions instead + print "\nREVERSE again\n" ; + my ($s, $k, $v) ; + for ($s = $H->seq($k, $v, R_LAST) ; + $s == 0 ; + $s = $H->seq($k, $v, R_PREV)) + { print "$k: $v\n" } + + undef $H ; + untie @h ; + +and this is what it outputs: + + ORIGINAL + 0: zero + 1: one + 2: two + 3: three + 4: four + + The last record was [four] + The first record was [zero] + + REVERSE + 5: last + 4: three + 3: Newbie + 2: one + 1: New One + 0: first + + REVERSE again + 5: last + 4: three + 3: Newbie + 2: one + 1: New One + 0: first + +Notes: + +=over 5 + +=item 1. + +Rather than iterating through the array, C<@h> like this: + + foreach $i (@h) + +it is necessary to use either this: + + foreach $i (0 .. $H->length - 1) + +or this: + + for ($a = $H->get($k, $v, R_FIRST) ; + $a == 0 ; + $a = $H->get($k, $v, R_NEXT) ) + +=item 2. + +Notice that both times the C method was used the record index was +specified using a variable, C<$i>, rather than the literal value +itself. This is because C will return the record number of the +inserted line via that parameter. + +=back + +=head1 THE API INTERFACE As well as accessing Berkeley DB using a tied hash or array, it is also possible to make direct use of most of the API functions defined in the @@ -667,7 +1091,7 @@ as B methods directly like this: B If you have saved a copy of the object returned from C, the underlying database file will I be closed until both the tied variable is untied and all copies of the saved object are -destroyed. +destroyed. See L for more details. use DB_File ; $db = tie %hash, "DB_File", "filename" @@ -746,7 +1170,7 @@ Below is a list of the methods available. =over 5 -=item C<$status = $X-Eget($key, $value [, $flags]) ;> +=item B<$status = $X-Eget($key, $value [, $flags]) ;> Given a key (C<$key>) this method reads the value associated with it from the database. The value read from the database is returned in the @@ -756,7 +1180,7 @@ If the key does not exist the method returns 1. No flags are currently defined for this method. -=item C<$status = $X-Eput($key, $value [, $flags]) ;> +=item B<$status = $X-Eput($key, $value [, $flags]) ;> Stores the key/value pair in the database. @@ -766,7 +1190,7 @@ will have the record number of the inserted key/value pair set. Valid flags are R_CURSOR, R_IAFTER, R_IBEFORE, R_NOOVERWRITE and R_SETCURSOR. -=item C<$status = $X-Edel($key [, $flags]) ;> +=item B<$status = $X-Edel($key [, $flags]) ;> Removes all key/value pairs with key C<$key> from the database. @@ -775,14 +1199,14 @@ database. R_CURSOR is the only valid flag at present. -=item C<$status = $X-Efd ;> +=item B<$status = $X-Efd ;> Returns the file descriptor for the underlying database. -See L<"Locking Databases"> for an example of how to make use of the +See L for an example of how to make use of the C method to lock your database. -=item C<$status = $X-Eseq($key, $value, $flags) ;> +=item B<$status = $X-Eseq($key, $value, $flags) ;> This interface allows sequential retrieval from the database. See L for full details. @@ -793,7 +1217,7 @@ pair read from the database. The flags parameter is mandatory. The valid flag values are R_CURSOR, R_FIRST, R_LAST, R_NEXT and R_PREV. -=item C<$status = $X-Esync([$flags]) ;> +=item B<$status = $X-Esync([$flags]) ;> Flushes any cached buffers to disk. @@ -801,95 +1225,103 @@ R_RECNOSYNC is the only valid flag at present. =back -=head1 EXAMPLES - -It is always a lot easier to understand something when you see a real -example. So here are a few. - -=head2 Using HASH - - use DB_File ; - use Fcntl ; - - tie %h, "DB_File", "hashed", O_RDWR|O_CREAT, 0640, $DB_HASH - or die "Cannot open file 'hashed': $!\n"; - - # Add a key/value pair to the file - $h{"apple"} = "orange" ; - - # Check for existence of a key - print "Exists\n" if $h{"banana"} ; - - # Delete - delete $h{"apple"} ; - - untie %h ; +=head1 HINTS AND TIPS -=head2 Using BTREE +=head2 The strict untie pragma -Here is a sample of code which uses BTREE. Just to make life more -interesting the default comparison function will not be used. Instead -a Perl sub, C, will be used to do a case insensitive -comparison. +If you run Perl version 5.004 or later (actually any version from the +5.003_01 development release on will suffice) and you make use of the +Berkeley DB API, it is is I strongly recommended that you always +include the C pragma in any of your scripts that +make use of B. - use DB_File ; - use Fcntl ; +Even if you don't currently make use of the API interface, it is still +a good idea to include the pragma. It won't affect the performance of +your script, but it will prevent problems in the future. - sub Compare - { - my ($key1, $key2) = @_ ; +If possible you should try to run with the full strict pragma, but that +is another story. For further details see L and +L>. - "\L$key1" cmp "\L$key2" ; - } +To illustrate the importance of including the untie pragma, here is an +example script that fails in an unexpected place because it doesn't use +it: - $DB_BTREE->{'compare'} = 'Compare' ; + use DB_File ; + use Fcntl ; + + $X = tie %x, 'DB_File', 'tst.fil' , O_RDWR|O_CREAT + or die "Cannot tie first time: $!" ; + + $x{123} = 456 ; + + untie %x ; + + $X = tie %x, 'DB_File', 'tst.fil' , O_RDWR|O_CREAT + or die "Cannot tie second time: $!" ; - tie %h, "DB_File", "tree", O_RDWR|O_CREAT, 0640, $DB_BTREE - or die "Cannot open file 'tree': $!\n" ; + untie %x ; - # Add a key/value pair to the file - $h{'Wall'} = 'Larry' ; - $h{'Smith'} = 'John' ; - $h{'mouse'} = 'mickey' ; - $h{'duck'} = 'donald' ; +When run the script will produce this error message: - # Delete - delete $h{"duck"} ; + Cannot tie second time: Invalid argument at bad.file line 12. - # Cycle through the keys printing them in order. - # Note it is not necessary to sort the keys as - # the btree will have kept them in order automatically. - foreach (keys %h) - { print "$_\n" } +Although the error message above refers to the second tie statement in +the script, the source of the problem is really with the untie +statement that precedes it. - untie %h ; +To understand why there is a problem at all with the untie statement, +consider what the tie does for a moment. -Here is the output from the code above. +Whenever the tie is executed, it creates a logical link between a Perl +variable, the associative array C<%x> in this case, and a Berkeley DB +database, C. The logical link ensures that all operation on +the associative array are automatically mirrored to the database file. - mouse - Smith - Wall +In normal circumstances the untie is enough to break the logical link +and also close the database. In this particular case there is another +logical link, namely the API object returned from the tie and stored in +C<$X>. Whenever the untie is executed in this case, only the link +between the associative array and the database will be broken. The API +object in C<$X> is still valid, so the database will not be closed. +The end result of this is that when the second tie is executed, the +database will be in an inconsistent state (i.e. it is still opened by +the first tie) - thus the second tie will fail. -=head2 Using RECNO - -Here is a simple example that uses RECNO. +If the C pragma is included in the script, like +this: - use DB_File ; - use Fcntl ; + use DB_File ; + use Fcntl ; + use strict 'untie' ; + + $X = tie %x, 'DB_File', 'tst.fil' , O_RDWR|O_CREAT + or die "Cannot tie first time: $!" ; + + $x{123} = 456 ; + + untie %x ; + + $X = tie %x, 'DB_File', 'tst.fil' , O_RDWR|O_CREAT + or die "Cannot tie second time: $!" ; - $DB_RECNO->{'psize'} = 3000 ; +then the error message becomes: - tie @h, "DB_File", "text", O_RDWR|O_CREAT, 0640, $DB_RECNO - or die "Cannot open file 'text': $!\n" ; + Can't untie: 1 inner references still exist at bad.file line 11. - # Add a key/value pair to the file - $h[0] = "orange" ; +which pinpoints the real problem. Finally the script can now be +modified to fix the original problem by destroying the API object +before the untie: - # Check for existence of a key - print "Exists\n" if $h[1] ; + ... + $x{123} = 456 ; - untie @h ; + undef $X ; + untie %x ; + + $X = tie %x, 'DB_File', 'tst.fil' , O_RDWR|O_CREAT + ... =head2 Locking Databases @@ -899,7 +1331,7 @@ uses the I method to get the file descriptor, and then a careful open() to give something Perl will flock() for you. Run this repeatedly in the background to watch the locks granted in proper order. - use Fcntl; + use strict 'untie'; use DB_File; use strict; @@ -951,6 +1383,135 @@ in the background to watch the locks granted in proper order. close(DB_FH); print "$$: Updated db to $key=$value\n"; +=head2 Sharing databases with C applications + +There is no technical reason why a Berkeley DB database cannot be +shared by both a Perl and a C application. + +The vast majority of problems that are reported in this area boil down +to the fact that C strings are NULL terminated, whilst Perl strings are +not. + +Here is a real example. Netscape 2.0 keeps a record of the locations you +visit along with the time you last visited them in a DB_HASH database. +This is usually stored in the file F<~/.netscape/history.db>. The key +field in the database is the location string and the value field is the +time the location was last visited stored as a 4 byte binary value. + +If you haven't already guessed, the location string is stored with a +terminating NULL. This means you need to be careful when accessing the +database. + +Here is a snippet of code that is loosely based on Tom Christiansen's +I script (available from your nearest CPAN archive in +F). + + use DB_File ; + use Fcntl ; + use strict 'untie' ; + + $dotdir = $ENV{HOME} || $ENV{LOGNAME}; + + $HISTORY = "$dotdir/.netscape/history.db"; + + tie %hist_db, 'DB_File', $HISTORY + or die "Cannot open $HISTORY: $!\n" ;; + + # Dump the complete database + while ( ($href, $binary_time) = each %hist_db ) { + + # remove the terminating NULL + $href =~ s/\x00$// ; + + # convert the binary time into a user friendly string + $date = localtime unpack("V", $binary_time); + print "$date $href\n" ; + } + + # check for the existence of a specific key + # remember to add the NULL + if ( $binary_time = $hist_db{"http://mox.perl.com/\x00"} ) { + $date = localtime unpack("V", $binary_time) ; + print "Last visited mox.perl.com on $date\n" ; + } + else { + print "Never visited mox.perl.com\n" + } + + untie %hist_db ; + + +=head1 COMMON QUESTIONS + +=head2 Why is there Perl source in my database? + +If you look at the contents of a database file created by DB_File, +there can sometimes be part of a Perl script included in it. + +This happens because Berkeley DB uses dynamic memory to allocate +buffers which will subsequently be written to the database file. Being +dynamic, the memory could have been used for anything before DB +malloced it. As Berkeley DB doesn't clear the memory once it has been +allocated, the unused portions will contain random junk. In the case +where a Perl script gets written to the database, the random junk will +correspond to an area of dynamic memory that happened to be used during +the compilation of the script. + +Unless you don't like the possibility of there being part of your Perl +scripts embedded in a database file, this is nothing to worry about. + +=head2 How do I store complex data structures with DB_File? + +Although B cannot do this directly, there is a module which +can layer transparently over B to accomplish this feat. + +Check out the MLDBM module, available on CPAN in the directory +F. + +=head2 What does "Invalid Argument" mean? + +You will get this error message when one of the parameters in the +C call is wrong. Unfortunately there are quite a few parameters to +get wrong, so it can be difficult to figure out which one it is. + +Here are a couple of possibilities: + +=over 5 + +=item 1. + +Attempting to reopen a database without closing it. See +L for an example. + +=item 2. + +Using the O_WRONLY flag. + +=back + +=head2 What does "Bareword 'DB_File' not allowed" mean? + +You will encounter this particular error message when you have the +C pragma (or the full strict pragma) in your script. +Consider this script: + + use strict ; + use DB_File ; + use vars qw(%x) ; + tie %x, DB_File, "filename" ; + +Running it produces the error in question: + + Bareword "DB_File" not allowed while "strict subs" in use + +To get around the error, place the word C in either single or +double quotes, like this: + + tie %x, "DB_File", "filename" ; + +Although it might seem like a real pain, it is really worth the effort +of having a C in all your scripts. + =head1 HISTORY =over @@ -989,7 +1550,7 @@ an error. =item 1.02 -Merged OS2 specific code into DB_File.xs +Merged OS/2 specific code into DB_File.xs Removed some redundant code in DB_File.xs. @@ -1002,16 +1563,19 @@ Changed the default flags from O_RDWR to O_CREAT|O_RDWR. The example code which showed how to lock a database needed a call to C added. Without it the resultant database file was empty. -Added get_dups method. +Added get_dup method. -=head1 WARNINGS +=item 1.03 + +Documentation update. -If you happen to find any other functions defined in the source for -this module that have not been mentioned in this document -- beware. I -may drop them at a moments notice. +B now imports the constants (O_RDWR, O_CREAT etc.) from Fcntl +automatically. -If you cannot find any, then either you didn't look very hard or the -moment has passed and I have dropped them. +The standard hash function C is now supported. + +Modified the behavior of get_dup. When it returns an associative +array, the value is the count of the number of matching BTREE values. =head1 BUGS @@ -1024,6 +1588,9 @@ suggest any enhancements, I would welcome your comments. =head1 AVAILABILITY +B comes with the standard Perl source distribution. Look in +the directory F. + Berkeley DB is available at your nearest CPAN archive (see L for a list) in F, or via the host F in F. It is I under @@ -1037,9 +1604,6 @@ compile properly on IRIX 5.3. L, L, L, L, L -Berkeley DB is available from F in the directory -F. - =head1 AUTHOR The DB_File interface was written by Paul Marquess diff --git a/ext/DB_File/DB_File.xs b/ext/DB_File/DB_File.xs index f344794..aecfb0c 100644 --- a/ext/DB_File/DB_File.xs +++ b/ext/DB_File/DB_File.xs @@ -3,8 +3,8 @@ DB_File.xs -- Perl 5 interface to Berkeley DB written by Paul Marquess (pmarquess@bfsec.bt.co.uk) - last modified 26th June 1996 - version 1.02 + last modified 4th Sept 1996 + version 1.03 All comments/suggestions/problems are welcome @@ -22,6 +22,8 @@ Merged OS2 code into the main distribution. Allow negative subscripts with RECNO interface. Changed the default flags to O_CREAT|O_RDWR + 1.03 - Added EXISTS + */ #include "EXTERN.h" @@ -771,6 +773,21 @@ db_DELETE(db, key, flags=0) INIT: CurrentDB = db ; + +int +db_EXISTS(db, key) + DB_File db + DBTKEY key + CODE: + { + DBT value ; + + CurrentDB = db ; + RETVAL = (((db->dbp)->get)(db->dbp, &key, &value, 0) == 0) ; + } + OUTPUT: + RETVAL + int db_FETCH(db, key, flags=0) DB_File db @@ -887,9 +904,11 @@ pop(db) /* Now delete it */ if (RETVAL == 0) { + /* the call to del will trash value, so take a copy now */ + sv_setpvn(ST(0), value.data, value.size); RETVAL = (Db->del)(Db, &key, R_CURSOR) ; - if (RETVAL == 0) - sv_setpvn(ST(0), value.data, value.size); + if (RETVAL != 0) + sv_setsv(ST(0), &sv_undef); } } @@ -898,20 +917,22 @@ shift(db) DB_File db CODE: { - DBTKEY key ; DBT value ; + DBTKEY key ; DB * Db = db->dbp ; CurrentDB = db ; /* get the first value */ - RETVAL = (Db->seq)(Db, &key, &value, R_FIRST) ; + RETVAL = (Db->seq)(Db, &key, &value, R_FIRST) ; ST(0) = sv_newmortal(); /* Now delete it */ if (RETVAL == 0) { - RETVAL = (Db->del)(Db, &key, R_CURSOR) ; - if (RETVAL == 0) - sv_setpvn(ST(0), value.data, value.size); + /* the call to del will trash value, so take a copy now */ + sv_setpvn(ST(0), value.data, value.size); + RETVAL = (Db->del)(Db, &key, R_CURSOR) ; + if (RETVAL != 0) + sv_setsv (ST(0), &sv_undef) ; } } diff --git a/t/lib/db-btree.t b/t/lib/db-btree.t index d5d97d7..1944c38 100755 --- a/t/lib/db-btree.t +++ b/t/lib/db-btree.t @@ -1,4 +1,4 @@ -#!./perl +#!./perl -w BEGIN { @INC = '../lib'; @@ -12,7 +12,16 @@ BEGIN { use DB_File; use Fcntl; -print "1..86\n"; +print "1..91\n"; + +sub ok +{ + my $no = shift ; + my $result = shift ; + + print "not " unless $result ; + print "ok $no\n" ; +} $Dfile = "dbbtree.tmp"; unlink $Dfile; @@ -21,65 +30,68 @@ umask(0); # Check the interface to BTREEINFO -#$dbh = TIEHASH DB_File::BTREEINFO ; -$dbh = new DB_File::BTREEINFO ; -print (($dbh->{flags} == undef) ? "ok 1\n" : "not ok 1\n") ; -print (($dbh->{cachesize} == undef) ? "ok 2\n" : "not ok 2\n") ; -print (($dbh->{psize} == undef) ? "ok 3\n" : "not ok 3\n") ; -print (($dbh->{lorder} == undef) ? "ok 4\n" : "not ok 4\n") ; -print (($dbh->{minkeypage} == undef) ? "ok 5\n" : "not ok 5\n") ; -print (($dbh->{maxkeypage} == undef) ? "ok 6\n" : "not ok 6\n") ; -print (($dbh->{compare} == undef) ? "ok 7\n" : "not ok 7\n") ; -print (($dbh->{prefix} == undef) ? "ok 8\n" : "not ok 8\n") ; +my $dbh = new DB_File::BTREEINFO ; +$^W = 0 ; +ok(1, $dbh->{flags} == undef) ; +ok(2, $dbh->{cachesize} == undef) ; +ok(3, $dbh->{psize} == undef) ; +ok(4, $dbh->{lorder} == undef) ; +ok(5, $dbh->{minkeypage} == undef) ; +ok(6, $dbh->{maxkeypage} == undef) ; +ok(7, $dbh->{compare} == undef) ; +ok(8, $dbh->{prefix} == undef) ; +$^W = 1 ; $dbh->{flags} = 3000 ; -print ($dbh->{flags} == 3000 ? "ok 9\n" : "not ok 9\n") ; +ok(9, $dbh->{flags} == 3000) ; $dbh->{cachesize} = 9000 ; -print ($dbh->{cachesize} == 9000 ? "ok 10\n" : "not ok 10\n") ; -# +ok(10, $dbh->{cachesize} == 9000); + $dbh->{psize} = 400 ; -print (($dbh->{psize} == 400) ? "ok 11\n" : "not ok 11\n") ; +ok(11, $dbh->{psize} == 400) ; $dbh->{lorder} = 65 ; -print (($dbh->{lorder} == 65) ? "ok 12\n" : "not ok 12\n") ; +ok(12, $dbh->{lorder} == 65) ; $dbh->{minkeypage} = 123 ; -print (($dbh->{minkeypage} == 123) ? "ok 13\n" : "not ok 13\n") ; +ok(13, $dbh->{minkeypage} == 123) ; $dbh->{maxkeypage} = 1234 ; -print ($dbh->{maxkeypage} == 1234 ? "ok 14\n" : "not ok 14\n") ; +ok(14, $dbh->{maxkeypage} == 1234 ); $dbh->{compare} = 1234 ; -print ($dbh->{compare} == 1234 ? "ok 15\n" : "not ok 15\n") ; +ok(15, $dbh->{compare} == 1234) ; $dbh->{prefix} = 1234 ; -print ($dbh->{prefix} == 1234 ? "ok 16\n" : "not ok 16\n") ; +ok(16, $dbh->{prefix} == 1234 ); # Check that an invalid entry is caught both for store & fetch eval '$dbh->{fred} = 1234' ; -print ($@ =~ /^DB_File::BTREEINFO::STORE - Unknown element 'fred' at/ ? "ok 17\n" : "not ok 17\n") ; +ok(17, $@ =~ /^DB_File::BTREEINFO::STORE - Unknown element 'fred' at/ ) ; eval '$q = $dbh->{fred}' ; -print ($@ =~ /^DB_File::BTREEINFO::FETCH - Unknown element 'fred' at/ ? "ok 18\n" : "not ok 18\n") ; +ok(18, $@ =~ /^DB_File::BTREEINFO::FETCH - Unknown element 'fred' at/ ) ; # Now check the interface to BTREE -print (($X = tie(%h, DB_File,$Dfile, O_RDWR|O_CREAT, 0640, $DB_BTREE )) ? "ok 19\n" : "not ok 19"); +ok(19, $X = tie(%h, 'DB_File',$Dfile, O_RDWR|O_CREAT, 0640, $DB_BTREE )) ; ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime, $blksize,$blocks) = stat($Dfile); -print (($mode & 0777) == 0640 ? "ok 20\n" : "not ok 20\n"); +ok(20, ($mode & 0777) == 0640 ); while (($key,$value) = each(%h)) { $i++; } -print (!$i ? "ok 21\n" : "not ok 21\n"); +ok(21, !$i ) ; $h{'goner1'} = 'snork'; $h{'abc'} = 'ABC'; -print ($h{'abc'} eq 'ABC' ? "ok 22\n" : "not ok 22\n") ; -print (defined $h{'jimmy'} ? "not ok 23\n" : "ok 23\n"); +ok(22, $h{'abc'} eq 'ABC' ); +ok(23, ! defined $h{'jimmy'} ) ; +ok(24, ! exists $h{'jimmy'} ) ; +ok(25, defined $h{'abc'} ) ; $h{'def'} = 'DEF'; $h{'jkl','mno'} = "JKL\034MNO"; @@ -111,7 +123,7 @@ untie(%h); # tie to the same file again -print (($X = tie(%h,DB_File,$Dfile, O_RDWR, 0640, $DB_BTREE)) ? "ok 24\n" : "not ok 24\n"); +ok(26, $X = tie(%h,'DB_File',$Dfile, O_RDWR, 0640, $DB_BTREE)) ; # Modify an entry from the previous tie $h{'g'} = 'G'; @@ -142,8 +154,9 @@ $X->DELETE('goner3'); @keys = keys(%h); @values = values(%h); -if ($#keys == 29 && $#values == 29) {print "ok 25\n";} else {print "not ok 25\n";} +ok(27, $#keys == 29 && $#values == 29) ; +$i = 0 ; while (($key,$value) = each(%h)) { if ($key eq $keys[$i] && $value eq $values[$i] && $key gt $value) { $key =~ y/a-z/A-Z/; @@ -151,10 +164,10 @@ while (($key,$value) = each(%h)) { } } -if ($i == 30) {print "ok 26\n";} else {print "not ok 26\n";} +ok(28, $i == 30) ; @keys = ('blurfl', keys(%h), 'dyick'); -if ($#keys == 31) {print "ok 27\n";} else {print "not ok 27\n";} +ok(29, $#keys == 31) ; #Check that the keys can be retrieved in order $ok = 1 ; @@ -163,27 +176,27 @@ foreach (keys %h) ($ok = 0), last if defined $previous && $previous gt $_ ; $previous = $_ ; } -print ($ok ? "ok 28\n" : "not ok 28\n") ; +ok(30, $ok ) ; $h{'foo'} = ''; -print ($h{'foo'} eq '' ? "ok 29\n" : "not ok 29\n") ; +ok(31, $h{'foo'} eq '' ) ; $h{''} = 'bar'; -print ($h{''} eq 'bar' ? "ok 30\n" : "not ok 30\n") ; +ok(32, $h{''} eq 'bar' ); # check cache overflow and numeric keys and contents $ok = 1; for ($i = 1; $i < 200; $i++) { $h{$i + 0} = $i + 0; } for ($i = 1; $i < 200; $i++) { $ok = 0 unless $h{$i} == $i; } -print ($ok ? "ok 31\n" : "not ok 31\n"); +ok(33, $ok); ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime, $blksize,$blocks) = stat($Dfile); -print ($size > 0 ? "ok 32\n" : "not ok 32\n"); +ok(34, $size > 0 ); @h{0..200} = 200..400; @foo = @h{0..200}; -print join(':',200..400) eq join(':',@foo) ? "ok 33\n" : "not ok 33\n"; +ok(35, join(':',200..400) eq join(':',@foo) ); # Now check all the non-tie specific stuff @@ -192,52 +205,55 @@ print join(':',200..400) eq join(':',@foo) ? "ok 33\n" : "not ok 33\n"; # an existing record. $status = $X->put( 'x', 'newvalue', R_NOOVERWRITE) ; -print ($status == 1 ? "ok 34\n" : "not ok 34\n") ; +ok(36, $status == 1 ); # check that the value of the key 'x' has not been changed by the # previous test -print ($h{'x'} eq 'X' ? "ok 35\n" : "not ok 35\n") ; +ok(37, $h{'x'} eq 'X' ); # standard put $status = $X->put('key', 'value') ; -print ($status == 0 ? "ok 36\n" : "not ok 36\n") ; +ok(38, $status == 0 ); #check that previous put can be retrieved +$value = 0 ; $status = $X->get('key', $value) ; -print ($status == 0 ? "ok 37\n" : "not ok 37\n") ; -print ($value eq 'value' ? "ok 38\n" : "not ok 38\n") ; +ok(39, $status == 0 ); +ok(40, $value eq 'value' ); # Attempting to delete an existing key should work $status = $X->del('q') ; -print ($status == 0 ? "ok 39\n" : "not ok 39\n") ; +ok(41, $status == 0 ); $status = $X->del('') ; -print ($status == 0 ? "ok 40\n" : "not ok 40\n") ; +ok(42, $status == 0 ); # Make sure that the key deleted, cannot be retrieved -print (($h{'q'} eq undef) ? "ok 41\n" : "not ok 41\n") ; -print (($h{''} eq undef) ? "ok 42\n" : "not ok 42\n") ; +$^W = 0 ; +ok(43, $h{'q'} eq undef) ; +ok(44, $h{''} eq undef) ; +$^W = 1 ; undef $X ; untie %h ; -print (($X = tie(%h, DB_File,$Dfile, O_RDWR, 0640, $DB_BTREE )) ? "ok 43\n" : "not ok 43"); +ok(45, $X = tie(%h, 'DB_File',$Dfile, O_RDWR, 0640, $DB_BTREE )); # Attempting to delete a non-existant key should fail $status = $X->del('joe') ; -print ($status == 1 ? "ok 44\n" : "not ok 44\n") ; +ok(46, $status == 1 ); # Check the get interface # First a non-existing key $status = $X->get('aaaa', $value) ; -print ($status == 1 ? "ok 45\n" : "not ok 45\n") ; +ok(47, $status == 1 ); # Next an existing key $status = $X->get('a', $value) ; -print ($status == 0 ? "ok 46\n" : "not ok 46\n") ; -print ($value eq 'A' ? "ok 47\n" : "not ok 47\n") ; +ok(48, $status == 0 ); +ok(49, $value eq 'A' ); # seq # ### @@ -246,15 +262,15 @@ print ($value eq 'A' ? "ok 47\n" : "not ok 47\n") ; $key = 'ke' ; $value = '' ; $status = $X->seq($key, $value, R_CURSOR) ; -print ($status == 0 ? "ok 48\n" : "not ok 48\n") ; -print ($key eq 'key' ? "ok 49\n" : "not ok 49\n") ; -print ($value eq 'value' ? "ok 50\n" : "not ok 50\n") ; +ok(50, $status == 0 ); +ok(51, $key eq 'key' ); +ok(52, $value eq 'value' ); # seq when the key does not match $key = 'zzz' ; $value = '' ; $status = $X->seq($key, $value, R_CURSOR) ; -print ($status == 1 ? "ok 51\n" : "not ok 51\n") ; +ok(53, $status == 1 ); # use seq to set the cursor, then delete the record @ the cursor. @@ -262,35 +278,35 @@ print ($status == 1 ? "ok 51\n" : "not ok 51\n") ; $key = 'x' ; $value = '' ; $status = $X->seq($key, $value, R_CURSOR) ; -print ($status == 0 ? "ok 52\n" : "not ok 52\n") ; -print ($key eq 'x' ? "ok 53\n" : "not ok 53\n") ; -print ($value eq 'X' ? "ok 54\n" : "not ok 54\n") ; +ok(54, $status == 0 ); +ok(55, $key eq 'x' ); +ok(56, $value eq 'X' ); $status = $X->del(0, R_CURSOR) ; -print ($status == 0 ? "ok 55\n" : "not ok 55\n") ; +ok(57, $status == 0 ); $status = $X->get('x', $value) ; -print ($status == 1 ? "ok 56\n" : "not ok 56\n") ; +ok(58, $status == 1 ); # ditto, but use put to replace the key/value pair. $key = 'y' ; $value = '' ; $status = $X->seq($key, $value, R_CURSOR) ; -print ($status == 0 ? "ok 57\n" : "not ok 57\n") ; -print ($key eq 'y' ? "ok 58\n" : "not ok 58\n") ; -print ($value eq 'Y' ? "ok 59\n" : "not ok 59\n") ; +ok(59, $status == 0 ); +ok(60, $key eq 'y' ); +ok(61, $value eq 'Y' ); $key = "replace key" ; $value = "replace value" ; $status = $X->put($key, $value, R_CURSOR) ; -print ($status == 0 ? "ok 60\n" : "not ok 60\n") ; -print ($key eq 'replace key' ? "ok 61\n" : "not ok 61\n") ; -print ($value eq 'replace value' ? "ok 62\n" : "not ok 62\n") ; +ok(62, $status == 0 ); +ok(63, $key eq 'replace key' ); +ok(64, $value eq 'replace value' ); $status = $X->get('y', $value) ; -print ($status == 1 ? "ok 63\n" : "not ok 63\n") ; +ok(65, $status == 1 ); # use seq to walk forwards through a file $status = $X->seq($key, $value, R_FIRST) ; -print ($status == 0 ? "ok 64\n" : "not ok 64\n") ; +ok(66, $status == 0 ); $previous = $key ; $ok = 1 ; @@ -299,12 +315,12 @@ while (($status = $X->seq($key, $value, R_NEXT)) == 0) ($ok = 0), last if ($previous cmp $key) == 1 ; } -print ($status == 1 ? "ok 65\n" : "not ok 65\n") ; -print ($ok == 1 ? "ok 66\n" : "not ok 66\n") ; +ok(67, $status == 1 ); +ok(68, $ok == 1 ); # use seq to walk backwards through a file $status = $X->seq($key, $value, R_LAST) ; -print ($status == 0 ? "ok 67\n" : "not ok 67\n") ; +ok(69, $status == 0 ); $previous = $key ; $ok = 1 ; @@ -314,8 +330,8 @@ while (($status = $X->seq($key, $value, R_PREV)) == 0) #print "key = [$key] value = [$value]\n" ; } -print ($status == 1 ? "ok 68\n" : "not ok 68\n") ; -print ($ok == 1 ? "ok 69\n" : "not ok 69\n") ; +ok(70, $status == 1 ); +ok(71, $ok == 1 ); # check seq FIRST/LAST @@ -324,14 +340,14 @@ print ($ok == 1 ? "ok 69\n" : "not ok 69\n") ; # #### $status = $X->sync ; -print ($status == 0 ? "ok 70\n" : "not ok 70\n") ; +ok(72, $status == 0 ); # fd # ## $status = $X->fd ; -print ($status != 0 ? "ok 71\n" : "not ok 71\n") ; +ok(73, $status != 0 ); undef $X ; @@ -340,11 +356,11 @@ untie %h ; unlink $Dfile; # Now try an in memory file -print (($Y = tie(%h, DB_File,undef, O_RDWR|O_CREAT, 0640, $DB_BTREE )) ? "ok 72\n" : "not ok 72"); +ok(74, $Y = tie(%h, 'DB_File',undef, O_RDWR|O_CREAT, 0640, $DB_BTREE )); # fd with an in memory file should return failure $status = $Y->fd ; -print ($status == -1 ? "ok 73\n" : "not ok 73\n") ; +ok(75, $status == -1 ); undef $Y ; @@ -353,42 +369,44 @@ untie %h ; # Duplicate keys my $bt = new DB_File::BTREEINFO ; $bt->{flags} = R_DUP ; -print (($YY = tie(%hh, DB_File, $Dfile, O_RDWR|O_CREAT, 0640, $bt )) ? "ok 74\n" : "not ok 74"); +ok(76, $YY = tie(%hh, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $bt )) ; $hh{'Wall'} = 'Larry' ; $hh{'Wall'} = 'Stone' ; # Note the duplicate key $hh{'Wall'} = 'Brick' ; # Note the duplicate key +$hh{'Wall'} = 'Brick' ; # Note the duplicate key and value $hh{'Smith'} = 'John' ; $hh{'mouse'} = 'mickey' ; # first work in scalar context -print(scalar $YY->get_dup('Unknown') == 0 ? "ok 75\n" : "not ok 75\n") ; -print(scalar $YY->get_dup('Smith') == 1 ? "ok 76\n" : "not ok 76\n") ; -print(scalar $YY->get_dup('Wall') == 3 ? "ok 77\n" : "not ok 77\n") ; +ok(77, scalar $YY->get_dup('Unknown') == 0 ); +ok(78, scalar $YY->get_dup('Smith') == 1 ); +ok(79, scalar $YY->get_dup('Wall') == 4 ); # now in list context my @unknown = $YY->get_dup('Unknown') ; -print( "@unknown" eq "" ? "ok 78\n" : "not ok 78\n") ; +ok(80, "@unknown" eq "" ); my @smith = $YY->get_dup('Smith') ; -print( "@smith" eq "John" ? "ok 79\n" : "not ok 79\n") ; +ok(81, "@smith" eq "John" ); { - my @wall = $YY->get_dup('Wall') ; - my %wall ; - @wall{@wall} = @wall ; - print( (@wall == 3 && $wall{'Larry'} && $wall{'Stone'} && $wall{'Brick'}) ? "ok 80\n" : "not ok 80\n") ; +my @wall = $YY->get_dup('Wall') ; +my %wall ; +@wall{@wall} = @wall ; +ok(82, (@wall == 4 && $wall{'Larry'} && $wall{'Stone'} && $wall{'Brick'}) ); } # hash my %unknown = $YY->get_dup('Unknown', 1) ; -print( keys %unknown == 0 ? "ok 81\n" : "not ok 81\n") ; +ok(83, keys %unknown == 0 ); my %smith = $YY->get_dup('Smith', 1) ; -print( (keys %smith == 1 && $smith{'John'}) ? "ok 82\n" : "not ok 82\n") ; +ok(84, keys %smith == 1 && $smith{'John'}) ; -%wall = $YY->get_dup('Wall', 1) ; -print( (keys %wall == 3 && $wall{'Larry'} && $wall{'Stone'} && $wall{'Brick'}) ? "ok 83\n" : "not ok 83\n") ; +my %wall = $YY->get_dup('Wall', 1) ; +ok(85, keys %wall == 3 && $wall{'Larry'} == 1 && $wall{'Stone'} == 1 + && $wall{'Brick'} == 2); undef $YY ; untie %hh ; @@ -410,17 +428,19 @@ $dbh3 = TIEHASH DB_File::BTREEINFO ; $dbh3->{compare} = sub { length $_[0] <=> length $_[1] } ; -tie(%h, DB_File,$Dfile1, O_RDWR|O_CREAT, 0640, $dbh1 ) ; -tie(%g, DB_File,$Dfile2, O_RDWR|O_CREAT, 0640, $dbh2 ) ; -tie(%k, DB_File,$Dfile3, O_RDWR|O_CREAT, 0640, $dbh3 ) ; +tie(%h, 'DB_File',$Dfile1, O_RDWR|O_CREAT, 0640, $dbh1 ) ; +tie(%g, 'DB_File',$Dfile2, O_RDWR|O_CREAT, 0640, $dbh2 ) ; +tie(%k, 'DB_File',$Dfile3, O_RDWR|O_CREAT, 0640, $dbh3 ) ; @Keys = qw( 0123 12 -1234 9 987654321 def ) ; +$^W = 0 ; @srt_1 = sort { $a <=> $b } @Keys ; +$^W = 1 ; @srt_2 = sort { $a cmp $b } @Keys ; @srt_3 = sort { length $a <=> length $b } @Keys ; foreach (@Keys) { - $h{$_} = 1 ; + $^W = 0 ; $h{$_} = 1 ; $^W = 1 ; $g{$_} = 1 ; $k{$_} = 1 ; } @@ -439,13 +459,40 @@ sub ArrayCompare 1 ; } -print ( ArrayCompare (\@srt_1, [keys %h]) ? "ok 84\n" : "not ok 84\n") ; -print ( ArrayCompare (\@srt_2, [keys %g]) ? "ok 85\n" : "not ok 85\n") ; -print ( ArrayCompare (\@srt_3, [keys %k]) ? "ok 86\n" : "not ok 86\n") ; +ok(86, ArrayCompare (\@srt_1, [keys %h]) ); +ok(87, ArrayCompare (\@srt_2, [keys %g]) ); +ok(88, ArrayCompare (\@srt_3, [keys %k]) ); untie %h ; untie %g ; untie %k ; unlink $Dfile1, $Dfile2, $Dfile3 ; +# clear +# ##### + +ok(89, tie(%h, 'DB_File', $Dfile1, O_RDWR|O_CREAT, 0640, $DB_BTREE ) ); +foreach (1 .. 10) + { $h{$_} = $_ * 100 } + +# check that there are 10 elements in the hash +$i = 0 ; +while (($key,$value) = each(%h)) { + $i++; +} +ok(90, $i == 10); + +# now clear the hash +%h = () ; + +# check it is empty +$i = 0 ; +while (($key,$value) = each(%h)) { + $i++; +} +ok(91, $i == 0); + +untie %h ; +unlink $Dfile1 ; + exit ; diff --git a/t/lib/db-hash.t b/t/lib/db-hash.t index 5205fae..f5c9cc8 100755 --- a/t/lib/db-hash.t +++ b/t/lib/db-hash.t @@ -1,7 +1,9 @@ -#!./perl +#!./perl +#!./perl -w BEGIN { - @INC = '../lib'; + #@INC = '../lib' if -d '../lib' ; + @INC = '../lib' ; require Config; import Config; if ($Config{'extensions'} !~ /\bDB_File\b/) { print "1..0\n"; @@ -12,7 +14,16 @@ BEGIN { use DB_File; use Fcntl; -print "1..43\n"; +print "1..48\n"; + +sub ok +{ + my $no = shift ; + my $result = shift ; + + print "not " unless $result ; + print "ok $no\n" ; +} $Dfile = "dbhash.tmp"; unlink $Dfile; @@ -21,57 +32,61 @@ umask(0); # Check the interface to HASHINFO -#$dbh = TIEHASH DB_File::HASHINFO ; -$dbh = new DB_File::HASHINFO ; -print (($dbh->{bsize} == undef) ? "ok 1\n" : "not ok 1\n") ; -print (($dbh->{ffactor} == undef) ? "ok 2\n" : "not ok 2\n") ; -print (($dbh->{nelem} == undef) ? "ok 3\n" : "not ok 3\n") ; -print (($dbh->{cachesize} == undef) ? "ok 4\n" : "not ok 4\n") ; -print (($dbh->{hash} == undef) ? "ok 5\n" : "not ok 5\n") ; -print (($dbh->{lorder} == undef) ? "ok 6\n" : "not ok 6\n") ; +my $dbh = new DB_File::HASHINFO ; + +$^W = 0 ; +ok(1, $dbh->{bsize} == undef) ; +ok(2, $dbh->{ffactor} == undef) ; +ok(3, $dbh->{nelem} == undef) ; +ok(4, $dbh->{cachesize} == undef) ; +ok(5, $dbh->{hash} == undef) ; +ok(6, $dbh->{lorder} == undef) ; +$^W = 1 ; $dbh->{bsize} = 3000 ; -print ($dbh->{bsize} == 3000 ? "ok 7\n" : "not ok 7\n") ; +ok(7, $dbh->{bsize} == 3000 ); $dbh->{ffactor} = 9000 ; -print ($dbh->{ffactor} == 9000 ? "ok 8\n" : "not ok 8\n") ; -# +ok(8, $dbh->{ffactor} == 9000 ); + $dbh->{nelem} = 400 ; -print (($dbh->{nelem} == 400) ? "ok 9\n" : "not ok 9\n") ; +ok(9, $dbh->{nelem} == 400 ); $dbh->{cachesize} = 65 ; -print (($dbh->{cachesize} == 65) ? "ok 10\n" : "not ok 10\n") ; +ok(10, $dbh->{cachesize} == 65 ); $dbh->{hash} = "abc" ; -print (($dbh->{hash} eq "abc") ? "ok 11\n" : "not ok 11\n") ; +ok(11, $dbh->{hash} eq "abc" ); $dbh->{lorder} = 1234 ; -print ($dbh->{lorder} == 1234 ? "ok 12\n" : "not ok 12\n") ; +ok(12, $dbh->{lorder} == 1234 ); # Check that an invalid entry is caught both for store & fetch eval '$dbh->{fred} = 1234' ; -print ($@ =~ /^DB_File::HASHINFO::STORE - Unknown element 'fred' at/ ? "ok 13\n" : "not ok 13\n") ; +ok(13, $@ =~ /^DB_File::HASHINFO::STORE - Unknown element 'fred' at/ ); eval '$q = $dbh->{fred}' ; -print ($@ =~ /^DB_File::HASHINFO::FETCH - Unknown element 'fred' at/ ? "ok 14\n" : "not ok 14\n") ; +ok(14, $@ =~ /^DB_File::HASHINFO::FETCH - Unknown element 'fred' at/ ); # Now check the interface to HASH -print (($X = tie(%h, DB_File,$Dfile, O_RDWR|O_CREAT, 0640, $DB_HASH )) ? "ok 15\n" : "not ok 15"); +ok(15, $X = tie(%h, 'DB_File',$Dfile, O_RDWR|O_CREAT, 0640, $DB_HASH ) ); ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime, $blksize,$blocks) = stat($Dfile); -print (($mode & 0777) == 0640 ? "ok 16\n" : "not ok 16\n"); +ok(16, ($mode & 0777) == 0640 ); while (($key,$value) = each(%h)) { $i++; } -print (!$i ? "ok 17\n" : "not ok 17\n"); +ok(17, !$i ); $h{'goner1'} = 'snork'; $h{'abc'} = 'ABC'; -print ($h{'abc'} eq 'ABC' ? "ok 18\n" : "not ok 18\n") ; -print (defined $h{'jimmy'} ? "not ok 19\n" : "ok 19\n"); +ok(18, $h{'abc'} eq 'ABC' ); +ok(19, !defined $h{'jimmy'} ); +ok(20, !exists $h{'jimmy'} ); +ok(21, exists $h{'abc'} ); $h{'def'} = 'DEF'; $h{'jkl','mno'} = "JKL\034MNO"; @@ -103,7 +118,7 @@ untie(%h); # tie to the same file again, do not supply a type - should default to HASH -print (($X = tie(%h,DB_File,$Dfile, O_RDWR, 0640)) ? "ok 20\n" : "not ok 20: $!\n"); +ok(22, $X = tie(%h,'DB_File',$Dfile, O_RDWR, 0640) ); # Modify an entry from the previous tie $h{'g'} = 'G'; @@ -134,8 +149,9 @@ $X->DELETE('goner3'); @keys = keys(%h); @values = values(%h); -if ($#keys == 29 && $#values == 29) {print "ok 21\n";} else {print "not ok 21\n";} +ok(23, $#keys == 29 && $#values == 29) ; +$i = 0 ; while (($key,$value) = each(%h)) { if ($key eq $keys[$i] && $value eq $values[$i] && $key gt $value) { $key =~ y/a-z/A-Z/; @@ -143,30 +159,30 @@ while (($key,$value) = each(%h)) { } } -if ($i == 30) {print "ok 22\n";} else {print "not ok 22\n";} +ok(24, $i == 30) ; @keys = ('blurfl', keys(%h), 'dyick'); -if ($#keys == 31) {print "ok 23\n";} else {print "not ok 23\n";} +ok(25, $#keys == 31) ; $h{'foo'} = ''; -print ($h{'foo'} eq '' ? "ok 24\n" : "not ok 24\n") ; +ok(26, $h{'foo'} eq '' ); $h{''} = 'bar'; -print ($h{''} eq 'bar' ? "ok 25\n" : "not ok 25\n") ; +ok(27, $h{''} eq 'bar' ); # check cache overflow and numeric keys and contents $ok = 1; for ($i = 1; $i < 200; $i++) { $h{$i + 0} = $i + 0; } for ($i = 1; $i < 200; $i++) { $ok = 0 unless $h{$i} == $i; } -print ($ok ? "ok 26\n" : "not ok 26\n"); +ok(28, $ok ); ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime, $blksize,$blocks) = stat($Dfile); -print ($size > 0 ? "ok 27\n" : "not ok 27\n"); +ok(29, $size > 0 ); @h{0..200} = 200..400; @foo = @h{0..200}; -print join(':',200..400) eq join(':',@foo) ? "ok 28\n" : "not ok 28\n"; +ok(30, join(':',200..400) eq join(':',@foo) ); # Now check all the non-tie specific stuff @@ -175,44 +191,47 @@ print join(':',200..400) eq join(':',@foo) ? "ok 28\n" : "not ok 28\n"; # an existing record. $status = $X->put( 'x', 'newvalue', R_NOOVERWRITE) ; -print ($status == 1 ? "ok 29\n" : "not ok 29\n") ; +ok(31, $status == 1 ); # check that the value of the key 'x' has not been changed by the # previous test -print ($h{'x'} eq 'X' ? "ok 30\n" : "not ok 30\n") ; +ok(32, $h{'x'} eq 'X' ); # standard put $status = $X->put('key', 'value') ; -print ($status == 0 ? "ok 31\n" : "not ok 31\n") ; +ok(33, $status == 0 ); #check that previous put can be retrieved +$value = 0 ; $status = $X->get('key', $value) ; -print ($status == 0 ? "ok 32\n" : "not ok 32\n") ; -print ($value eq 'value' ? "ok 33\n" : "not ok 33\n") ; +ok(34, $status == 0 ); +ok(35, $value eq 'value' ); # Attempting to delete an existing key should work $status = $X->del('q') ; -print ($status == 0 ? "ok 34\n" : "not ok 34\n") ; +ok(36, $status == 0 ); # Make sure that the key deleted, cannot be retrieved -print (($h{'q'} eq undef) ? "ok 35\n" : "not ok 35\n") ; +$^W = 0 ; +ok(37, $h{'q'} eq undef ); +$^W = 1 ; # Attempting to delete a non-existant key should fail $status = $X->del('joe') ; -print ($status == 1 ? "ok 36\n" : "not ok 36\n") ; +ok(38, $status == 1 ); # Check the get interface # First a non-existing key $status = $X->get('aaaa', $value) ; -print ($status == 1 ? "ok 37\n" : "not ok 37\n") ; +ok(39, $status == 1 ); # Next an existing key $status = $X->get('a', $value) ; -print ($status == 0 ? "ok 38\n" : "not ok 38\n") ; -print ($value eq 'A' ? "ok 39\n" : "not ok 39\n") ; +ok(40, $status == 0 ); +ok(41, $value eq 'A' ); # seq # ### @@ -227,26 +246,54 @@ print ($value eq 'A' ? "ok 39\n" : "not ok 39\n") ; # #### $status = $X->sync ; -print ($status == 0 ? "ok 40\n" : "not ok 40\n") ; +ok(42, $status == 0 ); # fd # ## $status = $X->fd ; -print ($status != 0 ? "ok 41\n" : "not ok 41\n") ; +ok(43, $status != 0 ); undef $X ; untie %h ; unlink $Dfile; +# clear +# ##### + +ok(44, tie(%h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_HASH ) ); +foreach (1 .. 10) + { $h{$_} = $_ * 100 } + +# check that there are 10 elements in the hash +$i = 0 ; +while (($key,$value) = each(%h)) { + $i++; +} +ok(45, $i == 10); + +# now clear the hash +%h = () ; + +# check it is empty +$i = 0 ; +while (($key,$value) = each(%h)) { + $i++; +} +ok(46, $i == 0); + +untie %h ; +unlink $Dfile ; + + # Now try an in memory file -print (($X = tie(%h, DB_File,undef, O_RDWR|O_CREAT, 0640, $DB_HASH )) ? "ok 42\n" : "not ok 42"); +ok(47, $X = tie(%h, 'DB_File',undef, O_RDWR|O_CREAT, 0640, $DB_HASH ) ); # fd with an in memory file should return fail $status = $X->fd ; -print ($status == -1 ? "ok 43\n" : "not ok 43\n") ; +ok(48, $status == -1 ); untie %h ; undef $X ; diff --git a/t/lib/db-recno.t b/t/lib/db-recno.t index ba5c7ed..9a2695b 100755 --- a/t/lib/db-recno.t +++ b/t/lib/db-recno.t @@ -1,4 +1,4 @@ -#!./perl +#!./perl -w BEGIN { @INC = '../lib'; @@ -23,7 +23,7 @@ sub ok print "ok $no\n" ; } -print "1..35\n"; +print "1..47\n"; my $Dfile = "recno.tmp"; unlink $Dfile ; @@ -33,6 +33,7 @@ umask(0); # Check the interface to RECNOINFO my $dbh = new DB_File::RECNOINFO ; +$^W = 0 ; ok(1, $dbh->{bval} == undef ) ; ok(2, $dbh->{cachesize} == undef) ; ok(3, $dbh->{psize} == undef) ; @@ -40,52 +41,52 @@ ok(4, $dbh->{flags} == undef) ; ok(5, $dbh->{lorder} == undef); ok(6, $dbh->{reclen} == undef); ok(7, $dbh->{bfname} eq undef); +$^W = 0 ; $dbh->{bval} = 3000 ; -print ($dbh->{bval} == 3000 ? "ok 8\n" : "not ok 8\n") ; +ok(8, $dbh->{bval} == 3000 ); $dbh->{cachesize} = 9000 ; -print ($dbh->{cachesize} == 9000 ? "ok 9\n" : "not ok 9\n") ; +ok(9, $dbh->{cachesize} == 9000 ); $dbh->{psize} = 400 ; -print (($dbh->{psize} == 400) ? "ok 10\n" : "not ok 10\n") ; +ok(10, $dbh->{psize} == 400 ); $dbh->{flags} = 65 ; -print (($dbh->{flags} == 65) ? "ok 11\n" : "not ok 11\n") ; +ok(11, $dbh->{flags} == 65 ); $dbh->{lorder} = 123 ; -print (($dbh->{lorder} == 123) ? "ok 12\n" : "not ok 12\n") ; +ok(12, $dbh->{lorder} == 123 ); $dbh->{reclen} = 1234 ; -print ($dbh->{reclen} == 1234 ? "ok 13\n" : "not ok 13\n") ; +ok(13, $dbh->{reclen} == 1234 ); $dbh->{bfname} = 1234 ; -print ($dbh->{bfname} == 1234 ? "ok 14\n" : "not ok 14\n") ; +ok(14, $dbh->{bfname} == 1234 ); # Check that an invalid entry is caught both for store & fetch eval '$dbh->{fred} = 1234' ; -print ($@ =~ /^DB_File::RECNOINFO::STORE - Unknown element 'fred' at/ ? "ok 15\n" : "not ok 15\n") ; +ok(15, $@ =~ /^DB_File::RECNOINFO::STORE - Unknown element 'fred' at/ ); eval 'my $q = $dbh->{fred}' ; -print ($@ =~ /^DB_File::RECNOINFO::FETCH - Unknown element 'fred' at/ ? "ok 16\n" : "not ok 16\n") ; +ok(16, $@ =~ /^DB_File::RECNOINFO::FETCH - Unknown element 'fred' at/ ); # Now check the interface to RECNOINFO my $X ; my @h ; ok(17, $X = tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_RECNO ) ; -#print (($X = tie(%h, DB_File,$Dfile, O_RDWR|O_CREAT, 0640, $DB_BTREE )) ? "ok 19\n" : "not ok 19"); ok(18, ( (stat($Dfile))[2] & 0777) == 0640) ; #my $l = @h ; my $l = $X->length ; -print (!$l ? "ok 19\n" : "not ok 19\n"); +ok(19, !$l ); my @data = qw( a b c d ever f g h i j k longername m n o p) ; $h[0] = shift @data ; -print ($h[0] eq 'a' ? "ok 20\n" : "not ok 20\n") ; +ok(20, $h[0] eq 'a' ); my $ i; foreach (@data) @@ -93,45 +94,58 @@ foreach (@data) unshift (@data, 'a') ; -print (defined $h[1] ? "ok 21\n" : "not ok 21\n"); -print (! defined $h[16] ? "ok 22\n" : "not ok 22\n"); -print ($X->length == @data ? "ok 23\n" : "not ok 23\n") ; +ok(21, defined $h[1] ); +ok(22, ! defined $h[16] ); +ok(23, $X->length == @data ); # Overwrite an entry & check fetch it $h[3] = 'replaced' ; $data[3] = 'replaced' ; -print ($h[3] eq 'replaced' ? "ok 24\n" : "not ok 24\n"); +ok(24, $h[3] eq 'replaced' ); #PUSH my @push_data = qw(added to the end) ; -#push (@h, @push_data) ; +#my push (@h, @push_data) ; $X->push(@push_data) ; push (@data, @push_data) ; -print ($h[++$i] eq 'added' ? "ok 25\n" : "not ok 25\n"); +ok(25, $h[++$i] eq 'added' ); +ok(26, $h[++$i] eq 'to' ); +ok(27, $h[++$i] eq 'the' ); +ok(28, $h[++$i] eq 'end' ); # POP -pop (@data) ; -#$value = pop(@h) ; +my $popped = pop (@data) ; +#my $value = pop(@h) ; my $value = $X->pop ; -print ($value eq 'end' ? "not ok 26\n" : "ok 26\n"); +ok(29, $value eq $popped) ; # SHIFT #$value = shift @h $value = $X->shift ; -print ($value eq shift @data ? "not ok 27\n" : "ok 27\n"); +my $shifted = shift @data ; +ok(30, $value eq $shifted ); # UNSHIFT # empty list $X->unshift ; -print ($X->length == @data ? "ok 28\n" : "not ok 28\n") ; +ok(31, $X->length == @data ); my @new_data = qw(add this to the start of the array) ; #unshift @h, @new_data ; $X->unshift (@new_data) ; unshift (@data, @new_data) ; -print ($X->length == @data ? "ok 29\n" : "not ok 29\n") ; +ok(32, $X->length == @data ); +ok(33, $h[0] eq "add") ; +ok(34, $h[1] eq "this") ; +ok(35, $h[2] eq "to") ; +ok(36, $h[3] eq "the") ; +ok(37, $h[4] eq "start") ; +ok(38, $h[5] eq "of") ; +ok(39, $h[6] eq "the") ; +ok(40, $h[7] eq "array") ; +ok(41, $h[8] eq $data[8]) ; # SPLICE @@ -143,22 +157,22 @@ foreach (@data) { $ok = 0, last if $_ ne $h[$j ++] ; } -print ($ok ? "ok 30\n" : "not ok 30\n") ; +ok(42, $ok ); # Neagtive subscripts # get the last element of the array -print($h[-1] eq $data[-1] ? "ok 31\n" : "not ok 31\n") ; -print($h[-1] eq $h[$X->length -1] ? "ok 32\n" : "not ok 32\n") ; +ok(43, $h[-1] eq $data[-1] ); +ok(44, $h[-1] eq $h[$X->length -1] ); # get the first element using a negative subscript eval '$h[ - ( $X->length)] = "abcd"' ; -print ($@ eq "" ? "ok 33\n" : "not ok 33\n") ; -print ($h[0] eq "abcd" ? "ok 34\n" : "not ok 34\n") ; +ok(45, $@ eq "" ); +ok(46, $h[0] eq "abcd" ); # now try to read before the start of the array eval '$h[ - (1 + $X->length)] = 1234' ; -print ($@ =~ '^Modification of non-creatable array value attempted' ? "ok 35\n" : "not ok 35\n") ; +ok(47, $@ =~ '^Modification of non-creatable array value attempted' ); # IMPORTANT - $X must be undefined before the untie otherwise the # underlying DB close routine will not get called. -- 2.7.4