Author: timbo
Date: Tue Dec 21 15:08:28 2010
New Revision: 14600
Added:
dbi/trunk/t/48dbi_dbd_sqlengine.t
- copied unchanged from r14599,
/dbi/branches/sqlengine/t/48dbi_dbd_sqlengine.t
Modified:
dbi/trunk/ (props changed)
dbi/trunk/.gitignore
dbi/trunk/Changes
dbi/trunk/dbixs_rev.h
dbi/trunk/lib/DBD/DBM.pm
dbi/trunk/lib/DBD/File.pm
dbi/trunk/lib/DBI/DBD.pm
dbi/trunk/lib/DBI/DBD/SqlEngine.pm
dbi/trunk/lib/DBI/SQL/Nano.pm
dbi/trunk/t/51dbm_file.t
dbi/trunk/t/52dbm_complex.t
Log:
svn merge --reintegrate https://svn.perl.org/modules/dbi/branches/sqlengine
Plus resolution of the natural merge conflicts in Changes.
Modified: dbi/trunk/.gitignore
==============================================================================
--- dbi/trunk/.gitignore (original)
+++ dbi/trunk/.gitignore Tue Dec 21 15:08:28 2010
@@ -26,3 +26,4 @@
xx*
ModList
git-svn-modlist
+git-dpull
Modified: dbi/trunk/Changes
==============================================================================
--- dbi/trunk/Changes (original)
+++ dbi/trunk/Changes Tue Dec 21 15:08:28 2010
@@ -8,19 +8,27 @@
=head2 Changes in DBI 1.617 (svn rXXX) XXX
- Fixed typo in InactiveDestroy thanks to Emmanuel Rodriguez.
- Fixed compiler warnings RT#62640
- Added additional notes on DBDs which avoid creating a statement in
- the do method and the effects on error handlers (Martin J. Evans)
-
-=head2 Changes in DBI 1.616 (svn r14486) 15th October 2010
-
- Documented dbd_st_execute return (Martin J. Evans)
Fixed spurious dbi_profile lines written to the log when
profiling is enabled and a trace flag, like SQL, is used.
Fixed to recognize SQL::Statement errors even if instantiated
with RaiseError=0 (Jens Rehsack)
+ Fixed RT#61513 by catching attribute assignment to tied table access
+ interface (Jens Rehsack)
+ Fixing some misbehavior of DBD::File when running within the Gofer
+ server.
+ Fixed compiler warnings RT#62640
+
Optimized connect() to remove redundant FETCH of \%attrib values.
+ Improved initialization phases in DBI::DBD::SqlEngine (Jens Rehsack)
+
+ Added additional notes on DBDs which avoid creating a statement in
+ the do() method and the effects on error handlers (Martin J. Evans)
+ Adding new attribute "sql_dialect" to DBI::DBD::SqlEngine to allow
+ users control used SQL dialect (ANSI, CSV or AnyData), defaults to
+ CSV (Jens Rehsack)
+ Add documentation for DBI::DBD::SqlEngine attributes (Jens Rehsack)
+ Documented dbd_st_execute return (Martin J. Evans)
+ Fixed typo in InactiveDestroy thanks to Emmanuel Rodriguez.
=head2 Changes in DBI 1.615 (svn r14438) 21st September 2010
Modified: dbi/trunk/dbixs_rev.h
==============================================================================
--- dbi/trunk/dbixs_rev.h (original)
+++ dbi/trunk/dbixs_rev.h Tue Dec 21 15:08:28 2010
@@ -1,2 +1,4 @@
-/* Mon Aug 30 20:49:00 2010 */
-#define DBIXS_REVISION 14354
+/* Tue Dec 14 22:26:28 2010 */
+/* Mixed revision working copy (14564M:14571) */
+/* Code modified since last checkin */
+#define DBIXS_REVISION 14564
Modified: dbi/trunk/lib/DBD/DBM.pm
==============================================================================
--- dbi/trunk/lib/DBD/DBM.pm (original)
+++ dbi/trunk/lib/DBD/DBM.pm Tue Dec 21 15:08:28 2010
@@ -254,6 +254,8 @@
@DBD::DBM::Table::ISA = qw(DBD::File::Table);
+my $dirfext = $^O eq 'VMS' ? '.sdbm_dir' : '.dir';
+
sub file2table
{
my ( $self, $meta, $file, $file_is_table, $quoted ) = @_;
@@ -272,7 +274,7 @@
__PACKAGE__->register_reset_on_modify( \%reset_on_modify );
my %compat_map = (
- map { $_ => "dbm_$_" } qw(type mldbm store_metadata),
+ ( map { $_ => "dbm_$_" } qw(type mldbm store_metadata) ),
dbm_ext => 'f_ext',
dbm_file => 'f_file',
dbm_lockfile => ' f_lockfile',
@@ -444,9 +446,9 @@
$meta->{hash} and untie %{ $meta->{hash} };
$self->SUPER::drop($data);
# XXX extra_files
- -f $meta->{f_fqbn} . '.dir'
+ -f $meta->{f_fqbn} . $dirfext
and $meta->{f_ext} eq '.pag/r'
- and unlink( $meta->{f_fqbn} . '.dir' );
+ and unlink( $meta->{f_fqbn} . $dirfext );
return 1;
}
Modified: dbi/trunk/lib/DBD/File.pm
==============================================================================
--- dbi/trunk/lib/DBD/File.pm (original)
+++ dbi/trunk/lib/DBD/File.pm Tue Dec 21 15:08:28 2010
@@ -218,9 +218,16 @@
# DBI::BD::SqlEngine::dr::connect will detect old-style drivers and
# don't call twice
- defined $phase or $phase = 0;
+ unless (defined $phase) {
+ # we have an "old" driver here
+ $phase = defined $dbh->{sql_init_phase};
+ $phase and $phase = $dbh->{sql_init_phase};
+ }
if (0 == $phase) {
+ # check whether we're running in a Gofer server or not (see
+ # validate_FETCH_attr for details)
+ $dbh->{f_in_gofer} = (defined $INC{"DBD/Gofer.pm"} && (caller(5))[0] eq
"DBI::Gofer::Execute");
# f_ext should not be initialized
# f_map is deprecated (but might return)
$dbh->{f_dir} = Cwd::abs_path (File::Spec->curdir ());
@@ -234,7 +241,7 @@
my $ro_attrs = $drv_prefix . "readonly_attrs";
my @comp_attrs = ();
- if (exists $dbh->{$drv_prefix . "meta"}) {
+ if (exists $dbh->{$drv_prefix . "meta"} and !$dbh->{f_in_gofer}) {
my $attr = $dbh->{$drv_prefix . "meta"};
defined $attr and defined $dbh->{$valid_attrs} and
!defined $dbh->{$valid_attrs}{$attr} and
@@ -265,6 +272,27 @@
return $_[0]->SUPER::disconnect ();
} # disconnect
+sub validate_FETCH_attr
+{
+ my ($dbh, $attrib) = @_;
+
+ # If running in a Gofer server, access to our tied compatibility hash
+ # would force Gofer to serialize the tieing object including it's
+ # private $dbh reference used to do the driver function calls.
+ # This will result in nasty exceptions. So return a copy of the
+ # f_meta structure instead, which is the source of for the compatibility
+ # tie-hash. It's not as good as liked, but the best we can do in this
+ # situation.
+ if ($dbh->{f_in_gofer}) {
+ (my $drv_class = $dbh->{ImplementorClass}) =~ s/::db$//;
+ my $drv_prefix = DBI->driver_prefix ($drv_class);
+ exists $dbh->{$drv_prefix . "meta"} && $attrib eq $dbh->{$drv_prefix .
"meta"} and
+ $attrib = "f_meta";
+ }
+
+ return $attrib;
+ } # validate_FETCH_attr
+
sub validate_STORE_attr
{
my ($dbh, $attrib, $value) = @_;
@@ -281,6 +309,18 @@
carp "'$value' doesn't look like a valid file extension
attribute\n";
}
+ (my $drv_class = $dbh->{ImplementorClass}) =~ s/::db$//;
+ my $drv_prefix = DBI->driver_prefix ($drv_class);
+
+ if (exists $dbh->{$drv_prefix . "meta"}) {
+ my $attr = $dbh->{$drv_prefix . "meta"};
+ if ($attrib eq $attr) {
+ while (my ($k, $v) = each %$value) {
+ $dbh->{$attrib}{$k} = $v;
+ }
+ }
+ }
+
return $dbh->SUPER::validate_STORE_attr ($attrib, $value);
} # validate_STORE_attr
@@ -1145,9 +1185,9 @@
=head4 NULLABLE
-Not really working, always returns an array ref of ones, as DBD::CSV
-does not verify input data. Valid after C<< $sth->execute >>; undef for
-non-select statements.
+Not really working, always returns an array ref of ones, except the
+affected table has been created in this session. Valid after
+C<< $sth->execute >>; undef for non-select statements.
=head3 The following DBI attributes and methods are not supported:
Modified: dbi/trunk/lib/DBI/DBD.pm
==============================================================================
--- dbi/trunk/lib/DBI/DBD.pm (original)
+++ dbi/trunk/lib/DBI/DBD.pm Tue Dec 21 15:08:28 2010
@@ -3337,7 +3337,7 @@
q|END { delete $ENV{DBI_AUTOPROXY}; }| ],
},
n => { name => "DBI::SQL::Nano",
- match => qr/^(?:49dbd_file|5\ddbm_\w+|85gofer)\.t$/,
+ match =>
qr/^(?:48dbi_dbd_sqlengine|49dbd_file|5\ddbm_\w+|85gofer)\.t$/,
add => [ q{$ENV{DBI_SQL_NANO} = 1},
q|END { delete $ENV{DBI_SQL_NANO}; }| ],
},
Modified: dbi/trunk/lib/DBI/DBD/SqlEngine.pm
==============================================================================
--- dbi/trunk/lib/DBI/DBD/SqlEngine.pm (original)
+++ dbi/trunk/lib/DBI/DBD/SqlEngine.pm Tue Dec 21 15:08:28 2010
@@ -133,7 +133,8 @@
{
# must be done first, because setting flags implicitly calls
$dbdname::db->STORE
$dbh->func( 0, "init_default_attributes" );
- my $two_phased_init = defined $dbh->{sql_init_phase};
+ my $two_phased_init;
+ defined $dbh->{sql_init_phase} and $two_phased_init =
++$dbh->{sql_init_phase};
my %second_phase_attrs;
my ( $var, $val );
@@ -191,10 +192,8 @@
$dbh->func( 1, "init_default_attributes" );
%$attr = %second_phase_attrs;
}
- else
- {
- $dbh->func("init_done");
- }
+
+ $dbh->func("init_done");
$dbh->STORE( Active => 1 );
}
@@ -317,12 +316,14 @@
sql_nano_version => 1, # Nano
version
sql_statement_version => 1, # S:S
version
sql_flags => 1, # flags for
SQL::Parser
+ sql_dialect => 1, # dialect
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_sponge_driver => 1, # Sponge
driver for table_info ()
sql_valid_attrs => 1, # SQL valid
attributes
sql_readonly_attrs => 1, # SQL
readonly attributes
+ sql_init_phase => 1, # Only
during initialization
};
$dbh->{sql_readonly_attrs} = {
sql_engine_version => 1, #
DBI::DBD::SqlEngine version
@@ -348,6 +349,7 @@
{
# we have an "old" driver here
$phase = defined $dbh->{sql_init_phase};
+ $phase and $phase = $dbh->{sql_init_phase};
}
if ( 0 == $phase )
@@ -360,6 +362,8 @@
$dbh->{sql_identifier_case} = 2; # SQL_IC_LOWER
$dbh->{sql_quoted_identifier_case} = 3; # SQL_IC_SENSITIVE
+ $dbh->{sql_dialect} = "CSV";
+
$dbh->{sql_init_phase} = $given_phase;
# complete derived attributes, if required
@@ -381,34 +385,31 @@
and $dbh->{$ro_attrs}{$attr} = 1;
}
}
- else
- {
- delete $dbh->{sql_init_phase};
- }
return $dbh;
} # init_default_attributes
sub init_done
{
- delete $_[0]->{sql_init_phase};
+ defined $_[0]->{sql_init_phase} and delete $_[0]->{sql_init_phase};
+ delete $_[0]->{sql_valid_attrs}->{sql_init_phase};
return;
}
sub sql_parser_object
{
my $dbh = $_[0];
+ my $dialect = $dbh->{sql_dialect} || "CSV";
my $parser = {
- dialect => "CSV",
RaiseError => $dbh->FETCH("RaiseError"),
PrintError => $dbh->FETCH("PrintError"),
};
my $sql_flags = $dbh->FETCH("sql_flags") || {};
%$parser = ( %$parser, %$sql_flags );
- $parser = SQL::Parser->new( $parser->{dialect}, $parser );
+ $parser = SQL::Parser->new( $dialect, $parser );
$dbh->{sql_parser_object} = $parser;
return $parser;
-} # cache_sql_parser_object
+} # sql_parser_object
sub sql_sponge_driver
{
@@ -444,9 +445,12 @@
$attrib eq "AutoCommit"
and return 1;
+ # Driver private attributes are lower cased
if ( $attrib eq ( lc $attrib ) )
{
- # Driver private attributes are lower cased
+ # first let the implementation deliver an alias for the attribute to
fetch
+ # after it validates the legitimation of the fetch request
+ $attrib = $dbh->func( $attrib, "validate_FETCH_attr" ) or return;
my $attr_prefix;
$attrib =~ m/^([a-z]+_)/ and $attr_prefix = $1;
@@ -459,8 +463,6 @@
my $valid_attrs = $attr_prefix . "valid_attrs";
my $ro_attrs = $attr_prefix . "readonly_attrs";
- $attrib = $dbh->func( $attrib, "validate_FETCH_attr" ) or return;
-
exists $dbh->{$valid_attrs}
and ( $dbh->{$valid_attrs}{$attrib}
or return $dbh->set_err( $DBI::stderr, "Invalid attribute
'$attrib'" ) );
@@ -1005,6 +1007,148 @@
likely change in the near future to provide the table meta data basics
like DBD::File.
+=head2 Metadata
+
+The following attributes are handled by DBI itself and not by
+DBI::DBD::SqlEngine, thus they all work as expected:
+
+ Active
+ ActiveKids
+ CachedKids
+ CompatMode (Not used)
+ InactiveDestroy
+ AutoInactiveDestroy
+ Kids
+ PrintError
+ RaiseError
+ Warn (Not used)
+
+=head3 The following DBI attributes are handled by DBI::DBD::SqlEngine:
+
+=head4 AutoCommit
+
+Always on.
+
+=head4 ChopBlanks
+
+Works.
+
+=head4 NUM_OF_FIELDS
+
+Valid after C<< $sth->execute >>.
+
+=head4 NUM_OF_PARAMS
+
+Valid after C<< $sth->prepare >>.
+
+=head4 NAME
+
+Valid after C<< $sth->execute >>; probably undef for Non-Select statements.
+
+=head4 NULLABLE
+
+Not really working, always returns an array ref of ones, as DBD::CSV
+does not verify input data. Valid after C<< $sth->execute >>; undef for
+non-select statements.
+
+=head3 The following DBI attributes and methods are not supported:
+
+=over 4
+
+=item bind_param_inout
+
+=item CursorName
+
+=item LongReadLen
+
+=item LongTruncOk
+
+=back
+
+=head3 DBI::DBD::SqlEngine specific attributes
+
+In addition to the DBI attributes, you can use the following dbh
+attributes:
+
+=head4 sql_engine_version
+
+Contains the module version of this driver (B<readonly>)
+
+=head4 sql_nano_version
+
+Contains the module version of DBI::SQL::Nano (B<readonly>)
+
+=head4 sql_statement_version
+
+Contains the module version of SQL::Statement, if available (B<readonly>)
+
+=head4 sql_handler
+
+Contains the SQL Statement engine, either DBI::SQL::Nano or SQL::Statement
+(B<readonly>).
+
+=head4 sql_parser_object
+
+Contains an instantiated instance of SQL::Parser (B<readonly>).
+This is filled when used first time (only when used with SQL::Statement).
+
+=head4 sql_sponge_driver
+
+Contains an internally used DBD::Sponge handle (B<readonly>).
+
+=head4 sql_valid_attrs
+
+Contains the list of valid attributes for each DBI::DBD::SqlEngine based
+driver (B<readonly>).
+
+=head4 sql_readonly_attrs
+
+Contains the list of those attributes which are readonly (B<readonly>).
+
+=head4 sql_identifier_case
+
+Contains how DBI::DBD::SqlEngine deals with non-quoted SQL identifiers:
+
+ * SQL_IC_UPPER (1) means all identifiers are internally converted
+ into upper-cased pendants
+ * SQL_IC_LOWER (2) means all identifiers are internally converted
+ into lower-cased pendants
+ * SQL_IC_MIXED (4) means all identifiers are taken as they are
+
+These conversions happen if (and only if) no existing identifier matches.
+Once existing identifier is used as known.
+
+The SQL statement execution classes doesn't have to care, so don't expect
+C<sql_identifier_case> affects column names in statements like
+
+ SELECT * FROM foo
+
+=head4 sql_quoted_identifier_case
+
+Contains how DBI::DBD::SqlEngine deals with quoted SQL identifiers
+(B<readonly>). It's fixated to SQL_IC_SENSITIVE (3), which is interpreted
+as SQL_IC_MIXED.
+
+=head4 sql_flags
+
+Contains additional flags to instantiate an SQL::Parser. Because an
+SQL::Parser is instantiated only once, it's recommended to set this flag
+before any statement is executed.
+
+=head4 sql_dialect
+
+Controls the dialect understood by SQL::Parser. Possible values (delivery
+state of SQL::Statement):
+
+ * ANSI
+ * CSV
+ * AnyData
+
+Defaults to "CSV". Because an SQL::Parser is instantiated only once and
+SQL::Parser doesn't allow to modify the dialect once instantiated,
+it's strongly recommended to set this flag before any statement is
+executed (best place is connect attribute hash).
+
=head1 SUPPORT
You can find documentation for this module with the perldoc command.
Modified: dbi/trunk/lib/DBI/SQL/Nano.pm
==============================================================================
--- dbi/trunk/lib/DBI/SQL/Nano.pm (original)
+++ dbi/trunk/lib/DBI/SQL/Nano.pm Tue Dec 21 15:08:28 2010
@@ -110,7 +110,7 @@
$self->{where_clause} = $self->parse_where_clause($clauses) if
($clauses);
}
};
- /^\s*INSERT\s+INTO\s+(\S+)\s*(\((.*?)\))?\s*VALUES\s*\((.+)\)/is
+ /^\s*INSERT\s+(?:INTO\s+)?(\S+)\s*(\((.*?)\))?\s*VALUES\s*\((.+)\)/is
&& do
{
$self->{command} = 'INSERT';
Modified: dbi/trunk/t/51dbm_file.t
==============================================================================
--- dbi/trunk/t/51dbm_file.t (original)
+++ dbi/trunk/t/51dbm_file.t Tue Dec 21 15:08:28 2010
@@ -52,6 +52,14 @@
$dbh->do(q/create table FRED (a integer, b integer)/);
ok( -f File::Spec->catfile( $dir, "fred$dirfext" ), "fred$dirfext exists" );
+my $tblfext;
+unless( $using_dbd_gofer )
+{
+ $tblfext = $dbh->{dbm_tables}->{fred}->{f_ext} || '';
+ $tblfext =~ s{/r$}{};
+ ok( -f File::Spec->catfile( $dir, "fred$tblfext" ), "fred$tblfext exists"
);
+}
+
ok( $dbh->do(q/insert into fRED (a,b) values(1,2)/), 'insert into mixed case
table' );
# but change fRED to FRED and it works.
@@ -94,6 +102,29 @@
ok( @$r == 2, 'rows found via select via fully qualified path' );
}
-ok( $dbh->do(q/drop table if exists FRED/), 'drop table' );
+if( $using_dbd_gofer )
+{
+ ok( $dbh->do(q/drop table if exists FRED/), 'drop table' );
+ ok( !-f File::Spec->catfile( $dir, "fred$dirfext" ), "fred$dirfext
removed" );
+}
+else
+{
+ my $tbl_info = { file => "fred$tblfext" };
+
+ ok( $dbh->disconnect(), "disconnect" );
+ $dbh = DBI->connect( 'dbi:DBM:', undef, undef, {
+ f_dir => $dir,
+ sql_identifier_case => 2, # SQL_IC_LOWER
+ dbm_tables => { fred => $tbl_info },
+ }
+ );
+
+ $r = $dbh->selectall_arrayref(q/select * from Fred/);
+ ok( @$r == 2, 'rows found after reconnect using "dbm_tables"' );
+
+ ok( $dbh->do(q/drop table if exists FRED/), 'drop table' );
+ ok( !-f File::Spec->catfile( $dir, "fred$dirfext" ), "fred$dirfext
removed" );
+ ok( !-f File::Spec->catfile( $dir, "fred$tblfext" ), "fred$tblfext
removed" );
+}
done_testing();
Modified: dbi/trunk/t/52dbm_complex.t
==============================================================================
--- dbi/trunk/t/52dbm_complex.t (original)
+++ dbi/trunk/t/52dbm_complex.t Tue Dec 21 15:08:28 2010
@@ -92,7 +92,6 @@
plan skip_all => "Not running with SQL::Statement" unless ( $haveSS );
plan skip_all => "Not running with MLDBM" unless ( @mldbm_types );
-plan skip_all => "Needs more love to run with Gofer, too" if( $using_dbd_gofer
);
do "t/lib.pl";
@@ -103,22 +102,30 @@
my $suffix;
my $tbl_meta;
+sub break_at_warn
+{
+ note "break here";
+}
+$SIG{__WARN__} = \&break_at_warn;
+$SIG{__DIE__} = \&break_at_warn;
+
sub load_tables
{
my ( $dbmtype, $dbmmldbm ) = @_;
+ my $last_suffix;
if ($using_dbd_gofer)
{
$dbh->disconnect();
- $dbh = DBI->connect( "dbi:DBM:", undef, undef, { f_dir => $dir, f_meta
=> $tbl_meta, dbm_type => $dbmtype, dbm_mldbm => $dbmmldbm } );
+ $dbh = DBI->connect( "dbi:DBM:", undef, undef, { f_dir => $dir,
dbm_type => $dbmtype, dbm_mldbm => $dbmmldbm } );
}
else
{
+ $last_suffix = $suffix;
$dbh->{dbm_type} = $dbmtype;
$dbh->{dbm_mldbm} = $dbmmldbm;
}
- my $last_suffix = $suffix;
(my $serializer = $dbmmldbm ) =~ s/::/_/g;
$suffix = join( "_", $$, $dbmtype, $serializer );
@@ -131,7 +138,7 @@
my ($readsth);
ok( $readsth = $dbh->prepare($readsql), "prepare: $readsql" );
ok( $readsth->execute(), "execute: $readsql" );
- ok( $dbh->do( $impsql, {}, $readsth ), $impsql );
+ ok( $dbh->do( $impsql, {}, $readsth ), $impsql ) or warn
$dbh->errstr();
}
}
else