X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FStorage%2FDBIHacks.pm;h=41ca371b5f7f6edec4425d0f173dec6ce7421860;hb=887a0aefb7a00e6b1ad3265747a1af0d1fd64e5d;hp=f5a03c92a7d1c2afb045dad4e01e932e0111f949;hpb=0a3441ee8e0e747cfa05eff02df0d918ed5d6acb;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Storage/DBIHacks.pm b/lib/DBIx/Class/Storage/DBIHacks.pm index f5a03c9..41ca371 100644 --- a/lib/DBIx/Class/Storage/DBIHacks.pm +++ b/lib/DBIx/Class/Storage/DBIHacks.pm @@ -115,12 +115,19 @@ sub _adjust_select_args_for_complex_prefetch { group_by => ['dummy'], %$inner_attrs, }); - # if a multi-type join was needed in the subquery - add a group_by to simulate the - # collapse in the subq + my $inner_aliastypes = + $self->_resolve_aliastypes_from_select_args( $inner_from, $inner_select, $where, $inner_attrs ); + + # if a multi-type non-selecting (only restricting) join was needed in the subquery + # add a group_by to simulate the collapse in the subq if ( ! $inner_attrs->{group_by} and - first { ! $_->[0]{-is_single} } (@{$inner_from}[1 .. $#$inner_from]) + first { + $inner_aliastypes->{restricting}{$_} + and + ! $inner_aliastypes->{selecting}{$_} + } ( keys %{$inner_aliastypes->{multiplying}||{}} ) ) { $inner_attrs->{group_by} = $self->_group_over_selection ( $inner_from, $inner_select, $inner_attrs->{order_by} @@ -247,7 +254,9 @@ sub _resolve_aliastypes_from_select_args { # set up a botched SQLA my $sql_maker = $self->sql_maker; - local $sql_maker->{having_bind}; # these are throw away results + # these are throw away results, do not pollute the bind stack + local $sql_maker->{having_bind}; + local $sql_maker->{select_bind}; # we can't scan properly without any quoting (\b doesn't cut it # everywhere), so unless there is proper quoting set - use our @@ -259,7 +268,9 @@ sub _resolve_aliastypes_from_select_args { local $sql_maker->{name_sep} = $sql_maker->{name_sep}; unless (defined $sql_maker->{quote_char} and length $sql_maker->{quote_char}) { - $sql_maker->{quote_char} = "\x00"; + $sql_maker->{quote_char} = ["\x00", "\xFF"]; + # if we don't unset it we screw up retarded but unfortunately working + # 'MAX(foo.bar)' => { '>', 3 } $sql_maker->{name_sep} = ''; } @@ -274,8 +285,8 @@ sub _resolve_aliastypes_from_select_args { }), ], selecting => [ - $self->_extract_order_columns ($attrs->{order_by}, $sql_maker), $sql_maker->_recurse_fields ($select), + ( map { $_->[0] } $self->_extract_order_criteria ($attrs->{order_by}, $sql_maker) ), ], }; @@ -334,6 +345,7 @@ sub _resolve_aliastypes_from_select_args { return $aliases_by_type; } +# This is the engine behind { distinct => 1 } sub _group_over_selection { my ($self, $from, $select, $order_by) = @_; @@ -354,11 +366,12 @@ sub _group_over_selection { # add any order_by parts that are not already present in the group_by # we need to be careful not to add any named functions/aggregates - # i.e. select => [ ... { count => 'foo', -as 'foocount' } ... ] - for my $chunk ($self->_extract_order_columns($order_by)) { + # i.e. order_by => [ ... { count => 'foo' } ... ] + for ($self->_extract_order_criteria($order_by)) { # only consider real columns (for functions the user got to do an explicit group_by) - my $colinfo = $rs_column_list->{$chunk} - or next; + next if @$_ != 1; + my $chunk = $_->[0]; + my $colinfo = $rs_column_list->{$chunk} or next; $chunk = "$colinfo->{-source_alias}.$chunk" if $chunk !~ /\./; push @group_by, $chunk unless $group_index{$chunk}++; @@ -411,7 +424,7 @@ sub _resolve_column_info { my ($self, $ident, $colnames) = @_; my ($alias2src, $root_alias) = $self->_resolve_ident_sources($ident); - my (%return, %seen_cols, @auto_colnames); + my (%seen_cols, @auto_colnames); # compile a global list of column names, to be able to properly # disambiguate unqualified column names (if at all possible) @@ -428,25 +441,23 @@ sub _resolve_column_info { grep { @{$seen_cols{$_}} == 1 } (keys %seen_cols), ]; - COLUMN: + my (%return, $colinfos); foreach my $col (@$colnames) { - my ($alias, $colname) = $col =~ m/^ (?: ([^\.]+) \. )? (.+) $/x; + my ($source_alias, $colname) = $col =~ m/^ (?: ([^\.]+) \. )? (.+) $/x; - unless ($alias) { - # see if the column was seen exactly once (so we know which rsrc it came from) - if ($seen_cols{$colname} and @{$seen_cols{$colname}} == 1) { - $alias = $seen_cols{$colname}[0]; - } - else { - next COLUMN; - } - } + # if the column was seen exactly once - we know which rsrc it came from + $source_alias ||= $seen_cols{$colname}[0] + if ($seen_cols{$colname} and @{$seen_cols{$colname}} == 1); - my $rsrc = $alias2src->{$alias}; - $return{$col} = $rsrc && { - %{$rsrc->column_info($colname)}, + next unless $source_alias; + + my $rsrc = $alias2src->{$source_alias} + or next; + + $return{$col} = { + %{ ( $colinfos->{$source_alias} ||= $rsrc->columns_info )->{$colname} }, -result_source => $rsrc, - -source_alias => $alias, + -source_alias => $source_alias, }; } @@ -588,7 +599,7 @@ sub _strip_cond_qualifiers { return $cond; } -sub _extract_order_columns { +sub _extract_order_criteria { my ($self, $order_by, $sql_maker) = @_; my $parser = sub { @@ -598,8 +609,9 @@ sub _extract_order_columns { unless wantarray; my @chunks; - for my $chunk (map { ref $_ ? @$_ : $_ } ($sql_maker->_order_by_chunks ($order_by) ) ) { - $chunk =~ s/\s+ (?: ASC|DESC ) \s* $//ix; + for ($sql_maker->_order_by_chunks ($order_by) ) { + my $chunk = ref $_ ? $_ : [ $_ ]; + $chunk->[0] =~ s/\s+ (?: ASC|DESC ) \s* $//ix; push @chunks, $chunk; }