my $our_attrs = ($attrs->{_parent_attrs})
? { %{$attrs->{_parent_attrs}} }
: { %{$self->{attrs}} };
+ delete($attrs->{_parent_attrs}) if(exists $attrs->{_parent_attrs});
my $having = delete $our_attrs->{having};
- # XXX this is getting messy
+ # XXX should only maintain _live_join_stack and generate _live_join_h from that
if ($attrs->{_live_join_stack}) {
my $live_join = $attrs->{_live_join_stack};
foreach (reverse @{$live_join}) {
}
}
- # merge new attrs into old
+ # merge new attrs into inherited
foreach my $key (qw/join prefetch/) {
next unless (exists $attrs->{$key});
if ($attrs->{_live_join_stack} || $our_attrs->{_live_join_stack}) {
}
$our_attrs->{join} = $self->_merge_attr(
- $our_attrs->{join}, $attrs->{_live_join_h}, 1
+ $our_attrs->{join}, $attrs->{_live_join_h}
) if ($attrs->{_live_join_h});
if (defined $our_attrs->{prefetch}) {
$our_attrs->{join} = $self->_merge_attr(
- $our_attrs->{join}, $our_attrs->{prefetch}, 1
+ $our_attrs->{join}, $our_attrs->{prefetch}
);
}
my $rs = (ref $self)->new($self->result_source, $new_attrs);
$rs->{_parent_rs} = $self->{_parent_rs} if ($self->{_parent_rs});
- #XXX - hack to pass through parent of related resultsets
unless (@_) { # no search, effectively just a clone
my $rows = $self->get_cache;
$rs->set_cache($rows);
}
}
-
return $rs;
}
push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias}))
unless $seen{$p};
}
-
- # we're about to resolve_join on the current class, so we need to bring
- # the joins (which are from the original class) to the right level
- # XXX the below alg is ridiculous
- if ($attrs->{_live_join_stack}) {
- STACK:
- foreach (@{$attrs->{_live_join_stack}}) {
- if (ref $p eq 'HASH') {
- if (exists $p->{$_}) {
- $p = $p->{$_};
- } else {
- $p = undef;
- last STACK;
- }
- } elsif (ref $p eq 'ARRAY') {
- foreach my $pe (@{$p}) {
- if ($pe eq $_) {
- $p = undef;
- last STACK;
- }
- next unless(ref $pe eq 'HASH');
- next unless(exists $pe->{$_});
- $p = $pe->{$_};
- next STACK;
- }
- $p = undef;
- last STACK;
- } else {
- $p = undef;
- last STACK;
- }
- }
- }
-
+ # bring joins back to level of current class
+ $p = $self->_reduce_joins($p, $attrs) if ($attrs->{_live_join_stack});
if ($p) {
my @prefetch = $self->result_source->resolve_prefetch(
$p, $attrs->{alias}, {}, \@pre_order, $collapse
);
-
push(@{$attrs->{select}}, map { $_->[0] } @prefetch);
push(@{$attrs->{as}}, map { $_->[1] } @prefetch);
}
}
sub _merge_attr {
- my ($self, $a, $b, $is_prefetch) = @_;
+ my ($self, $a, $b) = @_;
return $b unless $a;
if (ref $b eq 'HASH' && ref $a eq 'HASH') {
foreach my $key (keys %{$b}) {
if (exists $a->{$key}) {
- $a->{$key} = $self->_merge_attr($a->{$key}, $b->{$key}, $is_prefetch);
+ $a->{$key} = $self->_merge_attr($a->{$key}, $b->{$key});
} else {
$a->{$key} = $b->{$key};
}
foreach ($a, $b) {
foreach my $element (@{$_}) {
if (ref $element eq 'HASH') {
- $hash = $self->_merge_attr($hash, $element, $is_prefetch);
+ $hash = $self->_merge_attr($hash, $element);
} elsif (ref $element eq 'ARRAY') {
$array = [@{$array}, @{$element}];
} else {
- if (($b == $_) && $is_prefetch) {
- $self->_merge_array($array, $element, $is_prefetch);
+ if ($b == $_) {
+ $self->_merge_array($array, $element);
} else {
push(@{$array}, $element);
}
}
}
+# bring the joins (which are from the original class) to the level
+# of the current class so that we can resolve them properly
+sub _reduce_joins {
+ my ($self, $p, $attrs) = @_;
+
+ STACK:
+ foreach (@{$attrs->{_live_join_stack}}) {
+ if (ref $p eq 'HASH') {
+ if (exists $p->{$_}) {
+ $p = $p->{$_};
+ } else {
+ return undef;
+ }
+ } elsif (ref $p eq 'ARRAY') {
+ foreach my $pe (@{$p}) {
+ if ($pe eq $_) {
+ return undef;
+ }
+ if ((ref $pe eq 'HASH') && (exists $pe->{$_})) {
+ $p = $pe->{$_};
+ next STACK;
+ }
+ }
+ return undef;
+ } else {
+ return undef;
+ }
+ }
+ return $p;
+}
+
sub _construct_object {
my ($self, @row) = @_;
my @as = @{ $self->{_attrs}{as} };
use Test::More;
use lib qw(t/lib);
use DBICTest;
-
+use Data::Dumper;
my $schema = DBICTest->init_schema();
-plan tests => 14;
+plan tests => 17;
my @rs1a_results = $schema->resultset("Artist")->search_related('cds', {title => 'Forkful of bees'}, {order_by => 'title'});
is($rs1a_results[0]->title, 'Forkful of bees', "bare field conditions okay after search related");
ok($prod_map_rs->next->producer, 'search related with prefetch okay');
my $stupid = $schema->resultset("Artist")->search_related('artist_undirected_maps', {}, { prefetch => 'artist1' })->search_related('mapped_artists')->search_related('cds', {'cds.cdid' => '2'}, { prefetch => 'tracks' });
-#use Data::Dumper; warn Dumper($stupid->{attrs});
my $cd_final = $schema->resultset("Artist")->search_related('artist_undirected_maps', {}, { prefetch => 'artist1' })->search_related('mapped_artists')->search_related('cds', {'cds.cdid' => '2'}, { prefetch => 'tracks' })->first;
is($cd_final->cdid, '2', 'bonkers search_related-with-join-midway okay');
+# should end up with cds and cds_2 joined
+my $merge_rs_1 = $schema->resultset("Artist")->search({ 'cds_2.cdid' => '2' }, { join => ['cds', 'cds'] });
+is(scalar(@{$merge_rs_1->{attrs}->{join}}), 2, 'both joins kept');
+ok($merge_rs_1->next, 'query on double joined rel runs okay');
+
+# should only end up with cds joined
+my $merge_rs_2 = $schema->resultset("Artist")->search({ }, { join => 'cds' })->search({ 'cds.cdid' => '2' }, { join => 'cds' });
+is(scalar(@{$merge_rs_2->{attrs}->{join}}), 1, 'only one join kept when inherited');
+my $merge_rs_2_cd = $merge_rs_2->next;
+
1;