MOAR optimization - tracking is_new_res is double work
[dbsrgits/DBIx-Class.git] / t / prefetch / manual.t
CommitLineData
69ab63d4 1use strict;
2use warnings;
3
4use Test::More;
52864fbd 5use Test::Deep;
69ab63d4 6use Test::Exception;
7use lib qw(t/lib);
8use DBICTest;
9
908aa1bb 10my $schema = DBICTest->init_schema(no_populate => 1);
11
742280c7 12$schema->resultset('Artist')->create({ name => 'JMJ', cds => [{
13 title => 'Magnetic Fields',
14 year => 1981,
15 genre => { name => 'electro' },
16 tracks => [
17 { title => 'm1' },
18 { title => 'm2' },
19 { title => 'm3' },
20 { title => 'm4' },
21 ],
22} ] });
23
908aa1bb 24$schema->resultset('CD')->create({
25 title => 'Equinoxe',
26 year => 1978,
27 artist => { name => 'JMJ' },
28 genre => { name => 'electro' },
29 tracks => [
30 { title => 'e1' },
31 { title => 'e2' },
32 { title => 'e3' },
33 ],
34 single_track => {
35 title => 'o1',
36 cd => {
37 title => 'Oxygene',
38 year => 1976,
742280c7 39 artist => { name => 'JMJ' },
908aa1bb 40 tracks => [
41 { title => 'o2', position => 2}, # the position should not be here, bug in MC
42 ],
43 },
44 },
45});
69ab63d4 46
47my $rs = $schema->resultset ('CD')->search ({}, {
48 join => [ 'tracks', { single_track => { cd => { artist => { cds => 'tracks' } } } } ],
49 collapse => 1,
50 columns => [
51 { 'year' => 'me.year' }, # non-unique
52 { 'genreid' => 'me.genreid' }, # nullable
53 { 'tracks.title' => 'tracks.title' }, # non-unique (no me.id)
54 { 'single_track.cd.artist.cds.cdid' => 'cds.cdid' }, # to give uniquiness to ...tracks.title below
3904d3c3 55 { 'single_track.cd.artist.artistid' => 'artist.artistid' }, # uniqufies entire parental chain
69ab63d4 56 { 'single_track.cd.artist.cds.year' => 'cds.year' }, # non-unique
57 { 'single_track.cd.artist.cds.genreid' => 'cds.genreid' }, # nullable
58 { 'single_track.cd.artist.cds.tracks.title' => 'tracks_2.title' }, # unique when combined with ...cds.cdid above
908aa1bb 59 { 'latest_cd' => \ "(SELECT MAX(year) FROM cd)" }, # random function
3904d3c3 60 { 'title' => 'me.title' }, # uniquiness for me
61 { 'artist' => 'me.artist' }, # uniquiness for me
69ab63d4 62 ],
908aa1bb 63 order_by => [{ -desc => 'cds.year' }, { -desc => 'me.title'} ],
69ab63d4 64});
65
908aa1bb 66my $hri_rs = $rs->search({}, { result_class => 'DBIx::Class::ResultClass::HashRefInflator' });
67
52864fbd 68cmp_deeply (
908aa1bb 69 [$hri_rs->all],
70 [
71 {
72 artist => 1,
73 genreid => 1,
74 latest_cd => 1981,
75 single_track => {
76 cd => {
77 artist => {
78 artistid => 1,
79 cds => [
80 {
81 cdid => 1,
82 genreid => 1,
83 tracks => [
84 {
85 title => "m1"
86 },
87 {
88 title => "m2"
89 },
90 {
91 title => "m3"
92 },
93 {
94 title => "m4"
95 }
96 ],
97 year => 1981
98 },
99 {
100 cdid => 3,
101 genreid => 1,
102 tracks => [
103 {
104 title => "e1"
105 },
106 {
107 title => "e2"
108 },
109 {
110 title => "e3"
111 }
112 ],
113 year => 1978
114 },
115 {
116 cdid => 2,
117 genreid => undef,
118 tracks => [
119 {
120 title => "o1"
121 },
122 {
123 title => "o2"
124 }
125 ],
126 year => 1976
127 }
128 ]
129 }
130 }
131 },
132 title => "Equinoxe",
133 tracks => [
134 {
135 title => "e1"
136 },
137 {
138 title => "e2"
139 },
140 {
141 title => "e3"
142 }
143 ],
144 year => 1978
145 },
146 {
147 artist => 1,
148 genreid => undef,
149 latest_cd => 1981,
150 single_track => undef,
151 title => "Oxygene",
152 tracks => [
153 {
154 title => "o1"
155 },
156 {
157 title => "o2"
158 }
159 ],
160 year => 1976
161 },
162 {
163 artist => 1,
164 genreid => 1,
165 latest_cd => 1981,
166 single_track => undef,
167 title => "Magnetic Fields",
168 tracks => [
169 {
170 title => "m1"
171 },
172 {
173 title => "m2"
174 },
175 {
176 title => "m3"
177 },
178 {
179 title => "m4"
180 }
181 ],
182 year => 1981
183 },
184 ],
185 'W00T, manual prefetch with collapse works'
186);
187
908aa1bb 188TODO: {
fcf32d04 189 my $row = $rs->next;
908aa1bb 190 local $TODO = 'Something is wrong with filter type rels, they throw on incomplete objects >.<';
191
192 lives_ok {
52864fbd 193 cmp_deeply (
908aa1bb 194 { $row->single_track->get_columns },
195 {},
196 'empty intermediate object ok',
197 )
198 } 'no exception';
199}
69ab63d4 200
908aa1bb 201is ($rs->cursor->next, undef, 'cursor exhausted');
69ab63d4 202
fcf32d04 203
4e9fc3f3 204TODO: {
205local $TODO = 'this does not work at all, need to promote rsattrs to an object on its own';
206# make sure has_many column redirection does not do weird stuff when collapse is requested
207for my $pref_args (
208 { prefetch => 'cds'},
209 { collapse => 1 }
210) {
211 for my $col_and_join_args (
212 { '+columns' => { 'cd_title' => 'cds_2.title' }, join => [ 'cds', 'cds' ] },
213 { '+columns' => { 'cd_title' => 'cds.title' }, join => 'cds', }
214 ) {
215
216 my $weird_rs = $schema->resultset('Artist')->search({}, {
217 %$col_and_join_args, %$pref_args,
218 });
219
220 for (qw/next all first/) {
221 throws_ok { $weird_rs->$_ } qr/not yet determined exception text/;
222 }
223 }
224}
225}
226
fcf32d04 227# multi-has_many with underdefined root, with rather random order
228$rs = $schema->resultset ('CD')->search ({}, {
229 join => [ 'tracks', { single_track => { cd => { artist => { cds => 'tracks' } } } } ],
230 collapse => 1,
231 columns => [
232 { 'single_track.trackid' => 'single_track.trackid' }, # definitive link to root from 1:1:1:1:M:M chain
233 { 'year' => 'me.year' }, # non-unique
234 { 'tracks.cd' => 'tracks.cd' }, # \ together both uniqueness for second multirel
235 { 'tracks.title' => 'tracks.title' }, # / and definitive link back to root
236 { 'single_track.cd.artist.cds.cdid' => 'cds.cdid' }, # to give uniquiness to ...tracks.title below
237 { 'single_track.cd.artist.cds.year' => 'cds.year' }, # non-unique
238 { 'single_track.cd.artist.artistid' => 'artist.artistid' }, # uniqufies entire parental chain
239 { 'single_track.cd.artist.cds.genreid' => 'cds.genreid' }, # nullable
240 { 'single_track.cd.artist.cds.tracks.title' => 'tracks_2.title' }, # unique when combined with ...cds.cdid above
241 ],
242});
243
244for (1..3) {
245 $rs->create({ artist => 1, year => 1977, title => "fuzzy_$_" });
246}
247
248my $rs_random = $rs->search({}, { order_by => \ 'RANDOM()' });
249is ($rs_random->count, 6, 'row count matches');
250
251if ($ENV{TEST_VERBOSE}) {
252 my @lines = (
253 [ "What are we actually trying to collapse (Select/As, tests below will see a *DIFFERENT* random order):" ],
254 [ map { my $s = $_; $s =~ s/single_track\./sngl_tr./; $s } @{$rs_random->{_attrs}{select} } ],
255 $rs_random->{_attrs}{as},
256 [ "-" x 159 ],
257 $rs_random->cursor->all,
258 );
259
260 diag join ' # ', map { sprintf '% 15s', (defined $_ ? $_ : 'NULL') } @$_
261 for @lines;
262}
263
264my $queries = 0;
265$schema->storage->debugcb(sub { $queries++ });
266my $orig_debug = $schema->storage->debug;
267$schema->storage->debug (1);
268
269for my $use_next (0, 1) {
270 my @random_cds;
271 if ($use_next) {
272 while (my $o = $rs_random->next) {
273 push @random_cds, $o;
274 }
275 }
276 else {
277 @random_cds = $rs_random->all;
278 }
279
280 is (@random_cds, 6, 'object count matches');
281
282 for my $cd (@random_cds) {
283 if ($cd->year == 1977) {
284 is( scalar $cd->tracks, 0, 'no tracks on 1977 cd' );
285 is( $cd->single_track, undef, 'no single_track on 1977 cd' );
286 }
287 elsif ($cd->year == 1976) {
288 is( scalar $cd->tracks, 2, 'Two tracks on 1976 cd' );
289 like( $_->title, qr/^o\d/, "correct title" )
290 for $cd->tracks;
291 is( $cd->single_track, undef, 'no single_track on 1976 cd' );
292 }
293 elsif ($cd->year == 1981) {
294 is( scalar $cd->tracks, 4, 'Four tracks on 1981 cd' );
295 like( $_->title, qr/^m\d/, "correct title" )
296 for $cd->tracks;
297 is( $cd->single_track, undef, 'no single_track on 1981 cd' );
298 }
299 elsif ($cd->year == 1978) {
300 is( scalar $cd->tracks, 3, 'Three tracks on 1978 cd' );
301 like( $_->title, qr/^e\d/, "correct title" )
302 for $cd->tracks;
303 ok( defined $cd->single_track, 'single track prefetched on 1987 cd' );
ce556881 304 is( $cd->single_track->cd->artist->id, 1, 'Single_track->cd->artist prefetched on 1978 cd' );
305 is( scalar $cd->single_track->cd->artist->cds, 6, '6 cds prefetched on artist' );
fcf32d04 306 }
307 }
308}
309
310$schema->storage->debugcb(undef);
311$schema->storage->debug($orig_debug);
312is ($queries, 2, "Only two queries for rwo prefetch calls total");
313
52864fbd 314# can't cmp_deeply a random set - need *some* order
aa1d8a87 315my $ord_rs = $rs->search({}, {
fcf32d04 316 order_by => [ 'tracks_2.title', 'tracks.title', 'cds.cdid', \ 'RANDOM()' ],
aa1d8a87 317 result_class => 'DBIx::Class::ResultClass::HashRefInflator',
318});
319my @hris_all = sort { $a->{year} cmp $b->{year} } $ord_rs->all;
320is (@hris_all, 6, 'hri count matches' );
321
322my $iter_rs = $rs->search({}, {
323 order_by => [ 'me.year', 'me.cdid', 'tracks_2.title', 'tracks.title', 'cds.cdid', \ 'RANDOM()' ],
324 result_class => 'DBIx::Class::ResultClass::HashRefInflator',
325});
326my @hris_iter;
327while (my $r = $iter_rs->next) {
328 push @hris_iter, $r;
329}
330
331cmp_deeply(
332 \@hris_iter,
333 \@hris_all,
334 'Iteration works correctly',
335);
fcf32d04 336
aa1d8a87 337cmp_deeply (\@hris_all, [
fcf32d04 338 {
339 single_track => undef,
340 tracks => [
341 {
342 cd => 2,
343 title => "o1"
344 },
345 {
346 cd => 2,
347 title => "o2"
348 }
349 ],
350 year => 1976
351 },
352 {
353 single_track => undef,
354 tracks => [],
355 year => 1977
356 },
357 {
358 single_track => undef,
359 tracks => [],
360 year => 1977
361 },
362 {
363 single_track => undef,
364 tracks => [],
365 year => 1977
366 },
367 {
368 single_track => {
369 cd => {
370 artist => {
371 artistid => 1,
372 cds => [
373 {
374 cdid => 4,
375 genreid => undef,
376 tracks => [],
377 year => 1977
378 },
379 {
380 cdid => 5,
381 genreid => undef,
382 tracks => [],
383 year => 1977
384 },
385 {
386 cdid => 6,
387 genreid => undef,
388 tracks => [],
389 year => 1977
390 },
391 {
392 cdid => 3,
393 genreid => 1,
394 tracks => [
395 {
396 title => "e1"
397 },
398 {
399 title => "e2"
400 },
401 {
402 title => "e3"
403 }
404 ],
405 year => 1978
406 },
407 {
408 cdid => 1,
409 genreid => 1,
410 tracks => [
411 {
412 title => "m1"
413 },
414 {
415 title => "m2"
416 },
417 {
418 title => "m3"
419 },
420 {
421 title => "m4"
422 }
423 ],
424 year => 1981
425 },
426 {
427 cdid => 2,
428 genreid => undef,
429 tracks => [
430 {
431 title => "o1"
432 },
433 {
434 title => "o2"
435 }
436 ],
437 year => 1976
438 }
439 ]
440 }
441 },
442 trackid => 6
443 },
444 tracks => [
445 {
446 cd => 3,
447 title => "e1"
448 },
449 {
450 cd => 3,
451 title => "e2"
452 },
453 {
454 cd => 3,
455 title => "e3"
456 },
457 ],
458 year => 1978
459 },
460 {
461 single_track => undef,
462 tracks => [
463 {
464 cd => 1,
465 title => "m1"
466 },
467 {
468 cd => 1,
469 title => "m2"
470 },
471 {
472 cd => 1,
473 title => "m3"
474 },
475 {
476 cd => 1,
477 title => "m4"
478 },
479 ],
480 year => 1981
481 },
482], 'W00T, multi-has_many manual underdefined root prefetch with collapse works');
483
aa1d8a87 484$rs = $rs->search({}, { order_by => [ 'me.year', 'me.cdid', \ 'RANDOM()' ] });
485my @objs_iter;
486while (my $r = $rs->next) {
487 push @objs_iter, $r;
488}
489
490for my $i (0 .. $#objs_iter) {
491 is ($objs_iter[$i]->year, $hris_all[$i]{year}, "Expected year on object $i" );
492 is (
493 (defined $objs_iter[$i]->single_track),
494 (defined $hris_all[$i]{single_track}),
495 "Expected single relation on object $i"
496 );
497}
498
499$rs = $schema->resultset('Artist')->search({}, {
500 join => 'cds',
501 columns => ['cds.title', 'cds.artist' ],
502 collapse => 1,
503 order_by => [qw( me.name cds.title )],
504});
505
506$rs->create({ name => "${_}_cdless" })
507 for (qw( Z A ));
508
509cmp_deeply (
510 $rs->all_hri,
511 [
512 { cds => [] },
513 { cds => [
514 { artist => 1, title => "Equinoxe" },
515 { artist => 1, title => "Magnetic Fields" },
516 { artist => 1, title => "Oxygene" },
517 { artist => 1, title => "fuzzy_1" },
518 { artist => 1, title => "fuzzy_2" },
519 { artist => 1, title => "fuzzy_3" },
520 ] },
521 { cds => [] },
522 ],
523 'Expected HRI of 1:M with empty root selection',
524);
525
908aa1bb 526done_testing;