Merge 'trunk' into 'mssql_top_fixes'
Peter Rabbitson [Sat, 20 Jun 2009 21:35:12 +0000 (21:35 +0000)]
r6734@Thesaurus (orig r6733):  ribasushi | 2009-06-20 10:16:02 +0200
todoify skip
r6736@Thesaurus (orig r6735):  ribasushi | 2009-06-20 12:37:52 +0200
Clarify test
r6740@Thesaurus (orig r6739):  ribasushi | 2009-06-20 15:22:06 +0200
Disambiguate populate() return
r6743@Thesaurus (orig r6742):  ribasushi | 2009-06-20 23:30:23 +0200
 r6737@Thesaurus (orig r6736):  ribasushi | 2009-06-20 12:39:34 +0200
 new branch to streamline count() and introduce count_rs()
 r6738@Thesaurus (orig r6737):  ribasushi | 2009-06-20 12:44:09 +0200
 Add count_rs, move the code back from DBI - leave only sql specific hooks
 r6739@Thesaurus (orig r6738):  ribasushi | 2009-06-20 12:54:11 +0200
 Test count_rs
 r6742@Thesaurus (orig r6741):  ribasushi | 2009-06-20 23:30:10 +0200
 More tests and a really working count_rs

1  2 
lib/DBIx/Class/ResultSet.pm
lib/DBIx/Class/Storage/DBI.pm

Simple merge
@@@ -1294,13 -1289,14 +1294,14 @@@ sub _select_args 
  sub _adjust_select_args_for_limited_prefetch {
    my ($self, $from, $select, $where, $attrs) = @_;
  
 -  if ($attrs->{group_by} and @{$attrs->{group_by}}) {
 -    $self->throw_exception ('Prefetch with limit (rows/offset) is not supported on resultsets with a group_by attribute');
 +  if ($attrs->{group_by} && @{$attrs->{group_by}}) {
 +    $self->throw_exception ('has_many prefetch with limit (rows/offset) is not supported on grouped resultsets');
    }
  
 -  $self->throw_exception ('Prefetch with limit (rows/offset) is not supported on resultsets with a custom from attribute')
 +  $self->throw_exception ('has_many prefetch with limit (rows/offset) is not supported on resultsets with a custom from attribute')
      if (ref $from ne 'ARRAY');
  
    # separate attributes
    my $sub_attrs = { %$attrs };
    delete $attrs->{$_} for qw/where bind rows offset/;
@@@ -1450,96 -1464,31 +1469,58 @@@ sub _resolve_ident_sources 
    return $alias2source;
  }
  
 +# Takes $ident, \@column_names
 +#
 +# returns { $column_name => \%column_info, ... }
 +# also note: this adds -result_source => $rsrc to the column info
 +#
 +# usage:
 +#   my $col_sources = $self->_resolve_column_info($ident, [map $_->[0], @{$bind}]);
 +sub _resolve_column_info {
 +  my ($self, $ident, $colnames) = @_;
 +  my $alias2src = $self->_resolve_ident_sources($ident);
 +
 +  my $sep = $self->_sql_maker_opts->{name_sep} || '.';
 +  $sep = "\Q$sep\E";
-   
++
 +  my %return;
 +  foreach my $col (@{$colnames}) {
 +    $col =~ m/^ (?: ([^$sep]+) $sep)? (.+) $/x;
 +
 +    my $alias = $1 || 'me';
 +    my $colname = $2;
 +
 +    my $rsrc = $alias2src->{$alias};
 +    $return{$col} = $rsrc && { %{$rsrc->column_info($colname)}, -result_source => $rsrc };
 +  }
 +  return \%return;
 +}
 +
- sub count {
-   my ($self, $source, $attrs) = @_;
-   my $tmp_attrs = { %$attrs };
-   # take off any limits, record_filter is cdbi, and no point of ordering a count
-   delete $tmp_attrs->{$_} for (qw/select as rows offset order_by record_filter/);
-   # overwrite the selector
-   $tmp_attrs->{select} = { count => '*' };
-   my $tmp_rs = $source->resultset_class->new($source, $tmp_attrs);
-   my ($count) = $tmp_rs->cursor->next;
-   # if the offset/rows attributes are still present, we did not use
-   # a subquery, so we need to make the calculations in software
-   $count -= $attrs->{offset} if $attrs->{offset};
-   $count = $attrs->{rows} if $attrs->{rows} and $attrs->{rows} < $count;
-   $count = 0 if ($count < 0);
-   return $count;
- }
- sub count_grouped {
-   my ($self, $source, $attrs) = @_;
-   # copy for the subquery, we need to do some adjustments to it too
-   my $sub_attrs = { %$attrs };
-   # these can not go in the subquery, and there is no point of ordering it
-   delete $sub_attrs->{$_} for qw/collapse select as order_by/;
-   # if we prefetch, we group_by primary keys only as this is what we would get out of the rs via ->next/->all
-   # simply deleting group_by suffices, as the code below will re-fill it
-   # Note: we check $attrs, as $sub_attrs has collapse deleted
-   if (ref $attrs->{collapse} and keys %{$attrs->{collapse}} ) {
-     delete $sub_attrs->{group_by};
-   }
-   $sub_attrs->{group_by} ||= [ map { "$attrs->{alias}.$_" } ($source->primary_columns) ];
-   $sub_attrs->{select} = $self->_grouped_count_select ($source, $sub_attrs);
-   $attrs->{from} = [{
-     count_subq => $source->resultset_class->new ($source, $sub_attrs )->as_query
-   }];
-   # the subquery replaces this
-   delete $attrs->{$_} for qw/where bind collapse group_by having having_bind rows offset/;
-   return $self->count ($source, $attrs);
+ # Returns a counting SELECT for a simple count
+ # query. Abstracted so that a storage could override
+ # this to { count => 'firstcol' } or whatever makes
+ # sense as a performance optimization
+ sub _count_select {
+   #my ($self, $source, $rs_attrs) = @_;
+   return { count => '*' };
  }
  
+ # Returns a SELECT which will end up in the subselect
+ # There may or may not be a group_by, as the subquery
+ # might have been called to accomodate a limit
  #
- # Returns a SELECT to go with a supplied GROUP BY
- # (caled by count_grouped so a group_by is present)
- # Most databases expect them to match, but some
- # choke in various ways.
+ # Most databases would be happy with whatever ends up
+ # here, but some choke in various ways.
  #
- sub _grouped_count_select {
-   my ($self, $source, $rs_args) = @_;
-   return $rs_args->{group_by};
+ sub _subq_count_select {
+   my ($self, $source, $rs_attrs) = @_;
+   return $rs_attrs->{group_by} if $rs_attrs->{group_by};
+   my @pcols = map { join '.', $rs_attrs->{alias}, $_ } ($source->primary_columns);
+   return @pcols ? \@pcols : [ 1 ];
  }
  
  sub source_bind_attributes {
    my ($self, $source) = @_;