As mentioned earlier, you can use the MLDBM module to store complex data structures in the DBM file (which apparently accepts only a scalar as a single value). Example 19-5 shows how to do this.

Example 19-5. mldbm.pl

use strict;
use MLDBM qw(DB_File);
use DB_File;
use Data::Dumper ( );
use Fcntl qw(O_RDWR O_CREAT);

my $r = shift;
$r->send_http_header("text/plain");

my $rh = {
          bar => ['a'..'c'],
          tar => { map {$_ => $_**2 } 1..4 },
         };

my $dbfile = "/tmp/foo.db";
tie my %dbm, 'MLDBM', $dbfile, O_RDWR|O_CREAT, 
    0600, $DB_HASH or die $!;
# assign a reference to a Perl datastructure
$dbm{foo} = $rh;
untie %dbm;

# read the assigned value
tie %dbm, 'MLDBM', $dbfile, O_RDWR|O_CREAT, 
    0600, $DB_HASH or die $!;
my $foo = exists $dbm{foo} ? $dbm{foo} : 'undefined';
untie %dbm;

print Data::Dumper::Dumper($foo);

As you can see, this example is very similar to the normal use of DB_File; we just use MLDBM instead, and tell it to use DB_File as an underlying DBM implementation. You can choose any other available implementation instead. If you don't specify one, SDBM_File is used.

The script creates a complicated nested data structure and stores it in the $rhscalar. Then we open the database and store this value as usual.

When we want to retrieve the stored value, we do pretty much the same thing as before. The script uses the Data::Dumper::Dumper method to print out the nested data structure. Here is what it prints:

$VAR1 = {
          'bar' => [
                     'a',
                     'b',
                     'c'
                   ],
          'tar' => {
                     '1' => '1',
                     '2' => '4',
                     '3' => '9',
                     '4' => '16'
                   }
        };

That's exactly what we inserted into the DBM file.

There is one important note, though. If you want to modify a value that is a reference to a data structure, you cannot modify it directly. You have to retrieve the value, modify it, and store it back.

For example, in the above example you cannot do:

tie my %dbm, 'MLDBM', $dbfile, O_RDWR|O_CREAT, 
    0600, $DB_HASH or die $!;
# update the existing key
$dbm{foo}->{bar} = ['a'..'z']; # this doesn't work
untie %dbm;

if the key bar existed before. Instead, you should do the following:

tie my %dbm, 'MLDBM', $dbfile, O_RDWR|O_CREAT, 
    0600, $DB_HASH or die $!;
# update the existing key
my $tmp     = $dbm{foo};
$tmp->{bar} = ['a'..'z'];
$dbm{foo}   = $tmp;       # this works
untie %dbm;

This limitation exists because the perl TIEHASH interface currently has no support for multidimensional ties.

By default, MLDBM uses Data::Dumper to serialize the nested data structures. You may want to use the FreezeThaw or Storableserializer instead. In fact, Storable is the preferred one. To use Storable in our example, you should do:

use MLDBM qw(DB_File Storable);

at the beginning of the script.

Refer to the MLDBM manpage to find out more information about it.