From: Matt S Trout Date: Sun, 17 Nov 2013 11:03:24 +0000 (+0000) Subject: first (naive) cut at identifier remapping and join injection X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=4424330663ce24ad915f8fe9c36c6076f58eae8a;p=dbsrgits%2FDBIx-Class.git first (naive) cut at identifier remapping and join injection --- diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index 91a5932..346eb39 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -399,7 +399,14 @@ sub search_rs { } if (blessed($call_cond) and $call_cond->isa('Data::Query::ExprBuilder')) { - $call_cond = \$call_cond->{expr}; + my ($mapped_expr, $extra_join) + = $self->_remap_identifiers($call_cond->{expr}); + $call_cond = \$mapped_expr; + if (@$extra_join) { + $self->throw_exception("Can't handle join-requiring DQ expr when join attribute specified") + if $call_attrs->{join}; + $call_attrs->{join} = $extra_join; + } } # see if we can keep the cache (no $rs changes) @@ -493,6 +500,44 @@ sub search_rs { return $rs; } +sub _remap_identifiers { + my ($self, $dq) = @_; + my $map = {}; + my $attrs = $self->_resolved_attrs; + foreach my $j ( @{$attrs->{from}}[1 .. $#{$attrs->{from}} ] ) { + next unless $j->[0]{-alias}; + next unless $j->[0]{-join_path}; + my $p = $map; + $p = $p->{$_} ||= {} for map { keys %$_ } @{$j->[0]{-join_path}}; + $p->{''} = $j->[0]{-alias}; + } + + my $seen_join = { %{$attrs->{seen_join}||{}} }; + my $storage = $self->result_source->storage; + my @need_join; + my $mapped = map_dq_tree { + return $_ unless is_Identifier; + my @el = @{$_->{elements}}; + my $last = pop @el; + unless (@el) { + return Identifier($attrs->{alias}, $last); + } + my $p = $map; + $p = $p->{$_} ||= {} for @el; + if (my $alias = $p->{''}) { + return Identifier($alias, $last); + } + my $need = my $j = {}; + $j = $j->{$_} = {} for @el; + push @need_join, $need; + my $alias = $storage->relname_to_table_alias( + $el[-1], ++$seen_join->{$el[-1]} + ); + return Identifier($alias, $last); + } $dq; + return ($mapped, \@need_join); +} + my $dark_sel_dumper; sub _normalize_selection { my ($self, $attrs) = @_; @@ -3528,7 +3573,7 @@ sub _resolved_attrs { $source->_resolve_join( $join, $alias, - { %{ $attrs->{seen_join} || {} } }, + ($attrs->{seen_join} = { %{ $attrs->{seen_join} || {} } }), ( $attrs->{seen_join} && keys %{$attrs->{seen_join}}) ? $attrs->{from}[-1][0]{-join_path} : [] diff --git a/t/dq/join.t b/t/dq/join.t index b6ca14b..099e672 100644 --- a/t/dq/join.t +++ b/t/dq/join.t @@ -32,4 +32,14 @@ is($mccrae->cds2->count, 3, 'CDs returned from expr join'); is($mccrae->cds2_pre2k->count, 2, 'CDs returned from expr w/cond'); +my $cds = $schema->resultset('CD') + ->search(expr { $_->artist->name eq 'Caterwauler McCrae' }); + +is($cds->count, 3, 'CDs via join injection'); + +my $tags = $schema->resultset('Tag') + ->search(expr { $_->cd->artist->name eq 'Caterwauler McCrae' }); + +is($tags->count, 5, 'Tags via two step join injection'); + done_testing;