Commit | Line | Data |
4e9fc3f3 |
1 | use strict; |
2 | use warnings; |
3 | |
4 | use Test::More; |
5 | use lib qw(t/lib); |
6 | use DBICTest; |
7 | use 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 |
14 | use Data::Dumper; |
15 | $Data::Dumper::Sortkeys = 1; |
16 | |
4e9fc3f3 |
17 | my $schema = DBICTest->init_schema(no_deploy => 1); |
18 | my $infmap = [qw/single_track.cd.artist.name year/]; |
19 | |
20 | is_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 | /]; |
47 | is_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 | |
72 | is_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 | |
117 | is_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 | |
178 | is_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 | |
242 | is_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 | |
303 | is_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 | |
351 | is_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 |
409 | done_testing; |
410 | |
411 | my $deparser; |
412 | sub 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 | |