# DB_File.pm -- Perl 5 interface to Berkeley DB ## written by Paul Marquess (pmarquess@bfsec.bt.co.uk)# last modified 14th November 1995# version 1.01package DB_File::HASHINFO ;use strict;use vars qw(%elements);use Carp;sub TIEHASH{    bless {} ;}%elements = ( 'bsize'     => 0,              'ffactor'   => 0,              'nelem'     => 0,              'cachesize' => 0,              'hash'      => 0,              'lorder'    => 0            ) ;sub FETCH {      return $_[0]{$_[1]} if defined $elements{$_[1]}  ;    croak "DB_File::HASHINFO::FETCH - Unknown element '$_[1]'" ;}sub STORE {    if ( defined $elements{$_[1]} )    {        $_[0]{$_[1]} = $_[2] ;        return ;    }        croak "DB_File::HASHINFO::STORE - Unknown element '$_[1]'" ;}sub DELETE {    if ( defined $elements{$_[1]} )    {        delete ${$_[0]}{$_[1]} ;        return ;    }        croak "DB_File::HASHINFO::DELETE - Unknown element '$_[1]'" ;}sub DESTROY {undef %{$_[0]} }sub FIRSTKEY { croak "DB_File::HASHINFO::FIRSTKEY is not implemented" }sub NEXTKEY { croak "DB_File::HASHINFO::NEXTKEY is not implemented" }sub EXISTS { croak "DB_File::HASHINFO::EXISTS is not implemented" }sub CLEAR { croak "DB_File::HASHINFO::CLEAR is not implemented" }package DB_File::BTREEINFO ;use strict;use vars qw(%elements);use Carp;sub TIEHASH{    bless {} ;}%elements = ( 'flags'	=> 0,              'cachesize'  => 0,              'maxkeypage' => 0,              'minkeypage' => 0,              'psize'      => 0,              'compare'    => 0,              'prefix'     => 0,              'lorder'     => 0            ) ;sub FETCH {      return $_[0]{$_[1]} if defined $elements{$_[1]}  ;    croak "DB_File::BTREEINFO::FETCH - Unknown element '$_[1]'" ;}sub STORE {    if ( defined $elements{$_[1]} )    {        $_[0]{$_[1]} = $_[2] ;        return ;    }        croak "DB_File::BTREEINFO::STORE - Unknown element '$_[1]'" ;}sub DELETE {    if ( defined $elements{$_[1]} )    {        delete ${$_[0]}{$_[1]} ;        return ;    }        croak "DB_File::BTREEINFO::DELETE - Unknown element '$_[1]'" ;}sub DESTROY {undef %{$_[0]} }sub FIRSTKEY { croak "DB_File::BTREEINFO::FIRSTKEY is not implemented" }sub NEXTKEY { croak "DB_File::BTREEINFO::NEXTKEY is not implemented" }sub EXISTS { croak "DB_File::BTREEINFO::EXISTS is not implemented" }sub CLEAR { croak "DB_File::BTREEINFO::CLEAR is not implemented" }package DB_File::RECNOINFO ;use strict;use vars qw(%elements);use Carp;sub TIEHASH{    bless {} ;}%elements = ( 'bval'      => 0,              'cachesize' => 0,              'psize'     => 0,              'flags'     => 0,              'lorder'    => 0,              'reclen'    => 0,              'bfname'    => 0            ) ;sub FETCH {      return $_[0]{$_[1]} if defined $elements{$_[1]}  ;    croak "DB_File::RECNOINFO::FETCH - Unknown element '$_[1]'" ;}sub STORE {    if ( defined $elements{$_[1]} )    {        $_[0]{$_[1]} = $_[2] ;        return ;    }        croak "DB_File::RECNOINFO::STORE - Unknown element '$_[1]'" ;}sub DELETE {    if ( defined $elements{$_[1]} )    {        delete ${$_[0]}{$_[1]} ;        return ;    }        croak "DB_File::RECNOINFO::DELETE - Unknown element '$_[1]'" ;}sub DESTROY {undef %{$_[0]} }sub FIRSTKEY { croak "DB_File::RECNOINFO::FIRSTKEY is not implemented" }sub NEXTKEY { croak "DB_File::RECNOINFO::NEXTKEY is not implemented" }sub EXISTS { croak "DB_File::BTREEINFO::EXISTS is not implemented" }sub CLEAR { croak "DB_File::BTREEINFO::CLEAR is not implemented" }package DB_File ;use strict;use vars qw($VERSION @ISA @EXPORT $AUTOLOAD $DB_BTREE $DB_HASH $DB_RECNO) ;use Carp;$VERSION = "1.01" ;#typedef enum { DB_BTREE, DB_HASH, DB_RECNO } DBTYPE;$DB_BTREE = TIEHASH DB_File::BTREEINFO ;$DB_HASH  = TIEHASH DB_File::HASHINFO ;$DB_RECNO = TIEHASH DB_File::RECNOINFO ;require Tie::Hash;require Exporter;use AutoLoader;require DynaLoader;@ISA = qw(Tie::Hash Exporter DynaLoader);@EXPORT = qw(        $DB_BTREE $DB_HASH $DB_RECNO 	BTREEMAGIC	BTREEVERSION	DB_LOCK	DB_SHMEM	DB_TXN	HASHMAGIC	HASHVERSION	MAX_PAGE_NUMBER	MAX_PAGE_OFFSET	MAX_REC_NUMBER	RET_ERROR	RET_SPECIAL	RET_SUCCESS	R_CURSOR	R_DUP	R_FIRST	R_FIXEDLEN	R_IAFTER	R_IBEFORE	R_LAST	R_NEXT	R_NOKEY	R_NOOVERWRITE	R_PREV	R_RECNOSYNC	R_SETCURSOR	R_SNAPSHOT	__R_UNUSED);sub AUTOLOAD {    my($constname);    ($constname = $AUTOLOAD) =~ s/.*:://;    my $val = constant($constname, @_ ? $_[0] : 0);    if ($! != 0) {	if ($! =~ /Invalid/) {	    $AutoLoader::AUTOLOAD = $AUTOLOAD;	    goto &AutoLoader::AUTOLOAD;	}	else {	    my($pack,$file,$line) = caller;	    croak "Your vendor has not defined DB macro $constname, used at $file line $line.";	}    }    eval "sub $AUTOLOAD { $val }";    goto &$AUTOLOAD;}bootstrap DB_File $VERSION;# Preloaded methods go here.  Autoload methods go after __END__, and are# processed by the autosplit program.1;__END__=cut=head1 NAMEDB_File - Perl5 access to Berkeley DB=head1 SYNOPSIS use DB_File ;   [$X =] tie %hash,  DB_File, $filename [, $flags, $mode, $DB_HASH] ; [$X =] tie %hash,  DB_File, $filename, $flags, $mode, $DB_BTREE ; [$X =] tie @array, DB_File, $filename, $flags, $mode, $DB_RECNO ;    $status = $X->del($key [, $flags]) ; $status = $X->put($key, $value [, $flags]) ; $status = $X->get($key, $value [, $flags]) ; $status = $X->seq($key, $value [, $flags]) ; $status = $X->sync([$flags]) ; $status = $X->fd ;     untie %hash ; untie @array ;=head1 DESCRIPTIONB<DB_File> 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 manualpage athand. The interface defined here mirrors the Berkeley DB interfaceclosely.Berkeley DB is a C library which provides a consistent interface to anumber of database formats.  B<DB_File> provides an interface to allthree of the database types currently supported by Berkeley DB.The file types are:=over 5=item DB_HASHThis database type allows arbitrary key/data pairs to be stored in datafiles. This is equivalent to the functionality provided by otherhashing packages like DBM, NDBM, ODBM, GDBM, and SDBM. Remember though,the files created using DB_HASH are not compatible with any of theother packages mentioned.A default hashing algorithm, which will be adequate for mostapplications, is built into Berkeley DB. If you do need to use your ownhashing algorithm it is possible to write your own in Perl and haveB<DB_File> use it instead.=item DB_BTREEThe btree format allows arbitrary key/data pairs to be stored in asorted, balanced binary tree.As with the DB_HASH format, it is possible to provide a user definedPerl routine to perform the comparison of keys. By default, though, thekeys are stored in lexical order.=item DB_RECNODB_RECNO allows both fixed-length and variable-length flat text filesto be manipulated using the same key/value pair interface as in DB_HASHand DB_BTREE.  In this case the key will consist of a record (line)number.=back=head2 How does DB_File interface to Berkeley DB?B<DB_File> allows access to Berkeley DB files using the tie() mechanismin Perl 5 (for full details, see L<perlfunc/tie()>). This facilityallows B<DB_File> to access Berkeley DB files using either anassociative array (for DB_HASH & DB_BTREE file types) or an ordinaryarray (for the DB_RECNO file type).In addition to the tie() interface, it is also possible to use most ofthe functions provided in the Berkeley DB API.=head2 Differences with Berkeley DBBerkeley DB uses the function dbopen() to open or create a database.Below is the C prototype for dbopen().      DB*      dbopen (const char * file, int flags, int mode,               DBTYPE type, const void * openinfo)The parameter C<type> is an enumeration which specifies which of the 3interface methods (DB_HASH, DB_BTREE or DB_RECNO) is to be used.Depending on which of these is actually chosen, the final parameter,I<openinfo> points to a data structure which allows tailoring of thespecific interface method.This interface is handled slightly differently in B<DB_File>. Here isan equivalent call using B<DB_File>.        tie %array, DB_File, $filename, $flags, $mode, $DB_HASH ;The C<filename>, C<flags> and C<mode> parameters are the directequivalent of their dbopen() counterparts. The final parameter $DB_HASHperforms the function of both the C<type> and C<openinfo> parameters indbopen().In the example above $DB_HASH is actually a reference to a hashobject. B<DB_File> has three of these pre-defined references. Apartfrom $DB_HASH, there is also $DB_BTREE and $DB_RECNO.The keys allowed in each of these pre-defined references is limited tothe names used in the equivalent C structure. So, for example, the$DB_HASH reference will only allow keys called C<bsize>, C<cachesize>,C<ffactor>, C<hash>, C<lorder> and C<nelem>.To change one of these elements, just assign to it like this	$DB_HASH->{cachesize} = 10000 ;=head2 RECNO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.=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<DB_File>uses C<undef> instead of NULL to provide this functionality.=head2 Using the Berkeley DB Interface DirectlyAs well as accessing Berkeley DB using a tied hash or array, it is alsopossible to make direct use of most of the functions defined in theBerkeley DB documentation.To do this you need to remember the return value from the tie.	$db = tie %hash, DB_File, "filename"Once you have done that, you can access the Berkeley DB API functionsdirectly.	$db->put($key, $value, R_NOOVERWRITE) ;All the functions defined in L<dbx(3X)> are available except forclose() and dbopen() itself. The B<DB_File> interface to thesefunctions have been implemented to mirror the the way Berkeley DBworks. In particular note that all the functions return only a statusvalue. Whenever a Berkeley DB function returns data via one of itsparameters, the B<DB_File> equivalent does exactly the same.All the constants defined in L<dbopen> are also available.Below is a list of the functions available.=over 5=item getSame as in C<recno> except that the flags parameter is optional.Remember the value associated with the key you request is returned inthe $value parameter.=item putAs usual the flags parameter is optional. If you use either the R_IAFTER or R_IBEFORE flags, the key parameterwill have the record number of the inserted key/value pair set.=item delThe flags parameter is optional.=item fdAs in I<recno>.=item seqThe flags parameter is optional.Both the key and value parameters will be set.=item syncThe flags parameter is optional.=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 ;		# 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 ;=head2 Using BTREEHere is sample of code which used BTREE. Just to make life moreinteresting the default comparision function will not be used. Insteada Perl sub, C<Compare()>, will be used to do a case insensitivecomparison.        use DB_File ;        use Fcntl ;	 	sub Compare        {	    my ($key1, $key2) = @_ ;		    "\L$key1" cmp "\L$key2" ;	}	        $DB_BTREE->{compare} = 'Compare' ;	         tie %h,  'DB_File', "tree", O_RDWR|O_CREAT, 0640, $DB_BTREE ;	         # 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=head2 Using RECNO	use DB_File ;	use Fcntl ;		$DB_RECNO->{psize} = 3000 ;		tie @h,  DB_File, "text", O_RDWR|O_CREAT, 0640, $DB_RECNO ;		# Add a key/value pair to the file	$h[0] = "orange" ;		# Check for existence of a key	print "Exists\n" if $h[1] ;		untie @h ;=head2 Locking DatabasesConcurrent access of a read-write database by several parties requiresthem all to use some kind of locking.  Here's an example of Tom's thatuses the I<fd> 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 DB_File;    use strict;    sub LOCK_SH { 1 }    sub LOCK_EX { 2 }    sub LOCK_NB { 4 }    sub LOCK_UN { 8 }    my($oldval, $fd, $db, %db, $value, $key);    $key = shift || 'default';    $value = shift || 'magic';    $value .= " $$";    $db = tie(%db, 'DB_File', '/tmp/foo.db', O_CREAT|O_RDWR, 0644) 	    || die "dbcreat /tmp/foo.db $!";    $fd = $db->fd;    print "$$: db fd is $fd\n";    open(DB_FH, "+<&=$fd") || die "dup $!";    unless (flock (DB_FH, LOCK_SH | LOCK_NB)) {	print "$$: CONTENTION; can't read during write update!		    Waiting for read lock ($!) ....";	unless (flock (DB_FH, LOCK_SH)) { die "flock: $!" }    }     print "$$: Read lock granted\n";    $oldval = $db{$key};    print "$$: Old value was $oldval\n";    flock(DB_FH, LOCK_UN);    unless (flock (DB_FH, LOCK_EX | LOCK_NB)) {	print "$$: CONTENTION; must have exclusive lock!		    Waiting for write lock ($!) ....";	unless (flock (DB_FH, LOCK_EX)) { die "flock: $!" }    }     print "$$: Write lock granted\n";    $db{$key} = $value;    sleep 10;    flock(DB_FH, LOCK_UN);    untie %db;    close(DB_FH);    print "$$: Updated db to $key=$value\n";=head1 HISTORY=over=item 0.1First Release.=item 0.2When B<DB_File> is opening a database file it no longer terminates theprocess if I<dbopen> returned an error. This allows file protectionerrors to be caught at run time. Thanks to Judith GrassE<lt>grass@cybercash.comE<gt> for spotting the bug.=item 0.3Added prototype support for multiple btree compare callbacks.=item 1.0B<DB_File> has been in use for over a year. To reflect that, theversion number has been incremented to 1.0.Added complete support for multiple concurrent callbacks.Using the I<push> method on an empty list didn't work properly. Thishas been fixed.=item 1.01Fixed a core dump problem with SunOS.The return value from TIEHASH wasn't set to NULL when dbopen returnedan error.=head1 WARNINGSIf you happen 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.If you cannot find any, then either you didn't look very hard or themoment has passed and I have dropped them.=head1 BUGSSome older versions of Berkeley DB had problems with fixed lengthrecords using the RECNO file format. The newest version at the time ofwriting was 1.85 - this seems to have fixed the problems with RECNO.I am sure there are bugs in the code. If you do find any, or cansuggest any enhancements, I would welcome your comments.=head1 AVAILABILITYBerkeley DB is available at your nearest CPAN archive (seeL<perlmod/"CPAN"> for a list) in F<src/misc/db.1.85.tar.gz>, or via thehost F<ftp.cs.berkeley.edu> in F</ucb/4bsd/db.tar.gz>.  It is I<not> underthe GPL.=head1 SEE ALSOL<perl(1)>, L<dbopen(3)>, L<hash(3)>, L<recno(3)>, L<btree(3)> Berkeley DB is available from F<ftp.cs.berkeley.edu> in the directoryF</ucb/4bsd>.=head1 AUTHORThe DB_File interface was written by Paul Marquess<pmarquess@bfsec.bt.co.uk>.Questions about the DB system itself may be addressed to Keith Bostic<bostic@cs.berkeley.edu>.=cut