Author: REHSACK
Date: Thu Jun 10 04:17:13 2010
New Revision: 14128
Modified:
dbi/trunk/lib/DBD/DBM.pm
dbi/trunk/lib/DBD/File.pm
Log:
- add proper support of public table meta access via "${prefix}tables"
- improve FETCH/STORE restrictions (access invalid attributes, fetch
readonly attributes)
- add special table "." to get_file_meta/set_file_meta to access global
attributes
- fix get_versions internal function
- improve dbh attribute initialization
- add support for table meta attribute "f_file" (for DBD::CSV backward
compatibility)
- DBD::DBM gots the required changes for the new DBD::File extensions with
this commit
- new head1 for BUGS AND LIMITATIONS is added especially for the INSERT
behaves as UPDATE bug.
Modified: dbi/trunk/lib/DBD/DBM.pm
==============================================================================
--- dbi/trunk/lib/DBD/DBM.pm (original)
+++ dbi/trunk/lib/DBD/DBM.pm Thu Jun 10 04:17:13 2010
@@ -186,7 +186,7 @@
sub init_valid_attributes
{
- my $sth = shift;
+ my $dbh = shift;
# define valid private attributes
#
@@ -197,16 +197,28 @@
#
# see the STORE methods below for how to check these attrs
#
- $sth->{dbm_valid_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 $sth->SUPER::init_valid_attributes();
+ return $dbh->SUPER::init_valid_attributes();
}
# this is an example of a private method
@@ -1182,22 +1194,28 @@
=head1 DBI database handle attributes
-=head2 Statement handle ($sth) attributes and methods
+=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.
-=head2 Metadata
+=head3 Driver handle ($dbh) attributes
-=over 4
+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.
-=item dbm_cols
+=head4 dbm_cols
Contains a comma separated list of column names or an array reference to
a column names.
-=item dbm_type
+=head4 dbm_type
Contains the DBM storage type. Currently know supported ones are
C<< ODBM_File >>, C<< NDBM_File >>, C<< SDBM_File >>, C<< GDBM_File >>,
@@ -1205,7 +1223,7 @@
of the first three types - even if C<< SDBM_File >> is the most common
available I<dbm_type>.
-=item dbm_mldbm
+=head4 dbm_mldbm
Contains the serializer for DBM storage (value column). Requires the
CPAN module L<MLDBM> installed. Currently know supported serializers are:
@@ -1231,57 +1249,54 @@
=back
-=item dbm_store_metadata
+=head4 dbm_store_metadata
Boolean value whether to store some metadata in DBM storage or not.
-=item dbm_berkeley_flags
+=head4 dbm_berkeley_flags
Hash reference with additional flags for BerkeleyDB::Hash instantiation.
-=item dbm_version
+=head4 dbm_version
Readonly attribute containing this version of DBD::DBM.
-=item f_meta
+=head4 f_meta
In addition to the attributes L<DBD::File> recognizes, DBD::DBM cares about
-the (public) attributes col_names (B<Note> not I<dbm_cols> here!), dbm_type,
-dbm_mldbm, dbm_store_metadata and dbm_berkeley_flags. There are, like in
-DBD::File undocumented, internally used attributes. Be very careful when
-modifying attributes you do not know, the consequence might a destroyed
-table.
+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>.
+There are, like in DBD::File undocumented, internally used attributes.
+Be very careful when modifying attributes you do not know, the consequence
+might a destroyed 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 key
+(accessing to 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>.
-=back
+=head3 Following attributes are no longer handled by DBD::DBM:
-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.
-
-Following attributes are no longer handled by DBD::DBM:
-
-=over 4
-
-=item dbm_ext
+=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 one fine day it will be removed.
-=item dbm_lockfile
+=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 one fine day it will be removed.
-=item dbm_tables
-
-This attribute is forbidden and accessing it will throw an error.
-
-=back
-
=head1 DBI database handle methods
=head2 The $dbh->dbm_versions() method
@@ -1340,6 +1355,11 @@
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!
+See the entire section on L<Table locking and flock()> for gotchas and
+warnings about the use of flock().
+
+=head1 BUGS AND LIMITATIONS
+
This modules uses hash interfaces of two column file databases. While
none of supported SQL engines have a support for indices, following
statements really do the same (even if they mean something completely
@@ -1352,8 +1372,7 @@
# ... 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().
+This is considered as a bug and might change in a future release.
=head1 GETTING HELP, MAKING SUGGESTIONS, AND REPORTING BUGS
Modified: dbi/trunk/lib/DBD/File.pm
==============================================================================
--- dbi/trunk/lib/DBD/File.pm (original)
+++ dbi/trunk/lib/DBD/File.pm Thu Jun 10 04:17:13 2010
@@ -40,7 +40,7 @@
DBI->setup_driver ("DBD::File"); # only needed once but harmless to repeat
my %accessors = (
- versions => "get_file_versions",
+ versions => "get_versions",
get_meta => "get_file_meta",
set_meta => "set_file_meta",
clear_meta => "clear_file_meta",
@@ -121,15 +121,7 @@
if ($this) {
# must be done first, because setting flags implicitly calls $dbdname::db->STORE
- $this->func ("init_valid_attributes");
-
- # f_ext should not be initialized
- # f_map is deprecated (but might return)
- $this->{f_dir} = File::Spec->curdir ();
- $this->{f_meta} = {};
- $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
+ $this->func ("init_default_attributes");
my ($var, $val);
while (length $dbname) {
@@ -154,7 +146,6 @@
}
$this->STORE (Active => 1);
- $this->func ("set_versions");
}
return $this;
@@ -211,6 +202,15 @@
use Carp;
require File::Spec;
require Cwd;
+use Scalar::Util qw(refaddr); # in CORE since 5.7.3
+
+if (eval { require Clone; }) {
+ Clone->import ("clone");
+ }
+else {
+ require Storable; # in CORE since 5.7.3
+ *clone = \&Storable::dclone;
+ }
$DBD::File::db::imp_data_size = 0;
@@ -291,15 +291,18 @@
f_lock => 1, # Table locking mode
f_lockfile => 1, # Table lockfile extension
f_encoding => 1, # Encoding of the file
+ f_valid_attrs => 1, # File valid attributes
f_readonly_attrs => 1, # File readonly attributes
};
$dbh->{sql_valid_attrs} = {
sql_handler => 1, # Nano or S:S
sql_nano_version => 1, # Nano version
sql_statement_version => 1, # S:S version
+ sql_flags => 1, # flags for SQL::Parser
sql_quoted_identifier_case => 1, # case for quoted identifiers
sql_identifier_case => 1, # case for non-quoted identifiers
sql_parser_object => 1, # SQL::Parser instance
+ sql_valid_attrs => 1, # SQL valid attributes
sql_readonly_attrs => 1, # SQL readonly attributes
};
$dbh->{f_readonly_attrs} = {
@@ -320,6 +323,54 @@
return $dbh;
} # init_valid_attributes
+sub init_default_attributes
+{
+ my $dbh = shift;
+
+ # must be done first, because setting flags implicitly calls $dbdname::db->STORE
+ $dbh->func ("init_valid_attributes");
+
+ $dbh->func ("set_versions");
+
+ # f_ext should not be initialized
+ # f_map is deprecated (but might return)
+ $dbh->{f_dir} = Cwd::abs_path (File::Spec->curdir ());
+ $dbh->{f_meta} = {};
+ $dbh->{f_meta_map} = {}; # choose new name because it contains other keys
+
+ $dbh->{sql_identifier_case} = 2; # SQL_IC_LOWER
+ $dbh->{sql_quoted_identifier_case} = 3; # SQL_IC_SENSITIVE
+
+ # complete derived attributes, if required
+ (my $drv_class = $dbh->{ImplementorClass}) =~ s/::db$//;
+ my $drv_prefix = DBI->driver_prefix ($drv_class);
+ my $valid_attrs = $drv_prefix . "valid_attrs";
+ my $ro_attrs = $drv_prefix . "readonly_attrs";
+
+ my @comp_attrs = qw(valid_attrs version readonly_attrs);
+ if (exists $dbh->{$drv_prefix . "meta"}) {
+ my $attr = $dbh->{$drv_prefix . "meta"};
+ defined $attr and defined $dbh->{$valid_attrs} and !defined $dbh->{$valid_attrs}{$attr} and
+ $dbh->{$valid_attrs}{$attr} = 1;
+
+ my %h;
+ tie %h, "DBD::File::TieTables", $dbh;
+ $dbh->{$attr} = \%h;
+
+ push @comp_attrs, "meta";
+ }
+
+ foreach my $comp_attr (@comp_attrs) {
+ my $attr = $drv_prefix . $comp_attr;
+ defined $dbh->{$valid_attrs} and !defined $dbh->{$valid_attrs}{$attr} and
+ $dbh->{$valid_attrs}{$attr} = 1;
+ defined $dbh->{$ro_attrs} and !defined $dbh->{$ro_attrs}{$attr} and
+ $dbh->{$ro_attrs}{$attr} = 1;
+ }
+
+ return $dbh;
+ } # init_default_attributes
+
sub sql_parser_object
{
my $dbh = shift;
@@ -338,7 +389,7 @@
sub disconnect ($)
{
$_[0]->STORE (Active => 0);
- $_[0]->STORE (f_meta => {});
+ %{$_[0]->{f_meta}} = ();
return 1;
} # disconnect
@@ -351,9 +402,28 @@
if ($attrib eq (lc $attrib)) {
# Driver private attributes are lower cased
- # Error-check for valid attributes
+ # XXX Error-check for valid attributes
# not implemented yet, see STORE
#
+ # XXX return cloned value when readonly attr
+ # and not scalar type
+ # use: *clone = $haveClone ? \&Clone::clone : \&Storable::dclone;
+ #
+ my $attr_prefix;
+ $attrib =~ m/^([a-z]+_)/ and $attr_prefix = $1;
+ unless ($attr_prefix) {
+ (my $drv_class = $dbh->{ImplementorClass}) =~ s/::db$//;
+ $attr_prefix = DBI->driver_prefix ($drv_class);
+ $attrib = $attr_prefix . $attrib;
+ }
+ my $valid_attrs = $attr_prefix . "valid_attrs";
+ my $ro_attrs = $attr_prefix . "readonly_attrs";
+ exists $dbh->{$valid_attrs} and ($dbh->{$valid_attrs}{$attrib} or
+ return $dbh->set_err ($DBI::stderr, "Invalid attribute '$attrib'"));
+ exists $dbh->{$ro_attrs} and $dbh->{$ro_attrs}{$attrib} and
+ defined $dbh->{$attrib} and refaddr ($dbh->{$attrib}) and
+ return clone ($dbh->{$attrib});
+
return $dbh->{$attrib};
}
# else pass up to DBI to handle
@@ -413,6 +483,7 @@
and
$value < 1 || $value > 4) {
croak "attribute '$attrib' must have a value from 1 .. 4 (SQL_IC_UPPER .. SQL_IC_MIXED)";
+ # XXX correctly a remap of all entries in f_meta/f_meta_map is required here
}
# if (($attrib =~ m/^f_/ && $dbh->{f_readonly_attrs}{$attrib} or
@@ -452,28 +523,35 @@
sub get_file_meta
{
my ($dbh, $table, $attr) = @_;
+ my $meta;
+
+ $table eq "." and
+ return $dbh->FETCH ($attr);
my $class = $dbh->FETCH ("ImplementorClass");
$class =~ s/::db$/::Table/;
- my (undef, $meta) = $class->get_table_meta ($dbh, $table, 1);
+ (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;
+ return $class->get_table_meta_attr( $meta, $attr );
} # get_file_meta
sub set_file_meta
{
my ($dbh, $table, $attr, $value) = @_;
+ my $meta;
- 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;
+ if ($table eq ".") {
+ return $dbh->STORE ($attr, $value);
+ }
+ else {
+ my $class = $dbh->FETCH ("ImplementorClass");
+ $class =~ s/::db$/::Table/;
+ (undef, $meta) = $class->get_table_meta ($dbh, $table, 1);
+ $meta or croak "No such table '$table'";
+ return $class->set_table_meta_attr( $meta, $attr, $value );
+ }
} # set_file_meta
sub clear_file_meta
@@ -640,6 +718,154 @@
return 0;
} # rollback
+# ====== Tie-Meta ==============================================================
+
+package DBD::File::TieMeta;
+
+use Carp qw(croak);
+require Tie::Hash;
+@DBD::File::TieMeta::ISA = qw(Tie::Hash);
+
+sub TIEHASH
+{
+ my ($class, $tblClass, $tblMeta) = @_;
+
+ my $self = bless ({ tblClass => $tblClass, tblMeta => $tblMeta, }, $class);
+ return $self;
+ } # new
+
+sub STORE
+{
+ my ($self, $meta_attr, $meta_val) = @_;
+
+ $self->{tblClass}->set_table_meta_attr ($self->{tblMeta}, $meta_attr, $meta_val);
+
+ return;
+ } # STORE
+
+sub FETCH
+{
+ my ($self, $meta_attr) = @_;
+
+ return $self->{tblClass}->get_table_meta_attr ($self->{tblMeta}, $meta_attr);
+ } # FETCH
+
+sub FIRSTKEY
+{
+ my $a = scalar keys %{$_[0]->{tblMeta}};
+ each %{$_[0]->{tblMeta}};
+ } # FIRSTKEY
+
+sub NEXTKEY
+{
+ each %{$_[0]->{tblMeta}};
+ } # NEXTKEY
+
+sub EXISTS
+{
+ exists $_[0]->{tblMeta}{$_[1]};
+ } # EXISTS
+
+sub DELETE
+{
+ croak "Can't delete single attributes from table meta structure";
+ } # DELETE
+
+sub CLEAR
+{
+ %{$_[0]->{tblMeta}} = ()
+ } # CLEAR
+
+sub SCALAR
+{
+ scalar %{$_[0]->{tblMeta}}
+ } # SCALAR
+
+# ====== Tie-Tables ============================================================
+
+package DBD::File::TieTables;
+
+use Carp qw(croak);
+require Tie::Hash;
+@DBD::File::TieTables::ISA = qw(Tie::Hash);
+
+sub TIEHASH
+{
+ my ($class, $dbh) = @_;
+
+ (my $tbl_class = $dbh->{ImplementorClass}) =~ s/::db$/::Table/;
+ my $self = bless ({ dbh => $dbh, tblClass => $tbl_class, }, $class);
+ return $self;
+ } # new
+
+sub STORE
+{
+ my ($self, $table, $tbl_meta) = @_;
+
+ "HASH" eq ref $tbl_meta or
+ croak "Invalid data for storing as table meta data (must be hash)";
+
+ (undef, my $meta) = $self->{tblClass}->get_table_meta ($self->{dbh}, $table, 1);
+ $meta or croak "Invalid table name '$table'";
+
+ while (my ($meta_attr, $meta_val) = each %$tbl_meta) {
+ $self->{tblClass}->set_table_meta_attr ($meta, $meta_attr, $meta_val);
+ }
+
+ return;
+ } # STORE
+
+sub FETCH
+{
+ my ($self, $table) = @_;
+
+ (undef, my $meta) = $self->{tblClass}->get_table_meta ($self->{dbh}, $table, 1);
+ $meta or croak "Invalid table name '$table'";
+
+ my %h;
+ tie %h, "DBD::File::TieMeta", $self->{tblClass}, $meta;
+
+ return \%h;
+ } # FETCH
+
+sub FIRSTKEY
+{
+ my $a = scalar keys %{$_[0]->{dbh}->{f_meta}};
+ each %{$_[0]->{dbh}->{f_meta}};
+ } # FIRSTKEY
+
+sub NEXTKEY
+{
+ each %{$_[0]->{dbh}->{f_meta}};
+ } # NEXTKEY
+
+sub EXISTS
+{
+ exists $_[0]->{dbh}->{f_meta}->{$_[1]} or
+ exists $_[0]->{dbh}->{f_meta_map}->{$_[1]};
+ } # EXISTS
+
+sub DELETE
+{
+ my ($self, $table) = @_;
+
+ (undef, my $meta) = $self->{tblClass}->get_table_meta ($self->{dbh}, $table, 1);
+ $meta or croak "Invalid table name '$table'";
+
+ delete $_[0]->{dbh}->{f_meta}->{$meta->{table_name}};
+ } # DELETE
+
+sub CLEAR
+{
+ %{$_[0]->{dbh}->{f_meta}} = ();
+ %{$_[0]->{dbh}->{f_meta_map}} = ();
+ } # CLEAR
+
+sub SCALAR
+{
+ scalar %{$_[0]->{dbh}->{f_meta}}
+ } # SCALAR
+
# ====== STATEMENT =============================================================
package DBD::File::st;
@@ -857,63 +1083,77 @@
}
# (my $tbl = $file) =~ s/$ext$//i;
- my ($tbl, $dir, undef) = File::Basename::fileparse ($file, $ext);
+ my ($tbl, $dir, $user_spec_file);
+ if ($file_is_table and defined $meta->{f_file}) {
+ $tbl = $file;
+ ($file, $dir, undef) = File::Basename::fileparse ($meta->{f_file});
+ $user_spec_file = 1;
+ }
+ else {
+ ($tbl, $dir, undef) = File::Basename::fileparse ($file, $ext);
+ $user_spec_file = 0;
+ }
+
(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;
!$respect_case and $meta->{sql_identifier_case} == 2 and # XXX SQL_IC_LOWER
$tbl = lc $tbl;
- $file_is_table and $file = "$tbl$ext";
my $searchdir = File::Spec->file_name_is_absolute ($dir)
? $dir
: File::Spec->catdir ($meta->{f_dir}, $dir);
- # Fully Qualified File Name
- my $cmpsub;
- if ($respect_case) {
- $cmpsub = sub {
- my ($fn, undef, $sfx) = File::Basename::fileparse ($_, qr/\.[^.]*/);
- $fn eq $tbl and
- return (lc $sfx eq lc $ext or !$req && !$sfx);
- return 0;
+ unless ($user_spec_file) {
+ $file_is_table and $file = "$tbl$ext";
+
+ # Fully Qualified File Name
+ my $cmpsub;
+ if ($respect_case) {
+ $cmpsub = sub {
+ my ($fn, undef, $sfx) = File::Basename::fileparse ($_, qr/\.[^.]*/);
+ $fn eq $tbl and
+ return (lc $sfx eq lc $ext or !$req && !$sfx);
+ return 0;
+ }
}
- }
- else {
- $cmpsub = sub {
- my ($fn, undef, $sfx) = File::Basename::fileparse ($_, qr/\.[^.]*/);
- lc $fn eq lc $tbl and
- return (lc $sfx eq lc $ext or !$req && !$sfx);
- return 0;
+ else {
+ $cmpsub = sub {
+ my ($fn, undef, $sfx) = File::Basename::fileparse ($_, qr/\.[^.]*/);
+ lc $fn eq lc $tbl and
+ return (lc $sfx eq lc $ext or !$req && !$sfx);
+ 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 && @f <= 2 and $file = $f[0];
- !$respect_case && $meta->{sql_identifier_case} == 4 and # XXX SQL_IC_MIXED
- ($tbl = $file) =~ s/$ext$//i;
- closedir $dh or croak "Can't close '$searchdir': $!";
+ opendir my $dh, $searchdir or croak "Can't open '$searchdir': $!";
+ my @f = sort { length $b <=> length $a } grep { &$cmpsub ($_) } readdir $dh;
+ @f > 0 && @f <= 2 and $file = $f[0];
+ !$respect_case && $meta->{sql_identifier_case} == 4 and # XXX SQL_IC_MIXED
+ ($tbl = $file) =~ s/$ext$//i;
+ closedir $dh or croak "Can't close '$searchdir': $!";
+
+ #(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);
+
+ my $tmpfn = $file;
+ if ($ext) {
+ if ($req) {
+ # File extension required
+ $tmpfn =~ s/$ext$//i or return;
+ }
+# else {
+# # File extension optional, skip if file with extension exists
+# grep m/$ext$/i, glob "$fqfn.*" and return;
+# $tmpfn =~ s/$ext$//i;
+# }
+ }
+ }
my $fqfn = Cwd::abs_path (File::Spec->catfile ($searchdir, $file));
- my $fqbn = File::Spec->catfile ($searchdir, $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) {
- 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;
-# }
- }
+ my $fqbn = Cwd::abs_path (File::Spec->catfile ($searchdir, $tbl));
$meta->{f_fqfn} = $fqfn;
$meta->{f_fqbn} = $fqbn;
@@ -987,6 +1227,30 @@
return ($table, $meta);
} # get_table_meta
+sub get_table_meta_attr
+{
+ my ($class, $meta, $attrib) = @_;
+ exists $meta->{$attrib} and
+ return $meta->{$attrib};
+ return;
+ } # get_table_meta_attr
+
+my %reset_on_modify = (
+ f_file => "f_fqfn",
+ f_dir => "f_fqfn",
+ f_ext => "f_fqfn",
+ f_lockfile => "f_fqfn", # forces new file2table call
+);
+
+sub set_table_meta_attr
+{
+ my ($class, $meta, $attrib, $value) = @_;
+ defined $reset_on_modify{$attrib} and
+ delete $meta->{$reset_on_modify{$attrib}} and
+ delete $meta->{initialized};
+ $meta->{$attrib} = $value;
+ } # set_table_meta_attr
+
# ====== FILE OPEN =============================================================
sub open_file ($$$)
@@ -1222,7 +1486,12 @@
This attribute is used for setting the directory where the files are
opened and it defaults to the current directory (C<.>). Usually you set
-it on the dbh but it may be overridden on the statement handle.
+it on the dbh but it may be overridden per table (see L<f_meta>).
+
+When the value for C<f_dir> is a relative path, it's converted into the
+appropriate absolute path name (based on the current working directory)
+when the dbh attribute is set.
+
See L<KNOWN BUGS AND LIMITATIONS>.
=head4 f_ext
@@ -1265,6 +1534,9 @@
The C<r> flag means the file extension is required and any filename
that does not match the extension is ignored.
+Usually you set it on the dbh but it may be overridden per table
+(see L<f_meta>).
+
=head4 f_schema
This will set the schema name and defaults to the owner of the
@@ -1339,9 +1611,29 @@
possible to pre-initialize attributes for each table wanted to use.
DBD::File recognizes the (public) attributes C<f_ext>, C<f_dir>,
-C<f_encoding>, C<f_lock>, and C<f_lockfile>. Be very careful when
-modifying attributes you do not know, the consequence might be a
-destroyed table.
+C<f_file>, C<f_encoding>, C<f_lock>, C<f_lockfile>, C<f_schema>,
+C<col_names>, C<table_name> and C<sql_identifier_case>. Be very careful
+when modifying attributes you do not know, the consequence might be a
+destroyed or corrupted table.
+
+C<f_file> is an attribute applicable to table meta data only, you won't
+find a corresponding attribute in the dbh. While it may reasonable to
+have several tables with the same column names, it's not for the same
+file name. If you need access to the same file using different table
+names, use C<SQL::Statement> as SQL engine and the C<AS> keyword:
+
+ SELECT * FROM tbl AS t1, tbl AS t2 WHERE t1.id = t2.id
+
+C<f_file> can be an absolute path name or a relative path name. If it's
+relative, it's interpreted being relative to the C<f_dir> attribute of
+the table meta data. It's not tried to find alternate files for the
+table when C<f_file> is set.
+
+While C<f_meta> is a private and readonly attribute (which means, you
+cannot modify it's values), derived drivers might provide restricted
+write access through another attribute. Well known accessors are
+C<csv_tables> for L<DBD::CSV>, C<ad_tables> for L<DBD::AnyData> and
+C<dbm_tables> for L<DBD::DBM>.
=head3 Internally private attributes to deal with SQL backends:
@@ -1366,8 +1658,16 @@
Contains optionally temporary tables.
+=head4 sql_flags
+
+Contains optional flags to instantiate the SQL::Parser parsing engine
+when SQL::Statement is used as SQL engine. See L<SQL::Parser> for valid
+flags.
+
=head2 Driver private methods
+=head3 Default DBI methods
+
=head4 data_sources
The C<data_sources> method returns a list of subdirectories of the current
@@ -1389,12 +1689,98 @@
Note that the list includes all files contained in the directory, even
those that have non-valid table names, from the view of SQL.
+=head3 Additional methods
+
+Following methods are only available via the here decribed name when
+DBD::File is used directly. Because this is only reasonable for testing
+purposes, the real names must be used instead. Those names can be computed
+by replacing the C<f_> in the method name with the driver prefix.
+
+=head4 f_versions
+
+Signature:
+
+ sub f_versions (;$)
+ {
+ my ($table_name) = @_;
+ $table_name ||= ".";
+ ...
+ }
+
+Returns the versions of the driver, including the DBI version, the Perl
+version, DBI::PurePerl version (if DBI::PurePerl is active) and the version
+of the used SQL engine.
+
+ my $dbh = DBI->connect ("dbi:File:");
+ my $f_versions = $dbh->f_versions ();
+ print "$f_versions\n";
+ __END__
+ # DBD::File 0.39 using SQL::Statement 1.28
+ # DBI 1.612
+ # OS netbsd (5.99.24)
+ # Perl 5.010001 (x86_64-netbsd-thread-multi)
+
+Called in list context, f_versions will return an array containing each
+line as single entry.
+
+Some drivers might use the optional (table name) argument and modify some
+version information related to the table (e.g. DBD::DBM provides storage
+backend information for the requested table, when it has a table name).
+
+=head4 f_get_meta
+
+Signature:
+
+ sub f_get_meta ($$)
+ {
+ my ($table_name, $attrib) = @_;
+ ...
+ }
+
+Returns the value of a meta attribute set for a specific table, if any.
+See L<f_meta> for the possible attributes.
+
+A table name of C<'.'> (single dot) is interpreted as the default table.
+This causes in getting the appropriate attribute globally from the dbh.
+This has the same restrictions as C<< $dbh->{$attrib} >>.
+
+=head4 f_set_meta
+
+Signature:
+
+ sub f_set_meta ($$$)
+ {
+ my ($table_name, $attrib, $value) = @_;
+ ...
+ }
+
+Sets the value of a meta attribute set for a specific table.
+See L<f_meta> for the possible attributes.
+
+A table name of C<'.'> (single dot) is interpreted as the default table.
+This causes in setting the appropriate attribute globally for the dbh.
+This has the same restrictions as C<< $dbh->{$attrib} = $value >>.
+
+=head4 f_clear_meta
+
+Signature:
+
+ sub f_clear_meta ($)
+ {
+ my ($table_name) = @_;
+ ...
+ }
+
+Clears the table specific meta information in the private storage of the
+dbh.
+
=head1 SQL ENGINES
-DBD::File currently supports two SQL engines: L<DBI::SQL::Nano|DBI::SQL::Nano>
-and L<SQL::Statement|SQL::Statement>. DBI::SQL::Nano supports a I<very> limited
-subset of SQL statements, but it might be faster for some very simple tasks.
-SQL::Statement in contrast supports a much larger subset of ANSI SQL.
+DBD::File currently supports two SQL engines: L<SQL::Statement|SQL::Statement>
+and L<DBI::SQL::Nano::Statement_|DBI::SQL::Nano>. DBI::SQL::Nano supports a
+I<very> limited subset of SQL statements, but it might be faster for some
+very simple tasks. SQL::Statement in contrast supports a much larger subset
+of ANSI SQL.
To use SQL::Statement, the module version 1.28 of SQL::Statement is a
prerequisite and the environment variable C<DBI_SQL_NANO> must not be
@@ -1419,8 +1805,9 @@
cause different table instances and private data areas.
This data area is filled for the first time when a table is accessed,
-either via an SQL statement or via C<table_info> and is not
-destroyed when the table is dropped or the driver handle is released.
+either via an SQL statement or via C<table_info> and is not
+destroyed until the table is dropped or the driver handle is released.
+Manual destruction is possible via L<f_clear_meta>.
Following attributes are preserved in the data area and will evaluated
instead of driver globals:
@@ -1435,6 +1822,38 @@
=item f_lockfile
+=item f_encoding
+
+=item f_schema
+
+=item col_names
+
+=item sql_identifier_case
+
+=back
+
+Following attributes are preserved in the data area only and cannot be set
+globally.
+
+=over 8
+
+=item f_file
+
+=back
+
+Following attributes are preserved in the data area only and are computed
+when initializing the data area:
+
+=over 8
+
+=item f_fqfn
+
+=item f_fqbn
+
+=item f_fqln
+
+=item table_name
+
=back
For DBD::CSV tables this means, once opened 'foo.csv' as table named 'foo',
@@ -1442,6 +1861,9 @@
Accessing 'foo' will always access the file 'foo.csv' in memorized
C<f_dir>, locking C<f_lockfile> via memorized C<f_lock>.
+You can use L<f_clear_meta> or the C<f_file> attribute for a specific table
+to find a way out.
+
=item *
When used with SQL::Statement and the feature of temporary tables is
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.