[svn:dbi] r14600 - in dbi/trunk: . lib/DBD lib/DBI lib/DBI/DBD lib/DBI/SQL t

[email protected] Tue, 21 Dec 2010 15:08:28 -0800 (PST)
Newsgroups perl.dbi.changes
Message-ID <[email protected]>
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