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