- Fixed HRI returning too many empty results on multilevel
nonexisting prefetch
- Fixed the prefetch with limit bug
+ - New resultsed method count_rs, returns a ::ResultSetColumn
+ returning a single count value
0.08107 2009-06-14 08:21:00 (UTC)
- Fix serialization regression introduced in 0.08103 (affects
$self->throw_exception ('Prefetch with limit (rows/offset) is not supported on resultsets with a custom from attribute')
if (ref $from ne 'ARRAY');
+
# separate attributes
my $sub_attrs = { %$attrs };
delete $attrs->{$_} for qw/where bind rows offset/;
];
}
+ # mangle {from}
+ $from = [ @$from ];
+ my $select_root = shift @$from;
+ my @outer_from = @$from;
- # mangle the head of the {from}
- my $self_ident = shift @$from;
-
+ my %inner_joins;
my %join_info = map { $_->[0]{-alias} => $_->[0] } (@$from);
- my (%inner_joins);
+ # in complex search_related chains $alias may *not* be 'me'
+ # so always include it in the inner join, and also shift away
+ # from the outer stack, so that the two datasets actually do
+ # meet
+ if ($select_root->{-alias} ne $alias) {
+ $inner_joins{$alias} = 1;
+
+ while (@outer_from && $outer_from[0][0]{-alias} ne $alias) {
+ shift @outer_from;
+ }
+ if (! @outer_from) {
+ $self->throw_exception ("Unable to find '$alias' in the {from} stack, something is wrong");
+ }
+
+ shift @outer_from; # the new subquery will represent this alias, so get rid of it
+ }
+
# decide which parts of the join will remain on the inside
#
}
# construct the inner $from for the subquery
- my $inner_from = [ $self_ident ];
- if (keys %inner_joins) {
- for my $j (@$from) {
- push @$inner_from, $j if $inner_joins{$j->[0]{-alias}};
- }
+ my $inner_from = [ $select_root ];
+ for my $j (@$from) {
+ push @$inner_from, $j if $inner_joins{$j->[0]{-alias}};
+ }
- # if a multi-type join was needed in the subquery ("multi" is indicated by
- # presence in {collapse}) - add a group_by to simulate the collapse in the subq
- for my $alias (keys %inner_joins) {
+ # if a multi-type join was needed in the subquery ("multi" is indicated by
+ # presence in {collapse}) - add a group_by to simulate the collapse in the subq
- # the dot comes from some weirdness in collapse
- # remove after the rewrite
- if ($attrs->{collapse}{".$alias"}) {
- $sub_attrs->{group_by} = $sub_select;
- last;
- }
+ for my $alias (keys %inner_joins) {
+
+ # the dot comes from some weirdness in collapse
+ # remove after the rewrite
+ if ($attrs->{collapse}{".$alias"}) {
+ $sub_attrs->{group_by} = $sub_select;
+ last;
}
}
$sub_attrs
);
- # put it back in $from
- unshift @$from, { $alias => $subq };
+ # put it in the new {from}
+ unshift @outer_from, { $alias => $subq };
# This is totally horrific - the $where ends up in both the inner and outer query
# Unfortunately not much can be done until SQLA2 introspection arrives
#
# OTOH it can be seen as a plus: <ash> (notes that this query would make a DBA cry ;)
- return ($from, $select, $where, $attrs);
+ return (\@outer_from, $select, $where, $attrs);
}
sub _resolve_ident_sources {
--- /dev/null
+use strict;
+use warnings;
+
+use lib qw(t/lib);
+
+use Test::More;
+use DBICTest;
+use DBIC::SqlMakerTest;
+use DBIC::DebugObj;
+
+plan tests => 10;
+
+my $schema = DBICTest->init_schema();
+
+# non-collapsing prefetch (no multi prefetches)
+{
+ my $rs = $schema->resultset("CD")
+ ->search_related('tracks',
+ { position => [1,2] },
+ { prefetch => [qw/disc lyrics/], rows => 3, offset => 8 },
+ );
+ is ($rs->all, 2, 'Correct number of objects');
+
+
+ my ($sql, @bind);
+ $schema->storage->debugobj(DBIC::DebugObj->new(\$sql, \@bind));
+ $schema->storage->debug(1);
+
+ is ($rs->count, 2, 'Correct count via count()');
+
+ is_same_sql_bind (
+ $sql,
+ \@bind,
+ 'SELECT COUNT( * )
+ FROM cd me
+ LEFT JOIN track tracks ON tracks.cd = me.cdid
+ JOIN cd disc ON disc.cdid = tracks.cd
+ LEFT JOIN lyrics lyrics ON lyrics.track_id = tracks.trackid
+ WHERE ( ( position = ? OR position = ? ) )
+ ',
+ [ qw/'1' '2'/ ],
+ 'count softlimit applied',
+ );
+
+ my $crs = $rs->count_rs;
+ is ($crs->next, 2, 'Correct count via count_rs()');
+
+ is_same_sql_bind (
+ $crs->as_query,
+ '(SELECT COUNT( * )
+ FROM (
+ SELECT tracks.trackid
+ FROM cd me
+ LEFT JOIN track tracks ON tracks.cd = me.cdid
+ JOIN cd disc ON disc.cdid = tracks.cd
+ LEFT JOIN lyrics lyrics ON lyrics.track_id = tracks.trackid
+ WHERE ( ( position = ? OR position = ? ) )
+ LIMIT 3 OFFSET 8
+ ) count_subq
+ )',
+ [ [ position => 1 ], [ position => 2 ] ],
+ 'count_rs db-side limit applied',
+ );
+}
+
+# has_many prefetch with limit
+{
+ my $rs = $schema->resultset("Artist")
+ ->search_related('cds',
+ { 'tracks.position' => [1,2] },
+ { prefetch => [qw/tracks artist/], rows => 3, offset => 4 },
+ );
+ is ($rs->all, 1, 'Correct number of objects');
+
+ my ($sql, @bind);
+ $schema->storage->debugobj(DBIC::DebugObj->new(\$sql, \@bind));
+ $schema->storage->debug(1);
+
+ is ($rs->count, 1, 'Correct count via count()');
+
+ is_same_sql_bind (
+ $sql,
+ \@bind,
+ 'SELECT COUNT( * )
+ FROM (
+ SELECT cds.cdid
+ FROM artist me
+ LEFT JOIN cd cds ON cds.artist = me.artistid
+ LEFT JOIN track tracks ON tracks.cd = cds.cdid
+ JOIN artist artist ON artist.artistid = cds.artist
+ WHERE tracks.position = ? OR tracks.position = ?
+ GROUP BY cds.cdid
+ ) count_subq
+ ',
+ [ qw/'1' '2'/ ],
+ 'count softlimit applied',
+ );
+
+ my $crs = $rs->count_rs;
+ is ($crs->next, 1, 'Correct count via count_rs()');
+
+ is_same_sql_bind (
+ $crs->as_query,
+ '(SELECT COUNT( * )
+ FROM (
+ SELECT cds.cdid
+ FROM artist me
+ LEFT JOIN cd cds ON cds.artist = me.artistid
+ LEFT JOIN track tracks ON tracks.cd = cds.cdid
+ JOIN artist artist ON artist.artistid = cds.artist
+ WHERE tracks.position = ? OR tracks.position = ?
+ GROUP BY cds.cdid
+ LIMIT 3 OFFSET 4
+ ) count_subq
+ )',
+ [ [ 'tracks.position' => 1 ], [ 'tracks.position' => 2 ] ],
+ 'count_rs db-side limit applied',
+ );
+}