X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FStorage%2FDBIHacks.pm;h=d4f4058b347f78f0abd39c27d84ab02a075f1e60;hb=975b573aff1f568f24951ca574fc636de396e506;hp=fd290f0d361b5b3a3542acd9a2fa8a7b21caa2a0;hpb=437a9cfaa7ef361284eee806578be7690a229ff8;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Storage/DBIHacks.pm b/lib/DBIx/Class/Storage/DBIHacks.pm index fd290f0..d4f4058 100644 --- a/lib/DBIx/Class/Storage/DBIHacks.pm +++ b/lib/DBIx/Class/Storage/DBIHacks.pm @@ -34,8 +34,8 @@ sub _prune_unused_joins { my $aliastypes = $self->_resolve_aliastypes_from_select_args(@_); - # don't care - delete $aliastypes->{joining}; + my $orig_joins = delete $aliastypes->{joining}; + my $orig_multiplying = $aliastypes->{multiplying}; # a grouped set will not be affected by amount of rows. Thus any # {multiplying} joins can go @@ -56,13 +56,17 @@ sub _prune_unused_joins { for my $j (@{$from}[1..$#$from]) { push @newfrom, $j if ( - (! $j->[0]{-alias}) # legacy crap + (! defined $j->[0]{-alias}) # legacy crap || $need_joins{$j->[0]{-alias}} ); } - return \@newfrom; + return ( \@newfrom, { + multiplying => { map { $need_joins{$_} ? ($_ => $orig_multiplying->{$_}) : () } keys %$orig_multiplying }, + %$aliastypes, + joining => { map { $_ => $orig_joins->{$_} } keys %need_joins }, + } ); } # @@ -79,10 +83,10 @@ sub _adjust_select_args_for_complex_prefetch { # generate inner/outer attribute lists, remove stuff that doesn't apply my $outer_attrs = { %$attrs }; - delete $outer_attrs->{$_} for qw/where bind rows offset group_by _grouped_by_distinct having/; + delete @{$outer_attrs}{qw(where bind rows offset group_by _grouped_by_distinct having)}; my $inner_attrs = { %$attrs }; - delete $inner_attrs->{$_} for qw/from for collapse select as _related_results_construction/; + delete @{$inner_attrs}{qw(from for collapse select as _related_results_construction)}; # there is no point of ordering the insides if there is no limit delete $inner_attrs->{order_by} if ( @@ -146,7 +150,7 @@ sub _adjust_select_args_for_complex_prefetch { # We can not just fetch everything because a potential has_many restricting # join collapse *will not work* on heavy data types. my $connecting_aliastypes = $self->_resolve_aliastypes_from_select_args( - [grep { ref($_) eq 'ARRAY' or ref($_) eq 'HASH' } @{$from}[$root_node_offset .. $#$from]], + $from, [], $where, $inner_attrs @@ -175,18 +179,16 @@ sub _adjust_select_args_for_complex_prefetch { local $self->{_use_join_optimizer} = 1; # throw away multijoins since we def. do not care about those inside the subquery - my $inner_from = $self->_prune_unused_joins ($from, $inner_select, $where, { + my ($inner_from, $inner_aliastypes) = $self->_prune_unused_joins ($from, $inner_select, $where, { %$inner_attrs, _force_prune_multiplying_joins => 1 }); - my $inner_aliastypes = - $self->_resolve_aliastypes_from_select_args( $inner_from, $inner_select, $where, $inner_attrs ); - # uh-oh a multiplier (which is not us) left in, this is a problem if ( $inner_aliastypes->{multiplying} and - !$inner_aliastypes->{grouping} # if there are groups - assume user knows wtf they are up to + # if there are user-supplied groups - assume user knows wtf they are up to + ( ! $inner_aliastypes->{grouping} or $inner_attrs->{_grouped_by_distinct} ) and my @multipliers = grep { $_ ne $root_alias } keys %{$inner_aliastypes->{multiplying}} ) { @@ -200,9 +202,11 @@ sub _adjust_select_args_for_complex_prefetch { ) { my $unprocessed_order_chunks; - ($inner_attrs->{group_by}, $unprocessed_order_chunks) = $self->_group_over_selection ( - $inner_from, $inner_select, $inner_attrs->{order_by} - ); + ($inner_attrs->{group_by}, $unprocessed_order_chunks) = $self->_group_over_selection ({ + %$inner_attrs, + from => $inner_from, + select => $inner_select, + }); $self->throw_exception ( 'A required group_by clause could not be constructed automatically due to a complex ' @@ -221,17 +225,6 @@ sub _adjust_select_args_for_complex_prefetch { # for DESC, and group_by the root columns. The end result should be # exactly what we expect - # FIXME REMOVE LATER - (just a sanity check) - if (defined ( my $impostor = first - { $_ ne $root_alias } - keys %{ $inner_aliastypes->{selecting} } - ) ) { - $self->throw_exception(sprintf - 'Unexpected inner selection during complex prefetch (%s)...', - join ', ', keys %{ $inner_aliastypes->{joining}{$impostor}{-seen_columns} || {} } - ); - } - # supplement the main selection with pks if not already there, # as they will have to be a part of the group_by to colapse # things properly @@ -298,9 +291,11 @@ sub _adjust_select_args_for_complex_prefetch { # do not care about leftovers here - it will be all the functions # we just created - ($inner_attrs->{group_by}) = $self->_group_over_selection ( - $inner_from, $inner_select, $inner_attrs->{order_by} - ); + ($inner_attrs->{group_by}) = $self->_group_over_selection ({ + %$inner_attrs, + from => $inner_from, + select => $inner_select, + }); } } @@ -334,7 +329,7 @@ sub _adjust_select_args_for_complex_prefetch { # we may not be the head if ($root_node_offset) { - # first generate the outer_from, up to the substitution point + # first generate the outer_from, up and including the substitution point @outer_from = splice @$from, 0, $root_node_offset; push @outer_from, [ @@ -354,11 +349,11 @@ sub _adjust_select_args_for_complex_prefetch { }; } - shift @$from; # it's replaced in @outer_from already + shift @$from; # what we just replaced above # scan the *remaining* from spec against different attributes, and see which joins are needed # in what role - my $outer_aliastypes = + my $outer_aliastypes = $outer_attrs->{_aliastypes} = $self->_resolve_aliastypes_from_select_args( $from, $outer_select, $where, $outer_attrs ); # unroll parents @@ -386,9 +381,11 @@ sub _adjust_select_args_for_complex_prefetch { if ( $need_outer_group_by and $attrs->{_grouped_by_distinct} ) { my $unprocessed_order_chunks; - ($outer_attrs->{group_by}, $unprocessed_order_chunks) = $self->_group_over_selection ( - \@outer_from, $outer_select, $outer_attrs->{order_by} - ); + ($outer_attrs->{group_by}, $unprocessed_order_chunks) = $self->_group_over_selection ({ + %$outer_attrs, + from => \@outer_from, + select => $outer_select, + }); $self->throw_exception ( 'A required group_by clause could not be constructed automatically due to a complex ' @@ -571,43 +568,49 @@ sub _resolve_aliastypes_from_select_args { # This is the engine behind { distinct => 1 } sub _group_over_selection { - my ($self, $from, $select, $order_by) = @_; + my ($self, $attrs) = @_; - my $rs_column_list = $self->_resolve_column_info ($from); + my $colinfos = $self->_resolve_column_info ($attrs->{from}); my (@group_by, %group_index); # the logic is: if it is a { func => val } we assume an aggregate, # otherwise if \'...' or \[...] we assume the user knows what is # going on thus group over it - for (@$select) { + for (@{$attrs->{select}}) { if (! ref($_) or ref ($_) ne 'HASH' ) { push @group_by, $_; $group_index{$_}++; - if ($rs_column_list->{$_} and $_ !~ /\./ ) { + if ($colinfos->{$_} and $_ !~ /\./ ) { # add a fully qualified version as well - $group_index{"$rs_column_list->{$_}{-source_alias}.$_"}++; + $group_index{"$colinfos->{$_}{-source_alias}.$_"}++; } } } - # add any order_by parts that are not already present in the group_by + # add any order_by parts *from the main source* that are not already + # present in the group_by # we need to be careful not to add any named functions/aggregates # i.e. order_by => [ ... { count => 'foo' } ... ] my @leftovers; - for ($self->_extract_order_criteria($order_by)) { + for ($self->_extract_order_criteria($attrs->{order_by})) { # only consider real columns (for functions the user got to do an explicit group_by) if (@$_ != 1) { push @leftovers, $_; next; } my $chunk = $_->[0]; - my $colinfo = $rs_column_list->{$chunk} or do { + + if ( + !$colinfos->{$chunk} + or + $colinfos->{$chunk}{-source_alias} ne $attrs->{alias} + ) { push @leftovers, $_; next; - }; + } - $chunk = "$colinfo->{-source_alias}.$chunk" if $chunk !~ /\./; + $chunk = $colinfos->{$chunk}{-fq_colname}; push @group_by, $chunk unless $group_index{$chunk}++; } @@ -621,14 +624,12 @@ sub _resolve_ident_sources { my ($self, $ident) = @_; my $alias2source = {}; - my $rs_alias; # the reason this is so contrived is that $ident may be a {from} # structure, specifying multiple tables to join if ( blessed $ident && $ident->isa("DBIx::Class::ResultSource") ) { # this is compat mode for insert/update/delete which do not deal with aliases $alias2source->{me} = $ident; - $rs_alias = 'me'; } elsif (ref $ident eq 'ARRAY') { @@ -636,7 +637,6 @@ sub _resolve_ident_sources { my $tabinfo; if (ref $_ eq 'HASH') { $tabinfo = $_; - $rs_alias = $tabinfo->{-alias}; } if (ref $_ eq 'ARRAY' and ref $_->[0] eq 'HASH') { $tabinfo = $_->[0]; @@ -647,7 +647,7 @@ sub _resolve_ident_sources { } } - return ($alias2source, $rs_alias); + return $alias2source; } # Takes $ident, \@column_names @@ -659,7 +659,7 @@ sub _resolve_ident_sources { # for all sources sub _resolve_column_info { my ($self, $ident, $colnames) = @_; - my ($alias2src, $root_alias) = $self->_resolve_ident_sources($ident); + my $alias2src = $self->_resolve_ident_sources($ident); my (%seen_cols, @auto_colnames);