Merge 'trunk' into 'count_distinct'
Justin Hunter [Sat, 14 Mar 2009 13:55:55 +0000 (13:55 +0000)]
created count_distinct branch

Makefile.PL
lib/DBIx/Class.pm
lib/DBIx/Class/Manual/FAQ.pod
lib/DBIx/Class/Relationship/Accessor.pm
lib/DBIx/Class/ResultSet.pm
lib/DBIx/Class/Schema.pm
lib/DBIx/Class/Storage/DBI.pm
t/39load_namespaces_rt41083.t
t/distinct_count.t [deleted file]

index 9c1633a..8acdc1e 100644 (file)
@@ -82,6 +82,18 @@ if ($Module::Install::AUTHOR) {
 
 auto_provides;
 
+if ($Module::Install::AUTHOR) {
+  warn <<'EOW';
+******************************************************************************
+******************************************************************************
+***                                                                        ***
+*** AUTHOR MODE: all optional test dependencies converted to hard requires ***
+***                                                                        ***
+******************************************************************************
+******************************************************************************
+
+EOW
+}
 auto_install;
 
 # Have all prerequisites, check DBD::SQLite sanity
@@ -210,3 +222,4 @@ if ($Module::Install::AUTHOR) {
   ];
   Meta->write;
 }
+
index b0612ae..bb73581 100644 (file)
@@ -207,8 +207,6 @@ andyg: Andy Grundman <andy@hybridized.org>
 
 ank: Andres Kievsky
 
-arcanez: Justin Hunter <justin.d.hunter@gmail.com>
-
 ash: Ash Berlin <ash@cpan.org>
 
 bert: Norbert Csongradi <bert@cpan.org>
index 273397a..7d4505e 100644 (file)
@@ -274,7 +274,7 @@ replaced with the following.)
 
 Or, if you have quoting off:
 
- ->search({ 'YEAR(date_of_birth' => 1979 });
+ ->search({ 'YEAR(date_of_birth)' => 1979 });
 
 =item .. find more help on constructing searches?
 
@@ -353,6 +353,20 @@ to get a new, fresh copy of the row, just re-fetch the row from storage.
 L<DBIx::Class::PK/discard_changes> does just that by re-fetching the row from storage
 using the row's primary key.
 
+=item .. fetch my data a "page" at a time?
+
+Pass the C<rows> and C<page> attributes to your search, eg:
+
+  ->search({}, { rows => 10, page => 1});
+
+=item .. get a count of all rows even when paging?
+
+Call C<pager> on the paged resultset, it will return a L<Data::Page>
+object. Calling C<total_entries> on the pager will return the correct
+total.
+
+C<count> on the resultset will only return the total number in the page.
+
 =back
 
 =head2 Inserting and updating data
index 02f7db5..065cf69 100644 (file)
@@ -31,17 +31,11 @@ sub add_relationship_accessor {
           $rel_info->{cond}, $rel, $self
         );
         if ($rel_info->{attrs}->{undef_on_null_fk}){
-          return unless ref($cond) eq 'HASH';
-          return if grep { not defined } values %$cond;
+          return undef unless ref($cond) eq 'HASH';
+          return undef if grep { not defined $_ } values %$cond;
         }
         my $val = $self->find_related($rel, {}, {});
-
-        # this really should have been:
-        # return $val unless $val
-        # however someone might already be relying on return() as in:
-        # my @things = map { $_->might_have_acc } ($rs->all)
-        # thus keeping the quirky behavior
-        return unless defined $val;
+        return $val unless $val;  # $val instead of undef so that null-objects can go through
 
         return $self->{_relationship_data}{$rel} = $val;
       }
index b5d8086..dd911ad 100644 (file)
@@ -102,6 +102,21 @@ another.
     });
   }
 
+=head3 Resolving conditions and attributes
+
+When a resultset is chained from another resultset, conditions and
+attributes with the same keys need resolving.
+
+L</join>, L</prefetch>, L</+select>, L</+as> attributes are merged
+into the existing ones from the original resultset.
+
+The L</where>, L</having> attribute, and any search conditions are
+merged with an SQL C<AND> to the existing condition from the original
+resultset.
+
+All other attributes are overridden by any new ones supplied in the
+search attributes.
+
 =head2 Multiple queries
 
 Since a resultset just defines a query, you can do all sorts of
@@ -313,11 +328,6 @@ sub search_rs {
     : undef
   );
 
-  foreach my $key (keys %$cond) {
-    next unless my ($alias) = ($key =~ /^(\w+)\.\w+$/);
-    push @{$new_attrs->{join}}, $alias unless grep(/${alias}/, @{$new_attrs->{join}}) or $alias eq 'me';
-  }
-
   if (defined $where) {
     $new_attrs->{where} = (
       defined $new_attrs->{where}
@@ -1133,11 +1143,11 @@ sub count {
 
 sub _count { # Separated out so pager can get the full count
   my $self = shift;
-  my $attrs = { %{$self->_resolved_attrs} };
+  my $select = { count => '*' };
 
-  if (my $group_by = $attrs->{group_by}) {
+  my $attrs = { %{$self->_resolved_attrs} };
+  if (my $group_by = delete $attrs->{group_by}) {
     delete $attrs->{having};
-    delete $attrs->{order_by};
     my @distinct = (ref $group_by ?  @$group_by : ($group_by));
     # todo: try CONCAT for multi-column pk
     my @pk = $self->result_source->primary_columns;
@@ -1151,15 +1161,14 @@ sub _count { # Separated out so pager can get the full count
       }
     }
 
-    $attrs->{select} = $group_by; 
-    $attrs->{from} = [ { 'mesub' => (ref $self)->new($self->result_source, $attrs)->cursor->as_query } ];
+    $select = { count => { distinct => \@distinct } };
   }
 
-  $attrs->{select} = { count => '*' };
+  $attrs->{select} = $select;
   $attrs->{as} = [qw/count/];
 
-  # offset, order by, group by, where and page are not needed to count. record_filter is cdbi
-  delete $attrs->{$_} for qw/rows offset order_by group_by where page pager record_filter/;
+  # offset, order by and page are not needed to count. record_filter is cdbi
+  delete $attrs->{$_} for qw/rows offset order_by page pager record_filter/;
 
   my $tmp_rs = (ref $self)->new($self->result_source, $attrs);
   my ($count) = $tmp_rs->cursor->next;
@@ -1642,6 +1651,9 @@ sub _normalize_populate_args {
 Return Value a L<Data::Page> object for the current resultset. Only makes
 sense for queries with a C<page> attribute.
 
+To get the full count of entries for a paged resultset, call
+C<total_entries> on the L<Data::Page> object.
+
 =cut
 
 sub pager {
@@ -2863,6 +2875,10 @@ on it.
 
 If L<rows> attribute is not specified it defualts to 10 rows per page.
 
+When you have a paged resultset, L</count> will only return the number
+of rows in the page. To get the total, use the L</pager> and call
+C<total_entries> on it.
+
 =head2 rows
 
 =over 4
index 2c0b43f..cf7267d 100644 (file)
@@ -483,6 +483,12 @@ Note that C<connect_info> expects an arrayref of arguments, but
 C<connect> does not. C<connect> wraps it's arguments in an arrayref
 before passing them to C<connect_info>.
 
+=head3 Overloading
+
+C<connect> is a convenience method. It is equivalent to calling
+$schema->clone->connection(@connectinfo). To write your own overloaded
+version, overload L</connection> instead.
+
 =cut
 
 sub connect { shift->clone->connection(@_) }
@@ -760,6 +766,9 @@ Similar to L</connect> except sets the storage object and connection
 data in-place on the Schema class. You should probably be calling
 L</connect> to get a proper Schema object instead.
 
+=head3 Overloading
+
+Overload C<connection> to change the behaviour of C<connect>.
 
 =cut
 
index c27647c..4bfa2e8 100644 (file)
@@ -95,9 +95,6 @@ sub _find_syntax {
 
 sub select {
   my ($self, $table, $fields, $where, $order, @rest) = @_;
-  local $self->{having_bind} = [];
-  local $self->{from_bind} = [];
-
   if (ref $table eq 'SCALAR') {
     $table = $$table;
   }
@@ -109,7 +106,8 @@ sub select {
   @rest = (-1) unless defined $rest[0];
   die "LIMIT 0 Does Not Compute" if $rest[0] == 0;
     # and anyway, SQL::Abstract::Limit will cause a barf if we don't first
-  my ($sql, @where_bind) = $self->SUPER::select(
+  local $self->{having_bind} = [];
+  my ($sql, @ret) = $self->SUPER::select(
     $table, $self->_recurse_fields($fields), $where, $order, @rest
   );
   $sql .= 
@@ -121,7 +119,7 @@ sub select {
     ) :
     ''
   ;
-  return wantarray ? ($sql, @{$self->{from_bind}}, @where_bind, @{$self->{having_bind}}) : $sql;
+  return wantarray ? ($sql, @ret, @{$self->{having_bind}}) : $sql;
 }
 
 sub insert {
@@ -269,10 +267,11 @@ sub _recurse_from {
 }
 
 sub _bind_to_sql {
-  my ($self, $arr) = @_;
-  my ($sql, @bind) = @{${$arr}};
-  push (@{$self->{from_bind}}, @bind);
-  return $sql;
+  my $self = shift;
+  my $arr  = shift;
+  my $sql = shift @$$arr;
+  $sql =~ s/\?/$self->_quote((shift @$$arr)->[1])/eg;
+  return $sql
 }
 
 sub _make_as {
index ba99fe5..7d8dc5c 100644 (file)
@@ -6,7 +6,7 @@ use Test::More;
 
 use lib 't/lib';
 
-plan tests => 4;
+plan tests => 6;
 
 sub _chk_warning {
        defined $_[0]? 
@@ -14,6 +14,11 @@ sub _chk_warning {
                1
 }
 
+sub _chk_extra_sources_warning {
+       my $p = qr/already has a source, use register_extra_source for additional sources/;
+       defined $_[0]? $_[0] !~ /$p/ : 1;
+}
+
 my $warnings;
 eval {
     local $SIG{__WARN__} = sub { $warnings .= shift };
@@ -26,7 +31,9 @@ eval {
     );
 };
 ok(!$@) or diag $@;
-ok(_chk_warning($warnings), 'expected no complaint');
+ok(_chk_warning($warnings), 'expected no resultset complaint');
+ok(_chk_extra_sources_warning($warnings), 'expected no extra sources complaint');
+undef $warnings;
 
 eval {
     local $SIG{__WARN__} = sub { $warnings .= shift };
@@ -39,4 +46,6 @@ eval {
     );
 };
 ok(!$@) or diag $@;
-ok(_chk_warning($warnings), 'expected no complaint') or diag $warnings;
+ok(_chk_warning($warnings), 'expected no resultset complaint') or diag $warnings;
+ok(_chk_extra_sources_warning($warnings), 'expected no extra sources complaint');
+undef $warnings;
diff --git a/t/distinct_count.t b/t/distinct_count.t
deleted file mode 100644 (file)
index b3f3446..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-use strict;
-use warnings;  
-
-use Test::More;
-use lib qw(t/lib);
-use lib '/sporkrw/xfer/DBIx-Class/0.08/branches/count_distinct/lib';
-use DBICTest;
-
-my $schema = DBICTest->init_schema();
-
-eval "use DBD::SQLite";
-plan skip_all => 'needs DBD::SQLite for testing' if $@;
-plan tests => 4;
-
-cmp_ok($schema->resultset("Tag")->count({ tag => 'Blue' }),
-           '==', 9, 'Count without DISTINCT ok');
-
-cmp_ok($schema->resultset("Tag")->count({ tag => [ 'Blue', 'Shiny' ] }, { group_by => 'tag' }),
-           '==', 2, 'Count with single column group_by ok');
-
-cmp_ok($schema->resultset("Tag")->count({ tag => 'Blue' }, { group_by => [ qw/tag cd/ ]}), 
-           '==', 4, 'Count with multiple column group_by ok');
-
-cmp_ok($schema->resultset("Tag")->count({ tag => 'Blue' }, { distinct => 1 }),
-           '==', 4, "Count with single column distinct ok");
-