Author: REHSACK
Date: Fri Jun 4 04:12:18 2010
New Revision: 14096
Modified:
dbi/trunk/lib/DBD/DBM.pm
dbi/trunk/lib/DBD/File.pm
Log:
- introduce general accessors to table meta information
(including some code injections into derived DBD's)
- allow extended initialization via DSN (for DBD::AnyData)
- fix issue described in RT59038
- fix meta guessing
Modified: dbi/trunk/lib/DBD/DBM.pm
==============================================================================
--- dbi/trunk/lib/DBD/DBM.pm (original)
+++ dbi/trunk/lib/DBD/DBM.pm Fri Jun 4 04:12:18 2010
@@ -45,7 +45,7 @@
# but you can write private methods before official registration
# by hacking the $dbd_prefix_registry in a private copy of DBI.pm
#
- if ( $DBI::VERSION >= 1.37 and !$methods_already_installed++ )
+ unless ( $methods_already_installed++ )
{
DBD::DBM::db->install_method('dbm_versions');
DBD::DBM::st->install_method('dbm_schema');
@@ -215,35 +215,32 @@
#
sub dbm_versions
{
- my $dbh = shift;
- my $table = shift || '';
- my $dtype =
- $dbh->{f_meta}->{$table}->{dbm_type}
- || $dbh->{dbm_type}
- || 'SDBM_File';
- my $mldbm =
- $dbh->{f_meta}->{$table}->{dbm_mldbm}
- || $dbh->{dbm_mldbm}
- || '';
- $dtype .= ' + MLDBM + ' . $mldbm if ($mldbm);
-
- my %version = ( DBI => $DBI::VERSION );
- $version{"DBI::PurePerl"} = $DBI::PurePerl::VERSION if ($DBI::PurePerl);
- $version{OS} = "$^O ($Config::Config{osvers})";
- $version{Perl} = "$] ($Config::Config{archname})";
- my $str = sprintf( "%-16s %s\n%-16s %s\n%-16s %s\n",
- 'DBD::DBM', $dbh->{Driver}->{Version} . " using $dtype",
- ' DBD::File', $dbh->{f_version},
- ' DBI::SQL::Nano',
- $dbh->{sql_nano_version} );
- $str .= sprintf( "%-16s %s\n", ' SQL::Statement',
$dbh->{sql_statement_version} )
- if ( $dbh->{sql_handler} eq 'SQL::Statement' );
+ my $class = $_[0]->FETCH ("ImplementorClass");
+ my $get_versions = $class->can( 'get_versions' );
+ goto &$get_versions;
+}
- for ( sort keys %version )
- {
- $str .= sprintf( "%-16s %s\n", $_, $version{$_} );
- }
- return "$str\n";
+sub get_versions
+{
+ my ($dbh, $table) = @_;
+ $table ||= '';
+ my @versions = $dbh->SUPER::get_versions( $table );
+
+ # update first line (optical)
+ my $dbdfv = shift @versions;
+ my ($pkg, $info) = split( /\s+/, $dbdfv, 2 );
+ unshift (@versions, sprintf ("%-16s %s", ' ' . $pkg, $info ));
+
+ my $class = $dbh->FETCH ("ImplementorClass");
+ $class =~ s/::db$/::Table/;
+ my (undef, $meta) = $class->get_table_meta( $dbh, $table, 1 );
+ $meta or $meta = {} and $class->bootstrap_table_meta( $dbh, $meta, $table
);
+
+ my $dtype = $meta->{dbm_type};
+ $dtype .= ' + MLDBM + ' . $meta->{dbm_mldbm} if( $meta->{dbm_mldbm} );
+ unshift( @versions, sprintf( "%-16s %s using %s", 'DBD::DBM',
$dbh->{dbm_version}, $dtype ) );
+
+ return wantarray ? @versions : join ("\n", @versions);
}
# you may need to over-ride some DBD::File::db methods here
@@ -1343,6 +1340,18 @@
them in Perl. Obviously, this can present dangers, so if you don't know
what's in a file, be careful before you access it with MLDBM turned on!
+This modules uses hash interfaces of two column file databases. While
+none of supported SQL engines have a support for indeces, following
+statements really do the same (even if they mean something completely
+different):
+
+ $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')" );
+
See the entire section on L<Table locking and flock()> for gotchas and
warnings about the use of flock().
Modified: dbi/trunk/lib/DBD/File.pm
==============================================================================
--- dbi/trunk/lib/DBD/File.pm (original)
+++ dbi/trunk/lib/DBD/File.pm Fri Jun 4 04:12:18 2010
@@ -31,12 +31,21 @@
use strict;
use Carp;
-use vars qw( @ISA $VERSION $drh $valid_attrs );
+use vars qw( @ISA $VERSION $drh );
$VERSION = "0.39";
$drh = undef; # holds driver handle(s) once initialized
+DBI->setup_driver ("DBD::File"); # only needed once but harmless to repeat
+
+my %accessors = (
+ versions => 'get_file_versions',
+ get_meta => 'get_file_meta',
+ set_meta => 'set_file_meta',
+ clear_meta => 'clear_file_meta',
+ );
+
sub driver ($;$)
{
my ($class, $attr) = @_;
@@ -50,7 +59,6 @@
# their own caching, so caching here just provides extra safety.
$drh->{$class} and return $drh->{$class};
- DBI->setup_driver ("DBD::File"); # only needed once but harmless to repeat
$attr ||= {};
{ no strict "refs";
unless ($attr->{Attribution}) {
@@ -65,6 +73,23 @@
$drh->{$class} = DBI::_new_drh ($class . "::dr", $attr);
$drh->{$class}->STORE (ShowErrorStatement => 1);
+
+ my $prefix = DBI->driver_prefix ($class);
+ my $dbclass = $class . "::db";
+ while (my ($accessor, $funcname) = each %accessors) {
+ my $method = $prefix . $accessor;
+ $dbclass->can ($method) and next;
+ my $inject = sprintf <<'EOI', $dbclass, $method, $dbclass, $funcname;
+sub %s::%s
+{
+ my $func = %s->can( '%s' );
+ goto &$func;
+ }
+EOI
+ eval $inject;
+ $dbclass->install_method ($method);
+ }
+
return $drh->{$class};
} # driver
@@ -95,10 +120,16 @@
if ($this) {
# must be done first, because setting flags implicitly calls
$dbdname::st->STORE
$this->func ("init_valid_attributes");
+
+ #$this->{f_ext} = "";
+ $this->{f_dir} = File::Spec->curdir ();
+ $this->{f_meta} = {};
+ #$this->{f_map} = {};
+ $this->{f_meta_map} = {}; # choose new name because it contains other
keys
+ $this->STORE (sql_identifier_case => 2); # SQL_IC_LOWER
+ $this->STORE (sql_quoted_identifier_case => 3); # SQL_IC_SENSITIVE
+
my ($var, $val);
- $this->{f_dir} = File::Spec->curdir ();
- $this->{f_ext} = "";
- $this->{f_map} = {};
while (length $dbname) {
if ($dbname =~ s/^((?:[^\\;]|\\.)*?);//s) {
$var = $1;
@@ -112,12 +143,18 @@
($val = $2) =~ s/\\(.)/$1/g;
$this->{$var} = $val;
}
+ elsif ($var =~ m/^(.+?)=>(.*)/s) {
+ $var = $1;
+ ($val = $2) =~ s/\\(.)/$1/g;
+ my $ref = eval($val);
+ $this->$var ($ref);
+ }
}
+
+ $this->STORE (Active => 1);
+ $this->func ("set_versions");
}
- $this->STORE (Active => 1);
- $this->STORE (sql_identifier_case => 2); # SQL_IC_LOWER
- $this->STORE (sql_quoted_identifier_case => 3); # SQL_IC_SENSITIVE
- $this->func ("set_versions");
+
return $this;
} # connect
@@ -234,6 +271,7 @@
$this->{sql_handler} = $this->{sql_statement_version}
? "SQL::Statement"
: "DBI::SQL::Nano";
+
return $this;
} # set_versions
@@ -296,6 +334,7 @@
sub disconnect ($)
{
$_[0]->STORE (Active => 0);
+ $_[0]->STORE (f_meta => {});
return 1;
} # disconnect
@@ -374,11 +413,71 @@
return $dbh->SUPER::STORE ($attrib, $value);
} # STORE
+sub get_versions
+{
+ my $dbh = $_[0];
+ my %version;
+ $version{'DBD::File'} = $dbh->{f_version} . " using " .
$dbh->{sql_handler} . " ";
+ $version{'DBD::File'} .= $dbh->{sql_handler} eq "SQL::Statement"
+ ? $dbh->{sql_statement_version}
+ : $dbh->{sql_nano_version};
+ $version{'DBI'} = $DBI::VERSION;
+ $version{"DBI::PurePerl"} = $DBI::PurePerl::VERSION if ($DBI::PurePerl);
+ $version{OS} = "$^O ($Config::Config{osvers})";
+ $version{Perl} = "$] ($Config::Config{archname})";
+
+ my @versions;
+ foreach my $vk (sort keys %version) {
+ push (@versions, sprintf ("%-16s %s", $vk, $version{$vk} ));
+ }
+
+ return wantarray ? @versions : join ("\n", @versions);
+ } # get_file_versions
+
+sub get_file_meta
+{
+ my ($dbh, $table, $attr) = @_;
+
+ my $class = $dbh->FETCH ("ImplementorClass");
+ $class =~ s/::db$/::Table/;
+ my (undef, $meta) = $class->get_table_meta( $dbh, $table, 1 );
+ $meta or croak "No such table '$table'";
+
+ # prevent creation of undef attributes
+ exists $meta->{$attr} and return $meta->{$attr};
+ return;
+ } # get_file_meta
+
+sub set_file_meta
+{
+ my ($dbh, $table, $attr, $value) = @_;
+
+ my $class = $dbh->FETCH ("ImplementorClass");
+ $class =~ s/::db$/::Table/;
+ my (undef, $meta) = $class->get_table_meta( $dbh, $table, 1 );
+ $meta or croak "No such table '$table'";
+
+ $meta->{$attr} = $value;
+ return;
+ } # set_file_meta
+
+sub clear_file_meta
+{
+ my ($dbh, $table, $attr, $value) = @_;
+
+ my $class = $dbh->FETCH ("ImplementorClass");
+ $class =~ s/::db$/::Table/;
+ my (undef, $meta) = $class->get_table_meta( $dbh, $table, 1 );
+ $meta and %{$meta} = ();
+
+ return;
+ } # clear_file_meta
+
sub DESTROY ($)
{
my $dbh = shift;
$dbh->SUPER::FETCH ("Active") and $dbh->disconnect ;
- undef $dbh->{csv_sql_parser_object};
+ undef $dbh->{sql_parser_object};
} # DESTROY
sub type_info_all ($)
@@ -744,7 +843,7 @@
# (my $tbl = $file) =~ s/$ext$//i;
my ($tbl, $dir, undef) = File::Basename::fileparse ($file, $ext);
- Cwd::abs_path ($dir) eq $meta->{f_dir} and
+ (Cwd::abs_path ($dir) eq $meta->{f_dir} or $dir eq "./") and
$dir = "";
!$respect_case and $meta->{sql_identifier_case} == 1 and # XXX SQL_IC_UPPER
$tbl = uc $tbl;
@@ -756,19 +855,35 @@
: File::Spec->catdir ($meta->{f_dir}, $dir);
# Fully Qualified File Name
- unless ($respect_case) { # table names are case insensitive in SQL
- opendir my $dh, $searchdir or croak "Can't open '$searchdir': $!";
- my @f = grep { lc $_ eq lc $file } readdir $dh;
- @f == 1 and $file = $f[0];
- !$respect_case and $meta->{sql_identifier_case} == 4 and # XXX
SQL_IC_MIXED
- ($tbl = $file) =~ s/$ext$//i;
- closedir $dh or croak "Can't close '$searchdir': $!";
+ my $cmpsub;
+ if ($respect_case) {
+ $cmpsub = sub {
+ my ( $fn, undef, $sfx ) = File::Basename::fileparse( $_,
qr/\.[^.]*/ );
+ return ( lc $sfx eq lc $ext or (!$req and !$sfx) ) if $fn eq $tbl;
+ return 0;
+ }
}
+ else {
+ $cmpsub = sub {
+ my ( $fn, undef, $sfx ) = File::Basename::fileparse( $_,
qr/\.[^.]*/ );
+ return ( lc $sfx eq lc $ext or (!$req and !$sfx) ) if lc $fn eq lc
$tbl;
+ return 0;
+ }
+ }
+
+ opendir my $dh, $searchdir or croak "Can't open '$searchdir': $!";
+ my @f = sort { length $b <=> length $a } ( grep { &$cmpsub($_) } readdir
$dh );
+ @f > 0 and @f <= 2 and $file = $f[0];
+ !$respect_case and $meta->{sql_identifier_case} == 4 and # XXX SQL_IC_MIXED
+ ($tbl = $file) =~ s/$ext$//i;
+ closedir $dh or croak "Can't close '$searchdir': $!";
+
my $fqfn = Cwd::abs_path (File::Spec->catfile ($searchdir, $file));
my $fqbn = File::Spec->catfile ($searchdir, $tbl);
- (my $tdir = $dir) =~ s{^\./}{}; # We do not want all tables to start
with ./
- $tdir and $tbl = File::Spec->catfile ($tdir, $tbl);
+ #(my $tdir = $dir) =~ s{^\./}{}; # XXX We do not want all tables to
start with ./
+ #$tdir and $tbl = File::Spec->catfile ($tdir, $tbl);
+ $dir and $tbl = File::Spec->catfile ($dir, $tbl);
$file = $fqfn;
if ($ext) {
@@ -776,25 +891,24 @@
# 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;
- }
+# 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;
+ $meta->{f_fqbn} = $fqbn;
!defined $meta->{f_lockfile} && $meta->{f_lockfile} and
$meta->{f_fqln} = $meta->{f_fqbn} . $meta->{f_lockfile};
$meta->{table_name} = $tbl;
- $respect_case or $tbl = lc $tbl; # for internal storage
return $tbl;
} # file2table
-sub bootstrap_table_meta ($$$$$)
+sub bootstrap_table_meta
{
my ($self, $dbh, $meta, $table) = @_;
@@ -808,7 +922,7 @@
$meta->{sql_identifier_case} = $dbh->{sql_identifier_case};
} # bootstrap_table_meta
-sub init_table_meta ($$$$$)
+sub init_table_meta
{
my ($self, $dbh, $meta, $table) = @_;
@@ -824,15 +938,23 @@
$table =~ s/\"$//;
}
- my $meta = $dbh->{f_meta}{$table} || {};
+ defined $dbh->{f_meta_map}{$table} and $table = $dbh->{f_meta_map}{$table};
+
+ my $meta = {};
+ defined ($dbh->{f_meta}{$table}) and $meta = $dbh->{f_meta}{$table};
unless ($meta->{initialized}) {
$self->bootstrap_table_meta ($dbh, $meta, $table);
unless (defined $meta->{f_fqfn}) {
- $table = $self->file2table ($meta, $table, $file_is_table,
$respect_case);
- $table or return;
+ $self->file2table ($meta, $table, $file_is_table, $respect_case) or
return;
+ }
+
+ if( defined $meta->{table_name} and $table ne $meta->{table_name} ) {
+ $dbh->{f_meta_map}{$table} = $meta->{table_name};
+ $table = $meta->{table_name};
}
+ # XXX check $dbh->{f_meta}{ $meta->{table_name} }
$self->init_table_meta ($dbh, $meta, $table);
$meta->{initialized} = 1;
$dbh->{f_meta}{$table} = $meta;
@@ -917,8 +1039,9 @@
my ($className, $data, $attrs, $flags) = @_;
my $dbh = $data->{Database};
- my $meta;
- ($attrs->{table}, $meta) = $className->get_table_meta ($dbh,
$attrs->{table}, 1);
+ my ($tblnm, $meta) = $className->get_table_meta ($dbh, $attrs->{table}, 1)
or
+ croak "Cannot find appropriate file for table '$attrs->{table}'";
+ $attrs->{table} = $tblnm;
$className->open_file ($meta, $attrs, $flags);