From: Peter Rabbitson Date: Wed, 27 Oct 2010 10:18:41 +0000 (+0200) Subject: Add undocumented ability to disable the join optimizer X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=ea95892eb6a71366db32b04137c7f2ee3b4ef841;p=dbsrgits%2FDBIx-Class-Historic.git Add undocumented ability to disable the join optimizer The capability system in its current form is insufficient - pending a redesign/rewrite. In the meantime remove mention of it from Changes and add an extra cap to disable the join-optimizer on request (prodded by timbunce :) --- diff --git a/Changes b/Changes index ea01590..78a2a38 100644 --- a/Changes +++ b/Changes @@ -78,8 +78,6 @@ Revision history for DBIx::Class (first trigger instead of trigger for column) * Misc - - Refactored capability handling in Storage::DBI, allows for - standardized capability handling wrt query/enable/disable - Entire test suite now passes under DBIC_TRACE=1 - Makefile.PL no longer imports GetOptions() to interoperate better with Catalyst installers diff --git a/lib/DBIx/Class/Storage/DBI.pm b/lib/DBIx/Class/Storage/DBI.pm index 29fc1bf..d07bb76 100644 --- a/lib/DBIx/Class/Storage/DBI.pm +++ b/lib/DBIx/Class/Storage/DBI.pm @@ -54,10 +54,13 @@ __PACKAGE__->mk_group_accessors('simple' => @storage_options); # will get the same rdbms version). _determine_supports_X does not need to # exist on a driver, as we ->can for it before calling. -my @capabilities = (qw/insert_returning placeholders typeless_placeholders/); +my @capabilities = (qw/insert_returning placeholders typeless_placeholders join_optimizer/); __PACKAGE__->mk_group_accessors( dbms_capability => map { "_supports_$_" } @capabilities ); -__PACKAGE__->mk_group_accessors( use_dbms_capability => map { "_use_$_" } @capabilities ); +__PACKAGE__->mk_group_accessors( use_dbms_capability => map { "_use_$_" } (@capabilities ) ); +# on by default, not strictly a capability (pending rewrite) +__PACKAGE__->_use_join_optimizer (1); +sub _determine_supports_join_optimizer { 1 }; # Each of these methods need _determine_driver called before itself # in order to function reliably. This is a purely DRY optimization diff --git a/lib/DBIx/Class/Storage/DBIHacks.pm b/lib/DBIx/Class/Storage/DBIHacks.pm index 5256700..240fa3c 100644 --- a/lib/DBIx/Class/Storage/DBIHacks.pm +++ b/lib/DBIx/Class/Storage/DBIHacks.pm @@ -23,10 +23,11 @@ use namespace::clean; # {from} specs, aiding the RDBMS query optimizer # sub _prune_unused_joins { - my ($self) = shift; - + my $self = shift; my ($from, $select, $where, $attrs) = @_; + return $from unless $self->_use_join_optimizer; + if (ref $from ne 'ARRAY' || ref $from->[0] ne 'HASH' || ref $from->[1] ne 'ARRAY') { return $from; # only standard {from} specs are supported } @@ -100,31 +101,41 @@ sub _adjust_select_args_for_complex_prefetch { push @{$inner_attrs->{as}}, $attrs->{as}[$i]; } - # construct the inner $from for the subquery + # construct the inner $from and lock it in a subquery # we need to prune first, because this will determine if we need a group_by below # the fake group_by is so that the pruner throws away all non-selecting, non-restricting # multijoins (since we def. do not care about those inside the subquery) - my $inner_from = $self->_prune_unused_joins ($from, $inner_select, $where, { - 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 - $inner_attrs->{group_by} ||= $inner_select - if first { ! $_->[0]{-is_single} } (@{$inner_from}[1 .. $#$inner_from]); - - # generate the subquery - my $subq = $self->_select_args_to_query ( - $inner_from, - $inner_select, - $where, - $inner_attrs, - ); - my $subq_joinspec = { - -alias => $attrs->{alias}, - -source_handle => $inner_from->[0]{-source_handle}, - $attrs->{alias} => $subq, + my $subq_joinspec = do { + + # must use it here regardless of user requests + local $self->{_use_join_optimizer} = 1; + + my $inner_from = $self->_prune_unused_joins ($from, $inner_select, $where, { + 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 + $inner_attrs->{group_by} ||= $inner_select + if first { ! $_->[0]{-is_single} } (@{$inner_from}[1 .. $#$inner_from]); + + # we already optimized $inner_from above + local $self->{_use_join_optimizer} = 0; + + # generate the subquery + my $subq = $self->_select_args_to_query ( + $inner_from, + $inner_select, + $where, + $inner_attrs, + ); + + +{ + -alias => $attrs->{alias}, + -source_handle => $inner_from->[0]{-source_handle}, + $attrs->{alias} => $subq, + }; }; # Generate the outer from - this is relatively easy (really just replace @@ -156,7 +167,7 @@ sub _adjust_select_args_for_complex_prefetch { } } - # scan the from spec against different attributes, and see which joins are needed + # scan the *remaining* from spec against different attributes, and see which joins are needed # in what role my $outer_aliastypes = $self->_resolve_aliastypes_from_select_args( $from, $outer_select, $where, $outer_attrs ); @@ -304,10 +315,10 @@ sub _resolve_aliastypes_from_select_args { ); } - # mark all join parents as mentioned + # mark all restricting/selecting join parents as such # (e.g. join => { cds => 'tracks' } - tracks will need to bring cds too ) - for my $type (keys %$aliases_by_type) { - for my $alias (keys %{$aliases_by_type->{$type}}) { + for my $type (qw/restricting selecting/) { + for my $alias (keys %{$aliases_by_type->{$type}||{}}) { $aliases_by_type->{$type}{$_} = 1 for (map { values %$_ } @{ $alias_list->{$alias}{-join_path} || [] }); }