Improve complex order+prefetch exception message
[dbsrgits/DBIx-Class.git] / t / prefetch / with_limit.t
CommitLineData
a04af85f 1# Test to ensure we get a consistent result set wether or not we use the
2# prefetch option in combination rows (LIMIT).
3use strict;
4use warnings;
5
6use Test::More;
61fd5dfc 7use Test::Exception;
a04af85f 8use lib qw(t/lib);
9use DBICTest;
53c29913 10use DBIC::SqlMakerTest;
fcb7fcbb 11use DBIx::Class::SQLMaker::LimitDialects;
12
13my $ROWS = DBIx::Class::SQLMaker::LimitDialects->__rows_bindtype;
a04af85f 14
a04af85f 15my $schema = DBICTest->init_schema();
51a296b4 16
17
a04af85f 18my $no_prefetch = $schema->resultset('Artist')->search(
51a296b4 19 [ # search deliberately contrived
20 { 'artwork.cd_id' => undef },
21 { 'tracks.title' => { '!=' => 'blah-blah-1234568' }}
22 ],
96faafb4 23 { rows => 3, join => { cds => [qw/artwork tracks/] },
24 }
25);
26
27my $use_prefetch = $no_prefetch->search(
28 {},
25cac750 29 {
c9d29bb2 30 select => ['me.artistid', 'me.name'],
31 as => ['artistid', 'name'],
25cac750 32 prefetch => 'cds',
51a296b4 33 order_by => { -desc => 'name' },
25cac750 34 }
a04af85f 35);
36
f7f53a89 37# add an extra +select to make sure it does not throw things off
36fd7f07 38# we also expect it to appear in both selectors, as we can not know
39# for sure which part of the query it applies to (may be order_by,
40# maybe something else)
41#
42# we use a reference to the same array in bind vals, because
43# is_deeply picks up this difference too (not sure if bug or
44# feature)
36fd7f07 45$use_prefetch = $use_prefetch->search({}, {
f7f53a89 46 '+columns' => { monkeywrench => \[ 'me.artistid + ?', [ \ 'inTEger' => 1 ] ] },
36fd7f07 47});
48
0e773352 49my $bind_int_resolved = sub { [ { sqlt_datatype => 'inTEger' } => 1 ] };
50my $bind_vc_resolved = sub { [
51 { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'tracks.title' }
52 => 'blah-blah-1234568'
53] };
36fd7f07 54is_same_sql_bind (
55 $use_prefetch->as_query,
56 '(
f7f53a89 57 SELECT me.artistid + ?,
58 me.artistid, me.name,
59 cds.cdid, cds.artist, cds.title, cds.year, cds.genreid, cds.single_track
36fd7f07 60 FROM (
f7f53a89 61 SELECT me.artistid + ?,
62 me.artistid, me.name
36fd7f07 63 FROM artist me
64 LEFT JOIN cd cds
65 ON cds.artist = me.artistid
66 LEFT JOIN cd_artwork artwork
67 ON artwork.cd_id = cds.cdid
68 LEFT JOIN track tracks
69 ON tracks.cd = cds.cdid
70 WHERE artwork.cd_id IS NULL
71 OR tracks.title != ?
f7f53a89 72 GROUP BY me.artistid + ?, me.artistid, me.name
fcb7fcbb 73 ORDER BY name DESC LIMIT ?
36fd7f07 74 ) me
75 LEFT JOIN cd cds
76 ON cds.artist = me.artistid
77 LEFT JOIN cd_artwork artwork
78 ON artwork.cd_id = cds.cdid
79 LEFT JOIN track tracks
80 ON tracks.cd = cds.cdid
81 WHERE artwork.cd_id IS NULL
82 OR tracks.title != ?
908aa1bb 83 ORDER BY name DESC
36fd7f07 84 )',
85 [
0e773352 86 $bind_int_resolved->(), # outer select
87 $bind_int_resolved->(), # inner select
88 $bind_vc_resolved->(), # inner where
89 $bind_int_resolved->(), # inner group_by
fcb7fcbb 90 [ $ROWS => 3 ],
0e773352 91 $bind_vc_resolved->(), # outer where
36fd7f07 92 ],
93 'Expected SQL on complex limited prefetch'
94);
95
a04af85f 96is($no_prefetch->count, $use_prefetch->count, '$no_prefetch->count == $use_prefetch->count');
5624ba1f 97is(
9117ccfb 98 scalar ($no_prefetch->all),
99 scalar ($use_prefetch->all),
100 "Amount of returned rows is right"
5624ba1f 101);
102
9117ccfb 103my $artist_many_cds = $schema->resultset('Artist')->search ( {}, {
104 join => 'cds',
105 group_by => 'me.artistid',
106 having => \ 'count(cds.cdid) > 1',
107})->first;
108
109
5624ba1f 110$no_prefetch = $schema->resultset('Artist')->search(
9117ccfb 111 { artistid => $artist_many_cds->id },
5624ba1f 112 { rows => 1 }
113);
114
61fd5dfc 115$use_prefetch = $no_prefetch->search ({}, { prefetch => 'cds' });
5624ba1f 116
61fd5dfc 117my $normal_artist = $no_prefetch->single;
118my $prefetch_artist = $use_prefetch->find({ name => $artist_many_cds->name });
119my $prefetch2_artist = $use_prefetch->first;
5624ba1f 120
121is(
122 $prefetch_artist->cds->count,
123 $normal_artist->cds->count,
61fd5dfc 124 "Count of child rel with prefetch + rows => 1 is right (find)"
125);
126is(
127 $prefetch2_artist->cds->count,
128 $normal_artist->cds->count,
129 "Count of child rel with prefetch + rows => 1 is right (first)"
5624ba1f 130);
61fd5dfc 131
9117ccfb 132is (
133 scalar ($prefetch_artist->cds->all),
134 scalar ($normal_artist->cds->all),
61fd5dfc 135 "Amount of child rel rows with prefetch + rows => 1 is right (find)"
136);
137is (
138 scalar ($prefetch2_artist->cds->all),
139 scalar ($normal_artist->cds->all),
140 "Amount of child rel rows with prefetch + rows => 1 is right (first)"
141);
142
143throws_ok (
144 sub { $use_prefetch->single },
5b309063 145 qr/\Qsingle() can not be used on resultsets collapsing a has_many/,
61fd5dfc 146 'single() with multiprefetch is illegal',
9117ccfb 147);
01c781fe 148
14e26c5f 149throws_ok (
150 sub {
151 $use_prefetch->search(
152 {'tracks.title' => { '!=' => 'foo' }},
153 { order_by => \ 'some oddball literal sql', join => { cds => 'tracks' } }
154 )->next
9736be65 155 }, qr/Unable to programatically derive a required group_by from the supplied order_by criteria/,
156);
14e26c5f 157
09707a31 158my $artist = $use_prefetch->search({'cds.title' => $artist_many_cds->cds->first->title })->next;
01c781fe 159is($artist->cds->count, 1, "count on search limiting prefetched has_many");
160
a3683eae 161# try with double limit
162my $artist2 = $use_prefetch->search({'cds.title' => { '!=' => $artist_many_cds->cds->first->title } })->slice (0,0)->next;
163is($artist2->cds->count, 2, "count on search limiting prefetched has_many");
164
53c29913 165# make sure 1:1 joins do not force a subquery (no point to exercise the optimizer, if at all available)
166# get cd's that have any tracks and their artists
167my $single_prefetch_rs = $schema->resultset ('CD')->search (
168 { 'me.year' => 2010, 'artist.name' => 'foo' },
169 { prefetch => ['tracks', 'artist'], rows => 15 },
170);
171is_same_sql_bind (
172 $single_prefetch_rs->as_query,
173 '(
174 SELECT
175 me.cdid, me.artist, me.title, me.year, me.genreid, me.single_track,
3d98c75e 176 tracks.trackid, tracks.cd, tracks.position, tracks.title, tracks.last_updated_on, tracks.last_updated_at,
53c29913 177 artist.artistid, artist.name, artist.rank, artist.charfield
178 FROM (
179 SELECT
180 me.cdid, me.artist, me.title, me.year, me.genreid, me.single_track
181 FROM cd me
182 JOIN artist artist ON artist.artistid = me.artist
183 WHERE ( ( artist.name = ? AND me.year = ? ) )
fcb7fcbb 184 LIMIT ?
53c29913 185 ) me
186 LEFT JOIN track tracks
187 ON tracks.cd = me.cdid
188 JOIN artist artist
189 ON artist.artistid = me.artist
190 WHERE ( ( artist.name = ? AND me.year = ? ) )
53c29913 191 )',
fcb7fcbb 192 [
193 [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'artist.name' } => 'foo' ],
194 [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'me.year' } => 2010 ],
195 [ $ROWS => 15 ],
196 [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'artist.name' } => 'foo' ],
197 [ { sqlt_datatype => 'varchar', sqlt_size => 100, dbic_colname => 'me.year' } => 2010 ],
198 ],
53c29913 199 'No grouping of non-multiplying resultsets',
200);
201
eb58c082 202my $many_one_many_rs = $schema->resultset('CD')->search({}, {
203 prefetch => { tracks => { lyrics => 'lyric_versions' } },
204 rows => 2,
205 order_by => ['lyrics.track_id'],
206});
207
208is_same_sql_bind(
209 $many_one_many_rs->as_query,
210 '(
211 SELECT me.cdid, me.artist, me.title, me.year, me.genreid, me.single_track,
212 tracks.trackid, tracks.cd, tracks.position, tracks.title, tracks.last_updated_on, tracks.last_updated_at,
213 lyrics.lyric_id, lyrics.track_id, lyric_versions.id, lyric_versions.lyric_id, lyric_versions.text
214 FROM (
215 SELECT me.cdid, me.artist, me.title, me.year, me.genreid, me.single_track
216 FROM cd me
217 LEFT JOIN track tracks
218 ON tracks.cd = me.cdid
219 LEFT JOIN lyrics lyrics
220 ON lyrics.track_id = tracks.trackid
221 GROUP BY me.cdid, me.artist, me.title, me.year, me.genreid, me.single_track
222 ORDER BY MIN(lyrics.track_id)
223 LIMIT ?
224 ) me
225 LEFT JOIN track tracks
226 ON tracks.cd = me.cdid
227 LEFT JOIN lyrics lyrics
228 ON lyrics.track_id = tracks.trackid
229 LEFT JOIN lyric_versions lyric_versions
230 ON lyric_versions.lyric_id = lyrics.lyric_id
231 ORDER BY lyrics.track_id
232 )',
233 [
234 [ { sqlt_datatype => 'integer' } => 2 ]
235 ],
236 'Correct SQL on indirectly multiplied orderer',
237);
238
239my $cond_on_multi_ord_by_single = $schema->resultset('CD')->search(
240 {
241 'tracks.position' => { '!=', 1 },
242 },
243 {
244 prefetch => [qw( tracks artist )],
245 order_by => 'artist.name',
246 rows => 1,
247 },
248);
249
250is_same_sql_bind(
251 $cond_on_multi_ord_by_single->as_query,
252 '(
253 SELECT me.cdid, me.artist, me.title, me.year, me.genreid, me.single_track,
254 tracks.trackid, tracks.cd, tracks.position, tracks.title, tracks.last_updated_on, tracks.last_updated_at,
255 artist.artistid, artist.name, artist.rank, artist.charfield
256 FROM (
257 SELECT me.cdid, me.artist, me.title, me.year, me.genreid, me.single_track
258 FROM cd me
259 LEFT JOIN track tracks
260 ON tracks.cd = me.cdid
261 JOIN artist artist
262 ON artist.artistid = me.artist
263 WHERE tracks.position != ?
264 GROUP BY me.cdid, me.artist, me.title, me.year, me.genreid, me.single_track, artist.name
265 ORDER BY artist.name
266 LIMIT ?
267 ) me
268 LEFT JOIN track tracks
269 ON tracks.cd = me.cdid
270 JOIN artist artist
271 ON artist.artistid = me.artist
272 WHERE tracks.position != ?
273 ORDER BY artist.name
274 )',
275 [
276 [ { dbic_colname => "tracks.position", sqlt_datatype => "int" }
277 => 1
278 ],
279 [ { sqlt_datatype => "integer" }
280 => 1
281 ],
282 [ { dbic_colname => "tracks.position", sqlt_datatype => "int" }
283 => 1
284 ],
285 ],
286 'Correct SQl on prefetch with limit of restricting multi ordered by a single'
287);
288
c9d29bb2 289done_testing;