Author: REHSACK
Date: Tue May 25 12:15:23 2010
New Revision: 14044
Modified:
dbi/trunk/lib/DBD/DBM.pm
Log:
- massive documentation updates, fixes and warnings added
- small pieces of code adapted to behave like documented
Modified: dbi/trunk/lib/DBD/DBM.pm
==============================================================================
--- dbi/trunk/lib/DBD/DBM.pm (original)
+++ dbi/trunk/lib/DBD/DBM.pm Tue May 25 12:15:23 2010
@@ -79,7 +79,7 @@
my $this = $drh->SUPER::connect( $dbname, $user, $auth, $attr );
$this->STORE( 'f_lockfile', '.lck' );
- $this->STORE( 'Active', 1 );
+ $this->STORE( 'Active', 1 );
return $this;
}
@@ -111,21 +111,21 @@
#
if ( ( $attrib eq lc($attrib) ) && ( -1 == index( $attrib, "_" ) ) )
{
- # carp "Usage of '$attrib' is depreciated, use 'dbm_$attrib' instead"
if( $^W );
+ # carp "Usage of '$attrib' is depreciated, use 'dbm_$attrib' instead"
if( $^W );
$attrib = "dbm_" . $attrib; # backward compatibility - would like
to carp here
}
- if( $attrib eq "dbm_ext" or $attrib eq "dbm_lockfile" )
+ if ( $attrib eq "dbm_ext" or $attrib eq "dbm_lockfile" )
{
- ( my $newattrib = $attrib ) =~ s/^dbm_/f_/g;
- # carp "Attribute '$attrib' is depreciated, use '$newattrib' instead"
if( $^W );
- $attrib = $newattrib;
+ ( my $newattrib = $attrib ) =~ s/^dbm_/f_/g;
+ # carp "Attribute '$attrib' is depreciated, use '$newattrib' instead"
if( $^W );
+ $attrib = $newattrib;
}
return $dbh->SUPER::STORE( $attrib, $value ) unless ( 0 == index( $attrib,
'dbm_' ) );
# throw an error if it has our prefix but isn't a valid attr name
#
if (
- $dbh->{dbm_valid_attrs}->{$attrib}
+ $dbh->{dbm_valid_attrs}->{$attrib}
or $attrib eq 'dbm_valid_attrs' # gotta start somewhere :-)
)
{
@@ -137,7 +137,7 @@
}
else
{
- # throw an error if it has our prefix but isn't a valid attr name
+ # throw an error if it has our prefix but isn't a valid attr name
return $dbh->set_err( $DBI::stderr, "Invalid attribute '$attrib'!" );
}
}
@@ -152,16 +152,16 @@
{
$attrib = "dbm_" . $attrib; # backward compatibility - would like
to carp here
}
- if( $attrib eq "dbm_ext" or $attrib eq "dbm_lockfile" )
+ if ( $attrib eq "dbm_ext" or $attrib eq "dbm_lockfile" )
{
- ( my $newattrib = $attrib ) =~ s/^dbm_/f_/g;
- # carp "Attribute '$attrib' is depreciated, use '$newattrib' instead"
if( $^W );
- $attrib = $newattrib;
+ ( my $newattrib = $attrib ) =~ s/^dbm_/f_/g;
+ # carp "Attribute '$attrib' is depreciated, use '$newattrib' instead"
if( $^W );
+ $attrib = $newattrib;
}
return $dbh->SUPER::FETCH($attrib) unless ( 0 == index( $attrib, 'dbm_' )
);
if (
- $dbh->{dbm_valid_attrs}->{$attrib}
+ $dbh->{dbm_valid_attrs}->{$attrib}
or $attrib eq 'dbm_valid_attrs' # gotta start somewhere :-)
)
{
@@ -172,7 +172,7 @@
}
else
{
- # throw an error if it has our prefix but isn't a valid attr name
+ # throw an error if it has our prefix but isn't a valid attr name
return $dbh->set_err( $DBI::stderr, "Invalid attribute '$attrib'" );
}
}
@@ -336,6 +336,8 @@
$meta->{dbm_mldbm} ||= $dbh->{dbm_mldbm} if ( $dbh->{dbm_mldbm} );
$meta->{dbm_berkeley_flags} ||= $dbh->{dbm_berkeley_flags};
+ defined $meta->{f_ext}
+ or $meta->{f_ext} = $dbh->{f_ext};
unless ( defined( $meta->{f_ext} ) )
{
my $ext;
@@ -469,8 +471,16 @@
}
$col_names ||= $meta->{col_names} || [ 'k', 'v' ];
$col_names = [ split /,/, $col_names ] if ( ref $col_names ne 'ARRAY'
);
- $meta->{schema} = $schema;
+ if ( $meta->{dbm_store_metadata} and not $meta->{hash}->{"_metadata
\0"} )
+ {
+ $schema or $schema = '';
+ $meta->{hash}->{"_metadata \0"} = join( "",
+ "<dbd_metadata>",
"<schema>$schema</schema>",
+ "<col_names>", join( ",",
@{$col_names} ) . "</col_names>",
+ "</dbd_metadata>" );
+ }
+ $meta->{schema} = $schema;
$meta->{col_names} = $col_names;
}
}
@@ -656,37 +666,47 @@
=head1 SYNOPSIS
use DBI;
- $dbh = DBI->connect('dbi:DBM:'); # defaults to SDBM_File
- $dbh = DBI->connect('DBI:DBM(RaiseError=1):'); # defaults to SDBM_File
- $dbh = DBI->connect('dbi:DBM:type=GDBM_File'); # defaults to GDBM_File
- $dbh = DBI->connect('dbi:DBM:mldbm=Storable'); # MLDBM with SDBM_File
- # and Storable
-
-or
+ $dbh = DBI->connect('dbi:DBM:'); # defaults to SDBM_File
+ $dbh = DBI->connect('DBI:DBM(RaiseError=1):'); # defaults to SDBM_File
+ $dbh = DBI->connect('dbi:DBM:dbm_type=DB_File'); # defaults to DB_File
+ $dbh = DBI->connect('dbi:DBM:dbm_mldbm=Storable'); # MLDBM with SDBM_File
+ # or
$dbh = DBI->connect('dbi:DBM:', undef, undef);
- $dbh = DBI->connect('dbi:DBM:', undef, undef, { dbm_type => 'ODBM_File' });
-
-and other variations on connect() as shown in the DBI docs and with
-the dbm_ attributes shown below
+ $dbh = DBI->connect('dbi:DBM:', undef, undef, {
+ f_ext => '.db/r',
+ f_dir => '/path/to/dbfiles/',
+ f_lockfile => '.lck',
+ dbm_type => 'BerkeleyDB',
+ dbm_mldbm => 'FreezeThaw',
+ dbm_store_metadata => 1,
+ dbm_berkeley_flags => {
+ '-Cachesize' => 1000, # set a ::Hash flag
+ },
+ });
+
+and other variations on connect() as shown in the L<DBI> docs, the
+L<DBD::File/Metadata|DBD::File metadata> and with the L</Metadata>
+shown below.
-... and then use standard DBI prepare, execute, fetch, placeholders, etc.,
+Use standard DBI prepare, execute, fetch, placeholders, etc.,
see L<QUICK START> for an example
=head1 DESCRIPTION
DBD::DBM is a database management sytem that can work right out of the box.
If you have a standard installation of Perl and a standard installation of
-DBI, you can begin creating, accessing, and modifying database tables
-without any further installation. You can also add some other modules to
-it for more robust capabilities if you wish.
+DBI, you can begin creating, accessing, and modifying simple database tables
+without any further installation. You can (and should) also add some other
+modules to it for more robust capabilities if you wish.
The module uses a DBM file storage layer. DBM file storage is common on
-many platforms and files can be created with it in many languages. That
-means that, in addition to creating files with DBI/SQL, you can also use
-DBI/SQL to access and modify files created by other DBM modules and
-programs. You can also use those programs to access files created with
-DBD::DBM.
+many platforms and files can be created with it in many programming
+languages using different APIs. That means that, in addition to creating
+files with DBI/SQL, you can also use DBI/SQL to access and modify files
+created by other DBM modules and programs and vice versa. B<Note> that
+in those cases it might be necessary to use a common subset of the
+provided features.
DBM files are stored in binary format optimized for quick retrieval when
using a key field. That optimization can be used advantageously to make
@@ -699,7 +719,8 @@
DBD::DBM has been tested with the following DBM types: SDBM_File,
NDBM_File, ODBM_File, GDBM_File, DB_File, BerekeleyDB. Each type was
-tested both with and without MLDBM.
+tested both with and without MLDBM and serializers Data::Dumper, Storable,
+FreezeThaw and YAML.
=head1 QUICK START
@@ -737,28 +758,53 @@
=head1 USAGE
+This chapter will explain some use cases a bit deeper. To get an overview
+about the available attributes, see L</Metadata>.
+
=head2 Specifiying Files and Directories
DBD::DBM will automatically supply an appropriate file extension for the
type of DBM you are using. For example, if you use SDBM_File, a table
called "fruit" will be stored in two files called "fruit.pag" and
-"fruit.dir". You should I<never> specify the file extensions in your SQL
+"fruit.dir". You should B<never> specify the file extensions in your SQL
statements.
-However, I am not aware (and therefore DBD::DBM is not aware) of all
-possible extensions for various DBM types. If your DBM type uses an
-extension other than .pag and .dir, you should set the I<f_ext> attribute
-to the extension. B<And> you should write me with the name of the
-implementation and extension so I can add it to DBD::DBM! Thanks in advance
-for that :-).
+DBD::DBM recognizes following default extensions for following types:
+
+=over 4
- $dbh = DBI->connect('dbi:DBM:f_ext=.db'); # .db extension is used
- $dbh = DBI->connect('dbi:DBM:f_ext='); # no extension is used
+=item .pag/r
-or
+Chosen for dbm_type C<< SDBM_File >>, C<< ODBM_File >> and C<< NDBM_File >>
+when an implementation is detected which wraps C<< -ldbm >> for
+C<< NDBM_File >> (e.g. Solaris, AIX, ...).
- $dbh->{f_ext}='.db'; # global setting
- $dbh->{f_meta}->{'qux'}->{f_ext}='.db'; # setting for table 'qux'
+For those types, the C<< .dir >> extension is recognized, too (for being
+deleted when dropping a table).
+
+=item .db/r
+
+Chosen for dbm_type C<< NDBM_File >> when an implementation is detected
+which wraps BerkeleyDB 1.x for C<< NDBM_File >> (typically BSD's, Darwin).
+
+=back
+
+C<< GDBM_File >>, C<< DB_File >> and C<< BerkeleyDB >> are known to
+avoid adding own file extension.
+
+However, we are not aware (and therefore DBD::DBM is not aware) of all
+possible extensions for various DBM types. If your DBM type uses an
+extension other than recognized types and extensions, you should set the
+I<f_ext> attribute to the extension. B<And> you should fill a bug report
+as described in DBI with the name of the implementation and extension so
+we can add it to DBD::DBM! Thanks in advance for that :-).
+
+ $dbh = DBI->connect('dbi:DBM:f_ext=.db'); # .db extension is used
+ $dbh = DBI->connect('dbi:DBM:f_ext='); # no extension is used
+
+ # or
+ $dbh->{f_ext}='.db'; # global setting
+ $dbh->{f_meta}->{'qux'}->{f_ext}='.db'; # setting for table 'qux'
By default files are assumed to be in the current working directory. To
have the module look in a different directory, specify the I<f_dir>
@@ -768,14 +814,15 @@
For example, this will look for the file /foo/bar/fruit (or
/foo/bar/fruit.pag for DBM types that use that extension)
- my $dbh = DBI->connect('dbi:DBM:f_dir=/foo/bar');
- my $ary = $dbh->selectall_arrayref(q{ SELECT * FROM fruit });
-
-And this will too:
+ my $dbh = DBI->connect('dbi:DBM:f_dir=/foo/bar');
+ # and this will do, too:
+ my $dbh = DBI->connect('dbi:DBM:');
+ $dbh->{f_dir} = '/foo/bar';
+ # even if only this is recommended
+ my $dbh = DBI->connect('dbi:DBM:', undef, undef, { f_dir => '/foo/bar' } );
- my $dbh = DBI->connect('dbi:DBM:');
- $dbh->{f_dir} = '/foo/bar';
- my $ary = $dbh->selectall_arrayref(q{ SELECT x FROM fruit });
+ # now you can do
+ my $ary = $dbh->selectall_arrayref(q{ SELECT x FROM fruit });
You can also use delimited identifiers to specify paths directly in SQL
statements. This looks in the same place as the two examples above but
@@ -797,12 +844,13 @@
=head2 Table locking and flock()
-Table locking is accomplished using a lockfile which has the same name as
-the table's file but with the file extension '.lck' (or a lockfile
+Table locking is accomplished using a lockfile which has the same basename
+as the table's file but with the file extension '.lck' (or a lockfile
extension that you supply, see below). This file is created along with the
table during a CREATE and removed during a DROP. Every time the table
itself is opened, the lockfile is flocked(). For SELECT, this is an shared
-lock. For all other operations, it is an exclusive lock.
+lock. For all other operations, it is an exclusive lock (except you specify
+something different using the I<f_lock> attribute).
Since the locking depends on flock(), it only works on operating systems
that support flock(). In cases where flock() is not implemented, DBD::DBM
@@ -823,29 +871,31 @@
=head2 Specifying the DBM type
Each "flavor" of DBM stores its files in a different format and has
-different capabilities and different limitations. See L<AnyDBM_File> for a
+different capabilities and different limitations. See L<AnyDBM_File> for a
comparison of DBM types.
-By default, DBD::DBM uses the SDBM_File type of storage since SDBM_File
-comes with Perl itself. But if you have other types of DBM storage
-available, you can use any of them with DBD::DBM also.
-
-You can specify the DBM type using the "dbm_type" attribute which can be
-set in the connection string or with the $dbh->{dbm_type} attribute for
-global settings or with the $dbh->{dbm_tables}->{$table_name}->{type}
+By default, DBD::DBM uses the C<< SDBM_File >> type of storage since
+C<< SDBM_File >> comes with Perl itself. But if you have other types of DBM
+storage available, you can use any of them with DBD::DBM also. It is
+strongly recommended to use at least C<< DB_File >>, because C<< SDBM_File >>
+has quirks and limitations and C<< ODBM_file >>, C<< NDBM_File >> and
+C<< GDBM_File >> are not always available.
+
+You can specify the DBM type using the I<dbm_type> attribute which can be
+set in the connection string or with the C<< $dbh->{dbm_type} >> attribute
+for global settings or with the C<< $dbh->{f_meta}->{$table_name}->{type} >>
attribute for per-table settings in cases where a single script is
accessing more than one kind of DBM file.
-In the connection string, just set type=TYPENAME where TYPENAME is any DBM
-type such as GDBM_File, DB_File, etc. Do I<not> use MLDBM as your
-dbm_type, that is set differently, see below.
-
- my $dbh=DBI->connect('dbi:DBM:'); # uses the default SDBM_File
- my $dbh=DBI->connect('dbi:DBM:type=GDBM_File'); # uses the GDBM_File
+In the connection string, just set C<< dbm_type=TYPENAME >> where
+C<< TYPENAME >> is any DBM type such as GDBM_File, DB_File, etc. Do I<not>
+use MLDBM as your I<dbm_type>, that is set differently, see below.
-You can also use $dbh->{dbm_type} to set global DBM type:
+ my $dbh=DBI->connect('dbi:DBM:'); # uses the default
SDBM_File
+ my $dbh=DBI->connect('dbi:DBM:dbm_type=GDBM_File'); # uses the GDBM_File
- $dbh->{dbm_type} = 'GDBM_File'; # set the global DBM type
+ # You can also use $dbh->{dbm_type} to set global DBM type:
+ $dbh->{dbm_type} = 'DB_File'; # set the global DBM type
print $dbh->{dbm_type}; # display the global DBM type
If you are going to have several tables in your script that come from
@@ -853,58 +903,62 @@
different settings for the various tables. You can even use this to
perform joins on files that have completely different storage mechanisms.
- my $dbh->('dbi:DBM:type=GDBM_File');
- #
# sets global default of GDBM_File
+ my $dbh->('dbi:DBM:type=GDBM_File');
- my $dbh->{dbm_tables}->{foo}->{type} = 'DB_File';
- #
- # over-rides the global setting, but only for the table called "foo"
+ # over-rides the global setting, but only for the tables called
+ # I<foo> and I<bar>
+ my $dbh->{f_meta}->{foo}->{dbm_type} = 'DB_File';
+ my $dbh->{f_meta}->{bar}->{dbm_type} = 'BerkeleyDB';
- print $dbh->{dbm_tables}->{foo}->{type};
- #
# prints the dbm_type for the table "foo"
+ print $dbh->{f_meta}->{foo}->{dbm_type};
+
+B<Note> that you must change the I<dbm_type> of a table before you access
+it first time.
=head2 Adding multi-column support with MLDBM
-Most of the DBM types only support two columns. However a CPAN module
-called MLDBM overcomes this limitation by allowing more than two columns.
-It does this by serializing the data - basically it puts a reference to an
-array into the second column. It can also put almost any kind of Perl
-object or even Perl coderefs into columns.
+Most of the DBM types only support two columns and even if it would support
+more, DBD::DBM would only use two. However a CPAN module called MLDBM
+overcomes this limitation by allowing more than two columns. It does this
+by serializing the data - basically it puts a reference to an array into
+the second column. It can also put almost any kind of Perl object or even
+B<Perl coderefs> into columns.
-If you want more than two columns, you must install MLDBM. It's available
+If you want more than two columns, you B<must> install MLDBM. It's available
for many platforms and is easy to install.
-MLDBM can use three different modules to serialize the column -
-Data::Dumper, Storable, and FreezeThaw. Data::Dumper is the default,
-Storable is the fastest. MLDBM can also make use of user-defined
-serialization methods. All of this is available to you through DBD::DBM
-with just one attribute setting.
-
-To use MLDBM with DBD::DBM, you need to set the dbm_mldbm attribute to the
-name of the serialization module.
+MLDBM is defaultly distributed with three serializers - Data::Dumper,
+Storable, and FreezeThaw. Data::Dumper is the default, Storable is the
+fastest. MLDBM can also make use of user-defined serialization methods or
+other serialization modules (e.g. L<YAML::MLDBM>). All of this is available
+to you through DBD::DBM with just one attribute setting: I<dbm_mldbm>.
Some examples:
- $dbh=DBI->connect('dbi:DBM:mldbm=Storable'); # use MLDBM with Storable
+ $dbh=DBI->connect('dbi:DBM:dbm_mldbm=Storable'); # use MLDBM with Storable
$dbh=DBI->connect(
- 'dbi:DBM:mldbm=MySerializer' # use MLDBM with a user defined
module
+ 'dbi:DBM:dbm_mldbm=MySerializer' # use MLDBM with a user defined module
);
- $dbh->{dbm_mldbm} = 'MySerializer'; # same as above
- print $dbh->{dbm_mldbm} # show the MLDBM serializer
- $dbh->{dbm_tables}->{foo}->{mldbm}='Data::Dumper'; # set Data::Dumper for
table "foo"
- print $dbh->{dbm_tables}->{foo}->{mldbm}; # show serializer for table "foo"
+ $dbh=DBI->connect('dbi::dbm:', undef,
+ undef, { dbm_mldbm => 'YAML' }); # use 3rd party serializer
+ $dbh->{dbm_mldbm} = 'YAML'; # same as above
+ print $dbh->{dbm_mldbm} # show the MLDBM serializer
+ $dbh->{f_meta}->{foo}->{dbm_mldbm}='Data::Dumper'; # set Data::Dumper for
table "foo"
+ print $dbh->{f_meta}->{foo}->{mldbm}; # show serializer for table "foo"
MLDBM works on top of other DBM modules so you can also set a DBM type
along with setting dbm_mldbm. The examples above would default to using
SDBM_File with MLDBM. If you wanted GDBM_File instead, here's how:
- $dbh = DBI->connect('dbi:DBM:type=GDBM_File;mldbm=Storable');
- #
- # uses GDBM_File with MLDBM and Storable
+ # uses DB_File with MLDBM and Storable
+ $dbh = DBI->connect('dbi:DBM:', undef, undef, {
+ dbm_type => 'DB_File',
+ dbm_mldbm => 'Storable',
+ });
-SDBM_File, the default file type is quite limited, so if you are going to
+SDBM_File, the default I<dbm_type> is quite limited, so if you are going to
use MLDBM, you should probably use a different type, see L<AnyDBM_File>.
See below for some L<GOTCHAS AND WARNINGS> about MLDBM.
@@ -924,27 +978,79 @@
With BerkeleyDB, you can specify initialization flags by setting them in
your script like this:
- my $dbh = DBI->connect('dbi:DBM:type=BerkeleyDB;mldbm=Storable');
use BerkeleyDB;
my $env = new BerkeleyDB::Env -Home => $dir; # and/or other Env flags
- $dbh->{dbm_berkeley_flags} = {
- 'DB_CREATE' => DB_CREATE # pass in constants
- , 'DB_RDONLY' => DB_RDONLY # pass in constants
- , '-Cachesize' => 1000 # set a ::Hash flag
- , '-Env' => $env # pass in an environment
- };
-
-Do I<not> set the -Flags or -Filename flags, those are determined by the
-SQL (e.g. -Flags => DB_RDONLY is set automatically when you issue a SELECT
-statement).
+ $dbh = DBI->connect('dbi:DBM:', undef, undef, {
+ dbm_type => 'BerkeleyDB',
+ dbm_mldbm => 'Storable',
+ dbm_berkeley_flags => {
+ 'DB_CREATE' => DB_CREATE, # pass in constants
+ 'DB_RDONLY' => DB_RDONLY, # pass in constants
+ '-Cachesize' => 1000, # set a ::Hash flag
+ '-Env' => $env, # pass in an environment
+ },
+ });
+
+Do I<not> set the -Flags or -Filename flags, those are determined and
+overwritten by the SQL (e.g. -Flags => DB_RDONLY is set automatically
+when you issue a SELECT statement).
-Time has not permitted me to provide support in this release of DBD::DBM
+Time has not permitted us to provide support in this release of DBD::DBM
for further Berkeley DB features such as transactions, concurrency,
-locking, etc. I will be working on these in the future and would value
+locking, etc. We will be working on these in the future and would value
suggestions, patches, etc.
See L<DB_File> and L<BerkeleyDB> for further details.
+=head2 Optimizing use of key fields
+
+Most "flavors" of DBM have only two physical columns (but can contain
+multiple logical columns as explained above in
+L<Adding multi-column support with MLDBM>). They work similarly to a
+Perl hash with the first column serving as the key. Like a Perl hash, DBM
+files permit you to do quick lookups by specifying the key and thus avoid
+looping through all records (supported by DBI::SQL::Nano only). Also like
+a Perl hash, the keys must be unique. It is impossible to create two
+records with the same key. To put this all more simply and in SQL terms,
+the key column functions as the I<PRIMARY KEY>.
+
+In DBD::DBM, you can take advantage of the speed of keyed lookups by using
+DBI::SQL::Nano and a WHERE clause with a single equal comparison on the key
+field. For example, the following SQL statements are optimized for keyed
+lookup:
+
+ CREATE TABLE user ( user_name TEXT, phone TEXT);
+ INSERT INTO user VALUES ('Fred Bloggs','233-7777');
+ # ... many more inserts
+ SELECT phone FROM user WHERE user_name='Fred Bloggs';
+
+The "user_name" column is the key column since it is the first column. The
+SELECT statement uses the key column in a single equal comparison -
+"user_name='Fred Bloggs' - so the search will find it very quickly without
+having to loop through however many names were inserted into the table.
+
+In contrast, thes searches on the same table are not optimized:
+
+ 1. SELECT phone FROM user WHERE user_name < 'Fred';
+ 2. SELECT user_name FROM user WHERE phone = '233-7777';
+
+In #1, the operation uses a less-than (<) comparison rather than an equals
+comparison, so it will not be optimized for key searching. In #2, the key
+field "user_name" is not specified in the WHERE clause, and therefore the
+search will need to loop through all rows to find the desired result.
+
+B<Note> that the underlying DBM storage needs to loop over all I<key/value>
+pairs when the optimized fetch is used. SQL::Statement has a massively
+improved where clause evaluation which costs around 15% of the evaluation
+in DBI::SQL::Nano - combined with the loop in the DBM storage the speed
+improvement isn't so impressive.
+
+Even if lookups are sped up by circa 50%, DBI::SQL::Nano and SQL::Statement
+can benefit from the key field optimizations on updating and deleting some
+rows - and here the improved where clause evaluation of SQL::Statement
+might beat DBI::SQL::Nano every time the where clause contains not only
+the key field (or more than one).
+
=head2 Supported SQL syntax
DBD::DBM uses a subset of SQL. The robustness of that subset depends on
@@ -955,8 +1061,9 @@
B<Option #1:> By default, this module inherits its SQL support from
DBI::SQL::Nano that comes with DBI. Nano is, as its name implies, a *very*
small SQL engine. Although limited in scope, it is faster than option #2
-for some operations. See L<DBI::SQL::Nano> for a description of the SQL it
-supports and comparisons of it with option #2.
+for some operations (especially single I<primary key> lookups). See
+L<DBI::SQL::Nano> for a description of the SQL it supports and comparisons
+of it with option #2.
B<Option #2:> If you install the pure Perl CPAN module SQL::Statement,
DBD::DBM will use it instead of Nano. This adds support for table aliases,
@@ -971,44 +1078,44 @@
dbm_versions() method or, if you don't need the full output and version
numbers, just do this:
- print $dbh->{sql_handler};
+ say $dbh->{sql_handler};
That will print out either "SQL::Statement" or "DBI::SQL::Nano".
-=head2 Optimizing use of key fields
-
-Most "flavors" of DBM have only two physical columns (but can contain
-multiple logical columns as explained below). They work similarly to a
-Perl hash with the first column serving as the key. Like a Perl hash, DBM
-files permit you to do quick lookups by specifying the key and thus avoid
-looping through all records. Also like a Perl hash, the keys must be
-unique. It is impossible to create two records with the same key. To put
-this all more simply and in SQL terms, the key column functions as the
-PRIMARY KEY.
-
-In DBD::DBM, you can take advantage of the speed of keyed lookups by using
-a WHERE clause with a single equal comparison on the key field. For
-example, the following SQL statements are optimized for keyed lookup:
-
- CREATE TABLE user ( user_name TEXT, phone TEXT);
- INSERT INTO user VALUES ('Fred Bloggs','233-7777');
- # ... many more inserts
- SELECT phone FROM user WHERE user_name='Fred Bloggs';
-
-The "user_name" column is the key column since it is the first column. The
-SELECT statement uses the key column in a single equal comparison -
-"user_name='Fred Bloggs' - so the search will find it very quickly without
-having to loop through however many names were inserted into the table.
-
-In contrast, thes searches on the same table are not optimized:
-
- 1. SELECT phone FROM user WHERE user_name < 'Fred';
- 2. SELECT user_name FROM user WHERE phone = '233-7777';
+Having the section about optimized access to the DBM storage in mind,
+comparing the benefits of both engines:
-In #1, the operation uses a less-than (<) comparison rather than an equals
-comparison, so it will not be optimized for key searching. In #2, the key
-field "user_name" is not specified in the WHERE clause, and therefore the
-search will need to loop through all rows to find the desired result.
+ # DBI::SQL::Nano is faster
+ $sth = $dbh->prepare( "update foo set value='new' where key=15" );
+ $sth->execute();
+ $sth = $dbh->prepare( "delete from foo where key=27" );
+ $sth->execute();
+ $sth = $dbh->prepare( "select * from foo where key='abc'" );
+
+ # SQL::Statement might faster (depending on DB size)
+ $sth = $dbh->prepare( "update foo set value='new' where key=?" );
+ $sth->execute(15);
+ $sth = $dbh->prepare( "update foo set value=? where key=15" );
+ $sth->execute('new');
+ $sth = $dbh->prepare( "delete from foo where key=?" );
+ $sth->execute(27);
+
+ # SQL::Statement is faster
+ $sth = $dbh->prepare( "update foo set value='new' where value='old'" );
+ $sth->execute();
+ # must be expressed using "where key = 15 or key = 27 or key = 42 or key =
'abc'"
+ # in DBI::SQL::Nano
+ $sth = $dbh->prepare( "delete from foo where key in (15,27,42,'abc')" );
+ $sth->execute();
+ # must be expressed using "where key > 10 and key < 90" in DBI::SQL::Nano
+ $sth = $dbh->prepare( "select * from foo where key between (10,90)" );
+ $sth->execute();
+
+ # only SQL::Statement can handle
+ $sth->prepare( "select * from foo,bar where foo.name = bar.name" );
+ $sth->execute();
+ $sth->prepare( "insert into foo values ( 1, 'foo' ), ( 2, 'bar' )" );
+ $sth->execute();
=head2 Specifying Column Names
@@ -1026,45 +1133,43 @@
Will create a file that has a structure something like this:
- _metadata \0 | foo,bar
+ _metadata \0 |
<dbd_metadata><schema></schema><col_names>foo,bar</col_names></dbd_metadata>
zippy | 1
The next time you access this table with DBD::DBM, it will treat the
-_metadata row as a header rather than as data and will pull the column
+I<_metadata \0> row as a header rather than as data and will pull the column
names from there. However, if you access the file with something other
than DBD::DBM, the row will be treated as a regular data row.
If you do not want the column names stored as a data row in the table you
can set the I<dbm_store_metadata> attribute to 0.
- my $dbh = DBI->connect('dbi:DBM:store_metadata=0');
-
-or
+ my $dbh = DBI->connect('dbi:DBM:', undef, undef, { dbm_store_metadata => 0 });
+ # or
$dbh->{dbm_store_metadata} = 0;
-or, for per-table setting
-
- $dbh->{dbm_tables}->{qux}->{store_metadata} = 0;
+ # or for per-table setting
+ $dbh->{f_meta}->{qux}->{dbm_store_metadata} = 0;
By default, DBD::DBM assumes that you have two columns named "k" and "v"
(short for "key" and "value"). So if you have I<dbm_store_metadata> set to
1 and you want to use alternate column names, you need to specify the
column names like this:
- my $dbh = DBI->connect('dbi:DBM:store_metadata=0;cols=foo,bar');
-
-or
+ my $dbh = DBI->connect('dbi:DBM:', undef, undef, {
+ dbm_store_metadata => 0,
+ dbm_cols => [ qw(foo bar) ],
+ });
+ # or
$dbh->{dbm_store_metadata} = 0;
$dbh->{dbm_cols} = 'foo,bar';
-To set the column names on per-table basis, do this:
-
- $dbh->{dbm_tables}->{qux}->{store_metadata} = 0;
- $dbh->{dbm_tables}->{qux}->{cols} = 'foo,bar';
- #
+ # or to set the column names on per-table basis, do this:
# sets the column names only for table "qux"
+ $dbh->{f_meta}->{qux}->{dbm_store_metadata} = 0;
+ $dbh->{f_meta}->{qux}->{col_names} = [qw(foo bar)];
If you have a file that was created by another DBM program or created with
I<dbm_store_metadata> set to zero and you want to convert it to using
@@ -1074,12 +1179,82 @@
either I<dbm_store_metadata> or setting I<dbm_cols> because the names will
be stored in the file.
+=head1 DBI database handle attributes
+
=head2 Statement handle ($sth) attributes and methods
Most statement handle attributes such as NAME, NUM_OF_FIELDS, etc. are
available only after an execute. The same is true of $sth->rows which is
available after the execute but does I<not> require a fetch.
+=head2 Metadata
+
+=over 4
+
+=item dbm_cols
+
+Contains a comma separated list of column names or an array reference to
+a column names.
+
+=item dbm_type
+
+Contains the DBM storage type. Currently know supported ones are
+C<< ODBM_File >>, C<< NDBM_File >>, C<< SDBM_File >>, C<< GDBM_File >>,
+C<< DB_File >> and C<< BerkeleyDB >>. It's not recommended to use one
+of the first three types - even if C<< SDBM_File >> is the most common
+available I<dbm_type>.
+
+=item dbm_mldbm
+
+Contains the serializer for DBM storage (value column). Requires the
+CPAN module L<MLDBM> installed. Currently know supported serializers are:
+
+=over 8
+
+=item Data::Dumper
+
+Default serializer. Deployed with Perl core.
+
+=item Storable
+
+Faster serializer. Deployed with Perl core.
+
+=item FreezeThaw
+
+Pure Perl serializer, requires L<FreezeThaw> being installed.
+
+=item YAML
+
+Very portable serializer (between languages, not between archirectures).
+Requires L<YAML::MLDBM> being installed.
+
+=back
+
+=item dbm_store_metadata
+
+Boolean value whether to store some metadata in DBM storage or not.
+
+=item dbm_berkeley_flags
+
+Hash reference with additional flags for BerkeleyDB::Hash instantiation.
+
+=item dbm_version
+
+Readonly attribute containing this version of DBD::DBM.
+
+=item f_meta
+
+In addition to the attributes L<DBD::File> recognizes, DBD::DBM cares about
+the (public) attributes col_names (B<Note> not I<dbm_cols> here!), dbm_type,
+dbm_mldbm, dbm_store_metadata and dbm_berkeley_flags. There are, like in
+DBD::File undocumented, internally used attributes. Be very careful when
+modifying attributes you do not know, the consequence might a destroyed
+table.
+
+=back
+
+=head1 DBI database handle methods
+
=head2 The $dbh->dbm_versions() method
The private method dbm_versions() presents a summary of what other modules
@@ -1105,13 +1280,11 @@
declare it as a column of type BLOB (the type is *currently* ignored by the
SQL engine, but heh, it's good form).
-You *must* use placeholders to insert or refer to the data.
-
=head1 GOTCHAS AND WARNINGS
Using the SQL DROP command will remove any file that has the name specified
-in the command with either '.pag' or '.dir' or your {f_ext} appended to
-it. So this be dangerous if you aren't sure what file it refers to:
+in the command with either '.pag' and '.dir', '.db' or your {f_ext} appended
+to it. So this be dangerous if you aren't sure what file it refers to:
$dbh->do(qq{DROP TABLE "/path/to/any/file"});
@@ -1123,9 +1296,7 @@
Different DBM implementations return records in different orders. That
means that you can I<not> depend on the order of records unless you use an
-ORDER BY statement. DBI::SQL::Nano does not currently support ORDER BY
-(though it may soon) so if you need ordering, you'll have to install
-SQL::Statement.
+ORDER BY statement.
DBM data files are platform-specific. To move them from one platform to
another, you'll need to do something along the lines of dumping your data
@@ -1151,24 +1322,45 @@
questions quickly and there are many on the mailing list or in the
newsgroup who can.
+DBD developers for DBD's which rely on DBD::File or DBD::DBM or use one
+of them as example are enforced to join the DBI developers mailing list at
[email protected] and strongly encouraged to join our IRC channel at
+L<irc://irc.perl.org/dbi>.
+
If you have suggestions, ideas for improvements, or bugs to report, please
-write me directly at the email shown below.
+report a bug as described in DBI. Do not mail any of the authors directly,
+you might not get an answer.
When reporting bugs, please send the output of $dbh->dbm_versions($table)
-for a table that exhibits the bug and, if possible, as small a sample as
-you can make of the code that produces the bug. And of course, patches are
-welcome too :-).
+for a table that exhibits the bug and a small sample as you can make of
+the code that produces the bug. And of course, patches are welcome, too
+:-).
+
+If you need enhancements quickly, you can get commercial support like
+described at L<http://dbi.perl.org/support/> or you can contact Jens Rehsack
+at [email protected] for commercial support in Germany.
+
+Please don't bother Jochen Wiedmann or Jeff Zucker for support - they
+hand over the further maintaining to H.Merijn Brand and Jens Rehsack.
=head1 ACKNOWLEDGEMENTS
Many, many thanks to Tim Bunce for prodding me to write this, and for
-copious, wise, and patient suggestions all along the way.
+copious, wise, and patient suggestions all along the way. (Jeff Zucker)
+
+I send my thanks and acknowledgements to H.Merijn Brand for his initial
+refactoring of DBD::File and his strong and ongoing support of
+SQL::Statement. Without him, the current progress never had been reached.
+And I have to name Martin J. Evans for each laugh (and correction) of all
+those funny word creations I (as non-native speaker) made to the
+documentation. And - of course - I have to thank all those unnamed
+contributors and testers from the Perl community. (Jens Rehsack)
=head1 AUTHOR AND COPYRIGHT
This module is written by Jeff Zucker < jzucker AT cpan.org >, who also
-maintained it till 2010. After that, Jens Rehsack & H.Merijn Brand took
-over maintenance.
+maintained it till 2007. After that, in 2010, Jens Rehsack & H.Merijn Brand
+took over maintenance.
Copyright (c) 2004 by Jeff Zucker, all rights reserved.
Copyright (c) 2010 by Jens Rehsack & H.Merijn Brand, all rights reserved.