=cut
-my @count_via_subq_attrs = qw/join seen_join group_by/;
+my @count_via_subq_attrs = qw/join seen_join prefetch group_by/;
sub count {
my $self = shift;
return $self->search(@_)->count if @_ and defined $_[0];
my $attrs = { %{$self->_resolved_attrs} };
- my $select_cols = $attrs->{group_by} || [ map { "$attrs->{alias}.$_" } ($self->result_source->primary_columns) ];
+ # copy for the subquery, we need to do some adjustments to it too
+ my $sub_attrs = { %$attrs };
+
+ # these can not go in the subquery either
+ delete $sub_attrs->{$_} for qw/prefetch select +select as +as columns +columns/;
+
+ # force a group_by and the same set of columns (most databases require this)
+ $sub_attrs->{columns} = $sub_attrs->{group_by} ||= [ map { "$attrs->{alias}.$_" } ($self->result_source->primary_columns) ];
+
$attrs->{from} = [{
- count_subq => $self->search ({}, { columns => $select_cols, group_by => $select_cols })
- ->as_query
+ count_subq => (ref $self)->new ($self->result_source, $sub_attrs )->as_query
}];
- # the subquery above will integrate everything, including 'where' and any pagers
- delete $attrs->{$_} for (@count_via_subq_attrs, qw/where rows offset pager page/ );
+ # the subquery replaces this
+ delete $attrs->{where};
return $self->__count ($attrs);
}
$attrs ||= { %{$self->{attrs}} };
+ # take off any subquery attrs (they'd be incorporated in the subquery),
+ # any column specs, any pagers, record_filter is cdbi, and no point of ordering a count
+ delete $attrs->{$_} for (@count_via_subq_attrs, qw/columns +columns select +select as +as rows offset page pager order_by record_filter/);
+
$attrs->{select} = { count => '*' };
$attrs->{as} = [qw/count/];
- # take off any pagers, record_filter is cdbi, and no point of ordering a count
- delete $attrs->{$_} for qw/rows offset page pager order_by record_filter/;
-
my $tmp_rs = (ref $self)->new($self->result_source, $attrs);
my ($count) = $tmp_rs->cursor->next;
sub pager {
my ($self) = @_;
+
+ return $self->{pager} if $self->{pager};
+
my $attrs = $self->{attrs};
$self->throw_exception("Can't create pager for non-paged rs")
unless $self->{attrs}{page};
# with a subselect) to get the real total count
my $count_attrs = { %$attrs };
delete $count_attrs->{$_} for qw/rows offset page pager/;
- my $total_count = (ref $self)->new($self->result_source, $count_attrs);
+ my $total_count = (ref $self)->new($self->result_source, $count_attrs)->count;
- return $self->{pager} ||= Data::Page->new(
- $total_count, $attrs->{rows}, $self->{attrs}{page});
+ return $self->{pager} = Data::Page->new(
+ $total_count,
+ $attrs->{rows},
+ $self->{attrs}{page}
+ );
}
=head2 page
"search_related: result source '" . $self->result_source->source_name .
"' has no such relationship $rel")
unless $rel_obj;
-
+
my ($from,$seen) = $self->_resolve_from($rel);
my $join_count = $seen->{$rel};
return ($self->{attrs} || {})->{alias} || 'me';
}
+# This code is called by search_related, and makes sure there
+# is clear separation between the joins before, during, and
+# after the relationship. This information is needed later
+# in order to properly resolve prefetch aliases (any alias
+# with a relation_chain_depth less than the depth of the
+# current prefetch is not considered)
sub _resolve_from {
my ($self, $extra_join) = @_;
my $source = $self->result_source;
my $attrs = $self->{attrs};
-
+
my $from = $attrs->{from}
|| [ { $attrs->{alias} => $source->from } ];
my $seen = { %{$attrs->{seen_join}||{}} };
- my $join = ($attrs->{join}
- ? [ $attrs->{join}, $extra_join ]
- : $extra_join);
-
# we need to take the prefetch the attrs into account before we
# ->resolve_join as otherwise they get lost - captainL
- my $merged = $self->_merge_attr( $join, $attrs->{prefetch} );
+ my $merged = $self->_merge_attr( $attrs->{join}, $attrs->{prefetch} );
+
+ push @$from, $source->resolve_join($merged, $attrs->{alias}, $seen) if ($merged);
+
+ ++$seen->{-relation_chain_depth};
- $from = [
- @$from,
- ($join ? $source->resolve_join($merged, $attrs->{alias}, $seen) : ()),
- ];
+ push @$from, $source->resolve_join($extra_join, $attrs->{alias}, $seen);
+
+ ++$seen->{-relation_chain_depth};
return ($from,$seen);
}
if ( my $prefetch = delete $attrs->{prefetch} ) {
$prefetch = $self->_merge_attr( {}, $prefetch );
my @pre_order;
- my $seen = { %{ $attrs->{seen_join} || {} } };
foreach my $p ( ref $prefetch eq 'ARRAY' ? @$prefetch : ($prefetch) ) {
# bring joins back to level of current class
+ my $join_map = $self->_joinpath_aliases ($attrs->{from}, $attrs->{seen_join});
my @prefetch =
- $source->resolve_prefetch( $p, $alias, $seen, \@pre_order, $collapse );
+ $source->resolve_prefetch( $p, $alias, $join_map, \@pre_order, $collapse );
push( @{ $attrs->{select} }, map { $_->[0] } @prefetch );
push( @{ $attrs->{as} }, map { $_->[1] } @prefetch );
}
}
$attrs->{collapse} = $collapse;
- if ( $attrs->{page} ) {
- $attrs->{offset} ||= 0;
- $attrs->{offset} += ( $attrs->{rows} * ( $attrs->{page} - 1 ) );
+ if ( $attrs->{page} and not defined $attrs->{offset} ) {
+ $attrs->{offset} = ( $attrs->{rows} * ( $attrs->{page} - 1 ) );
}
return $self->{_attrs} = $attrs;
}
+sub _joinpath_aliases {
+ my ($self, $fromspec, $seen) = @_;
+
+ my $paths = {};
+ return $paths unless ref $fromspec eq 'ARRAY';
+
+ for my $j (@$fromspec) {
+
+ next if ref $j ne 'ARRAY';
+ next if $j->[0]{-relation_chain_depth} < ( $seen->{-relation_chain_depth} || 0);
+
+ my $p = $paths;
+ $p = $p->{$_} ||= {} for @{$j->[0]{-join_path}};
+ push @{$p->{-join_aliases} }, $j->[0]{-join_alias};
+ }
+
+ return $paths;
+}
+
sub _rollout_attr {
my ($self, $attr) = @_;