From: Peter Rabbitson Date: Sat, 25 Dec 2010 03:38:41 +0000 (+0100) Subject: Fail early on literal-ordered complex prefetch without explicit group_by clause X-Git-Tag: v0.08125~6 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=dbsrgits%2FDBIx-Class.git;a=commitdiff_plain;h=14e26c5f3516e39d9191ca9b2a9d460f8f495654 Fail early on literal-ordered complex prefetch without explicit group_by clause --- diff --git a/Changes b/Changes index 132c4e5..d14c03c 100644 --- a/Changes +++ b/Changes @@ -34,6 +34,9 @@ Revision history for DBIx::Class - Fix regressions in IC::DT registration logic - Fix regression in select-associated bind value handling (RT#61025) - Simplify SQL generated by some LIMITed prefetching queries + - Throw an exception when a required group_by on a complex prefetch + can not be auto-constructed, instead of continuing to eventually + produce invalid SQL - Fix infinite loops on old perls with a recent Try::Tiny - Improve "fork()" on Win32 by reimplementing a more robust DBIC thread support (still problematic, pending a DBI fix) diff --git a/lib/DBIx/Class/Storage/DBIHacks.pm b/lib/DBIx/Class/Storage/DBIHacks.pm index 41ca371..ba1faa4 100644 --- a/lib/DBIx/Class/Storage/DBIHacks.pm +++ b/lib/DBIx/Class/Storage/DBIHacks.pm @@ -129,9 +129,16 @@ sub _adjust_select_args_for_complex_prefetch { ! $inner_aliastypes->{selecting}{$_} } ( keys %{$inner_aliastypes->{multiplying}||{}} ) ) { - $inner_attrs->{group_by} = $self->_group_over_selection ( + my $unprocessed_order_chunks; + ($inner_attrs->{group_by}, $unprocessed_order_chunks) = $self->_group_over_selection ( $inner_from, $inner_select, $inner_attrs->{order_by} ); + + $self->throw_exception ( + 'A required group_by clause could not be constructed automatically due to a complex ' + . 'order_by criteria. Either order_by columns only (no functions) or construct a suitable ' + . 'group_by by hand' + ) if $unprocessed_order_chunks; } # we already optimized $inner_from above @@ -367,17 +374,27 @@ 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. order_by => [ ... { count => 'foo' } ... ] + my @leftovers; for ($self->_extract_order_criteria($order_by)) { # only consider real columns (for functions the user got to do an explicit group_by) - next if @$_ != 1; + if (@$_ != 1) { + push @leftovers, $_; + next; + } my $chunk = $_->[0]; - my $colinfo = $rs_column_list->{$chunk} or next; + my $colinfo = $rs_column_list->{$chunk} or do { + push @leftovers, $_; + next; + }; $chunk = "$colinfo->{-source_alias}.$chunk" if $chunk !~ /\./; push @group_by, $chunk unless $group_index{$chunk}++; } - return \@group_by; + return wantarray + ? (\@group_by, (@leftovers ? \@leftovers : undef) ) + : \@group_by + ; } sub _resolve_ident_sources { diff --git a/t/746mssql.t b/t/746mssql.t index 674046a..6584050 100644 --- a/t/746mssql.t +++ b/t/746mssql.t @@ -336,6 +336,7 @@ for my $dialect ( { prefetch => 'books', order_by => { -asc => \['name + ?', [ test => 'xxx' ]] }, # test bindvar propagation + group_by => [ map { "me.$_" } $schema->source('Owners')->columns ], # the literal order_by requires an explicit group_by rows => 3, # 8 results total unsafe_subselect_ok => 1, }, diff --git a/t/prefetch/with_limit.t b/t/prefetch/with_limit.t index bc035de..4acdcab 100644 --- a/t/prefetch/with_limit.t +++ b/t/prefetch/with_limit.t @@ -84,6 +84,15 @@ throws_ok ( 'single() with multiprefetch is illegal', ); +throws_ok ( + sub { + $use_prefetch->search( + {'tracks.title' => { '!=' => 'foo' }}, + { order_by => \ 'some oddball literal sql', join => { cds => 'tracks' } } + )->next + }, qr/A required group_by clause could not be constructed automatically/, +); + my $artist = $use_prefetch->search({'cds.title' => $artist_many_cds->cds->first->title })->next; is($artist->cds->count, 1, "count on search limiting prefetched has_many");