Merge 'trunk' into 'pg_unqualified_schema'
Peter Rabbitson [Wed, 2 Sep 2009 18:30:52 +0000 (18:30 +0000)]
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)

16 files changed:
Changes
Makefile.PL
lib/DBIx/Class.pm
lib/DBIx/Class/ResultSet.pm
lib/DBIx/Class/ResultSource.pm
lib/DBIx/Class/SQLAHacks.pm
lib/DBIx/Class/Schema/Versioned.pm
lib/DBIx/Class/Storage/DBI.pm
lib/DBIx/Class/Storage/DBI/MSSQL.pm
lib/DBIx/Class/Storage/DBI/mysql.pm
t/71mysql.t
t/72pg.t
t/746mssql.t
t/86sqlt.t
t/94versioning.t
t/99dbic_sqlt_parser.t

diff --git a/Changes b/Changes
index 0faba32..4a8e98e 100644 (file)
--- a/Changes
+++ b/Changes
@@ -4,6 +4,7 @@ Revision history for DBIx::Class
           coderef, allowing better intergration with Catalyst
         - Fixed a complex prefetch + regular join regression introduced
           in 0.08108
+        - Fixed insert_bulk rebless handling
         - SQLT related fixes:
           - sqlt_type is now called on the correct storage object
           - hooks can now see the correct producer_type
index 59ec4b1..8151718 100644 (file)
@@ -52,7 +52,8 @@ my %replication_requires = (
   'Hash::Merge',              => '0.11',
 );
 
-my $sqlt_recommends = '0.09004';
+# when changing also adjust $DBIx::Class::minimum_sqlt_version
+my $sqlt_recommends = '0.11002';
 
 recommends 'SQL::Translator'  => $sqlt_recommends;
 
index 1bc4c9a..9d49b69 100644 (file)
@@ -29,6 +29,10 @@ $VERSION = '0.08109';
 
 $VERSION = eval $VERSION; # numify for warning-free dev releases
 
+# what version of sqlt do we require if deploy() without a ddl_dir is invoked
+# when changing also adjust $sqlt_recommends in Makefile.PL
+my $minimum_sqlt_version = '0.11002';
+
 sub MODIFY_CODE_ATTRIBUTES {
   my ($class,$code,@attrs) = @_;
   $class->mk_classdata('__attr_cache' => {})
@@ -44,6 +48,34 @@ sub _attr_cache {
   return $@ ? $cache : { %$cache, %$rest };
 }
 
+# SQLT version handling
+{
+  my $_sqlt_version_ok;     # private
+  my $_sqlt_version_error;  # private
+
+  sub _sqlt_version_ok {
+    if (!defined $_sqlt_version_ok) {
+      eval "use SQL::Translator $minimum_sqlt_version";
+      if ($@) {
+        $_sqlt_version_ok = 0;
+        $_sqlt_version_error = $@;
+      }
+      else {
+        $_sqlt_version_ok = 1;
+      }
+    }
+    return $_sqlt_version_ok;
+  }
+
+  sub _sqlt_version_error {
+    shift->_sqlt_version_ok unless defined $_sqlt_version_ok;
+    return $_sqlt_version_error;
+  }
+
+  sub _sqlt_minimum_version { $minimum_sqlt_version };
+}
+
+
 1;
 
 =head1 NAME
@@ -201,6 +233,11 @@ merged back to trunk for a major release.
 L<DBIx::Class::Manual::DocMap> lists each task you might want help on, and
 the modules where you will find documentation.
 
+=head1 COPYRIGHT
+
+Copyright (c) 2005 - 2009 the DBIx::Class L</AUTHOR> and L</CONTRIBUTORS>
+as listed below.
+
 =head1 AUTHOR
 
 mst: Matt S. Trout <mst@shadowcatsystems.co.uk>
@@ -354,6 +391,7 @@ zamolxes: Bogdan Lucaciu <bogdan@wiz.ro>
 
 =head1 LICENSE
 
-You may distribute this code under the same terms as Perl itself.
+This library is free software and may be distributed under the same terms
+as perl itself.
 
 =cut
index a4a87d6..9bbeb73 100644 (file)
@@ -2853,7 +2853,7 @@ sub _resolved_attrs {
 
   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} || {};
@@ -2999,6 +2999,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') {
index dfa4c78..eee5e3d 100644 (file)
@@ -1237,9 +1237,10 @@ sub _resolve_join {
     my $type;
     if ($force_left) {
       $type = 'left';
-    } else {
-      $type = $rel_info->{attrs}{join_type} || '';
-      $force_left = 1 if lc($type) eq 'left';
+    }
+    else {
+      $type = $rel_info->{attrs}{join_type};
+      $force_left = 1 if lc($type||'') eq 'left';
     }
 
     my $rel_src = $self->related_source($join);
index d5041ba..93c1009 100644 (file)
@@ -508,15 +508,21 @@ sub _recurse_from {
   foreach my $j (@join) {
     my ($to, $on) = @$j;
 
+
     # check whether a join type exists
-    my $join_clause = '';
     my $to_jt = ref($to) eq 'ARRAY' ? $to->[0] : $to;
-    if (ref($to_jt) eq 'HASH' and exists($to_jt->{-join_type})) {
-      $join_clause = ' '.uc($to_jt->{-join_type}).' JOIN ';
-    } else {
-      $join_clause = ' JOIN ';
+    my $join_type;
+    if (ref($to_jt) eq 'HASH' and defined($to_jt->{-join_type})) {
+      $join_type = $to_jt->{-join_type};
+      $join_type =~ s/^\s+ | \s+$//xg;
     }
-    push(@sqlf, $join_clause);
+
+    $join_type = $self->{_default_jointype} if not defined $join_type;
+
+    my $join_clause = sprintf ('%s JOIN ',
+      $join_type ?  ' ' . uc($join_type) : ''
+    );
+    push @sqlf, $join_clause;
 
     if (ref $to eq 'ARRAY') {
       push(@sqlf, '(', $self->_recurse_from(@$to), ')');
index 0874167..50cae7e 100644 (file)
@@ -520,10 +520,8 @@ sub _create_db_to_schema_diff {
     return;
   }
 
-  eval 'require SQL::Translator "0.09003"';
-  if ($@) {
-    $self->throw_exception("SQL::Translator 0.09003 required");
-  }
+  $self->throw_exception($self->_sqlt_version_error)
+    if (not $self->_sqlt_version_ok);
 
   my $db_tr = SQL::Translator->new({ 
                                     add_drop_table => 1, 
index 436f540..d7f1e9e 100644 (file)
@@ -1303,13 +1303,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);
 
@@ -2277,9 +2282,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 );
 
@@ -2421,9 +2425,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
@@ -2510,21 +2513,6 @@ sub build_datetime_parser {
   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
 
index 8742559..8bcb8cb 100644 (file)
@@ -198,6 +198,8 @@ L<DBIx::Class::Storage::DBI::Sybase::Microsoft_SQL_Server>.
 
 =head1 IMPLEMENTATION NOTES
 
+=head2 IDENTITY information
+
 Microsoft SQL Server supports three methods of retrieving the IDENTITY
 value for inserted row: IDENT_CURRENT, @@IDENTITY, and SCOPE_IDENTITY().
 SCOPE_IDENTITY is used here because it is the safest.  However, it must
@@ -216,6 +218,14 @@ This is more dangerous, as inserting into a table with an on insert trigger that
 inserts into another table with an identity will give erroneous results on
 recent versions of SQL Server.
 
+=head2 bulk_insert
+
+Be aware that we have tried to make things as simple as possible for our users.
+For MSSQL that means that when a user tries to do a populate/bulk_insert which
+includes an autoincrementing column, we will try to tell the database to allow
+the insertion of the autoinc column.  But the user must have the db_ddladmin
+role membership, otherwise you will get a fairly opaque error message.
+
 =head1 AUTHOR
 
 See L<DBIx::Class/CONTRIBUTORS>.
index 6224d53..9fa6d31 100644 (file)
@@ -33,6 +33,21 @@ sub _dbh_last_insert_id {
   $dbh->{mysql_insertid};
 }
 
+# we need to figure out what mysql version we're running
+sub sql_maker {
+  my $self = shift;
+
+  unless ($self->_sql_maker) {
+    my $maker = $self->next::method (@_);
+
+    # mysql 3 does not understand a bare JOIN
+    my $mysql_ver = $self->_get_dbh->get_info(18);
+    $maker->{_default_jointype} = 'INNER' if $mysql_ver =~ /^3/;
+  }
+
+  return $self->_sql_maker;
+}
+
 sub sqlt_type {
   return 'MySQL';
 }
index 031529c..c9e03af 100644 (file)
@@ -6,6 +6,7 @@ use Test::Exception;
 use lib qw(t/lib);
 use DBICTest;
 use DBI::Const::GetInfoType;
+use DBIC::SqlMakerTest;
 
 my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_MYSQL_${_}" } qw/DSN USER PASS/};
 
@@ -14,8 +15,6 @@ my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_MYSQL_${_}" } qw/DSN USER PASS/};
 plan skip_all => 'Set $ENV{DBICTEST_MYSQL_DSN}, _USER and _PASS to run this test'
   unless ($dsn && $user);
 
-plan tests => 19;
-
 my $schema = DBICTest::Schema->connect($dsn, $user, $pass);
 
 my $dbh = $schema->storage->dbh;
@@ -153,12 +152,41 @@ SKIP: {
 
     my $type_info = $schema->storage->columns_info_for('artist');
     is_deeply($type_info, $test_type_info, 'columns_info_for - column data types');
+
+
 }
 
 my $cd = $schema->resultset ('CD')->create ({});
 my $producer = $schema->resultset ('Producer')->create ({});
 lives_ok { $cd->set_producers ([ $producer ]) } 'set_relationship doesnt die';
 
+{
+  my $artist = $schema->resultset('Artist')->next;
+  my $cd = $schema->resultset('CD')->next;
+  $cd->set_from_related ('artist', $artist);
+  $cd->update;
+
+  my $rs = $schema->resultset('CD')->search ({}, { prefetch => 'artist' });
+
+  lives_ok sub {
+    my $cd = $rs->next;
+    is ($cd->artist->name, $artist->name, 'Prefetched artist');
+  }, 'join does not throw (mysql 3 test)';
+
+  # induce a jointype override, make sure it works even if we don't have mysql3
+  local $schema->storage->sql_maker->{_default_jointype} = 'inner';
+  is_same_sql_bind (
+    $rs->as_query,
+    '(
+      SELECT me.cdid, me.artist, me.title, me.year, me.genreid, me.single_track,
+             artist.artistid, artist.name, artist.rank, artist.charfield
+        FROM cd me
+        INNER JOIN artist artist ON artist.artistid = me.artist
+    )',
+    [],
+    'overriden default join type works',
+  );
+}
 
 ## Can we properly deal with the null search problem?
 ##
@@ -190,3 +218,5 @@ NULLINSEARCH: {
     is $artist => undef
       => 'Nothing Found!';
 }
+
+done_testing;
index e633198..4b94d9b 100644 (file)
--- a/t/72pg.t
+++ b/t/72pg.t
@@ -60,6 +60,8 @@ create_test_schema($dbh);
 
 ###  auto-pk / last_insert_id / sequence discovery
 {
+    local $SIG{__WARN__} = sub {};
+    _cleanup ($schema);
 
     $schema->source("Artist")->name("testschema.artist");
 
@@ -366,8 +368,9 @@ for (1..5) {
 my $st = $schema->resultset('SequenceTest')->create({ name => 'foo', pkid1 => 55 });
 is($st->pkid1, 55, "Oracle Auto-PK without trigger: First primary key set manually");
 
-drop_test_schema($dbh);
-done_testing;
+sub _cleanup {
+  my $schema = shift or return;
+  local $SIG{__WARN__} = sub {};
 
 exit;
 END { drop_test_schema($dbh) }
@@ -459,26 +462,18 @@ sub drop_test_schema {
   return unless $dbh->ping;
 
   for my $stat (
-    'DROP TABLE unq_nextval_schema2.artist',
-    'DROP SCHEMA unq_nextval_schema2',
-    'DROP SEQUENCE public.artist_artistid_seq',
-    'DROP TABLE unq_nextval_schema.artist',
-    'DROP SCHEMA unq_nextval_schema',
-    'DROP TABLE testschema.artist',
-    'DROP TABLE testschema.casecheck',
-    'DROP TABLE testschema.sequence_test',
-    'DROP TABLE testschema.array_test',
+    'DROP SCHEMA testschema CASCADE',
+    'DROP SCHEMA anothertestschema CASCADE',
+    'DROP SCHEMA yetanothertestschema CASCADE',
     'DROP SEQUENCE pkid1_seq',
     'DROP SEQUENCE pkid2_seq',
     'DROP SEQUENCE nonpkid_seq',
-    'DROP SCHEMA testschema',
-    'DROP TABLE anothertestschema.artist',
-    'DROP SCHEMA anothertestschema',
-    'DROP TABLE yetanothertestschema.artist',
-    'DROP SCHEMA yetanothertestschema',
   ) {
-    eval { $dbh->do ($stat) };
+    eval { $schema->storage->_do_query ($stat) };
     diag $@ if $@ && !$no_warn;
   }
 }
 
+done_testing;
+
+END { _cleanup($schema) }
index 40a6157..2ee8cc3 100644 (file)
@@ -198,6 +198,8 @@ SQL
 });
 
 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/],
@@ -219,6 +221,8 @@ lives_ok ( sub {
 }, '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/],
index 65f2dc8..1962431 100644 (file)
@@ -5,8 +5,12 @@ use Test::More;
 use lib qw(t/lib);
 use DBICTest;
 
-eval "use SQL::Translator";
-plan skip_all => 'SQL::Translator required' if $@;
+BEGIN {
+  require DBIx::Class;
+  plan skip_all =>
+      'Test needs SQL::Translator ' . DBIx::Class->_sqlt_minimum_version
+    if not DBIx::Class->_sqlt_version_ok;
+}
 
 my $schema = DBICTest->init_schema (no_deploy => 1);
 
index d62f117..9ea6762 100644 (file)
@@ -1,4 +1,5 @@
 #!/usr/bin/perl
+
 use strict;
 use warnings;
 use Test::More;
@@ -15,11 +16,10 @@ BEGIN {
   plan skip_all => 'Set $ENV{DBICTEST_MYSQL_DSN}, _USER and _PASS to run this test'
     unless ($dsn);
 
-
-    eval "use DBD::mysql; use SQL::Translator 0.09003;";
-    plan $@
-        ? ( skip_all => 'needs DBD::mysql and SQL::Translator 0.09003 for testing' )
-        : ( tests => 22 );
+  require DBIx::Class;
+  plan skip_all =>
+      'Test needs SQL::Translator ' . DBIx::Class->_sqlt_minimum_version
+    if not DBIx::Class->_sqlt_version_ok;
 }
 
 my $version_table_name = 'dbix_class_schema_versions';
@@ -182,3 +182,5 @@ TODO: {
 unless ($ENV{DBICTEST_KEEP_VERSIONING_DDL}) {
     unlink $_ for (values %$fn);
 }
+
+done_testing;
index 5bbd302..6f3a3e2 100644 (file)
@@ -5,12 +5,11 @@ use Test::More;
 use lib qw(t/lib);
 use DBICTest;
 
-
 BEGIN {
-    eval "use SQL::Translator 0.09003;";
-    if ($@) {
-        plan skip_all => 'needs SQL::Translator 0.09003 for testing';
-    }
+  require DBIx::Class;
+  plan skip_all =>
+      'Test needs SQL::Translator ' . DBIx::Class->_sqlt_minimum_version
+    if not DBIx::Class->_sqlt_version_ok;
 }
 
 my $schema = DBICTest->init_schema();
@@ -23,8 +22,6 @@ my @sources = grep
   $schema->sources
 ;
 
-plan tests => ( @sources * 3);
-
 { 
        my $sqlt_schema = create_schema({ schema => $schema, args => { parser_args => { } } });
 
@@ -65,6 +62,8 @@ plan tests => ( @sources * 3);
        }
 }
 
+done_testing;
+
 sub create_schema {
        my $args = shift;