From: Peter Rabbitson Date: Sat, 20 Jun 2009 21:35:12 +0000 (+0000) Subject: Merge 'trunk' into 'mssql_top_fixes' X-Git-Tag: v0.08108~12^2~24 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=2b832e14bedc071000f24e74c8aaa6b031850e54;p=dbsrgits%2FDBIx-Class.git Merge 'trunk' into 'mssql_top_fixes' 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 --- 2b832e14bedc071000f24e74c8aaa6b031850e54 diff --cc lib/DBIx/Class/Storage/DBI.pm index f7298cf,7998423..56dd7e2 --- a/lib/DBIx/Class/Storage/DBI.pm +++ b/lib/DBIx/Class/Storage/DBI.pm @@@ -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) = @_;