Merge 'trunk' into 'sybase'
Peter Rabbitson [Thu, 3 Sep 2009 17:54:50 +0000 (17:54 +0000)]
r7449@Thesaurus (orig r7446):  caelum | 2009-08-31 04:36:08 +0200
support coderef connect_infos for repicated storage
r7450@Thesaurus (orig r7447):  caelum | 2009-08-31 04:58:43 +0200
make replicant dsn detection a bit nicer
r7451@Thesaurus (orig r7448):  caelum | 2009-08-31 17:30:37 +0200
fix case where repelicant coderef dsn does not connect
r7452@Thesaurus (orig r7449):  arcanez | 2009-08-31 23:13:50 +0200
remove . from end of =head links
r7455@Thesaurus (orig r7452):  ribasushi | 2009-09-01 10:38:37 +0200
Quote deps, avoid floating problems
r7456@Thesaurus (orig r7453):  ribasushi | 2009-09-01 11:10:11 +0200
Fix misleading FAQ entry
r7464@Thesaurus (orig r7461):  ribasushi | 2009-09-01 16:51:58 +0200
Fix insert_bulk with rebless
r7465@Thesaurus (orig r7462):  ribasushi | 2009-09-01 16:52:39 +0200
Comment
r7466@Thesaurus (orig r7463):  matthewt | 2009-09-01 17:17:08 +0200
clearer copyright
r7467@Thesaurus (orig r7464):  matthewt | 2009-09-01 17:18:31 +0200
split copyright and license
r7469@Thesaurus (orig r7466):  frew | 2009-09-01 20:27:36 +0200
pod describing strife with MSSQL
r7483@Thesaurus (orig r7480):  ribasushi | 2009-09-02 11:07:04 +0200
Streamline pg test-schemas cleanup
r7484@Thesaurus (orig r7481):  ribasushi | 2009-09-02 11:20:25 +0200
Centralize handling of minimum sqlt version to DBIx::Class
Bump version to the latest unborked sqlt (still just a recommend)
r7485@Thesaurus (orig r7482):  ribasushi | 2009-09-02 11:31:50 +0200
Some cleanup... don't remember where it came from
r7486@Thesaurus (orig r7483):  ribasushi | 2009-09-02 12:19:11 +0200
First part of mysql insanity
r7487@Thesaurus (orig r7484):  ribasushi | 2009-09-02 12:25:35 +0200
Invoke default_join_type only on undefined types
r7488@Thesaurus (orig r7485):  ribasushi | 2009-09-02 12:42:39 +0200
No fancy methods for the default_jointype, as we don't have proper sqlahacks inheritance and they are... well hacks
r7489@Thesaurus (orig r7486):  ribasushi | 2009-09-02 13:00:07 +0200
Mysql v3 support (ick)
r7494@Thesaurus (orig r7491):  rbuels | 2009-09-02 20:33:47 +0200
POD patch, corrected erroneous usage of dbh_do in Storage::DBI synopsis
r7500@Thesaurus (orig r7497):  ribasushi | 2009-09-03 11:11:29 +0200
POD lists the storable hooks, but does no load them
r7501@Thesaurus (orig r7498):  ribasushi | 2009-09-03 11:11:50 +0200
Storable sanification
r7502@Thesaurus (orig r7499):  ribasushi | 2009-09-03 11:24:17 +0200
Storable is now in Core
r7503@Thesaurus (orig r7500):  ribasushi | 2009-09-03 11:36:58 +0200
Make sure mysql is fixed
r7506@Thesaurus (orig r7503):  ribasushi | 2009-09-03 17:16:17 +0200
Add podcoverage skip
r7507@Thesaurus (orig r7504):  ribasushi | 2009-09-03 17:23:19 +0200
Consolidate _verify_pid calls

1  2 
Changes
Makefile.PL
lib/DBIx/Class/ResultSet.pm
lib/DBIx/Class/Storage/DBI.pm
lib/DBIx/Class/Storage/DBI/Replicated.pm
t/746mssql.t

diff --combined Changes
+++ b/Changes
@@@ -1,23 -1,17 +1,28 @@@
  Revision history for DBIx::Class
  
 +        - Complete Sybase RDBMS support including:
 +          - Support for TEXT/IMAGE columns
 +          - Support for the 'money' datatype
 +          - Transaction savepoints support
 +          - DateTime inflation support
 +          - Support for bind variables when connecting to a newer Sybase with
 +            OpenClient libraries
 +          - Support for connections via FreeTDS with CASTs for bind variables
 +            when needed
 +          - Support for interpolated variables with proper quoting when
 +            connecting to an older Sybase and/or via FreeTDS
          - The hashref to connection_info now accepts a 'dbh_maker'
            coderef, allowing better intergration with Catalyst
          - Fixed a complex prefetch + regular join regression introduced
            in 0.08108
+         - Fixed insert_bulk rebless handling
+         - Fixed Storable roundtrip regression, and general serialization
+           cleanup
          - SQLT related fixes:
            - sqlt_type is now called on the correct storage object
            - hooks can now see the correct producer_type
+           - optional SQLT requirements for e.g. deploy() bumped to 0.11002
+         - Automatically detect MySQL v3 and use INNER JOIN instead of JOIN
          - POD improvements
  
  0.08109 2009-08-18 08:35:00 (UTC)
@@@ -42,7 -36,7 +47,7 @@@
          - Support for MSSQL 'money' type
          - Support for 'smalldatetime' type used in MSSQL and Sybase for
            InflateColumn::DateTime
 -        - support for Postgres 'timestamp without timezone' type in
 +        - Support for Postgres 'timestamp without timezone' type in
            InflateColumn::DateTime (RT#48389)
          - Added new MySQL specific on_connect_call macro 'set_strict_mode'
            (also known as make_mysql_not_suck_as_much)
diff --combined Makefile.PL
@@@ -10,103 -10,100 +10,106 @@@ perl_version '5.006001'
  all_from 'lib/DBIx/Class.pm';
  
  
- test_requires 'Test::Builder'       => 0.33;
- test_requires 'Test::Deep'          => 0;
- test_requires 'Test::Exception'     => 0;
- test_requires 'Test::More'          => 0.92;
- test_requires 'Test::Warn'          => 0.21;
+ test_requires 'Test::Builder'       => '0.33';
+ test_requires 'Test::Deep'          => '0';
+ test_requires 'Test::Exception'     => '0';
+ test_requires 'Test::More'          => '0.92';
+ test_requires 'Test::Warn'          => '0.21';
  
- test_requires 'File::Temp'          => 0.22;
+ test_requires 'File::Temp'          => '0.22';
  
  
  # Core
- requires 'List::Util'               => 0;
- requires 'Scalar::Util'             => 0;
- requires 'Storable'                 => 0;
+ requires 'List::Util'               => '0';
+ requires 'Scalar::Util'             => '0';
+ requires 'Storable'                 => '0';
  
  # Perl 5.8.0 doesn't have utf8::is_utf8()
- requires 'Encode'                   => 0 if ($] <= 5.008000);
+ requires 'Encode'                   => '0' if ($] <= 5.008000);
  
  # Dependencies (keep in alphabetical order)
- requires 'Carp::Clan'               => 6.0;
- requires 'Class::Accessor::Grouped' => 0.09000;
- requires 'Class::C3::Componentised' => 1.0005;
- requires 'Class::Inspector'         => 1.24;
- requires 'Data::Page'               => 2.00;
- requires 'DBD::SQLite'              => 1.25;
- requires 'DBI'                      => 1.605;
- requires 'JSON::Any'                => 1.18;
- requires 'MRO::Compat'              => 0.09;
- requires 'Module::Find'             => 0.06;
- requires 'Path::Class'              => 0.16;
- requires 'Scope::Guard'             => 0.03;
- requires 'SQL::Abstract'            => 1.56;
- requires 'SQL::Abstract::Limit'     => 0.13;
- requires 'Sub::Name'                => 0.04;
- recommends 'SQL::Translator'        => 0.09004;
+ requires 'Carp::Clan'               => '6.0';
+ requires 'Class::Accessor::Grouped' => '0.09000';
+ requires 'Class::C3::Componentised' => '1.0005';
+ requires 'Class::Inspector'         => '1.24';
+ requires 'Data::Page'               => '2.00';
+ requires 'DBD::SQLite'              => '1.25';
+ requires 'DBI'                      => '1.605';
+ requires 'JSON::Any'                => '1.18';
+ requires 'MRO::Compat'              => '0.09';
+ requires 'Module::Find'             => '0.06';
+ requires 'Path::Class'              => '0.16';
+ requires 'Scope::Guard'             => '0.03';
+ requires 'SQL::Abstract'            => '1.56';
+ requires 'SQL::Abstract::Limit'     => '0.13';
+ requires 'Sub::Name'                => '0.04';
  
  my %replication_requires = (
-   'Moose',                    => 0.87,
-   'MooseX::AttributeHelpers'  => 0.21,
-   'MooseX::Types',            => 0.16,
-   'namespace::clean'          => 0.11,
-   'Hash::Merge',              => 0.11,
+   'Moose',                    => '0.87',
+   'MooseX::AttributeHelpers'  => '0.21',
+   'MooseX::Types',            => '0.16',
+   'namespace::clean'          => '0.11',
+   'Hash::Merge',              => '0.11',
  );
  
+ # when changing also adjust $DBIx::Class::minimum_sqlt_version
+ my $sqlt_recommends = '0.11002';
+ recommends 'SQL::Translator'  => $sqlt_recommends;
  my %force_requires_if_author = (
    %replication_requires,
  
- #  'Module::Install::Pod::Inherit' => 0.01,
-   'Test::Pod::Coverage'       => 1.04,
-   'SQL::Translator'           => 0.09007,
+ #  'Module::Install::Pod::Inherit' => '0.01',
+   'Test::Pod::Coverage'       => '1.04',
+   'SQL::Translator'           => $sqlt_recommends,
  
    # CDBI-compat related
-   'DBIx::ContextualFetch'     => 0,
-   'Class::DBI::Plugin::DeepAbstractSearch' => 0,
-   'Class::Trigger'            => 0,
-   'Time::Piece::MySQL'        => 0,
-   'Clone'                     => 0,
-   'Date::Simple'              => 3.03,
+   'DBIx::ContextualFetch'     => '0',
+   'Class::DBI::Plugin::DeepAbstractSearch' => '0',
+   'Class::Trigger'            => '0',
+   'Time::Piece::MySQL'        => '0',
+   'Clone'                     => '0',
+   'Date::Simple'              => '3.03',
  
    # t/52cycle.t
-   'Test::Memory::Cycle'       => 0,
-   'Devel::Cycle'              => 1.10,
+   'Test::Memory::Cycle'       => '0',
+   'Devel::Cycle'              => '1.10',
  
    # t/36datetime.t
    # t/60core.t
-   'DateTime::Format::SQLite'  => 0,
+   'DateTime::Format::SQLite'  => '0',
  
    # t/96_is_deteministic_value.t
-   'DateTime::Format::Strptime'=> 0,
+   'DateTime::Format::Strptime'=> '0',
  
    # database-dependent reqs
    #
    $ENV{DBICTEST_PG_DSN}
      ? (
-       'Sys::SigAction' => 0,
-       'DBD::Pg' => 2.009002,
-       'DateTime::Format::Pg' => 0,
+       'Sys::SigAction' => '0',
+       'DBD::Pg' => '2.009002',
+       'DateTime::Format::Pg' => '0',
      ) : ()
    ,
  
    $ENV{DBICTEST_MYSQL_DSN}
      ? (
-       'DateTime::Format::MySQL' => 0,
+       'DateTime::Format::MySQL' => '0',
      ) : ()
    ,
  
    $ENV{DBICTEST_ORACLE_DSN}
      ? (
-       'DateTime::Format::Oracle' => 0,
+       'DateTime::Format::Oracle' => '0',
      ) : ()
    ,
 +
 +  $ENV{DBICTEST_SYBASE_DSN}
 +    ? (
 +      'DateTime::Format::Sybase' => 0,
 +    ) : ()
 +  ,
  );
  
  
@@@ -123,7 -120,7 +126,7 @@@ resources 'license'     => 'http://dev.
  resources 'repository'  => 'http://dev.catalyst.perl.org/svnweb/bast/browse/DBIx-Class/';
  resources 'MailingList' => 'http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class';
  
 -no_index 'DBIx::Class::Storage::DBI::Sybase::Base';
 +no_index 'DBIx::Class::Storage::DBI::Sybase::Common';
  no_index 'DBIx::Class::SQLAHacks';
  no_index 'DBIx::Class::SQLAHacks::MSSQL';
  no_index 'DBIx::Class::Storage::DBI::AmbiguousGlob';
@@@ -2778,38 -2778,24 +2778,38 @@@ sub _resolved_attrs 
  
    # build columns (as long as select isn't set) into a set of as/select hashes
    unless ( $attrs->{select} ) {
 -      @colbits = map {
 -          ( ref($_) eq 'HASH' )
 -              ? $_
 -              : {
 -                  (
 -                    /^\Q${alias}.\E(.+)$/
 -                      ? "$1"
 -                      : "$_"
 -                  )
 -                =>
 -                  (
 -                    /\./
 -                      ? "$_"
 -                      : "${alias}.$_"
 -                  )
 -            }
 -      } ( ref($attrs->{columns}) eq 'ARRAY' ) ? @{ delete $attrs->{columns}} : (delete $attrs->{columns} || $source->columns );
 +
 +    my @cols = ( ref($attrs->{columns}) eq 'ARRAY' )
 +      ? @{ delete $attrs->{columns}}
 +      : (
 +          ( delete $attrs->{columns} )
 +            ||
 +          $source->storage->_order_select_columns(
 +              $source,
 +              [ $source->columns ],
 +          )
 +        )
 +    ;
 +
 +    @colbits = map {
 +      ( ref($_) eq 'HASH' )
 +      ? $_
 +      : {
 +          (
 +            /^\Q${alias}.\E(.+)$/
 +              ? "$1"
 +              : "$_"
 +          )
 +            =>
 +          (
 +            /\./
 +              ? "$_"
 +              : "${alias}.$_"
 +          )
 +        }
 +    } @cols;
    }
 +
    # add the additional columns on
    foreach ( 'include_columns', '+columns' ) {
        push @colbits, map {
  
    if ( $attrs->{join} || $attrs->{prefetch} ) {
  
-     $self->throw_exception ('join/prefetch can not be used with a literal scalarref {from}')
+     $self->throw_exception ('join/prefetch can not be used with a custom {from}')
        if ref $attrs->{from} ne 'ARRAY';
  
      my $join = delete $attrs->{join} || {};
@@@ -3013,6 -2999,13 +3013,13 @@@ sub _rollout_hash 
  sub _calculate_score {
    my ($self, $a, $b) = @_;
  
+   if (defined $a xor defined $b) {
+     return 0;
+   }
+   elsif (not defined $a) {
+     return 1;
+   }
    if (ref $b eq 'HASH') {
      my ($b_key) = keys %{$b};
      if (ref $a eq 'HASH') {
@@@ -44,7 -44,14 +44,14 @@@ DBIx::Class::Storage::DBI - DBI storag
    my $schema = MySchema->connect('dbi:SQLite:my.db');
  
    $schema->storage->debug(1);
-   $schema->dbh_do("DROP TABLE authors");
+   my @stuff = $schema->storage->dbh_do(
+     sub {
+       my ($storage, $dbh, @args) = @_;
+       $dbh->do("DROP TABLE authors");
+     },
+     @column_list
+   );
  
    $schema->resultset('Book')->search({
       written_on => $schema->storage->datetime_parser(DateTime->now)
@@@ -556,7 -563,7 +563,7 @@@ sub dbh_do 
    my $self = shift;
    my $code = shift;
  
-   my $dbh = $self->_dbh;
+   my $dbh = $self->_get_dbh;
  
    return $self->$code($dbh, @_) if $self->{_in_dbh_do}
        || $self->{transaction_depth};
    my $want_array = wantarray;
  
    eval {
-     $self->_verify_pid if $dbh;
-     if(!$self->_dbh) {
-         $self->_populate_dbh;
-         $dbh = $self->_dbh;
-     }
  
      if($want_array) {
          @result = $self->$code($dbh, @_);
@@@ -618,8 -620,7 +620,7 @@@ sub txn_do 
    my $tried = 0;
    while(1) {
      eval {
-       $self->_verify_pid if $self->_dbh;
-       $self->_populate_dbh if !$self->_dbh;
+       $self->_get_dbh;
  
        $self->txn_begin;
        if($want_array) {
@@@ -680,8 -681,7 +681,8 @@@ sub disconnect 
  
      $self->_do_connection_actions(disconnect_call_ => $_) for @actions;
  
 -    $self->_dbh->rollback unless $self->_dbh_autocommit;
 +    $self->_dbh_rollback unless $self->_dbh_autocommit;
 +
      $self->_dbh->disconnect;
      $self->_dbh(undef);
      $self->{_dbh_gen}++;
@@@ -809,6 -809,7 +810,7 @@@ sub dbh 
  # this is the internal "get dbh or connect (don't check)" method
  sub _get_dbh {
    my $self = shift;
+   $self->_verify_pid if $self->_dbh;
    $self->_populate_dbh unless $self->_dbh;
    return $self->_dbh;
  }
@@@ -877,10 -878,18 +879,18 @@@ sub _determine_driver 
        if ($self->_dbh) { # we are connected
          $driver = $self->_dbh->{Driver}{Name};
        } else {
-         # try to use dsn to not require being connected, the driver may still
-         # force a connection in _rebless to determine version
-         ($driver) = $self->_dbi_connect_info->[0] =~ /dbi:([^:]+):/i;
-         $started_unconnected = 1;
+         # if connect_info is a CODEREF, we have no choice but to connect
+         if (ref $self->_dbi_connect_info->[0] &&
+             Scalar::Util::reftype($self->_dbi_connect_info->[0]) eq 'CODE') {
+           $self->_populate_dbh;
+           $driver = $self->_dbh->{Driver}{Name};
+         }
+         else {
+           # try to use dsn to not require being connected, the driver may still
+           # force a connection in _rebless to determine version
+           ($driver) = $self->_dbi_connect_info->[0] =~ /dbi:([^:]+):/i;
+           $started_unconnected = 1;
+         }
        }
  
        my $storage_class = "DBIx::Class::Storage::DBI::${driver}";
@@@ -952,7 -961,7 +962,7 @@@ sub _do_query 
      my @bind = map { [ undef, $_ ] } @do_args;
  
      $self->_query_start($sql, @bind);
-     $self->_dbh->do($sql, $attrs, @do_args);
+     $self->_get_dbh->do($sql, $attrs, @do_args);
      $self->_query_end($sql, @bind);
    }
  
@@@ -1097,36 -1106,27 +1107,36 @@@ sub txn_begin 
    if($self->{transaction_depth} == 0) {
      $self->debugobj->txn_begin()
        if $self->debug;
 -
 -    # being here implies we have AutoCommit => 1
 -    # if the user is utilizing txn_do - good for
 -    # him, otherwise we need to ensure that the
 -    # $dbh is healthy on BEGIN
 -    my $dbh_method = $self->{_in_dbh_do} ? '_dbh' : 'dbh';
 -    $self->$dbh_method->begin_work;
 -
 -  } elsif ($self->auto_savepoint) {
 +    $self->_dbh_begin_work;
 +  }
 +  elsif ($self->auto_savepoint) {
      $self->svp_begin;
    }
    $self->{transaction_depth}++;
  }
  
 +sub _dbh_begin_work {
 +  my $self = shift;
 +
 +  # if the user is utilizing txn_do - good for him, otherwise we need to
 +  # ensure that the $dbh is healthy on BEGIN.
 +  # We do this via ->dbh_do instead of ->dbh, so that the ->dbh "ping"
 +  # will be replaced by a failure of begin_work itself (which will be
 +  # then retried on reconnect)
 +  if ($self->{_in_dbh_do}) {
 +    $self->_dbh->begin_work;
 +  } else {
 +    $self->dbh_do(sub { $_[1]->begin_work });
 +  }
 +}
 +
  sub txn_commit {
    my $self = shift;
    if ($self->{transaction_depth} == 1) {
      my $dbh = $self->_dbh;
      $self->debugobj->txn_commit()
        if ($self->debug);
 -    $dbh->commit;
 +    $self->_dbh_commit;
      $self->{transaction_depth} = 0
        if $self->_dbh_autocommit;
    }
    }
  }
  
 +sub _dbh_commit {
 +  my $self = shift;
 +  $self->_dbh->commit;
 +}
 +
  sub txn_rollback {
    my $self = shift;
    my $dbh = $self->_dbh;
          if ($self->debug);
        $self->{transaction_depth} = 0
          if $self->_dbh_autocommit;
 -      $dbh->rollback;
 +      $self->_dbh_rollback;
      }
      elsif($self->{transaction_depth} > 1) {
        $self->{transaction_depth}--;
    }
  }
  
 +sub _dbh_rollback {
 +  my $self = shift;
 +  $self->_dbh->rollback;
 +}
 +
  # This used to be the top-half of _execute.  It was split out to make it
  #  easier to override in NoBindVars without duping the rest.  It takes up
  #  all of _execute's args, and emits $sql, @bind.
@@@ -1315,13 -1305,18 +1325,18 @@@ sub insert 
  ## only prepped once.
  sub insert_bulk {
    my ($self, $source, $cols, $data) = @_;
+ # redispatch to insert_bulk method of storage we reblessed into, if necessary
+   if (not $self->_driver_determined) {
+     $self->_determine_driver;
+     goto $self->can('insert_bulk');
+   }
    my %colvalues;
    my $table = $source->from;
    @colvalues{@$cols} = (0..$#$cols);
    my ($sql, @bind) = $self->sql_maker->insert($table, \%colvalues);
  
-   $self->_determine_driver;
    $self->_query_start( $sql, @bind );
    my $sth = $self->sth($sql);
  
  }
  
  sub update {
 -  my $self = shift @_;
 -  my $source = shift @_;
 -  $self->_determine_driver;
 +  my ($self, $source, @args) = @_; 
 +
 +# redispatch to update method of storage we reblessed into, if necessary
 +  if (not $self->_driver_determined) {
 +    $self->_determine_driver;
 +    goto $self->can('update');
 +  }
 +
    my $bind_attributes = $self->source_bind_attributes($source);
  
 -  return $self->_execute('update' => [], $source, $bind_attributes, @_);
 +  return $self->_execute('update' => [], $source, $bind_attributes, @args);
  }
  
  
@@@ -1966,18 -1956,6 +1981,18 @@@ sub _subq_count_select 
    return @pcols ? \@pcols : [ 1 ];
  }
  
 +#
 +# Returns an ordered list of column names before they are used
 +# in a SELECT statement. By default simply returns the list
 +# passed in.
 +#
 +# This may be overridden in a specific storage when there are
 +# requirements such as moving BLOB columns to the end of the 
 +# SELECT list.
 +sub _order_select_columns {
 +  #my ($self, $source, $columns) = @_;
 +  return @{$_[2]};
 +}
  
  sub source_bind_attributes {
    my ($self, $source) = @_;
@@@ -2175,36 -2153,6 +2190,36 @@@ sub _native_data_type 
    return undef
  }
  
 +# Check if placeholders are supported at all
 +sub _placeholders_supported {
 +  my $self = shift;
 +  my $dbh  = $self->_get_dbh;
 +
 +  # some drivers provide a $dbh attribute (e.g. Sybase and $dbh->{syb_dynamic_supported})
 +  # but it is inaccurate more often than not
 +  eval {
 +    local $dbh->{PrintError} = 0;
 +    local $dbh->{RaiseError} = 1;
 +    $dbh->do('select ?', {}, 1);
 +  };
 +  return $@ ? 0 : 1;
 +}
 +
 +# Check if placeholders bound to non-string types throw exceptions
 +#
 +sub _typeless_placeholders_supported {
 +  my $self = shift;
 +  my $dbh  = $self->_get_dbh;
 +
 +  eval {
 +    local $dbh->{PrintError} = 0;
 +    local $dbh->{RaiseError} = 1;
 +    # this specifically tests a bind that is NOT a string
 +    $dbh->do('select 1 where 1 = ?', {}, 1);
 +  };
 +  return $@ ? 0 : 1;
 +}
 +
  =head2 sqlt_type
  
  Returns the database driver name.
@@@ -2336,9 -2284,8 +2351,8 @@@ sub create_ddl_dir 
      %{$sqltargs || {}}
    };
  
-   $self->throw_exception(q{Can't create a ddl file without SQL::Translator 0.09003: '}
-       . $self->_check_sqlt_message . q{'})
-           if !$self->_check_sqlt_version;
+   $self->throw_exception("Can't create a ddl file without SQL::Translator: " . $self->_sqlt_version_error)
+     if !$self->_sqlt_version_ok;
  
    my $sqlt = SQL::Translator->new( $sqltargs );
  
@@@ -2480,9 -2427,8 +2494,8 @@@ sub deployment_statements 
        return join('', @rows);
    }
  
-   $self->throw_exception(q{Can't deploy without SQL::Translator 0.09003: '}
-       . $self->_check_sqlt_message . q{'})
-           if !$self->_check_sqlt_version;
+   $self->throw_exception("Can't deploy without either SQL::Translator or a ddl_dir: " . $self->_sqlt_version_error )
+     if !$self->_sqlt_version_ok;
  
    # sources needs to be a parser arg, but for simplicty allow at top level
    # coming in
@@@ -2564,26 -2510,10 +2577,10 @@@ See L</datetime_parser
  sub build_datetime_parser {
    my $self = shift;
    my $type = $self->datetime_parser_type(@_);
-   eval "use ${type}";
-   $self->throw_exception("Couldn't load ${type}: $@") if $@;
+   $self->ensure_class_loaded ($type);
    return $type;
  }
  
- {
-     my $_check_sqlt_version; # private
-     my $_check_sqlt_message; # private
-     sub _check_sqlt_version {
-         return $_check_sqlt_version if defined $_check_sqlt_version;
-         eval 'use SQL::Translator "0.09003"';
-         $_check_sqlt_message = $@ || '';
-         $_check_sqlt_version = !$@;
-     }
-     sub _check_sqlt_message {
-         _check_sqlt_version if !defined $_check_sqlt_message;
-         $_check_sqlt_message;
-     }
- }
  
  =head2 is_replicating
  
@@@ -222,7 -222,7 +222,7 @@@ has 'pool' => 
    isa=>'DBIx::Class::Storage::DBI::Replicated::Pool',
    lazy_build=>1,
    handles=>[qw/
 -    connect_replicants    
 +    connect_replicants
      replicants
      has_replicants
    /],
@@@ -277,7 -277,7 +277,7 @@@ has 'read_handler' => 
      select
      select_single
      columns_info_for
 -  /],    
 +  /],
  );
  
  =head2 write_handler
@@@ -290,9 -290,9 +290,9 @@@ has 'write_handler' => 
    is=>'ro',
    isa=>Object,
    lazy_build=>1,
 -  handles=>[qw/   
 +  handles=>[qw/
      on_connect_do
 -    on_disconnect_do       
 +    on_disconnect_do
      connect_info
      throw_exception
      sql_maker
      create_ddl_dir
      deployment_statements
      datetime_parser
 -    datetime_parser_type  
 -    build_datetime_parser      
 +    datetime_parser_type
 +    build_datetime_parser
      last_insert_id
      insert
      insert_bulk
      sth
      deploy
      with_deferred_fk_checks
 -      dbh_do
 +    dbh_do
      reload_row
 -      with_deferred_fk_checks
 +    with_deferred_fk_checks
      _prep_for_execute
  
 -      backup
 -      is_datatype_numeric
 -      _count_select
 -      _subq_count_select
 -      _subq_update_delete 
 -      svp_rollback
 -      svp_begin
 -      svp_release
 +    backup
 +    is_datatype_numeric
 +    _count_select
 +    _subq_count_select
 +    _subq_update_delete
 +    _order_select_columns
 +    svp_rollback
 +    svp_begin
 +    svp_release
    /],
  );
  
@@@ -365,7 -364,7 +365,7 @@@ around connect_info => sub 
      );
  
      $self->pool($self->_build_pool)
 -      if $self->pool;
 +      if $self->pool;
    }
  
    if (@opts{qw/balancer_type balancer_args/}) {
      );
  
      $self->balancer($self->_build_balancer)
 -      if $self->balancer;
 +      if $self->balancer;
    }
  
    $self->_master_connect_info_opts(\%opts);
@@@ -414,9 -413,9 +414,9 @@@ sub BUILDARGS 
    my ($class, $schema, $storage_type_args, @args) = @_;       
  
    return {
 -      schema=>$schema, 
 -      %$storage_type_args,
 -      @args
 +    schema=>$schema,
 +    %$storage_type_args,
 +    @args
    }
  }
  
@@@ -453,7 -452,7 +453,7 @@@ the balancer knows which pool it's bala
  sub _build_balancer {
    my $self = shift @_;
    $self->create_balancer(
 -    pool=>$self->pool, 
 +    pool=>$self->pool,
      master=>$self->master,
      %{$self->balancer_args},
    );
@@@ -502,7 -501,7 +502,7 @@@ around connect_replicants => sub 
      my $i = 0;
      $i++ while $i < @$r && (reftype($r->[$i])||'') ne 'HASH';
  
 -# make one if none    
 +# make one if none
      $r->[$i] = {} unless $r->[$i];
  
  # merge if two hashes
  # delete them
      splice @$r, $i+1, ($#{$r} - $i), ();
  
+ # make sure master/replicants opts don't clash
+     my %master_opts = %{ $self->_master_connect_info_opts };
+     if (exists $opts{dbh_maker}) {
+         delete @master_opts{qw/dsn user password/};
+     }
+     delete $master_opts{dbh_maker};
  # merge with master
-     %opts = %{ merge(\%opts, $self->_master_connect_info_opts) };
+     %opts = %{ merge(\%opts, \%master_opts) };
  
  # update
      $r->[$i] = \%opts;
@@@ -594,11 -600,11 +601,11 @@@ sub execute_reliably 
        ($result[0]) = ($coderef->(@args));
      } else {
        $coderef->(@args);
 -    }       
 +    }
    };
  
    ##Reset to the original state
 -  $self->read_handler($current); 
 +  $self->read_handler($current);
  
    ##Exception testing has to come last, otherwise you might leave the 
    ##read_handler set to master.
@@@ -732,7 -738,7 +739,7 @@@ sub debug 
    if(@_) {
      foreach my $source ($self->all_storages) {
        $source->debug(@_);
 -    }   
 +    }
    }
    return $self->master->debug;
  }
@@@ -748,7 -754,7 +755,7 @@@ sub debugobj 
    if(@_) {
      foreach my $source ($self->all_storages) {
        $source->debugobj(@_);
 -    }         
 +    }
    }
    return $self->master->debugobj;
  }
@@@ -764,7 -770,7 +771,7 @@@ sub debugfh 
    if(@_) {
      foreach my $source ($self->all_storages) {
        $source->debugfh(@_);
 -    }   
 +    }
    }
    return $self->master->debugfh;
  }
@@@ -780,7 -786,7 +787,7 @@@ sub debugcb 
    if(@_) {
      foreach my $source ($self->all_storages) {
        $source->debugcb(@_);
 -    }   
 +    }
    }
    return $self->master->debugcb;
  }
diff --combined t/746mssql.t
@@@ -145,11 -145,14 +145,11 @@@ $schema->storage->dbh_do (sub 
      my ($storage, $dbh) = @_;
      eval { $dbh->do("DROP TABLE money_test") };
      $dbh->do(<<'SQL');
 -
  CREATE TABLE money_test (
     id INT IDENTITY PRIMARY KEY,
     amount MONEY NULL
  )
 -
  SQL
 -
  });
  
  my $rs = $schema->resultset('Money');
@@@ -195,6 -198,8 +195,8 @@@ SQ
  });
  
  lives_ok ( sub {
+   # start a new connection, make sure rebless works
+   my $schema = DBICTest::Schema->connect($dsn, $user, $pass);
    $schema->populate ('Owners', [
      [qw/id  name  /],
      [qw/1   wiggle/],
  }, 'populate with PKs supplied ok' );
  
  lives_ok ( sub {
+   # start a new connection, make sure rebless works
+   my $schema = DBICTest::Schema->connect($dsn, $user, $pass);
    $schema->populate ('BooksInLibrary', [
      [qw/source  owner title   /],
      [qw/Library 1     secrets0/],