diff options
Diffstat (limited to 'lib/DBD/DBM.pm')
-rw-r--r-- | lib/DBD/DBM.pm | 1461 |
1 files changed, 1461 insertions, 0 deletions
diff --git a/lib/DBD/DBM.pm b/lib/DBD/DBM.pm new file mode 100644 index 0000000..3c621a3 --- /dev/null +++ b/lib/DBD/DBM.pm @@ -0,0 +1,1461 @@ +####################################################################### +# +# DBD::DBM - a DBI driver for DBM files +# +# Copyright (c) 2004 by Jeff Zucker < jzucker AT cpan.org > +# Copyright (c) 2010 by Jens Rehsack & H.Merijn Brand +# +# All rights reserved. +# +# You may freely distribute and/or modify this module under the terms +# of either the GNU General Public License (GPL) or the Artistic License, +# as specified in the Perl README file. +# +# USERS - see the pod at the bottom of this file +# +# DBD AUTHORS - see the comments in the code +# +####################################################################### +require 5.008; +use strict; + +################# +package DBD::DBM; +################# +use base qw( DBD::File ); +use vars qw($VERSION $ATTRIBUTION $drh $methods_already_installed); +$VERSION = '0.06'; +$ATTRIBUTION = 'DBD::DBM by Jens Rehsack'; + +# no need to have driver() unless you need private methods +# +sub driver ($;$) +{ + my ( $class, $attr ) = @_; + return $drh if ($drh); + + # do the real work in DBD::File + # + $attr->{Attribution} = 'DBD::DBM by Jens Rehsack'; + $drh = $class->SUPER::driver($attr); + + # install private methods + # + # this requires that dbm_ (or foo_) be a registered prefix + # but you can write private methods before official registration + # by hacking the $dbd_prefix_registry in a private copy of DBI.pm + # + unless ( $methods_already_installed++ ) + { + DBD::DBM::st->install_method('dbm_schema'); + } + + return $drh; +} + +sub CLONE +{ + undef $drh; +} + +##################### +package DBD::DBM::dr; +##################### +$DBD::DBM::dr::imp_data_size = 0; +@DBD::DBM::dr::ISA = qw(DBD::File::dr); + +# you could put some :dr private methods here + +# you may need to over-ride some DBD::File::dr methods here +# but you can probably get away with just letting it do the work +# in most cases + +##################### +package DBD::DBM::db; +##################### +$DBD::DBM::db::imp_data_size = 0; +@DBD::DBM::db::ISA = qw(DBD::File::db); + +sub validate_STORE_attr +{ + my ( $dbh, $attrib, $value ) = @_; + + 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; + } + + return $dbh->SUPER::validate_STORE_attr( $attrib, $value ); +} + +sub validate_FETCH_attr +{ + my ( $dbh, $attrib ) = @_; + + 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; + } + + return $dbh->SUPER::validate_FETCH_attr($attrib); +} + +sub set_versions +{ + my $this = $_[0]; + $this->{dbm_version} = $DBD::DBM::VERSION; + return $this->SUPER::set_versions(); +} + +sub init_valid_attributes +{ + my $dbh = shift; + + # define valid private attributes + # + # attempts to set non-valid attrs in connect() or + # with $dbh->{attr} will throw errors + # + # the attrs here *must* start with dbm_ or foo_ + # + # see the STORE methods below for how to check these attrs + # + $dbh->{dbm_valid_attrs} = { + dbm_type => 1, # the global DBM type e.g. SDBM_File + dbm_mldbm => 1, # the global MLDBM serializer + dbm_cols => 1, # the global column names + dbm_version => 1, # verbose DBD::DBM version + dbm_store_metadata => 1, # column names, etc. + dbm_berkeley_flags => 1, # for BerkeleyDB + dbm_valid_attrs => 1, # DBD::DBM::db valid attrs + dbm_readonly_attrs => 1, # DBD::DBM::db r/o attrs + dbm_meta => 1, # DBD::DBM public access for f_meta + dbm_tables => 1, # DBD::DBM public access for f_meta + }; + $dbh->{dbm_readonly_attrs} = { + dbm_version => 1, # verbose DBD::DBM version + dbm_valid_attrs => 1, # DBD::DBM::db valid attrs + dbm_readonly_attrs => 1, # DBD::DBM::db r/o attrs + dbm_meta => 1, # DBD::DBM public access for f_meta + }; + + $dbh->{dbm_meta} = "dbm_tables"; + + return $dbh->SUPER::init_valid_attributes(); +} + +sub init_default_attributes +{ + my ( $dbh, $phase ) = @_; + + $dbh->SUPER::init_default_attributes($phase); + $dbh->{f_lockfile} = '.lck'; + + return $dbh; +} + +sub get_dbm_versions +{ + my ( $dbh, $table ) = @_; + $table ||= ''; + + my $meta; + my $class = $dbh->{ImplementorClass}; + $class =~ s/::db$/::Table/; + $table and ( undef, $meta ) = $class->get_table_meta( $dbh, $table, 1 ); + $meta or ( $meta = {} and $class->bootstrap_table_meta( $dbh, $meta, $table ) ); + + my $dver; + my $dtype = $meta->{dbm_type}; + eval { + $dver = $meta->{dbm_type}->VERSION(); + + # *) when we're still alive here, everthing went ok - no need to check for $@ + $dtype .= " ($dver)"; + }; + if ( $meta->{dbm_mldbm} ) + { + $dtype .= ' + MLDBM'; + eval { + $dver = MLDBM->VERSION(); + $dtype .= " ($dver)"; # (*) + }; + eval { + my $ser_class = "MLDBM::Serializer::" . $meta->{dbm_mldbm}; + my $ser_mod = $ser_class; + $ser_mod =~ s|::|/|g; + $ser_mod .= ".pm"; + require $ser_mod; + $dver = $ser_class->VERSION(); + $dtype .= ' + ' . $ser_class; # (*) + $dver and $dtype .= " ($dver)"; # (*) + }; + } + return sprintf( "%s using %s", $dbh->{dbm_version}, $dtype ); +} + +# you may need to over-ride some DBD::File::db methods here +# but you can probably get away with just letting it do the work +# in most cases + +##################### +package DBD::DBM::st; +##################### +$DBD::DBM::st::imp_data_size = 0; +@DBD::DBM::st::ISA = qw(DBD::File::st); + +sub FETCH +{ + my ( $sth, $attr ) = @_; + + if ( $attr eq "NULLABLE" ) + { + my @colnames = $sth->sql_get_colnames(); + + # XXX only BerkeleyDB fails having NULL values for non-MLDBM databases, + # none accept it for key - but it requires more knowledge between + # queries and tables storage to return fully correct information + $attr eq "NULLABLE" and return [ map { 0 } @colnames ]; + } + + return $sth->SUPER::FETCH($attr); +} # FETCH + +sub dbm_schema +{ + my ( $sth, $tname ) = @_; + return $sth->set_err( $DBI::stderr, 'No table name supplied!' ) unless $tname; + return $sth->set_err( $DBI::stderr, "Unknown table '$tname'!" ) + unless ( $sth->{Database}->{f_meta} + and $sth->{Database}->{f_meta}->{$tname} ); + return $sth->{Database}->{f_meta}->{$tname}->{schema}; +} +# you could put some :st private methods here + +# you may need to over-ride some DBD::File::st methods here +# but you can probably get away with just letting it do the work +# in most cases + +############################ +package DBD::DBM::Statement; +############################ + +@DBD::DBM::Statement::ISA = qw(DBD::File::Statement); + +######################## +package DBD::DBM::Table; +######################## +use Carp; +use Fcntl; + +@DBD::DBM::Table::ISA = qw(DBD::File::Table); + +my $dirfext = $^O eq 'VMS' ? '.sdbm_dir' : '.dir'; + +sub file2table +{ + my ( $self, $meta, $file, $file_is_table, $quoted ) = @_; + + my $tbl = $self->SUPER::file2table( $meta, $file, $file_is_table, $quoted ) or return; + + $meta->{f_dontopen} = 1; + + return $tbl; +} + +my %reset_on_modify = ( + dbm_type => "dbm_tietype", + dbm_mldbm => "dbm_tietype", + ); +__PACKAGE__->register_reset_on_modify( \%reset_on_modify ); + +my %compat_map = ( + ( map { $_ => "dbm_$_" } qw(type mldbm store_metadata) ), + dbm_ext => 'f_ext', + dbm_file => 'f_file', + dbm_lockfile => ' f_lockfile', + ); +__PACKAGE__->register_compat_map (\%compat_map); + +sub bootstrap_table_meta +{ + my ( $self, $dbh, $meta, $table ) = @_; + + $meta->{dbm_type} ||= $dbh->{dbm_type} || 'SDBM_File'; + $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; + if ( $meta->{dbm_type} eq 'SDBM_File' or $meta->{dbm_type} eq 'ODBM_File' ) + { + $ext = '.pag/r'; + } + elsif ( $meta->{dbm_type} eq 'NDBM_File' ) + { + # XXX NDBM_File on FreeBSD (and elsewhere?) may actually be Berkeley + # behind the scenes and so create a single .db file. + if ( $^O =~ /bsd/i or lc($^O) eq 'darwin' ) + { + $ext = '.db/r'; + } + elsif ( $^O eq 'SunOS' or $^O eq 'Solaris' or $^O eq 'AIX' ) + { + $ext = '.pag/r'; # here it's implemented like dbm - just a bit improved + } + # else wrapped GDBM + } + defined($ext) and $meta->{f_ext} = $ext; + } + + $self->SUPER::bootstrap_table_meta( $dbh, $meta, $table ); +} + +sub init_table_meta +{ + my ( $self, $dbh, $meta, $table ) = @_; + + unless ( defined( $meta->{dbm_tietype} ) ) + { + my $tie_type = $meta->{dbm_type}; + $INC{"$tie_type.pm"} or require "$tie_type.pm"; + $tie_type eq 'BerkeleyDB' and $tie_type = 'BerkeleyDB::Hash'; + + if ( $meta->{dbm_mldbm} ) + { + $INC{"MLDBM.pm"} or require "MLDBM.pm"; + $meta->{dbm_usedb} = $tie_type; + $tie_type = 'MLDBM'; + } + + $meta->{dbm_tietype} = $tie_type; + } + + unless ( defined( $meta->{dbm_store_metadata} ) ) + { + my $store = $dbh->{dbm_store_metadata}; + defined($store) or $store = 1; + $meta->{dbm_store_metadata} = $store; + } + + unless ( defined( $meta->{col_names} ) ) + { + defined( $dbh->{dbm_cols} ) and $meta->{col_names} = $dbh->{dbm_cols}; + } + + $self->SUPER::init_table_meta( $dbh, $meta, $table ); +} + +sub open_file +{ + my ( $self, $meta, $attrs, $flags ) = @_; + $self->SUPER::open_file( $meta, $attrs, $flags ); + unless ( $flags->{dropMode} ) + { + # TIEING + # + # XXX allow users to pass in a pre-created tied object + # + my @tie_args; + if ( $meta->{dbm_type} eq 'BerkeleyDB' ) + { + my $DB_CREATE = BerkeleyDB::DB_CREATE(); + my $DB_RDONLY = BerkeleyDB::DB_RDONLY(); + my %tie_flags; + if ( my $f = $meta->{dbm_berkeley_flags} ) + { + defined( $f->{DB_CREATE} ) and $DB_CREATE = delete $f->{DB_CREATE}; + defined( $f->{DB_RDONLY} ) and $DB_RDONLY = delete $f->{DB_RDONLY}; + %tie_flags = %$f; + } + my $open_mode = $flags->{lockMode} || $flags->{createMode} ? $DB_CREATE : $DB_RDONLY; + @tie_args = ( + -Filename => $meta->{f_fqbn}, + -Flags => $open_mode, + %tie_flags + ); + } + else + { + my $open_mode = O_RDONLY; + $flags->{lockMode} and $open_mode = O_RDWR; + $flags->{createMode} and $open_mode = O_RDWR | O_CREAT | O_TRUNC; + + @tie_args = ( $meta->{f_fqbn}, $open_mode, 0666 ); + } + + if ( $meta->{dbm_mldbm} ) + { + $MLDBM::UseDB = $meta->{dbm_usedb}; + $MLDBM::Serializer = $meta->{dbm_mldbm}; + } + + $meta->{hash} = {}; + my $tie_class = $meta->{dbm_tietype}; + eval { tie %{ $meta->{hash} }, $tie_class, @tie_args }; + $@ and croak "Cannot tie(\%h $tie_class @tie_args): $@"; + -f $meta->{f_fqfn} or croak( "No such file: '" . $meta->{f_fqfn} . "'" ); + } + + unless ( $flags->{createMode} ) + { + my ( $meta_data, $schema, $col_names ); + if ( $meta->{dbm_store_metadata} ) + { + $meta_data = $col_names = $meta->{hash}->{"_metadata \0"}; + if ( $meta_data and $meta_data =~ m~<dbd_metadata>(.+)</dbd_metadata>~is ) + { + $schema = $col_names = $1; + $schema =~ s~.*<schema>(.+)</schema>.*~$1~is; + $col_names =~ s~.*<col_names>(.+)</col_names>.*~$1~is; + } + } + $col_names ||= $meta->{col_names} || [ 'k', 'v' ]; + $col_names = [ split /,/, $col_names ] if ( ref $col_names ne 'ARRAY' ); + if ( $meta->{dbm_store_metadata} and not $meta->{hash}->{"_metadata \0"} ) + { + $schema or $schema = ''; + $meta->{hash}->{"_metadata \0"} = + "<dbd_metadata>" + . "<schema>$schema</schema>" + . "<col_names>" + . join( ",", @{$col_names} ) + . "</col_names>" + . "</dbd_metadata>"; + } + + $meta->{schema} = $schema; + $meta->{col_names} = $col_names; + } +} + +# you must define drop +# it is called from execute of a SQL DROP statement +# +sub drop ($$) +{ + my ( $self, $data ) = @_; + my $meta = $self->{meta}; + $meta->{hash} and untie %{ $meta->{hash} }; + $self->SUPER::drop($data); + # XXX extra_files + -f $meta->{f_fqbn} . $dirfext + and $meta->{f_ext} eq '.pag/r' + and unlink( $meta->{f_fqbn} . $dirfext ); + return 1; +} + +# you must define fetch_row, it is called on all fetches; +# it MUST return undef when no rows are left to fetch; +# checking for $ary[0] is specific to hashes so you'll +# probably need some other kind of check for nothing-left. +# as Janis might say: "undef's just another word for +# nothing left to fetch" :-) +# +sub fetch_row ($$) +{ + my ( $self, $data ) = @_; + my $meta = $self->{meta}; + # fetch with %each + # + my @ary = each %{ $meta->{hash} }; + $meta->{dbm_store_metadata} + and $ary[0] + and $ary[0] eq "_metadata \0" + and @ary = each %{ $meta->{hash} }; + + my ( $key, $val ) = @ary; + unless ($key) + { + delete $self->{row}; + return; + } + my @row = ( ref($val) eq 'ARRAY' ) ? ( $key, @$val ) : ( $key, $val ); + $self->{row} = @row ? \@row : undef; + return wantarray ? @row : \@row; +} + +# you must define push_row except insert_new_row and update_specific_row is defined +# it is called on inserts and updates as primitive +# +sub insert_new_row ($$$) +{ + my ( $self, $data, $row_aryref ) = @_; + my $meta = $self->{meta}; + my $ncols = scalar( @{ $meta->{col_names} } ); + my $nitems = scalar( @{$row_aryref} ); + $ncols == $nitems + or croak "You tried to insert $nitems, but table is created with $ncols columns"; + + my $key = shift @$row_aryref; + my $exists; + eval { $exists = exists( $meta->{hash}->{$key} ); }; + $exists and croak "Row with PK '$key' already exists"; + + $meta->{hash}->{$key} = $meta->{dbm_mldbm} ? $row_aryref : $row_aryref->[0]; + + return 1; +} + +# this is where you grab the column names from a CREATE statement +# if you don't need to do that, it must be defined but can be empty +# +sub push_names ($$$) +{ + my ( $self, $data, $row_aryref ) = @_; + my $meta = $self->{meta}; + + # some sanity checks ... + my $ncols = scalar(@$row_aryref); + $ncols < 2 and croak "At least 2 columns are required for DBD::DBM tables ..."; + !$meta->{dbm_mldbm} + and $ncols > 2 + and croak "Without serializing with MLDBM only 2 columns are supported, you give $ncols"; + $meta->{col_names} = $row_aryref; + return unless $meta->{dbm_store_metadata}; + + my $stmt = $data->{sql_stmt}; + my $col_names = join( ',', @{$row_aryref} ); + my $schema = $data->{Database}->{Statement}; + $schema =~ s/^[^\(]+\((.+)\)$/$1/s; + $schema = $stmt->schema_str() if ( $stmt->can('schema_str') ); + $meta->{hash}->{"_metadata \0"} = + "<dbd_metadata>" + . "<schema>$schema</schema>" + . "<col_names>$col_names</col_names>" + . "</dbd_metadata>"; +} + +# fetch_one_row, delete_one_row, update_one_row +# are optimized for hash-style lookup without looping; +# if you don't need them, omit them, they're optional +# but, in that case you may need to define +# truncate() and seek(), see below +# +sub fetch_one_row ($$;$) +{ + my ( $self, $key_only, $key ) = @_; + my $meta = $self->{meta}; + $key_only and return $meta->{col_names}->[0]; + exists $meta->{hash}->{$key} or return; + my $val = $meta->{hash}->{$key}; + $val = ( ref($val) eq 'ARRAY' ) ? $val : [$val]; + my $row = [ $key, @$val ]; + return wantarray ? @{$row} : $row; +} + +sub delete_one_row ($$$) +{ + my ( $self, $data, $aryref ) = @_; + my $meta = $self->{meta}; + delete $meta->{hash}->{ $aryref->[0] }; +} + +sub update_one_row ($$$) +{ + my ( $self, $data, $aryref ) = @_; + my $meta = $self->{meta}; + my $key = shift @$aryref; + defined $key or return; + my $row = ( ref($aryref) eq 'ARRAY' ) ? $aryref : [$aryref]; + $meta->{hash}->{$key} = $meta->{dbm_mldbm} ? $row : $row->[0]; +} + +sub update_specific_row ($$$$) +{ + my ( $self, $data, $aryref, $origary ) = @_; + my $meta = $self->{meta}; + my $key = shift @$origary; + my $newkey = shift @$aryref; + return unless ( defined $key ); + $key eq $newkey or delete $meta->{hash}->{$key}; + my $row = ( ref($aryref) eq 'ARRAY' ) ? $aryref : [$aryref]; + $meta->{hash}->{$newkey} = $meta->{dbm_mldbm} ? $row : $row->[0]; +} + +# you may not need to explicitly DESTROY the ::Table +# put cleanup code to run when the execute is done +# +sub DESTROY ($) +{ + my $self = shift; + my $meta = $self->{meta}; + $meta->{hash} and untie %{ $meta->{hash} }; + + $self->SUPER::DESTROY(); +} + +# truncate() and seek() must be defined to satisfy DBI::SQL::Nano +# *IF* you define the *_one_row methods above, truncate() and +# seek() can be empty or you can use them without actually +# truncating or seeking anything but if you don't define the +# *_one_row methods, you may need to define these + +# if you need to do something after a series of +# deletes or updates, you can put it in truncate() +# which is called at the end of executing +# +sub truncate ($$) +{ + # my ( $self, $data ) = @_; + return 1; +} + +# seek() is only needed if you use IO::File +# though it could be used for other non-file operations +# that you need to do before "writes" or truncate() +# +sub seek ($$$$) +{ + # my ( $self, $data, $pos, $whence ) = @_; + return 1; +} + +# Th, th, th, that's all folks! See DBD::File and DBD::CSV for other +# examples of creating pure perl DBDs. I hope this helped. +# Now it's time to go forth and create your own DBD! +# Remember to check in with dbi-dev@perl.org before you get too far. +# We may be able to make suggestions or point you to other related +# projects. + +1; +__END__ + +=pod + +=head1 NAME + +DBD::DBM - a DBI driver for DBM & MLDBM files + +=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: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, { + 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, +L<DBD::File/Metadata|DBD::File metadata> and L</Metadata> +shown below. + +Use standard DBI prepare, execute, fetch, placeholders, etc., +see L<QUICK START> for an example. + +=head1 DESCRIPTION + +DBD::DBM is a database management system that works right out of the +box. If you have a standard installation of Perl and DBI you can +begin creating, accessing, and modifying simple database tables +without any further modules. You can add other modules (e.g., +SQL::Statement, DB_File etc) for improved functionality. + +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 programming +languages using different APIs. That means, 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 DBD::DBM SQL operations that use key fields very fast. There +are several different "flavors" of DBM which use different storage +formats supported by perl modules such as SDBM_File and MLDBM. This +module supports all of the flavors that perl supports and, when used +with MLDBM, supports tables with any number of columns and insertion +of Perl objects into tables. + +DBD::DBM has been tested with the following DBM types: SDBM_File, +NDBM_File, ODBM_File, GDBM_File, DB_File, BerkeleyDB. Each type was +tested both with and without MLDBM and with the Data::Dumper, +Storable, FreezeThaw, YAML and JSON serializers using the DBI::SQL::Nano +or the SQL::Statement engines. + +=head1 QUICK START + +DBD::DBM operates like all other DBD drivers - it's basic syntax and +operation is specified by DBI. If you're not familiar with DBI, you should +start by reading L<DBI> and the documents it points to and then come back +and read this file. If you are familiar with DBI, you already know most of +what you need to know to operate this module. Just jump in and create a +test script something like the one shown below. + +You should be aware that there are several options for the SQL engine +underlying DBD::DBM, see L<Supported SQL syntax>. There are also many +options for DBM support, see especially the section on L<Adding +multi-column support with MLDBM>. + +But here's a sample to get you started. + + use DBI; + my $dbh = DBI->connect('dbi:DBM:'); + $dbh->{RaiseError} = 1; + for my $sql( split /;\n+/," + CREATE TABLE user ( user_name TEXT, phone TEXT ); + INSERT INTO user VALUES ('Fred Bloggs','233-7777'); + INSERT INTO user VALUES ('Sanjay Patel','777-3333'); + INSERT INTO user VALUES ('Junk','xxx-xxxx'); + DELETE FROM user WHERE user_name = 'Junk'; + UPDATE user SET phone = '999-4444' WHERE user_name = 'Sanjay Patel'; + SELECT * FROM user + "){ + my $sth = $dbh->prepare($sql); + $sth->execute; + $sth->dump_results if $sth->{NUM_OF_FIELDS}; + } + $dbh->disconnect; + +=head1 USAGE + +This section will explain some useage cases in more detail. To get an +overview about the available attributes, see L</Metadata>. + +=head2 Specifying 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 B<never> specify the file extensions in your SQL +statements. + +DBD::DBM recognizes following default extensions for following types: + +=over 4 + +=item .pag/r + +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, ...). + +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 >> don't usually +use a file extension. + +If your DBM type uses an extension other than one of the recognized +types of extensions, you should set the I<f_ext> attribute to the +extension B<and> file 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 use other directories specify the I<f_dir> attribute in either the +connect string or by setting the database handle attribute. + +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'); + # and this will too: + my $dbh = DBI->connect('dbi:DBM:'); + $dbh->{f_dir} = '/foo/bar'; + # but this is recommended + my $dbh = DBI->connect('dbi:DBM:', undef, undef, { f_dir => '/foo/bar' } ); + + # 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 +without setting I<f_dir>: + + my $dbh = DBI->connect('dbi:DBM:'); + my $ary = $dbh->selectall_arrayref(q{ + SELECT x FROM "/foo/bar/fruit" + }); + +You can also tell DBD::DBM to use a specified path for a specific table: + + $dbh->{dbm_tables}->{f}->{file} = q(/foo/bar/fruit); + +Please be aware that you cannot specify this during connection. + +If you have SQL::Statement installed, you can use table aliases: + + my $dbh = DBI->connect('dbi:DBM:'); + my $ary = $dbh->selectall_arrayref(q{ + SELECT f.x FROM "/foo/bar/fruit" AS f + }); + +See the L<GOTCHAS AND WARNINGS> for using DROP on tables. + +=head2 Table locking and flock() + +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 lock file is +created 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 a shared lock. For all other operations, it is an +exclusive lock (except when 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 will simply behave as if the flock() had +occurred although no actual locking will happen. Read the +documentation for flock() for more information. + +Even on those systems that do support flock(), locking is only +advisory - as is always the case with flock(). This means that if +another program tries to access the table file while DBD::DBM has the +table locked, that other program will *succeed* at opening unless +it is also using flock on the '.lck' file. As a result DBD::DBM's +locking only really applies to other programs using DBD::DBM or other +program written to cooperate with DBD::DBM locking. + +=head2 Specifying the DBM type + +Each "flavor" of DBM stores its files in a different format and has +different capabilities and limitations. See L<AnyDBM_File> for a +comparison of DBM types. + +By default, DBD::DBM uses the C<< SDBM_File >> type of storage since +C<< SDBM_File >> comes with Perl itself. If you have other types of +DBM storage available, you can use any of them with DBD::DBM. 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 C<< $dbh->{dbm_type} >> and +C<< $dbh->{f_meta}->{$table_name}->{type} >> 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 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> as that is set differently, see below. + + 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 + + # You can also use $dbh->{dbm_type} to set the DBM type for the connection: + $dbh->{dbm_type} = 'DB_File'; # set the global DBM type + print $dbh->{dbm_type}; # display the global DBM type + +If you have several tables in your script that use different DBM +types, you can use the $dbh->{dbm_tables} hash to store different +settings for the various tables. You can even use this to perform +joins on files that have completely different storage mechanisms. + + # sets global default of GDBM_File + my $dbh->('dbi:DBM:type=GDBM_File'); + + # overrides 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'; + + # 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 for first time. + +=head2 Adding multi-column support with MLDBM + +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. MLDBM 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 B<must> install MLDBM. It's available +for many platforms and is easy to install. + +MLDBM is by default distributed with three serializers - Data::Dumper, +Storable, and FreezeThaw. Data::Dumper is the default and Storable is the +fastest. MLDBM can also make use of user-defined serialization methods or +other serialization modules (e.g. L<YAML::MLDBM> or +L<MLDBM::Serializer::JSON>. You select the serializer using the +I<dbm_mldbm> attribute. + +Some examples: + + $dbh=DBI->connect('dbi:DBM:dbm_mldbm=Storable'); # use MLDBM with Storable + $dbh=DBI->connect( + 'dbi:DBM:dbm_mldbm=MySerializer' # use MLDBM with a user defined module + ); + $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: + + # 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 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. + +=head2 Support for Berkeley DB + +The Berkeley DB storage type is supported through two different Perl +modules - DB_File (which supports only features in old versions of Berkeley +DB) and BerkeleyDB (which supports all versions). DBD::DBM supports +specifying either "DB_File" or "BerkeleyDB" as a I<dbm_type>, with or +without MLDBM support. + +The "BerkeleyDB" dbm_type is experimental and it's interface is likely to +change. It currently defaults to BerkeleyDB::Hash and does not currently +support ::Btree or ::Recno. + +With BerkeleyDB, you can specify initialization flags by setting them in +your script like this: + + use BerkeleyDB; + my $env = new BerkeleyDB::Env -Home => $dir; # and/or other Env flags + $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 as 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 us to provide support in this release of DBD::DBM +for further Berkeley DB features such as transactions, concurrency, +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 the 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 more simply and in SQL terms, +the key column functions as the I<PRIMARY KEY> or UNIQUE INDEX. + +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 all the names which were +inserted into the table. + +In contrast, these 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 requested row(s). + +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 faster by around 50%, DBI::SQL::Nano and +SQL::Statement can benefit from the key field optimizations on +updating and deleting 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 +what other modules you have installed. Both options support basic SQL +operations including CREATE TABLE, DROP TABLE, INSERT, DELETE, UPDATE, and +SELECT. + +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 (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, +functions, joins, and much more. If you're going to use DBD::DBM +for anything other than very simple tables and queries, you should install +SQL::Statement. You don't have to change DBD::DBM or your scripts in any +way, simply installing SQL::Statement will give you the more robust SQL +capabilities without breaking scripts written for DBI::SQL::Nano. See +L<SQL::Statement> for a description of the SQL it supports. + +To find out which SQL module is working in a given script, you can use the +dbm_versions() method or, if you don't need the full output and version +numbers, just do this: + + print $dbh->{sql_handler}, "\n"; + +That will print out either "SQL::Statement" or "DBI::SQL::Nano". + +Baring the section about optimized access to the DBM storage in mind, +comparing the benefits of both engines: + + # 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 + +DBM files don't have a standard way to store column names. DBD::DBM gets +around this issue with a DBD::DBM specific way of storing the column names. +B<If you are working only with DBD::DBM and not using files created by or +accessed with other DBM programs, you can ignore this section.> + +DBD::DBM stores column names as a row in the file with the key I<_metadata +\0>. So this code + + my $dbh = DBI->connect('dbi:DBM:'); + $dbh->do("CREATE TABLE baz (foo CHAR(10), bar INTEGER)"); + $dbh->do("INSERT INTO baz (foo,bar) VALUES ('zippy',1)"); + +Will create a file that has a structure something like this: + + _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 +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:', undef, undef, { dbm_store_metadata => 0 }); + + # or + $dbh->{dbm_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:', undef, undef, { + dbm_store_metadata => 0, + dbm_cols => [ qw(foo bar) ], + }); + + # or + $dbh->{dbm_store_metadata} = 0; + $dbh->{dbm_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 +DBD::DBM's column name storage, just use one of the methods above to name +the columns but *without* specifying I<dbm_store_metadata> as zero. You +only have to do that once - thereafter you can get by without setting +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 Metadata + +=head3 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. + +=head3 Driver handle ($dbh) attributes + +It is not supported anymore to use dbm-attributes without the dbm_-prefix. +Currently, if an DBD::DBM private attribute is accessed without an +underscore in it's name, dbm_ is prepended to that attribute and it's +processed further. If the resulting attribute name is invalid, an error is +thrown. + +=head4 dbm_cols + +Contains a comma separated list of column names or an array reference to +the column names. + +=head4 dbm_type + +Contains the DBM storage type. Currently known supported type are +C<< ODBM_File >>, C<< NDBM_File >>, C<< SDBM_File >>, C<< GDBM_File >>, +C<< DB_File >> and C<< BerkeleyDB >>. It is not recommended to use one +of the first three types - even if C<< SDBM_File >> is the most commonly +available I<dbm_type>. + +=head4 dbm_mldbm + +Contains the serializer for DBM storage (value column). Requires the +CPAN module L<MLDBM> installed. Currently known 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> to be installed. + +=item YAML + +Portable serializer (between languages but not architectures). +Requires L<YAML::MLDBM> installation. + +=item JSON + +Portable, fast serializer (between languages but not architectures). +Requires L<MLDBM::Serializer::JSON> installation. + +=back + +=head4 dbm_store_metadata + +Boolean value which determines if the metadata in DBM is stored or not. + +=head4 dbm_berkeley_flags + +Hash reference with additional flags for BerkeleyDB::Hash instantiation. + +=head4 dbm_version + +Readonly attribute containing the version of DBD::DBM. + +=head4 f_meta + +In addition to the attributes L<DBD::File> recognizes, DBD::DBM knows +about the (public) attributes C<col_names> (B<Note> not I<dbm_cols> +here!), C<dbm_type>, C<dbm_mldbm>, C<dbm_store_metadata> and +C<dbm_berkeley_flags>. As in DBD::File, there are undocumented, +internal attributes in DBD::DBM. Be very careful when modifying +attributes you do not know; the consequence might a destroyed or +corrupted table. + +=head4 dbm_tables + +This attribute provides restricted access to the table meta data. See +L<f_meta> and L<DBD::File/f_meta> for attribute details. + +dbm_tables is a tied hash providing the internal table names as keys +(accessing unknown tables might create an entry) and their meta +data as another tied hash. The table meta storage is obtained via +the C<get_table_meta> method from the table implementation (see +L<DBD::File::Developers>). Attribute setting and getting within the +table meta data is handled via the methods C<set_table_meta_attr> and +C<get_table_meta_attr>. + +=head3 Following attributes are no longer handled by DBD::DBM: + +=head4 dbm_ext + +This attribute is silently mapped to DBD::File's attribute I<f_ext>. +Later versions of DBI might show a depreciated warning when this attribute +is used and eventually it will be removed. + +=head4 dbm_lockfile + +This attribute is silently mapped to DBD::File's attribute I<f_lockfile>. +Later versions of DBI might show a depreciated warning when this attribute +is used and eventually it will be removed. + +=head1 DBI database handle methods + +=head2 The $dbh->dbm_versions() method + +The private method dbm_versions() returns a summary of what other modules +are being used at any given time. DBD::DBM can work with or without many +other modules - it can use either SQL::Statement or DBI::SQL::Nano as its +SQL engine, it can be run with DBI or DBI::PurePerl, it can use many kinds +of DBM modules, and many kinds of serializers when run with MLDBM. The +dbm_versions() method reports all of that and more. + + print $dbh->dbm_versions; # displays global settings + print $dbh->dbm_versions($table_name); # displays per table settings + +An important thing to note about this method is that when it called +with no arguments, it displays the *global* settings. If you override +these by setting per-table attributes, these will I<not> be shown +unless you specify a table name as an argument to the method call. + +=head2 Storing Objects + +If you are using MLDBM, you can use DBD::DBM to take advantage of its +serializing abilities to serialize any Perl object that MLDBM can handle. +To store objects in columns, you should (but don't absolutely need to) +declare it as a column of type BLOB (the type is *currently* ignored by +the SQL engine, but it's good form). + +=head1 EXTENSIBILITY + +=over 8 + +=item C<SQL::Statement> + +Improved SQL engine compared to the built-in DBI::SQL::Nano - see +L<Supported SQL syntax>. + +=item C<DB_File> + +Berkeley DB version 1. This database library is available on many +systems without additional installation and most systems are +supported. + +=item C<GDBM_File> + +Simple dbm type (comparable to C<DB_File>) under the GNU license. +Typically not available (or requires extra installation) on non-GNU +operating systems. + +=item C<BerkeleyDB> + +Berkeley DB version up to v4 (and maybe higher) - requires additional +installation but is easier than GDBM_File on non-GNU systems. + +db4 comes with a many tools which allow repairing and migrating +databases. This is the B<recommended> dbm type for production use. + +=item C<MLDBM> + +Serializer wrapper to support more than one column for the files. +Comes with serializers using C<Data::Dumper>, C<FreezeThaw> and +C<Storable>. + +=item C<YAML::MLDBM> + +Additional serializer for MLDBM. YAML is very portable between languanges. + +=item C<MLDBM::Serializer::JSON> + +Additional serializer for MLDBM. JSON is very portable between languanges, +probably more than YAML. + +=back + +=head1 GOTCHAS AND WARNINGS + +Using the SQL DROP command will remove any file that has the name specified +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"}); + +Each DBM type has limitations. SDBM_File, for example, can only store +values of less than 1,000 characters. *You* as the script author must +ensure that you don't exceed those bounds. If you try to insert a value +that is larger than DBM can store, the results will be unpredictable. +See the documentation for whatever DBM you are using for details. + +Different DBM implementations return records in different orders. +That means that you I<should not> rely on the order of records unless +you use an 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 +to CSV on platform #1 and then dumping from CSV to DBM on platform #2. +DBD::AnyData and DBD::CSV can help with that. There may also be DBM +conversion tools for your platforms which would probably be quicker. + +When using MLDBM, there is a very powerful serializer - it will allow +you to store Perl code or objects in database columns. When these get +de-serialized, they may be eval'ed - in other words MLDBM (or actually +Data::Dumper when used by MLDBM) may take the values and try to +execute them in Perl. Obviously, this can present dangers, so if you +do not know what is in a file, be careful before you access it with +MLDBM turned on! + +See the entire section on L<Table locking and flock()> for gotchas and +warnings about the use of flock(). + +=head1 BUGS AND LIMITATIONS + +This module uses hash interfaces of two column file databases. While +none of supported SQL engines have support for indices, the following +statements really do the same (even if they mean something completely +different) for each dbm type which lacks C<EXISTS> support: + + $sth->do( "insert into foo values (1, 'hello')" ); + + # this statement does ... + $sth->do( "update foo set v='world' where k=1" ); + # ... the same as this statement + $sth->do( "insert into foo values (1, 'world')" ); + +This is considered to be a bug and might change in a future release. + +Known affected dbm types are C<ODBM_File> and C<NDBM_File>. We highly +recommended you use a more modern dbm type such as C<DB_File>. + +=head1 GETTING HELP, MAKING SUGGESTIONS, AND REPORTING BUGS + +If you need help installing or using DBD::DBM, please write to the DBI +users mailing list at dbi-users@perl.org or to the +comp.lang.perl.modules newsgroup on usenet. I cannot always answer +every question quickly but 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 an example are suggested to join the DBI developers +mailing list at dbi-dev@perl.org 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 +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 as small a 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 as +described at L<http://dbi.perl.org/support/> or you can contact Jens Rehsack +at rehsack@cpan.org for commercial support in Germany. + +Please don't bother Jochen Wiedmann or Jeff Zucker for support - they +handed over further maintenance 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. (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 would never have +been made. 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 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. + +You may freely distribute and/or modify this module under the terms of +either the GNU General Public License (GPL) or the Artistic License, as +specified in the Perl README file. + +=head1 SEE ALSO + +L<DBI>, +L<SQL::Statement>, L<DBI::SQL::Nano>, +L<AnyDBM_File>, L<DB_File>, L<BerkeleyDB>, +L<MLDBM>, L<YAML::MLDBM>, L<MLDBM::Serializer::JSON> + +=cut |