first (naive) cut at identifier remapping and join injection
Matt S Trout [Sun, 17 Nov 2013 11:03:24 +0000 (11:03 +0000)]
lib/DBIx/Class/ResultSet.pm
t/dq/join.t

index 91a5932..346eb39 100644 (file)
@@ -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}
             : []
index b6ca14b..099e672 100644 (file)
@@ -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;