[Dbix-class] Re: Bug in prefetch subqueries?

Rob Kinyon rob.kinyon at gmail.com
Thu Jul 30 13:22:14 GMT 2009


On Thu, Jul 30, 2009 at 08:17, Peter Rabbitson<rabbit+dbic at rabbit.us> wrote:
> And now we come to your semi-bug which reads:
> $f2b_artist_rs->search ({}, { prefetch => 'cd' })
>                ->first
>                  ->cds
>                    ->count
>
> The reason this returns 1, is because we execute only *one* query
> (the whole point of prefetch => ). The result of the query is a
> single row of artist/cd data, which is used to pre-populate all the
> slots for ->has_many_accessor. In order to have the above return 2
> we would have to re-query the database, effectively nullifying the
> effect of prefetch. Now, if you are hell-bent on making this
> correct and still maintain the prefetch == single query paradigm,
> you would have to alter the prefetch mechanism to detect if there
> are *any* right-side join conditions, and if this is the case do
> something like:

That's my point. The result of the query should be -two- rows of artist/cd data.

> SELECT artist.*, cd2.*
>  FROM artist artist
>  LEFT JOIN cd cd ON artist.id = cd.artistid
>  LEFT JOIN cd cd2 ON artist.id = cd2.artistid
> WHERE cd.name = ?

Or this:
SELECT artist.*, cd.*
FROM (
SELECT artist.*
FROM artist artist
LEFT JOIN cd cd ON artist.id = cd.artistid
WHERE cd.name = ?
) artist
LEFT JOIN cd cd ON artist.id = cd.artistid

> Effectively we pull an additional "shadow" join that will be
> related to the artist rs limited by the initial join, and which
> will be used for the actual object inflation. Of course in order to
> make this happen we need both introspectable SQLA, and sane multi-
> has-many collapse (actually the collapse may not be ncessary, as we
> do not select anything from the first join). And even if we did, I
> am still not sure if going to such great lengths is in fact
> justified. Prefetched rs == non-prefetched rs is a flawed concept
> imo, as it is not quite compatible with prefetch == one query only,
> which as far as I am concerned *is* the selling point of prefetch
> (db-trip latencies are the most significant bottleneck I have
> encountered during my work).

We don't need introspectable SQLA and multi-has-many collapse is an
orthogonal issue. We just need to stop conflating the prefetched
results with the main resultset.

And I didn't say "Prefetched rs == non-prefetched rs". I said that the
rows from a search should behave identically regardless of whether:
    * the rows in question came from a prefetched search or not
    * the relation being accessed was prefetched or not

And the current implementation of prefetch violates that. Consider the
following:

----------------
sub print_cds {
    my $artist = shift;

    return sprintf "%s", join( ", ", sort map { $_->name } $artist->cds ), $/;
}

my $f2b_rs = Artist->search({
    'cd.name' => 'Fade to Black'
});

my $cds = print_cds( $f2b_rs->first );

my $f2b_pref_rs = $f2b_rs->search( {}, { prefetch => 'cds' } );

my $pref_cds = print_cds( $f2b_pref_rs->first );

is( $cds, $pref_cds );
----------------

That test will currently fail and it shouldn't. Prefetch is purely an
optimization. Optimizations should never change behavior.

Rob



More information about the DBIx-Class mailing list