<P>It is possible to omit some or all of the final 4 parameters in the
call to <A HREF="../lib/Pod/perlfunc.html#item_tie"><CODE>tie</CODE></A> and let them take default values. As DB_HASH is the most
common file format used, the call:</P>
<PRE>
tie %A, "DB_File", "filename" ;</PRE>
<P>is equivalent to:</P>
<PRE>
tie %A, "DB_File", "filename", O_CREAT|O_RDWR, 0666, $DB_HASH ;</PRE>
<P>It is also possible to omit the filename parameter as well, so the
call:</P>
<PRE>
tie %A, "DB_File" ;</PRE>
<P>is equivalent to:</P>
<PRE>
tie %A, "DB_File", undef, O_CREAT|O_RDWR, 0666, $DB_HASH ;</PRE>
<P>See <A HREF="#in memory databases">In Memory Databases</A> for a discussion on the use of <A HREF="../lib/Pod/perlfunc.html#item_undef"><CODE>undef</CODE></A>
<P>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
<A HREF="#the api interface">THE API INTERFACE</A>).</P>
<PRE>
use strict ;
use vars qw(@h $H $file $i) ;
use DB_File ;
use Fcntl ;
</PRE>
<PRE>
$file = "text" ;</PRE>
<PRE>
unlink $file ;</PRE>
<PRE>
$H = tie @h, "DB_File", $file, O_RDWR|O_CREAT, 0640, $DB_RECNO
or die "Cannot open file $file: $!\n" ;
</PRE>
<PRE>
# first create a text file to play with
$h[0] = "zero" ;
$h[1] = "one" ;
$h[2] = "two" ;
$h[3] = "three" ;
$h[4] = "four" ;</PRE>
<P></P>
<PRE>
# 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.</PRE>
<PRE>
print "\nORIGINAL\n" ;
foreach $i (0 .. $H->length - 1) {
print "$i: $h[$i]\n" ;
}</PRE>
<PRE>
# use the push & pop methods
$a = $H->pop ;
$H->push("last") ;
print "\nThe last record was [$a]\n" ;</PRE>
<PRE>
# and the shift & unshift methods
$a = $H->shift ;
$H->unshift("first") ;
print "The first record was [$a]\n" ;</PRE>
<PRE>
# Use the API to add a new record after record 2.
$i = 2 ;
$H->put($i, "Newbie", R_IAFTER) ;</PRE>
<PRE>
# and a new record before record 1.
$i = 1 ;
$H->put($i, "New One", R_IBEFORE) ;</PRE>
<PRE>
# delete record 3
$H->del(3) ;</PRE>
<PRE>
# now print the records in reverse order
print "\nREVERSE\n" ;
for ($i = $H->length - 1 ; $i >= 0 ; -- $i)
{ print "$i: $h[$i]\n" }</PRE>
<PRE>
# same again, but use the API functions instead
print "\nREVERSE again\n" ;
my ($s, $k, $v) = (0, 0, 0) ;
for ($s = $H->seq($k, $v, R_LAST) ;
$s == 0 ;
$s = $H->seq($k, $v, R_PREV))
{ print "$k: $v\n" }</PRE>
<PRE>
undef $H ;
untie @h ;</PRE>
<P>and this is what it outputs:</P>
<PRE>
ORIGINAL
0: zero
1: one
2: two
3: three
4: four</PRE>
<PRE>
The last record was [four]
The first record was [zero]</PRE>
<PRE>
REVERSE
5: last
4: three
3: Newbie
2: one
1: New One
0: first</PRE>
<PRE>
REVERSE again
5: last
4: three
3: Newbie
2: one
1: New One
0: first</PRE>
<P>Notes:</P>
<OL>
<LI>
Rather than iterating through the array, <CODE>@h</CODE> like this:
<PRE>
foreach $i (@h)</PRE>
<P>it is necessary to use either this:</P>
<PRE>
foreach $i (0 .. $H->length - 1)</PRE>
<P>or this:</P>
<PRE>
for ($a = $H->get($k, $v, R_FIRST) ;
$a == 0 ;
$a = $H->get($k, $v, R_NEXT) )</PRE>
<P></P>
<LI>
Notice that both times the <A HREF="#item_put"><CODE>put</CODE></A> method was used the record index was
specified using a variable, <CODE>$i</CODE>, rather than the literal value
itself. This is because <A HREF="#item_put"><CODE>put</CODE></A> will return the record number of the
inserted line via that parameter.
<P></P></OL>
<P>
<HR>
<H1><A NAME="the api interface">THE API INTERFACE</A></H1>
<P>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
Berkeley DB documentation.</P>
<P>To do this you need to store a copy of the object returned from the tie.</P>
<PRE>
$db = tie %hash, "DB_File", "filename" ;</PRE>
<P>Once you have done that, you can access the Berkeley DB API functions
as <STRONG>DB_File</STRONG> methods directly like this:</P>
<PRE>
$db->put($key, $value, R_NOOVERWRITE) ;</PRE>
<P><STRONG>Important:</STRONG> If you have saved a copy of the object returned from
<A HREF="../lib/Pod/perlfunc.html#item_tie"><CODE>tie</CODE></A>, the underlying database file will <EM>not</EM> be closed until both
the tied variable is untied and all copies of the saved object are
destroyed.</P>
<PRE>
use DB_File ;
$db = tie %hash, "DB_File", "filename"
or die "Cannot tie filename: $!" ;
...
undef $db ;
untie %hash ;</PRE>
<P>See <A HREF="#the untie() gotcha">The untie() Gotcha</A> for more details.</P>
<P>All the functions defined in <EM>dbopen</EM> are available except for
<A HREF="../lib/Pod/perlfunc.html#item_close"><CODE>close()</CODE></A> and <CODE>dbopen()</CODE> itself. The <STRONG>DB_File</STRONG> method interface to the
supported functions have been implemented to mirror the way Berkeley DB
works whenever possible. In particular note that:</P>
<UL>
<LI>
The methods return a status value. All return 0 on success.
All return -1 to signify an error and set <CODE>$!</CODE> to the exact
error code. The return code 1 generally (but not always) means that the
key specified did not exist in the database.
<P>Other return codes are defined. See below and in the Berkeley DB
documentation for details. The Berkeley DB documentation should be used
as the definitive source.</P>
<P></P>
<LI>
Whenever a Berkeley DB function returns data via one of its parameters,
the equivalent <STRONG>DB_File</STRONG> method does exactly the same.
<P></P>
<LI>
If you are careful, it is possible to mix API calls with the tied
hash/array interface in the same piece of code. Although only a few of
the methods used to implement the tied interface currently make use of
the cursor, you should always assume that the cursor has been changed
any time the tied hash/array interface is used. As an example, this
code will probably not do what you expect:
<PRE>
$X = tie %x, 'DB_File', $filename, O_RDWR|O_CREAT, 0777, $DB_BTREE
or die "Cannot tie $filename: $!" ;</PRE>
<PRE>
# Get the first key/value pair and set the cursor
$X->seq($key, $value, R_FIRST) ;</PRE>
<PRE>
# this line will modify the cursor
$count = scalar keys %x ;</PRE>
<PRE>
# Get the second key/value pair.
# oops, it didn't, it got the last key/value pair!
$X->seq($key, $value, R_NEXT) ;</PRE>
<P>The code above can be rearranged to get around the problem, like this:</P>
<PRE>
$X = tie %x, 'DB_File', $filename, O_RDWR|O_CREAT, 0777, $DB_BTREE
or die "Cannot tie $filename: $!" ;</PRE>
<PRE>
# this line will modify the cursor
$count = scalar keys %x ;</PRE>
<PRE>
# Get the first key/value pair and set the cursor
$X->seq($key, $value, R_FIRST) ;</PRE>
<PRE>
# Get the second key/value pair.
# worked this time.
$X->seq($key, $value, R_NEXT) ;</PRE>
<P></P></UL>
<P>All the constants defined in <EM>dbopen</EM> for use in the flags parameters
in the methods defined below are also available. Refer to the Berkeley
DB documentation for the precise meaning of the flags values.</P>
If a filter has been installed with this method, it will be invoked
every time you read a value from a DBM database.
<P></P></DL>
<P>You can use any combination of the methods, from none, to all four.</P>
<P>All filter methods return the existing filter, if present, or <A HREF="../lib/Pod/perlfunc.html#item_undef"><CODE>undef</CODE></A>
in not.</P>
<P>To delete a filter pass <A HREF="../lib/Pod/perlfunc.html#item_undef"><CODE>undef</CODE></A> to it.</P>
<P>
<H2><A NAME="the filter">The Filter</A></H2>
<P>When each filter is called by Perl, a local copy of <CODE>$_</CODE> will contain
the key or value to be filtered. Filtering is achieved by modifying
the contents of <CODE>$_</CODE>. The return code from the filter is ignored.</P>
<P>
<H2><A NAME="an example the null termination problem.">An Example -- the NULL termination problem.</A></H2>
<P>Consider the following scenario. You have a DBM database
that you need to share with a third-party C application. The C application
assumes that <EM>all</EM> keys and values are NULL terminated. Unfortunately
when Perl writes to DBM databases it doesn't use NULL termination, so
your Perl application will have to manage NULL termination itself. When
you write to the database you will have to use something like this:</P>
<PRE>
$hash{"$key\0"} = "$value\0" ;</PRE>
<P>Similarly the NULL needs to be taken into account when you are considering
the length of existing keys/values.</P>
<P>It would be much better if you could ignore the NULL terminations issue
in the main application code and have a mechanism that automatically
added the terminating NULL to all keys and values whenever you write to
the database and have them removed when you read from the database. As I'm
sure you have already guessed, this is a problem that DBM Filters can
fix very easily.</P>
<PRE>
use strict ;
use DB_File ;</PRE>
<PRE>
my %hash ;
my $filename = "/tmp/filt" ;
unlink $filename ;</PRE>
<PRE>
my $db = tie %hash, 'DB_File', $filename, O_CREAT|O_RDWR, 0666, $DB_HASH
or die "Cannot open $filename: $!\n" ;</PRE>
<PRE>
# Install DBM Filters
$db->filter_fetch_key ( sub { s/\0$// } ) ;
$db->filter_store_key ( sub { $_ .= "\0" } ) ;
$db->filter_fetch_value( sub { s/\0$// } ) ;
$db->filter_store_value( sub { $_ .= "\0" } ) ;</PRE>
<PRE>
$hash{"abc"} = "def" ;
my $a = $hash{"ABC"} ;
# ...
undef $db ;
untie %hash ;</PRE>
<P>Hopefully the contents of each of the filters should be
self-explanatory. Both ``fetch'' filters remove the terminating NULL,
and both ``store'' filters add a terminating NULL.</P>
<P>
<H2><A NAME="another example key is a c int.">Another Example -- Key is a C int.</A></H2>
<P>Here is another real-life example. By default, whenever Perl writes to
a DBM database it always writes the key and value as strings. So when
you use this:</P>
<PRE>
$hash{12345} = "soemthing" ;</PRE>
<P>the key 12345 will get stored in the DBM database as the 5 byte string
``12345''. If you actually want the key to be stored in the DBM database
as a C int, you will have to use <A HREF="../lib/Pod/perlfunc.html#item_pack"><CODE>pack</CODE></A> when writing, and <A HREF="../lib/Pod/perlfunc.html#item_unpack"><CODE>unpack</CODE></A>
when reading.</P>
<P>Here is a DBM Filter that does it:</P>
<PRE>
use strict ;
use DB_File ;
my %hash ;
my $filename = "/tmp/filt" ;
unlink $filename ;</PRE>
<PRE>
my $db = tie %hash, 'DB_File', $filename, O_CREAT|O_RDWR, 0666, $DB_HASH
<P>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 <EM>fd</EM> method to get the file descriptor, and then a careful
<A HREF="../lib/Pod/perlfunc.html#item_open"><CODE>open()</CODE></A> to give something Perl will <A HREF="../lib/Pod/perlfunc.html#item_flock"><CODE>flock()</CODE></A> for you. Run this repeatedly
in the background to watch the locks granted in proper order.</P>