Merge 'trunk' into 'cdbicompat_integration'
Michael G Schwern [Thu, 21 Feb 2008 08:03:47 +0000 (08:03 +0000)]
r54378@windhund (orig r3954):  matthewt | 2008-01-18 05:03:08 -0800
added strict and warnings to HashRefInflator, fixed inflation for empty has_many rels
r54379@windhund (orig r3955):  matthewt | 2008-01-20 04:28:55 -0800
made search_rs smarter about when to preserve the cache to fix mm prefetch usage
r54389@windhund (orig r3965):  semifor | 2008-01-22 07:13:11 -0800
Added build_datetime_parser method for MSSQL over ODBC.
r54394@windhund (orig r3970):  castaway | 2008-01-24 05:19:52 -0800
Oops, fix joining manual to be correct

r54395@windhund (orig r3971):  castaway | 2008-01-24 15:22:49 -0800
Version 0.08009

r54396@windhund (orig r3972):  castaway | 2008-01-24 15:36:59 -0800
0.08009 released

r54399@windhund (orig r3975):  tomboh | 2008-01-25 09:20:38 -0800
Fix a typo and a couple of links.

r54448@windhund (orig r4024):  oyse | 2008-02-05 00:42:32 -0800
Added Øystein Torget to the list of contributers
r54474@windhund (orig r4050):  ash | 2008-02-10 09:31:27 -0800
Add txn_scope_guard method/object

Changes
lib/DBIx/Class.pm
lib/DBIx/Class/Manual/Joining.pod
lib/DBIx/Class/ResultClass/HashRefInflator.pm
lib/DBIx/Class/ResultSet.pm
lib/DBIx/Class/Row.pm
lib/DBIx/Class/Schema.pm
lib/DBIx/Class/Storage.pm
lib/DBIx/Class/Storage/DBI/ODBC/Microsoft_SQL_Server.pm
lib/DBIx/Class/Storage/TxnScopeGuard.pm [new file with mode: 0644]
t/76joins.t

diff --git a/Changes b/Changes
index 3832053..1b5b144 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,11 @@
 Revision history for DBIx::Class
 
+        - Created Storage::TxnScopeGuard object and txn_scope_guard methods
+          on Schema and Storage as an alternative way of doing transactions
+
+0.08009 2008-01-20 13:30
+        - Made search_rs smarter about when to preserve the cache to fix
+          mm prefetch usage
         - Added Storage::DBI subclass for MSSQL over ODBC. 
         - Added freeze, thaw and dclone methods to Schema so that thawed
           objects will get re-attached to the schema.
index e4875e8..61b7d07 100644 (file)
@@ -24,7 +24,7 @@ sub component_base_class { 'DBIx::Class' }
 # i.e. first release of 0.XX *must* be 0.XX000. This avoids fBSD ports
 # brain damage and presumably various other packaging systems too
 
-$VERSION = '0.08008';
+$VERSION = '0.08009';
 
 sub MODIFY_CODE_ATTRIBUTES {
   my ($class,$code,@attrs) = @_;
@@ -254,6 +254,8 @@ ningu: David Kamholz <dkamholz@cpan.org>
 
 Numa: Dan Sully <daniel@cpan.org>
 
+oyse: Øystein Torget <oystein.torget@dnv.com>
+
 paulm: Paul Makepeace
 
 penguin: K J Cheetham
index fdc44f0..0333371 100644 (file)
@@ -165,7 +165,7 @@ automatically be numbered:
 
   join => [ 'room', 'room' ]
 
-The aliases are: C<room_1> and C<room_2>.
+The aliases are: C<room> and C<room_2>.
 
 =cut
 
index 6ad22ad..4aa362a 100644 (file)
@@ -1,5 +1,8 @@
 package DBIx::Class::ResultClass::HashRefInflator;
 
+use strict;
+use warnings;
+
 =head1 NAME
 
 DBIx::Class::ResultClass::HashRefInflator
@@ -61,7 +64,7 @@ sub mk_hash {
     # related sources.
 
     # to avoid emtpy has_many rels contain one empty hashref
-    return if (not keys %$me);
+    return undef if (not keys %$me);
 
     my $def;
 
@@ -71,13 +74,14 @@ sub mk_hash {
             last;
         }
     }
-    return unless $def;
+    return undef unless $def;
 
     return { %$me,
         map {
           ( $_ =>
-             ref($rest->{$_}[0]) eq 'ARRAY' ? [ map { mk_hash(@$_) } @{$rest->{$_}} ]
-                                            : mk_hash( @{$rest->{$_}} )
+             ref($rest->{$_}[0]) eq 'ARRAY'
+                 ? [ grep defined, map mk_hash(@$_), @{$rest->{$_}} ]
+                 : mk_hash( @{$rest->{$_}} )
           )
         } keys %$rest
     };
index ae39e27..775d032 100644 (file)
@@ -11,6 +11,7 @@ use Data::Page;
 use Storable;
 use DBIx::Class::ResultSetColumn;
 use DBIx::Class::ResultSourceHandle;
+use List::Util ();
 use base qw/DBIx::Class/;
 
 __PACKAGE__->mk_group_accessors('simple' => qw/result_class _source_handle/);
@@ -172,18 +173,26 @@ always return a resultset, even in list context.
 sub search_rs {
   my $self = shift;
 
-  my $rows;
-
-  unless (@_) {                 # no search, effectively just a clone
-    $rows = $self->get_cache;
-  }
-
   my $attrs = {};
   $attrs = pop(@_) if @_ > 1 and ref $_[$#_] eq 'HASH';
   my $our_attrs = { %{$self->{attrs}} };
   my $having = delete $our_attrs->{having};
   my $where = delete $our_attrs->{where};
 
+  my $rows;
+
+  my %safe = (alias => 1, cache => 1);
+
+  unless (
+    (@_ && defined($_[0])) # @_ == () or (undef)
+    || 
+    (keys %$attrs # empty attrs or only 'safe' attrs
+    && List::Util::first { !$safe{$_} } keys %$attrs)
+  ) {
+    # no search, effectively just a clone
+    $rows = $self->get_cache;
+  }
+
   my $new_attrs = { %{$our_attrs}, %{$attrs} };
 
   # merge new attrs into inherited
index 1bc569f..279e508 100644 (file)
@@ -161,11 +161,9 @@ sub insert {
                        %{$self->{_inflated_column} || {}});
 
   if(!$self->{_rel_in_storage}) {
-    $source->storage->txn_begin;
 
     # The guard will save us if we blow out of this scope via die
-
-    $rollback_guard = Scope::Guard->new(sub { $source->storage->txn_rollback });
+    $rollback_guard = $source->storage->txn_scope_guard;
 
     ## Should all be in relationship_data, but we need to get rid of the
     ## 'filter' reltype..
@@ -248,8 +246,7 @@ sub insert {
         }
       }
     }
-    $source->storage->txn_commit;
-    $rollback_guard->dismiss;
+    $rollback_guard->commit;
   }
 
   $self->in_storage(1);
@@ -283,7 +280,7 @@ UPDATE query to commit any changes to the object to the database if
 required.
 
 Also takes an options hashref of C<< column_name => value> pairs >> to update
-first. But be awawre that the hashref will be passed to
+first. But be aware that the hashref will be passed to
 C<set_inflated_columns>, which might edit it in place, so dont rely on it being
 the same after a call to C<update>.  If you need to preserve the hashref, it is
 sufficient to pass a shallow copy to C<update>, e.g. ( { %{ $href } } )
@@ -773,7 +770,7 @@ sub throw_exception {
 =head2 id
 
 Returns the primary key(s) for a row. Can't be called as a class method.
-Actually implemented in L<DBIx::Class::Pk>
+Actually implemented in L<DBIx::Class::PK>
 
 =head2 discard_changes
 
@@ -782,7 +779,7 @@ been made.
 
 This method can also be used to refresh from storage, retrieving any
 changes made since the row was last read from storage. Actually
-implemented in L<DBIx::Class::Pk>
+implemented in L<DBIx::Class::PK>
 
 =cut
 
index 09edb9b..c42f10a 100644 (file)
@@ -734,6 +734,15 @@ sub txn_do {
   $self->storage->txn_do(@_);
 }
 
+sub txn_scope_guard {
+  my $self = shift;
+
+  $self->storage or $self->throw_exception
+    ('txn_scope_guard called on $schema without storage');
+
+  $self->storage->txn_scope_guard(@_);
+}
+
 =head2 txn_begin
 
 Begins a transaction (does nothing if AutoCommit is off). Equivalent to
index cd29601..e298aa3 100644 (file)
@@ -8,6 +8,7 @@ use base qw/DBIx::Class/;
 use Scalar::Util qw/weaken/;
 use Carp::Clan qw/^DBIx::Class/;
 use IO::File;
+use DBIx::Class::Storage::TxnScopeGuard;
 
 __PACKAGE__->mk_group_accessors('simple' => qw/debug debugobj schema/);
 __PACKAGE__->mk_group_accessors('inherited' => 'cursor_class');
@@ -261,6 +262,16 @@ which allows the rollback to propagate to the outermost transaction.
 
 sub txn_rollback { die "Virtual method!" }
 
+=head2 txn_scope_guard
+
+Return an object that does stuff.
+
+=cut
+
+sub txn_scope_guard {
+  return DBIx::Class::Storage::TxnScopeGuard->new($_[0]);
+}
+
 =head2 sql_maker
 
 Returns a C<sql_maker> object - normally an object of class
index 5c03762..e656548 100644 (file)
@@ -38,6 +38,14 @@ sub _sql_maker_opts {
     return { limit_dialect => 'Top', %{$self->{_sql_maker_opts}||{}} };
 }
 
+sub build_datetime_parser {
+  my $self = shift;
+  my $type = "DateTime::Format::Strptime";
+  eval "use ${type}";
+  $self->throw_exception("Couldn't load ${type}: $@") if $@;
+  return $type->new( pattern => '%F %T' );
+}
+
 1;
 
 __END__
@@ -72,6 +80,11 @@ onto each INSERT to accommodate that requirement.
 
 =head2 sqlt_type
 
+=head2 build_datetime_parser
+
+The resulting parser handles the MSSQL C<DATETIME> type, but is almost
+certainly not sufficient for the other MSSQL 2008 date/time types.
+
 =head1 AUTHORS
 
 Marc Mims C<< <marc@questright.com> >>
diff --git a/lib/DBIx/Class/Storage/TxnScopeGuard.pm b/lib/DBIx/Class/Storage/TxnScopeGuard.pm
new file mode 100644 (file)
index 0000000..04978f9
--- /dev/null
@@ -0,0 +1,79 @@
+package DBIx::Class::Storage::TxnScopeGuard;
+
+use strict;
+use warnings;
+
+sub new {
+  my ($class, $storage) = @_;
+
+  $storage->txn_begin;
+  bless [ 0, $storage ], ref $class || $class;
+}
+
+sub commit {
+  my $self = shift;
+
+  $self->[1]->txn_commit;
+  $self->[0] = 1;
+}
+
+sub DESTROY {
+  my ($dismiss, $storage) = @{$_[0]};
+
+  $storage->txn_rollback unless $dismiss;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+DBIx::Class::Storage::TxnScopeGuard
+
+=head1 SYNOPSIS
+
+ sub foo {
+   my ($self, $schema) = @_;
+
+   my $guard = $schema->txn_scope_guard;
+
+   # Multiple database operations here
+
+   $guard->commit;
+ }
+
+=head1 DESCRIPTION
+
+An object that behaves much like L<Scope::Guard>, but hardcoded to do the
+right thing with transactions in DBIx::Class. 
+
+=head1 METHODS
+
+=head2 new
+
+Creating an instance of this class will start a new transaction. Expects a
+L<DBIx::Class::Storage> object as its only argument.
+
+=head2 commit
+
+Commit the transaction, and stop guarding the scope. If this method is not
+called (i.e. an exception is thrown) and this object goes out of scope then
+the transaction is rolled back.
+
+=cut
+
+=head1 SEE ALSO
+
+L<DBIx::Class::Schema/txn_scope_guard>.
+
+=head1 AUTHOR
+
+Ash Berlin, 2008.
+
+Insipred by L<Scope::Guard> by chocolateboy.
+
+This module is free software. It may be used, redistributed and/or modified
+under the same terms as Perl itself.
+
+=cut
index 92e5785..101d71f 100644 (file)
@@ -247,11 +247,7 @@ $cd = $schema->resultset('CD')->find(1, { prefetch => { cd_to_producer => 'produ
 
 is($cd->producers->first->name, 'Matt S Trout', 'many_to_many accessor ok');
 
-TODO: {
-  local $TODO = 'use prefetched values for many_to_many accessor';
-
-  is($queries, 1, 'many_to_many accessor with nested prefetch ran exactly 1 query');
-}
+is($queries, 1, 'many_to_many accessor with nested prefetch ran exactly 1 query');
 
 $queries = 0;