Move the infmap verification/exception way earlier
[dbsrgits/DBIx-Class.git] / t / resultset / rowparser_internals.t
CommitLineData
4e9fc3f3 1use strict;
2use warnings;
3
4use Test::More;
5use lib qw(t/lib);
6use DBICTest;
7use B::Deparse;
8
7d5371dc 9# globally set for the rest of test
10# the rowparser maker does not order its hashes by default for the miniscule
11# speed gain. But it does not disable sorting either - for this test
12# everything will be ordered nicely, and the hash randomization of 5.18
13# will not trip up anything
14use Data::Dumper;
15$Data::Dumper::Sortkeys = 1;
16
4e9fc3f3 17my $schema = DBICTest->init_schema(no_deploy => 1);
18my $infmap = [qw/single_track.cd.artist.name year/];
19
20is_same_src (
21 $schema->source ('CD')->_mk_row_parser({
22 inflate_map => $infmap,
23 }),
24 '$_ = [
25 { year => $_->[1] },
26 { single_track => [
27 undef,
28 { cd => [
29 undef,
30 { artist => [
31 { name => $_->[0] },
32 ] },
33 ]},
34 ]},
35 ] for @{$_[0]}',
36 'Simple 1:1 descending non-collapsing parser',
37);
38
39$infmap = [qw/
3faac878 40 single_track.cd.artist.cds.tracks.title
4e9fc3f3 41 single_track.cd.artist.artistid
42 year
4e9fc3f3 43 single_track.cd.artist.cds.cdid
44 title
45 artist
46/];
47is_same_src (
48 $schema->source ('CD')->_mk_row_parser({
49 inflate_map => $infmap,
50 }),
51 '$_ = [
3faac878 52 { artist => $_->[5], title => $_->[4], year => $_->[2] },
4e9fc3f3 53 { single_track => [
54 undef,
55 { cd => [
56 undef,
57 { artist => [
3faac878 58 { artistid => $_->[1] },
4e9fc3f3 59 { cds => [
60 { cdid => $_->[3] },
61 { tracks => [
3faac878 62 { title => $_->[0] }
4e9fc3f3 63 ] },
64 ] },
65 ] },
66 ] },
67 ] },
68 ] for @{$_[0]}',
69 '1:1 descending non-collapsing parser terminating with chained 1:M:M',
70);
71
72is_deeply (
3faac878 73 ($schema->source('CD')->_resolve_collapse({ as => {map { $infmap->[$_] => $_ } 0 .. $#$infmap} })),
4e9fc3f3 74 {
75 -node_index => 1,
3faac878 76 -idcols_current_node => [ 4, 5 ],
77 -idcols_extra_from_children => [ 0, 3 ],
4e9fc3f3 78
79 single_track => {
80 -node_index => 2,
3faac878 81 -idcols_current_node => [ 4, 5 ],
82 -idcols_extra_from_children => [ 0, 3 ],
4e9fc3f3 83 -is_optional => 1,
84 -is_single => 1,
85
86 cd => {
87 -node_index => 3,
3faac878 88 -idcols_current_node => [ 4, 5 ],
89 -idcols_extra_from_children => [ 0, 3 ],
4e9fc3f3 90 -is_single => 1,
91
92 artist => {
93 -node_index => 4,
3faac878 94 -idcols_current_node => [ 4, 5 ],
95 -idcols_extra_from_children => [ 0, 3 ],
4e9fc3f3 96 -is_single => 1,
97
98 cds => {
99 -node_index => 5,
3faac878 100 -idcols_current_node => [ 3, 4, 5 ],
101 -idcols_extra_from_children => [ 0 ],
4e9fc3f3 102 -is_optional => 1,
103
104 tracks => {
105 -node_index => 6,
3faac878 106 -idcols_current_node => [ 0, 3, 4, 5 ],
4e9fc3f3 107 -is_optional => 1,
108 },
109 },
110 },
111 },
112 },
113 },
114 'Correct collapse map for 1:1 descending chain terminating with chained 1:M:M'
115);
116
117is_same_src (
118 $schema->source ('CD')->_mk_row_parser({
119 inflate_map => $infmap,
120 collapse => 1,
121 }),
122 ' my($rows_pos, $result_pos, $cur_row, @cur_row_ids, @collapse_idx, $is_new_res) = (0, 0);
123
124 while ($cur_row = (
125 ( $rows_pos >= 0 and $_[0][$rows_pos++] ) or do { $rows_pos = -1; undef } )
126 ||
127 ( $_[1] and $_[1]->() )
128 ) {
129
fcf32d04 130 $cur_row_ids[$_] = defined $cur_row->[$_] ? $cur_row->[$_] : "\0NULL\xFF$rows_pos\xFF$_\0"
3faac878 131 for (0, 3, 4, 5);
4e9fc3f3 132
3faac878 133 # a present cref in $_[1] implies lazy prefetch, implies a supplied stash in $_[2]
4e9fc3f3 134 $_[1] and $result_pos and unshift(@{$_[2]}, $cur_row) and last
135 if $is_new_res = ! $collapse_idx[1]{$cur_row_ids[4]}{$cur_row_ids[5]};
136
3faac878 137 # the rowdata itself for root node
138 $collapse_idx[1]{$cur_row_ids[4]}{$cur_row_ids[5]} ||= [{ artist => $cur_row->[5], title => $cur_row->[4], year => $cur_row->[2] }];
139
140 # prefetch data of single_track (placed in root)
4e9fc3f3 141 $collapse_idx[1]{$cur_row_ids[4]}{$cur_row_ids[5]}[1]{single_track} ||= $collapse_idx[2]{$cur_row_ids[4]}{$cur_row_ids[5]};
3faac878 142
143 # prefetch data of cd (placed in single_track)
4e9fc3f3 144 $collapse_idx[2]{$cur_row_ids[4]}{$cur_row_ids[5]}[1]{cd} ||= $collapse_idx[3]{$cur_row_ids[4]}{$cur_row_ids[5]};
4e9fc3f3 145
3faac878 146 # prefetch data of artist ( placed in single_track->cd)
147 $collapse_idx[3]{$cur_row_ids[4]}{$cur_row_ids[5]}[1]{artist} ||= $collapse_idx[4]{$cur_row_ids[4]}{$cur_row_ids[5]} ||= [{ artistid => $cur_row->[1] }];
148
149 # prefetch data of cds (if available)
150 push @{$collapse_idx[4]{$cur_row_ids[4]}{$cur_row_ids[5]}[1]{cds}}, $collapse_idx[5]{$cur_row_ids[3]}{$cur_row_ids[4]}{$cur_row_ids[5]} ||= [{ cdid => $cur_row->[3] }]
151 unless $collapse_idx[5]{$cur_row_ids[3]}{$cur_row_ids[4]}{$cur_row_ids[5]};
4e9fc3f3 152
3faac878 153 # prefetch data of tracks (if available)
154 push @{$collapse_idx[5]{$cur_row_ids[3]}{$cur_row_ids[4]}{$cur_row_ids[5]}[1]{tracks}}, $collapse_idx[6]{$cur_row_ids[0]}{$cur_row_ids[3]}{$cur_row_ids[4]}{$cur_row_ids[5]} ||= [{ title => $cur_row->[0] }]
155 unless $collapse_idx[6]{$cur_row_ids[0]}{$cur_row_ids[3]}{$cur_row_ids[4]}{$cur_row_ids[5]};
4e9fc3f3 156
157 $_[0][$result_pos++] = $collapse_idx[1]{$cur_row_ids[4]}{$cur_row_ids[5]}
158 if $is_new_res;
159 }
160 splice @{$_[0]}, $result_pos;
161 ',
162 'Same 1:1 descending terminating with chained 1:M:M but with collapse',
163);
164
165$infmap = [qw/
166 tracks.lyrics.lyric_versions.text
167 existing_single_track.cd.artist.artistid
168 existing_single_track.cd.artist.cds.year
169 year
170 genreid
171 tracks.title
172 existing_single_track.cd.artist.cds.cdid
173 latest_cd
174 existing_single_track.cd.artist.cds.tracks.title
175 existing_single_track.cd.artist.cds.genreid
176/];
177
178is_deeply (
82f0e0aa 179 $schema->source('CD')->_resolve_collapse({ as => {map { $infmap->[$_] => $_ } 0 .. $#$infmap} }),
4e9fc3f3 180 {
181 -node_index => 1,
3faac878 182 -idcols_current_node => [ 1 ], # existing_single_track.cd.artist.artistid
183 -idcols_extra_from_children => [ 0, 5, 6, 8 ],
4e9fc3f3 184
185 existing_single_track => {
186 -node_index => 2,
3faac878 187 -idcols_current_node => [ 1 ], # existing_single_track.cd.artist.artistid
188 -idcols_extra_from_children => [ 6, 8 ],
4e9fc3f3 189 -is_single => 1,
190
191 cd => {
192 -node_index => 3,
3faac878 193 -idcols_current_node => [ 1 ], # existing_single_track.cd.artist.artistid
194 -idcols_extra_from_children => [ 6, 8 ],
4e9fc3f3 195 -is_single => 1,
196
197 artist => {
198 -node_index => 4,
3faac878 199 -idcols_current_node => [ 1 ], # existing_single_track.cd.artist.artistid
200 -idcols_extra_from_children => [ 6, 8 ],
4e9fc3f3 201 -is_single => 1,
202
203 cds => {
204 -node_index => 5,
3faac878 205 -idcols_current_node => [ 1, 6 ], # existing_single_track.cd.artist.cds.cdid
206 -idcols_extra_from_children => [ 8 ],
4e9fc3f3 207 -is_optional => 1,
208
209 tracks => {
210 -node_index => 6,
3faac878 211 -idcols_current_node => [ 1, 6, 8 ], # existing_single_track.cd.artist.cds.cdid, existing_single_track.cd.artist.cds.tracks.title
4e9fc3f3 212 -is_optional => 1,
213 }
214 }
215 }
216 }
217 },
218 tracks => {
219 -node_index => 7,
3faac878 220 -idcols_current_node => [ 1, 5 ], # existing_single_track.cd.artist.artistid, tracks.title
221 -idcols_extra_from_children => [ 0 ],
4e9fc3f3 222 -is_optional => 1,
223
224 lyrics => {
225 -node_index => 8,
3faac878 226 -idcols_current_node => [ 1, 5 ], # existing_single_track.cd.artist.artistid, tracks.title
227 -idcols_extra_from_children => [ 0 ],
4e9fc3f3 228 -is_single => 1,
229 -is_optional => 1,
230
231 lyric_versions => {
232 -node_index => 9,
3faac878 233 -idcols_current_node => [ 0, 1, 5 ], # tracks.lyrics.lyric_versions.text, existing_single_track.cd.artist.artistid, tracks.title
4e9fc3f3 234 -is_optional => 1,
235 },
236 },
237 }
238 },
239 'Correct collapse map constructed',
240);
241
242is_same_src (
243 $schema->source ('CD')->_mk_row_parser({
244 inflate_map => $infmap,
245 collapse => 1,
246 }),
247 ' my ($rows_pos, $result_pos, $cur_row, @cur_row_ids, @collapse_idx, $is_new_res) = (0,0);
248
249 while ($cur_row = (
250 ( $rows_pos >= 0 and $_[0][$rows_pos++] ) or do { $rows_pos = -1; undef } )
251 ||
252 ( $_[1] and $_[1]->() )
253 ) {
254
fcf32d04 255 $cur_row_ids[$_] = defined $cur_row->[$_] ? $cur_row->[$_] : "\0NULL\xFF$rows_pos\xFF$_\0"
4e9fc3f3 256 for (0, 1, 5, 6, 8);
257
258 $is_new_res = ! $collapse_idx[1]{$cur_row_ids[1]} and (
259 $_[1] and $result_pos and (unshift @{$_[2]}, $cur_row) and last
260 );
261
7d5371dc 262 $collapse_idx[1]{$cur_row_ids[1]} ||= [{ genreid => $cur_row->[4], latest_cd => $cur_row->[7], year => $cur_row->[3] }];
4e9fc3f3 263
264 $collapse_idx[1]{$cur_row_ids[1]}[1]{existing_single_track} ||= $collapse_idx[2]{$cur_row_ids[1]};
265 $collapse_idx[2]{$cur_row_ids[1]}[1]{cd} ||= $collapse_idx[3]{$cur_row_ids[1]};
266 $collapse_idx[3]{$cur_row_ids[1]}[1]{artist} ||= $collapse_idx[4]{$cur_row_ids[1]} ||= [{ artistid => $cur_row->[1] }];
267
3faac878 268 push @{ $collapse_idx[4]{$cur_row_ids[1]}[1]{cds} }, $collapse_idx[5]{$cur_row_ids[1]}{$cur_row_ids[6]} ||= [{ cdid => $cur_row->[6], genreid => $cur_row->[9], year => $cur_row->[2] }]
269 unless $collapse_idx[5]{$cur_row_ids[1]}{$cur_row_ids[6]};
4e9fc3f3 270
3faac878 271 push @{ $collapse_idx[5]{$cur_row_ids[1]}{$cur_row_ids[6]}[1]{tracks} }, $collapse_idx[6]{$cur_row_ids[1]}{$cur_row_ids[6]}{$cur_row_ids[8]} ||= [{ title => $cur_row->[8] }]
272 unless $collapse_idx[6]{$cur_row_ids[1]}{$cur_row_ids[6]}{$cur_row_ids[8]};
4e9fc3f3 273
4e9fc3f3 274 push @{ $collapse_idx[1]{$cur_row_ids[1]}[1]{tracks} }, $collapse_idx[7]{$cur_row_ids[1]}{$cur_row_ids[5]} ||= [{ title => $cur_row->[5] }]
275 unless $collapse_idx[7]{$cur_row_ids[1]}{$cur_row_ids[5]};
276
277 $collapse_idx[7]{$cur_row_ids[1]}{$cur_row_ids[5]}[1]{lyrics} ||= $collapse_idx[8]{$cur_row_ids[1]}{$cur_row_ids[5] };
278
4e9fc3f3 279 push @{ $collapse_idx[8]{$cur_row_ids[1]}{$cur_row_ids[5]}[1]{lyric_versions} }, $collapse_idx[9]{$cur_row_ids[0]}{$cur_row_ids[1]}{$cur_row_ids[5]} ||= [{ text => $cur_row->[0] }]
280 unless $collapse_idx[9]{$cur_row_ids[0]}{$cur_row_ids[1]}{$cur_row_ids[5]};
281
282 $_[0][$result_pos++] = $collapse_idx[1]{$cur_row_ids[1]}
283 if $is_new_res;
284 }
285
286 splice @{$_[0]}, $result_pos;
287 ',
288 'Multiple has_many on multiple branches torture test',
289);
290
fcf32d04 291$infmap = [
292 'single_track.trackid', # (0) definitive link to root from 1:1:1:1:M:M chain
293 'year', # (1) non-unique
294 'tracks.cd', # (2) \ together both uniqueness for second multirel
295 'tracks.title', # (3) / and definitive link back to root
296 'single_track.cd.artist.cds.cdid', # (4) to give uniquiness to ...tracks.title below
297 'single_track.cd.artist.cds.year', # (5) non-unique
298 'single_track.cd.artist.artistid', # (6) uniqufies entire parental chain
299 'single_track.cd.artist.cds.genreid', # (7) nullable
300 'single_track.cd.artist.cds.tracks.title',# (8) unique when combined with ...cds.cdid above
301];
302
303is_deeply (
304 $schema->source('CD')->_resolve_collapse({ as => {map { $infmap->[$_] => $_ } 0 .. $#$infmap} }),
305 {
306 -idcols_current_node => [],
307 -idcols_extra_from_children => [ 0, 2, 3, 4, 8 ],
308 -node_index => 1,
309 -root_node_idcol_variants => [
310 [ 0 ], [ 2 ],
311 ],
312 single_track => {
313 -idcols_current_node => [ 0 ],
314 -idcols_extra_from_children => [ 4, 8 ],
315 -is_optional => 1,
316 -is_single => 1,
317 -node_index => 2,
318 cd => {
319 -idcols_current_node => [ 0 ],
320 -idcols_extra_from_children => [ 4, 8 ],
321 -is_single => 1,
322 -node_index => 3,
323 artist => {
324 -idcols_current_node => [ 0 ],
325 -idcols_extra_from_children => [ 4, 8 ],
326 -is_single => 1,
327 -node_index => 4,
328 cds => {
329 -idcols_current_node => [ 0, 4 ],
330 -idcols_extra_from_children => [ 8 ],
331 -is_optional => 1,
332 -node_index => 5,
333 tracks => {
334 -idcols_current_node => [ 0, 4, 8 ],
335 -is_optional => 1,
336 -node_index => 6,
337 }
338 }
339 }
340 }
341 },
342 tracks => {
343 -idcols_current_node => [ 2, 3 ],
344 -is_optional => 1,
345 -node_index => 7,
346 }
347 },
348 'Correct underdefined root collapse map constructed'
349);
350
351is_same_src (
352 $schema->source ('CD')->_mk_row_parser({
353 inflate_map => $infmap,
354 collapse => 1,
355 }),
356 ' my($rows_pos, $result_pos, $cur_row, @cur_row_ids, @collapse_idx, $is_new_res) = (0, 0);
357
358 while ($cur_row = (
359 ( $rows_pos >= 0 and $_[0][$rows_pos++] ) or do { $rows_pos = -1; undef } )
360 ||
361 ( $_[1] and $_[1]->() )
362 ) {
363
364 $cur_row_ids[$_] = defined $$cur_row[$_] ? $$cur_row[$_] : "\0NULL\xFF$rows_pos\xFF$_\0"
365 for (0, 2, 3, 4, 8);
366
367 # cache expensive set of ops in a non-existent rowid slot
368 $cur_row_ids[9] = (
369 ( ( defined $cur_row->[0] ) && (join "\xFF", q{}, $cur_row->[0], q{} ))
370 or
371 ( ( defined $cur_row->[2] ) && (join "\xFF", q{}, $cur_row->[2], q{} ))
372 or
373 "\0$rows_pos\0"
374 );
375
376 $is_new_res = ! $collapse_idx[1]{$cur_row_ids[9]} and (
377 $_[1] and $result_pos and (unshift @{$_[2]}, $cur_row) and last
378 );
379
380 $collapse_idx[1]{$cur_row_ids[9]} ||= [{ year => $$cur_row[1] }];
381
382 $collapse_idx[1]{$cur_row_ids[9]}[1]{single_track} ||= ($collapse_idx[2]{$cur_row_ids[0]} ||= [{ trackid => $$cur_row[0] }]);
383
384 $collapse_idx[2]{$cur_row_ids[0]}[1]{cd} ||= $collapse_idx[3]{$cur_row_ids[0]};
385
386 $collapse_idx[3]{$cur_row_ids[0]}[1]{artist} ||= ($collapse_idx[4]{$cur_row_ids[0]} ||= [{ artistid => $$cur_row[6] }]);
387
388 push @{$collapse_idx[4]{$cur_row_ids[0]}[1]{cds}},
389 $collapse_idx[5]{$cur_row_ids[0]}{$cur_row_ids[4]} ||= [{ cdid => $$cur_row[4], genreid => $$cur_row[7], year => $$cur_row[5] }]
390 unless $collapse_idx[5]{$cur_row_ids[0]}{$cur_row_ids[4]};
391
392 push @{$collapse_idx[5]{$cur_row_ids[0]}{$cur_row_ids[4]}[1]{tracks}},
393 $collapse_idx[6]{$cur_row_ids[0]}{$cur_row_ids[4]}{$cur_row_ids[8]} ||= [{ title => $$cur_row[8] }]
394 unless $collapse_idx[6]{$cur_row_ids[0]}{$cur_row_ids[4]}{$cur_row_ids[8]};
395
396 push @{$collapse_idx[1]{$cur_row_ids[9]}[1]{tracks}},
397 $collapse_idx[7]{$cur_row_ids[2]}{$cur_row_ids[3]} ||= [{ cd => $$cur_row[2], title => $$cur_row[3] }]
398 unless $collapse_idx[7]{$cur_row_ids[2]}{$cur_row_ids[3]};
399
400 $_[0][$result_pos++] = $collapse_idx[1]{$cur_row_ids[9]}
401 if $is_new_res;
402 }
403
404 splice @{$_[0]}, $result_pos;
405 ',
406 'Multiple has_many on multiple branches with underdefined root torture test',
407);
408
4e9fc3f3 409done_testing;
410
411my $deparser;
412sub is_same_src {
413 $deparser ||= B::Deparse->new;
414 local $Test::Builder::Level = $Test::Builder::Level + 1;
415
416 my ($got, $expect) = map {
417 my $cref = eval "sub { $_ }" or do {
418 fail "Coderef does not compile!\n\n$@\n\n$_";
419 return undef;
420 };
421 $deparser->coderef2text($cref);
422 } @_[0,1];
423
424 is ($got, $expect, $_[2]||() )
425 or note ("Originals source:\n\n$_[0]\n\n$_[1]\n");
426}
427