Author: REHSACK
Date: Fri May 21 11:58:07 2010
New Revision: 14002
Modified:
dbi/trunk/lib/DBD/DBM.pm
dbi/trunk/lib/DBD/File.pm
Log:
Refactor DBD::File::Table and heavily use new attribute f_meta in $dbh
Modified: dbi/trunk/lib/DBD/DBM.pm
==============================================================================
--- dbi/trunk/lib/DBD/DBM.pm (original)
+++ dbi/trunk/lib/DBD/DBM.pm Fri May 21 11:58:07 2010
@@ -25,7 +25,7 @@
use base qw( DBD::File );
use vars qw($VERSION $ATTRIBUTION $drh $methods_already_installed);
$VERSION = '0.05';
-$ATTRIBUTION = 'DBD::DBM by Jeff Zucker';
+$ATTRIBUTION = 'DBD::DBM by Jens Rehsack';
# no need to have driver() unless you need private methods
#
@@ -36,7 +36,7 @@
# do the real work in DBD::File
#
- $attr->{Attribution} = 'DBD::DBM by Jeff Zucker';
+ $attr->{Attribution} = 'DBD::DBM by Jens Rehsack';
my $this = $class->SUPER::driver($attr);
# install private methods
@@ -186,12 +186,10 @@
# see the STORE methods below for how to check these attrs
#
$sth->{dbm_valid_attrs} = {
- dbm_tables => 1, # per-table
information
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_ext => 1, # file extension
dbm_lockfile => 1, # lockfile
extension
dbm_store_metadata => 1, # column names,
etc.
dbm_berkeley_flags => 1, # for BerkeleyDB
@@ -209,11 +207,11 @@
my $dbh = shift;
my $table = shift || '';
my $dtype =
- $dbh->{dbm_tables}->{$table}->{type}
+ $dbh->{f_meta}->{$table}->{dbm_type}
|| $dbh->{dbm_type}
|| 'SDBM_File';
my $mldbm =
- $dbh->{dbm_tables}->{$table}->{mldbm}
+ $dbh->{f_meta}->{$table}->{dbm_mldbm}
|| $dbh->{dbm_mldbm}
|| '';
$dtype .= ' + MLDBM + ' . $mldbm if ($mldbm);
@@ -252,9 +250,9 @@
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}->{dbm_tables}
- and $sth->{Database}->{dbm_tables}->{$tname} );
- return $sth->{Database}->{dbm_tables}->{$tname}->{schema};
+ 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
@@ -265,231 +263,190 @@
############################
package DBD::DBM::Statement;
############################
-use base qw( DBD::File::Statement );
-use Carp qw(croak);
-use IO::File; # for locking only
-use Fcntl;
-my $HAS_FLOCK = eval { flock STDOUT, 0; 1 };
+...@dbd::DBM::Statement::ISA = qw(DBD::File::Statement);
-sub get_table_meta ($$$)
-{
- my ( $self, $data, $table ) = @_;
- ( $table, my $meta ) = $self->SUPER::get_table_meta( $data, $table );
+########################
+package DBD::DBM::Table;
+########################
+use Carp;
+use Fcntl;
- return ( $table, $meta );
-}
+...@dbd::DBM::Table::ISA = qw(DBD::File::Table);
-# you must define open_table;
-# it is done at the start of all executes;
-# it doesn't necessarily have to "open" anything;
-# you must define the $tbl and at least the col_names and col_nums;
-# anything else you put in depends on what you need in your
-# ::Table methods below; you must bless the $tbl into the
-# appropriate class as shown
-#
-# see also the comments inside open_table() showing the difference
-# between global, per-table, and default settings
-#
-sub open_table ($$$$$)
+sub file2table
{
- my ( $self, $data, $table, $createMode, $lockMode ) = @_;
- my $dbh = $data->{Database};
+ my ( $self, $meta, $file, $file_is_table, $quoted ) = @_;
- my ( $file, $meta );
- ( $table, $meta ) = $self->get_table_meta( $data, $table );
- $file = $meta->{f_fqfn};
-
- # note the use of three levels of attribute settings below
- # first it looks for a per-table setting
- # if none is found, it looks for a global setting
- # if none is found, it sets a default
- #
- # your DBD may not need this, globals and defaults may be enough
- #
- my $dbm_type =
- $dbh->{dbm_tables}->{$table}->{type}
- || $dbh->{dbm_type}
- || 'SDBM_File';
- $dbh->{dbm_tables}->{$table}->{type} = $dbm_type;
+ my $tbl = $self->SUPER::file2table( $meta, $file, $file_is_table, $quoted
) or return;
- my $serializer =
- $dbh->{dbm_tables}->{$table}->{mldbm}
- || $dbh->{dbm_mldbm}
- || '';
- $dbh->{dbm_tables}->{$table}->{mldbm} = $serializer if $serializer;
+ $meta->{f_fqln} = $meta->{f_fqbn} . '.lck';
+ $meta->{f_dontopen} = 1;
- my $ext = '' if ( $dbm_type eq 'GDBM_File' or $dbm_type eq 'DB_File' or
$dbm_type eq 'BerkeleyDB' );
- # XXX NDBM_File on FreeBSD (and elsewhere?) may actually be Berkeley
- # behind the scenes and so create a single .db file.
- $ext = '.pag' if ( $dbm_type eq 'NDBM_File' or $dbm_type eq 'SDBM_File' or
$dbm_type eq 'ODBM_File' );
- $ext = $dbh->{dbm_ext} if ( defined $dbh->{dbm_ext} );
- $ext = $dbh->{dbm_tables}->{$table}->{ext} if ( defined
$dbh->{dbm_tables}->{$table}->{ext} );
- $ext = '' unless ( defined $ext );
-
- my $open_mode = O_RDONLY;
- $open_mode = O_RDWR if ($lockMode);
- $open_mode = O_RDWR | O_CREAT | O_TRUNC if ($createMode);
+ return $tbl;
+}
- my ($tie_type);
+sub init_table_meta ($$$$$)
+{
+ my ( $self, $dbh, $table, $file_is_table, $quoted ) = @_;
+ defined $dbh->{f_meta}->{$table} and "HASH" eq ref
$dbh->{f_meta}->{$table} or
+ $dbh->{f_meta}->{$table} = {};
+ my $meta = $dbh->{f_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};
- if ($serializer)
+ unless ( defined( $meta->{f_ext} ) )
{
- require 'MLDBM.pm';
- $MLDBM::UseDB = $dbm_type;
- $MLDBM::UseDB = 'BerkeleyDB::Hash' if ( $dbm_type eq 'BerkeleyDB'
);
- $MLDBM::Serializer = $serializer;
- $tie_type = 'MLDBM';
+ 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
+ }
+ $meta->{f_ext} = $ext if ( defined($ext) );
}
- else
+
+ unless ( defined( $meta->{dbm_tietype} ) )
{
- require "$dbm_type.pm";
- $tie_type = $dbm_type;
- }
+ my $tie_type = $meta->{dbm_type};
+ require "$tie_type.pm" unless ( $INC{"$tie_type.pm"} );
+ $tie_type = 'BerkeleyDB::Hash' if ( $tie_type eq 'BerkeleyDB' );
- # Second-guessing the file extension isn't great here (or in general)
- # could replace this by trying to open the file in non-create mode
- # first and dieing if that succeeds.
- # Currently this test doesn't work where NDBM is actually Berkeley (.db)
- croak "Cannot CREATE '$file$ext' because it already exists"
- if ( $createMode and ( -e "$file$ext" ) );
+ if ( $meta->{dbm_mldbm} )
+ {
+ require "MLDBM.pm" unless ( $INC{"MLDBM.pm"} );
+ $meta->{dbm_usedb} = $tie_type;
+ $tie_type = 'MLDBM';
+ }
- # LOCKING
- #
- my ( $nolock, $lockext, $lock_table );
- $lockext = $dbh->{dbm_tables}->{$table}->{lockfile};
- $lockext = $dbh->{dbm_lockfile} if !defined $lockext;
- if ( ( defined $lockext and $lockext == 0 ) or !$HAS_FLOCK )
- {
- undef $lockext;
- $nolock = 1;
+ $meta->{dbm_tietype} = $tie_type;
}
- else
+
+ unless ( defined( $meta->{dbm_store_metadata} ) )
{
- $lockext ||= '.lck';
+ my $store = $dbh->{dbm_store_metadata};
+ $store = 1 unless ( defined($store) );
+ $meta->{dbm_store_metadata} = $store;
}
- # open and flock the lockfile, creating it if necessary
- #
- if ( !$nolock )
+
+ unless ( defined( $meta->{col_names} ) )
{
- $lock_table = $self->SUPER::open_table( $data, "$table$lockext",
$createMode, $lockMode );
+ $meta->{col_names} = $dbh->{dbm_cols} if ( defined( $dbh->{dbm_cols} )
);
}
- my %h;
- if ( $self->{command} ne 'DROP' )
+ $self->SUPER::init_table_meta( $dbh, $table, $file_is_table, $quoted );
+}
+
+sub default_table_meta ($$$)
+{
+ my ( $self, $dbh, $table ) = @_;
+ my $meta = $self->SUPER::default_table_meta( $dbh, $table );
+
+ $meta->{dbm_type} = $dbh->{dbm_type} || 'SDBM_File';
+ $meta->{dbm_mldbm} = $dbh->{dbm_mldbm} if ( $dbh->{dbm_mldbm} );
+
+ return $meta;
+}
+
+sub open_file
+{
+ my ( $self, $meta, $attrs, $flags ) = @_;
+ $self->SUPER::open_file( $meta, $attrs, $flags );
+ unless ( $flags->{dropMode} )
{
# TIEING
#
- # allow users to pass in a pre-created tied object
+ # XXX allow users to pass in a pre-created tied object
#
my @tie_args;
- if ( $dbm_type eq 'BerkeleyDB' )
+ if ( $meta->{dbm_type} eq 'BerkeleyDB' )
{
my $DB_CREATE = 1; # but import constants if supplied
my $DB_RDONLY = 16; #
- my %flags;
- if ( my $f = $dbh->{dbm_berkeley_flags} )
+ my %tie_flags;
+ if ( my $f = $meta->{dbm_berkeley_flags} )
{
$DB_CREATE = $f->{DB_CREATE} if ( $f->{DB_CREATE} );
$DB_RDONLY = $f->{DB_RDONLY} if ( $f->{DB_RDONLY} );
delete $f->{DB_CREATE};
delete $f->{DB_RDONLY};
- %flags = %$f;
+ %tie_flags = %$f;
}
- $flags{'-Flags'} = $DB_RDONLY;
- $flags{'-Flags'} = $DB_CREATE if ( $lockMode or $createMode );
- my $t = 'BerkeleyDB::Hash';
- $t = 'MLDBM' if ($serializer);
+ $tie_flags{'-Flags'} = $DB_RDONLY;
+ $tie_flags{'-Flags'} = $DB_CREATE if ( $flags->{lockMode} or
$flags->{createMode} );
@tie_args = (
- $t,
- -Filename => $file,
- %flags
+ -Filename => $meta->{f_fqbn},
+ %tie_flags
);
}
else
{
- @tie_args = ( $tie_type, $file, $open_mode, 0666 );
+ my $open_mode = O_RDONLY;
+ $open_mode = O_RDWR if ( $flags->{lockMode} );
+ $open_mode = O_RDWR | O_CREAT | O_TRUNC if ( $flags->{createMode}
);
+
+ @tie_args = ( $meta->{f_fqbn}, $open_mode, 0666 );
}
- my $tie_class = shift @tie_args;
- eval { tie %h, $tie_class, @tie_args };
- croak "Cannot tie(%h $tie_class @tie_args): $@" if ($@);
- }
+ if ( $meta->{dbm_mldbm} )
+ {
+ $MLDBM::UseDB = $meta->{dbm_usedb};
+ $MLDBM::Serializer = $meta->{dbm_mldbm};
+ }
- my $store = $dbh->{dbm_tables}->{$table}->{store_metadata};
- $store = $dbh->{dbm_store_metadata} unless ( defined $store );
- $store = 1 unless ( defined $store );
- $dbh->{dbm_tables}->{$table}->{store_metadata} = $store;
-
- my $tbl = {
- table_name => $table,
- file => $file,
- ext => $ext,
- hash => \%h,
- dbm_type => $dbm_type,
- store_metadata => $store,
- mldbm => $serializer,
- lock_fh => $lock_table->{fh},
- lock_ext => $lockext,
- nolock => $nolock,
- col_names => [],
- col_nums => {},
- };
+ $meta->{hash} = {};
+ my $tie_class = $meta->{dbm_tietype};
+ eval { tie %{ $meta->{hash} }, $tie_class, @tie_args };
+ croak "Cannot tie(\%h $tie_class @tie_args): $@" if ($@);
+ }
- # COLUMN NAMES
- #
- unless ($createMode)
+ unless ( $flags->{createMode} )
{
my ( $meta_data, $schema, $col_names );
- $meta_data = $col_names = $h{"_metadata \0"} if $store;
- if ( $meta_data and $meta_data =~
m~<dbd_metadata>(.+)</dbd_metadata>~is )
+ if ( $meta->{dbm_store_metadata} )
{
- $schema = $col_names = $1;
- $schema =~ s~.*<schema>(.+)</schema>.*~$1~is;
- $col_names =~ s~.*<col_names>(.+)</col_names>.*~$1~is;
+ $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 ||=
- $dbh->{dbm_tables}->{$table}->{c_cols}
- || $dbh->{dbm_tables}->{$table}->{cols}
- || $dbh->{dbm_cols}
- || [ 'k', 'v' ];
+ $col_names ||= $meta->{col_names} || [ 'k', 'v' ];
$col_names = [ split /,/, $col_names ] if ( ref $col_names ne 'ARRAY'
);
- $dbh->{dbm_tables}->{$table}->{cols} = $col_names;
- $dbh->{dbm_tables}->{$table}->{schema} = $schema;
+ $meta->{schema} = $schema;
- my $i;
- my %col_nums = map { $_ => $i++ } @$col_names;
-
- $tbl->{col_nums} = \%col_nums;
- $tbl->{col_names} = $col_names;
+ $meta->{col_names} = $col_names;
}
-
- my $class = ref($self);
- $class =~ s/::Statement/::Table/;
- bless( $tbl, $class );
- $tbl;
}
-########################
-package DBD::DBM::Table;
-########################
-use base qw( DBD::File::Table );
-
# you must define drop
# it is called from execute of a SQL DROP statement
#
sub drop ($$)
{
my ( $self, $data ) = @_;
- untie %{ $self->{hash} } if ( $self->{hash} );
- my $ext = $self->{ext};
- unlink $self->{file} . $ext if ( -f $self->{file} . $ext );
- unlink $self->{file} . '.dir' if ( -f $self->{file} . '.dir' and $ext eq
'.pag' );
- if ( !$self->{nolock} )
- {
- $self->{lock_fh}->close if ( $self->{lock_fh} );
- unlink $self->{file} . $self->{lock_ext} if ( -f $self->{file} .
$self->{lock_ext} );
- }
+ my $meta = $self->{meta};
+ untie %{ $meta->{hash} } if ( $meta->{hash} );
+ $self->SUPER::drop($data);
+ # XXX extra_files
+ unlink $meta->{f_fqbn} . '.dir' if ( -f $meta->{f_fqbn} . '.dir' and
$meta->{f_ext} eq '.pag/r' );
return 1;
}
@@ -503,10 +460,11 @@
sub fetch_row ($$$)
{
my ( $self, $data, $row ) = @_;
+ my $meta = $self->{meta};
# fetch with %each
#
- my @ary = each %{ $self->{hash} };
- @ary = each %{ $self->{hash} } if ( $self->{store_metadata} and $ary[0]
and $ary[0] eq "_metadata \0" );
+ my @ary = each %{ $meta->{hash} };
+ @ary = each %{ $meta->{hash} } if ( $meta->{dbm_store_metadata} and
$ary[0] and $ary[0] eq "_metadata \0" );
my ( $key, $val ) = @ary;
unless ($key)
@@ -525,14 +483,15 @@
sub push_row ($$$)
{
my ( $self, $data, $row_aryref ) = @_;
+ my $meta = $self->{meta};
my $key = shift @$row_aryref;
- if ( $self->{mldbm} )
+ if ( $meta->{dbm_mldbm} )
{
- $self->{hash}->{$key} = $row_aryref;
+ $meta->{hash}->{$key} = $row_aryref;
}
else
{
- $self->{hash}->{$key} = $row_aryref->[0];
+ $meta->{hash}->{$key} = $row_aryref->[0];
}
1;
}
@@ -543,14 +502,15 @@
sub push_names ($$$)
{
my ( $self, $data, $row_aryref ) = @_;
- $data->{Database}->{dbm_tables}->{ $self->{table_name} }->{c_cols} =
$row_aryref;
- return unless $self->{store_metadata};
+ my $meta = $self->{meta};
+ $meta->{col_names} = $row_aryref;
+ return unless $meta->{dbm_store_metadata};
my $stmt = $data->{f_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') );
- $self->{hash}->{"_metadata \0"} =
+ $meta->{hash}->{"_metadata \0"} =
"<dbd_metadata>" . "<schema>$schema</schema>" .
"<col_names>$col_names</col_names>" . "</dbd_metadata>";
}
@@ -563,9 +523,10 @@
sub fetch_one_row ($$;$)
{
my ( $self, $key_only, $key ) = @_;
- return $self->{col_names}->[0] if ($key_only);
- return undef unless ( exists $self->{hash}->{$key} );
- my $val = $self->{hash}->{$key};
+ my $meta = $self->{meta};
+ return $meta->{col_names}->[0] if ($key_only);
+ return undef unless ( exists $meta->{hash}->{$key} );
+ my $val = $meta->{hash}->{$key};
$val = ( ref($val) eq 'ARRAY' ) ? $val : [$val];
my $row = [ $key, @$val ];
return wantarray ? @{$row} : $row;
@@ -574,27 +535,30 @@
sub delete_one_row ($$$)
{
my ( $self, $data, $aryref ) = @_;
- delete $self->{hash}->{ $aryref->[0] };
+ 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;
return unless ( defined $key );
my $row = ( ref($aryref) eq 'ARRAY' ) ? $aryref : [$aryref];
- $self->{hash}->{$key} = $self->{mldbm} ? $row : $row->[0];
+ $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 );
- delete $self->{hash}->{$key} unless ( $key eq $newkey );
+ delete $meta->{hash}->{$key} unless ( $key eq $newkey );
my $row = ( ref($aryref) eq 'ARRAY' ) ? $aryref : [$aryref];
- $self->{hash}->{$newkey} = $self->{mldbm} ? $row : $row->[0];
+ $meta->{hash}->{$newkey} = $meta->{dbm_mldbm} ? $row : $row->[0];
}
# you may not need to explicitly DESTROY the ::Table
@@ -603,9 +567,10 @@
sub DESTROY ($)
{
my $self = shift;
- untie %{ $self->{hash} } if ( $self->{hash} );
- # release the flock on the lock file
- $self->{lock_fh}->close if ( !$self->{nolock} and $self->{lock_fh} );
+ my $meta = $self->{meta};
+ untie %{ $meta->{hash} } if ( $meta->{hash} );
+
+ $self->SUPER::DESTROY();
}
# truncate() and seek() must be defined to satisfy DBI::SQL::Nano
Modified: dbi/trunk/lib/DBD/File.pm
==============================================================================
--- dbi/trunk/lib/DBD/File.pm (original)
+++ dbi/trunk/lib/DBD/File.pm Fri May 21 11:58:07 2010
@@ -341,50 +341,6 @@
undef $dbh->{csv_sql_parser_object};
} # DESTROY
-sub file2table
-{
- my ($dbh, $dir, $file, $file_is_tab, $quoted) = @_;
-
- $file eq "." || $file eq ".." and return;
-
- my ($ext, $req) = ("", 0);
- if ($dbh->{f_ext}) {
- ($ext, my $opt) = split m/\//, $dbh->{f_ext};
- if ($ext && $opt) {
- $opt =~ m/r/i and $req = 1;
- }
- }
-
- (my $tbl = $file) =~ s/$ext$//i;
- $file_is_tab and $file = "$tbl$ext";
-
- # Fully Qualified File Name
- my $fqfn;
- unless ($quoted) { # table names are case insensitive in SQL
- opendir my $dh, $dir or croak "Can't open '$dir': $!";
- my @f = grep { lc $_ eq lc $file } readdir $dh;
- @f == 1 and $file = $f[0];
- closedir $dh or croak "Can't close '$dir': $!";
- }
- $fqfn = File::Spec->catfile ($dir, $file);
-
- $file = $fqfn;
- if ($ext) {
- if ($req) {
- # File extension required
- $file =~ s/$ext$//i or return;
- }
- else {
- # File extension optional, skip if file with extension exists
- grep m/$ext$/i, glob "$fqfn.*" and return;
- $file =~ s/$ext$//i;
- }
- }
-
- $dbh->{f_meta}{$tbl} = { f_fqfn => $fqfn, f_fqbn => $file, };
- return $tbl;
- } # file2table
-
sub type_info_all ($)
{
[ { TYPE_NAME => 0,
@@ -440,13 +396,17 @@
return;
}
+ my $class = $dbh->FETCH ("ImplementorClass");
+ $class =~ s/::db$/::Table/;
my ($file, @tables, %names);
my $schema = exists $dbh->{f_schema}
? defined $dbh->{f_schema} && $dbh->{f_schema} ne ""
? $dbh->{f_schema} : undef
- : eval { getpwuid ((stat $dir)[4]) };
+ : eval { getpwuid ((stat $dir)[4]) }; # XXX Win32::pwent
while (defined ($file = readdir ($dirh))) {
- my $tbl = $dbh->func ($dir, $file, 0, 0, "file2table") or next;
+ # XXX $dbh->{f_meta} or ...
+ my ($tbl, $meta) = $class->get_table_meta ($dbh, $file, 0, 0) or
next; # XXX
+ # XXX collect from $dbh->{f_meta}
push @tables, [ undef, $schema, $tbl, "TABLE", undef ];
}
unless (closedir $dirh) {
@@ -673,15 +633,13 @@
return $_[0]->{f_stmt}{NUM_OF_ROWS};
} # rows
+# ====== SQL::STATEMENT
========================================================
+
package DBD::File::Statement;
use strict;
use Carp;
-# We may have a working flock () built-in but that doesn't mean that locking
-# will work on NFS (flock () may hang hard)
-my $locking = eval { flock STDOUT, 0; 1 };
-
# Jochen's old check for flock ()
#
# my $locking = $^O ne "MacOS" &&
@@ -690,111 +648,245 @@
@DBD::File::Statement::ISA = qw( DBI::SQL::Nano::Statement );
+sub open_table ($$$$$)
+{
+ my ($self, $data, $table, $createMode, $lockMode) = @_;
+
+ my $class = ref $self;
+ $class =~ s/::Statement/::Table/;
+
+ my %flags = (createMode => $createMode,
+ lockMode => $lockMode);
+ $self->{command} eq 'DROP' and $flags{dropMode} = 1;
+
+ return $class->new ($data, {table => $table}, \%flags);
+ } # open_table
+
+# ====== SQL::TABLE
============================================================
+
+package DBD::File::Table;
+
+use strict;
+use Carp;
+require IO::File;
+
+# We may have a working flock () built-in but that doesn't mean that locking
+# will work on NFS (flock () may hang hard)
+my $locking = eval { flock STDOUT, 0; 1 };
+
+...@dbd::File::Table::ISA = qw(DBI::SQL::Nano::Table);
+
+# ====== FLYWEIGHT SUPPORT
=====================================================
+
+# Flyweight support for table_info
+# The functions file2table, init_table_meta, default_table_meta and
+# get_table_meta are using $self arguments for polymorphism only. The
+# must not rely on an instantiated DBD::File::Table
+sub file2table
+{
+ my ($self, $meta, $file, $file_is_table, $quoted) = @_;
+
+ $file eq "." || $file eq ".." and return;
+
+ my ($ext, $req) = ("", 0); # XXX
+ if ($meta->{f_ext}) {
+ ($ext, my $opt) = split m/\//, $meta->{f_ext};
+ if ($ext && $opt) {
+ $opt =~ m/r/i and $req = 1;
+ }
+ }
+
+ (my $tbl = $file) =~ s/$ext$//i;
+ $file_is_table and $file = "$tbl$ext";
+
+ # Fully Qualified File Name
+ unless ($quoted) { # table names are case insensitive in SQL
+ my $dir = $meta->{f_dir};
+ opendir my $dh, $dir or croak "Can't open '$dir': $!";
+ my @f = grep { lc $_ eq lc $file } readdir $dh;
+ @f == 1 and $tbl = $file = $f[0];
+ $tbl =~ s/$ext$//i; # XXX /i flag only when not quoted?
+ closedir $dh or croak "Can't close '$dir': $!";
+ }
+ my $fqfn = File::Spec->catfile ($meta->{f_dir}, $file);
+ my $fqbn = File::Spec->catfile ($meta->{f_dir}, $tbl);
+
+ $file = $fqfn;
+ if ($ext) {
+ if ($req) {
+ # File extension required
+ $file =~ s/$ext$//i or return;
+ }
+ else {
+ # File extension optional, skip if file with extension exists
+ grep m/$ext$/i, glob "$fqfn.*" and return;
+ $file =~ s/$ext$//i;
+ }
+ }
+
+ $meta->{f_fqfn} = $fqfn;
+ $meta->{f_fqbn} = $file;
+ return $tbl;
+ } # file2table
+
my $open_table_re = sprintf "(?:%s|%s|%s)",
quotemeta (File::Spec->curdir ()),
quotemeta (File::Spec->updir ()),
quotemeta (File::Spec->rootdir ());
-sub get_table_meta ($$$)
+sub init_table_meta ($$$$$)
{
- my ($self, $data, $table) = @_;
- my $quoted = 0;
- $table =~ s/^\"// and $quoted = 1; # handle quoted identifiers
- $table =~ s/\"$//;
- my $file = $table;
+ my ($self, $dbh, $table, $file_is_table, $quoted) = @_;
+ defined $dbh->{f_meta}->{$table} and "HASH" eq ref
$dbh->{f_meta}->{$table} or
+ $dbh->{f_meta}->{$table} = {};
+ my $meta = $dbh->{f_meta}->{$table};
+ defined $meta->{f_dir} or
+ $meta->{f_dir} = $dbh->{f_dir};
+ defined $meta->{f_ext} or
+ $meta->{f_ext} = $dbh->{f_ext};
+ defined $meta->{f_encoding} or
+ $meta->{f_encoding} = $dbh->{f_encoding};
+ defined $meta->{f_lock} or
+ $meta->{f_lock} = $dbh->{f_lock};
+ defined $meta->{f_fqfn} or
+ $self->file2table ($meta, $table, $file_is_table, $quoted);
+ } # init_table_meta
+
+sub default_table_meta ($$$)
+{
+ my ($self, $dbh, $table) = @_;
+ my $meta = { f_fqfn => $table, f_fqbn => $table, };
+ return $meta;
+ } # init_table_meta
+
+sub get_table_meta ($$$$;$)
+{
+ my ($self, $dbh, $table, $file_is_table, $quoted) = @_;
+ unless( defined( $quoted ) ) {
+ $quoted = 0;
+ $table =~ s/^\"// and $quoted = 1; # handle quoted identifiers
+ $table =~ s/\"$//;
+ }
my $meta;
- if ( $file !~ m/^$open_table_re/o
- and $file !~ m{^[/\\]} # root
- and $file !~ m{^[a-z]\:} # drive letter
+ if ( $table !~ m/^$open_table_re/o
+ and $table !~ m{^[/\\]} # root
+ and $table !~ m{^[a-z]\:} # drive letter
) {
- my $dbh = $data->{Database};
- exists $dbh->{f_meta}->{$table} or
- $dbh->func ($dbh->{f_dir}, $file, 1,
- $quoted, "file2table");
- $meta = $dbh->{f_meta}->{$table} || {};
+ # should be done anyway, table_info might generate incomplete f_meta
+ $self->init_table_meta ($dbh, $table, $file_is_table, $quoted);
+ $meta = $dbh->{f_meta}->{$table};
}
else {
- $meta = { f_fqfn => $file, f_fqbn => $file, };
+ $meta = $self->default_table_meta ($dbh, $table);
}
return ($table, $meta);
} # get_table_meta
-sub open_table ($$$$$)
+# ====== FILE OPEN
=============================================================
+
+sub open_file ($$$)
{
- my ($self, $data, $table, $createMode, $lockMode) = @_;
- my $meta;
- ($table, $meta) = $self->get_table_meta ($data, $table);
- my $file = $meta->{f_fqfn};
- defined $file && $file ne "" or croak "No filename given";
- require IO::File;
- my $fh;
- my $safe_drop = $self->{ignore_missing_table} ? 1 : 0;
- if ($createMode) {
- -f $file and
- croak "Cannot create table $table: Already exists";
- $fh = IO::File->new ($file, "a+") or
- croak "Cannot open $file for writing: $!";
- $fh->seek (0, 0) or
- croak "Error while seeking back: $!";
- }
- else {
- unless ($fh = IO::File->new ($file, ($lockMode ? "r+" : "r"))) {
- $safe_drop or croak "Cannot open $file: $!";
+ my ($self, $meta, $attrs, $flags) = @_;
+
+ defined $meta->{f_fqfn} && $meta->{f_fqfn} ne "" or croak "No filename
given";
+
+ my ($fh, $fn);
+ unless ($meta->{f_dontopen}) {
+ $fn = $meta->{f_fqfn};
+ if ($flags->{createMode}) {
+ -f $meta->{f_fqfn} and
+ croak "Cannot create table $attrs->{table}: Already exists";
+ $fh = IO::File->new ($fn, "a+") or
+ croak "Cannot open $fn for writing: $!";
+ $fh->seek (0, 0) or
+ croak "Error while seeking back: $!";
}
+ else {
+ unless ($fh = IO::File->new ($fn, ($flags->{lockMode} ? "r+" :
"r"))) {
+ croak "Cannot open $fn: $!";
+ }
+ }
+
+ if ($fh) {
+ if (my $enc = $meta->{f_encoding}) {
+ binmode $fh, ":encoding($enc)" or
+ croak "Failed to set encoding layer '$enc' on $fn: $!";
+ }
+ else {
+ binmode $fh or croak "Failed to set binary mode on $fn: $!";
+ }
+ }
+
+ $meta->{fh} = $fh;
}
- if ($fh) {
- if (my $enc = $data->{Database}{f_encoding}) {
- binmode $fh, ":encoding($enc)" or
- croak "Failed to set encoding layer '$enc' on $file: $!";
+ if ($meta->{f_fqln}) {
+ $fn = $meta->{f_fqln};
+ if ($flags->{createMode}) {
+ -f $fn and
+ croak "Cannot create table lock for $attrs->{table}: Already
exists";
+ $fh = IO::File->new ($fn, "a+") or
+ croak "Cannot open $fn for writing: $!";
}
else {
- binmode $fh or croak "Failed to set binary mode on $file: $!";
+ unless ($fh = IO::File->new ($fn, ($flags->{lockMode} ? "r+" :
"r"))) {
+ croak "Cannot open $fn: $!";
+ }
}
+
+ $meta->{lockfh} = $fh;
}
+
if ($locking && $fh) {
- my $lm = defined $data->{Database}{f_lock}
- && $data->{Database}{f_lock} =~ m/^[012]$/
- ? $data->{Database}{f_lock}
- : $lockMode ? 2 : 1;
+ my $lm = defined $flags->{f_lock}
+ && $flags->{f_lock} =~ m/^[012]$/
+ ? $flags->{f_lock}
+ : $flags->{lockMode} ? 2 : 1;
if ($lm == 2) {
- flock $fh, 2 or croak "Cannot obtain exclusive lock on $file: $!";
+ flock $fh, 2 or croak "Cannot obtain exclusive lock on $fn: $!";
}
elsif ($lm == 1) {
- flock $fh, 1 or croak "Cannot obtain shared lock on $file: $!";
+ flock $fh, 1 or croak "Cannot obtain shared lock on $fn: $!";
}
# $lm = 0 is forced no locking at all
}
+ } # open_file
+
+# ====== SQL::Eval API
=========================================================
+
+sub new
+{
+ my ($className, $data, $attrs, $flags) = @_;
+ my $dbh = $data->{Database};
+
+ my $meta;
+ ($attrs->{table}, $meta) = $className->get_table_meta ($dbh,
$attrs->{table}, 1);
+
+ $className->open_file ($meta, $attrs, $flags);
+
my $columns = {};
my $array = [];
- my $pos = $fh ? $fh->tell () : undef;
my $tbl = {
- file => $file,
- fh => $fh,
- col_nums => $columns,
- col_names => $array,
- first_row_pos => $pos,
+ %{$attrs},
+ meta => $meta,
+ col_names => $meta->{col_names} || [],
};
- my $class = ref $self;
- $class =~ s/::Statement/::Table/;
- return $class->new ($tbl);
- } # open_table
-
-package DBD::File::Table;
-
-use strict;
-use Carp;
-
-...@dbd::File::Table::ISA = qw(DBI::SQL::Nano::Table);
+ return $className->SUPER::new ($tbl);
+ } # new
sub drop ($)
{
- my $self = shift;
+ my ($self, $data) = @_;
+ my $meta = $self->{meta};
# We have to close the file before unlinking it: Some OS'es will
# refuse the unlink otherwise.
- $self->{fh} and $self->{fh}->close ();
- undef $self->{fh};
- unlink $self->{file};
+ $meta->{fh} and $meta->{fh}->close ();
+ $meta->{lockfh} and $meta->{lockfh}->close ();
+ undef $meta->{fh};
+ undef $meta->{lockfh};
+ $meta->{f_fqfn} and unlink $meta->{f_fqfn};
+ $meta->{f_fqln} and unlink $meta->{f_fqln};
+ delete $data->{Database}->{f_meta}->{$self->{table}};
return 1;
} # drop
@@ -802,29 +894,32 @@
{
my ($self, $data, $pos, $whence) = @_;
if ($whence == 0 && $pos == 0) {
- $pos = $self->{first_row_pos};
+ $pos = defined $self->{first_row_pos} ? $self->{first_row_pos} : 0;
}
elsif ($whence != 2 || $pos != 0) {
croak "Illegal seek position: pos = $pos, whence = $whence";
}
- $self->{fh}->seek ($pos, $whence) or
- croak "Error while seeking in " . $self->{file} . ": $!";
+ $self->{meta}->{fh}->seek ($pos, $whence) or
+ croak "Error while seeking in " . $self->{meta}->{f_fqfn} . ": $!";
} # seek
sub truncate ($$)
{
my ($self, $data) = @_;
$self->{fh}->truncate ($self->{fh}->tell ()) or
- croak "Error while truncating " . $self->{file} . ": $!";
+ croak "Error while truncating " . $self->{meta}->{f_fqfn} . ": $!";
return 1;
} # truncate
sub DESTROY
{
my $self = shift;
- $self->{fh} and $self->{fh}->close ();
- undef $self->{fh};
+ my $meta = $self->{meta};
+ $meta->{fh} and $meta->{fh}->close ();
+ $meta->{lockfh} and $meta->{lockfh}->close ();
+ undef $meta->{fh};
+ undef $meta->{lockfh};
} # DESTROY
1;