I think we are done here
[dbsrgits/DBIx-Class.git] / t / resultset / rowparser_internals.t
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
9 my $schema = DBICTest->init_schema(no_deploy => 1);
10 my $infmap = [qw/single_track.cd.artist.name year/];
11
12 is_same_src (
13   $schema->source ('CD')->_mk_row_parser({
14     inflate_map => $infmap,
15   }),
16   '$_ = [
17     { year => $_->[1] },
18     { single_track => [
19       undef,
20       { cd => [
21         undef,
22         { artist => [
23           { name  => $_->[0] },
24         ] },
25       ]},
26     ]},
27   ] for @{$_[0]}',
28   'Simple 1:1 descending non-collapsing parser',
29 );
30
31 $infmap = [qw/
32   single_track.cd.artist.artistid
33   year
34   single_track.cd.artist.cds.tracks.title
35   single_track.cd.artist.cds.cdid
36   title
37   artist
38 /];
39 is_same_src (
40   $schema->source ('CD')->_mk_row_parser({
41     inflate_map => $infmap,
42   }),
43   '$_ = [
44     { artist => $_->[5], title => $_->[4], year => $_->[1] },
45     { single_track => [
46       undef,
47       { cd => [
48         undef,
49         { artist => [
50           { artistid => $_->[0] },
51           { cds => [
52             { cdid => $_->[3] },
53             { tracks => [
54               { title => $_->[2] }
55             ] },
56           ] },
57         ] },
58       ] },
59     ] },
60   ] for @{$_[0]}',
61   '1:1 descending non-collapsing parser terminating with chained 1:M:M',
62 );
63
64 is_deeply (
65   $schema->source('CD')->_resolve_collapse({map { $infmap->[$_] => $_ } 0 .. $#$infmap}),
66   {
67     -node_index => 1,
68     -node_id => [ 4, 5 ],
69     -branch_id => [ 0, 2, 3, 4, 5 ],
70
71     single_track => {
72       -node_index => 2,
73       -node_id => [ 4, 5],
74       -branch_id => [ 0, 2, 3, 4, 5],
75       -is_optional => 1,
76       -is_single => 1,
77
78       cd => {
79         -node_index => 3,
80         -node_id => [ 4, 5 ],
81         -branch_id => [ 0, 2, 3, 4, 5 ],
82         -is_single => 1,
83
84         artist => {
85           -node_index => 4,
86           -node_id => [ 0 ],
87           -branch_id => [ 0, 2, 3 ],
88           -is_single => 1,
89
90           cds => {
91             -node_index => 5,
92             -node_id => [ 3 ],
93             -branch_id => [ 2, 3 ],
94             -is_optional => 1,
95
96             tracks => {
97               -node_index => 6,
98               -node_id => [ 2, 3 ],
99               -branch_id => [ 2, 3 ],
100               -is_optional => 1,
101             },
102           },
103         },
104       },
105     },
106   },
107   'Correct collapse map for 1:1 descending chain terminating with chained 1:M:M'
108 );
109
110 is_same_src (
111   $schema->source ('CD')->_mk_row_parser({
112     inflate_map => $infmap,
113     collapse => 1,
114   }),
115   ' my($rows_pos, $result_pos, $cur_row, @cur_row_ids, @collapse_idx, $is_new_res) = (0, 0);
116
117     while ($cur_row = (
118       ( $rows_pos >= 0 and $_[0][$rows_pos++] ) or do { $rows_pos = -1; undef } )
119         ||
120       ( $_[1] and $_[1]->() )
121     ) {
122
123       $cur_row_ids[$_] = defined $cur_row->[$_] ? $cur_row->[$_] : "\xFF\xFFN\xFFU\xFFL\xFFL\xFF\xFF"
124         for (0, 2, 3, 4, 5);
125
126       # a present cref implies lazy prefetch, implies a supplied stash in $_[2]
127       $_[1] and $result_pos and unshift(@{$_[2]}, $cur_row) and last
128         if $is_new_res = ! $collapse_idx[1]{$cur_row_ids[4]}{$cur_row_ids[5]};
129
130       $collapse_idx[1]{$cur_row_ids[4]}{$cur_row_ids[5]} ||= [{ artist => $cur_row->[5], title => $cur_row->[4], year => $cur_row->[1] }];
131       $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]};
132       $collapse_idx[2]{$cur_row_ids[4]}{$cur_row_ids[5]}[1]{cd} ||= $collapse_idx[3]{$cur_row_ids[4]}{$cur_row_ids[5]};
133       $collapse_idx[3]{$cur_row_ids[4]}{$cur_row_ids[5]}[1]{artist} ||= $collapse_idx[4]{$cur_row_ids[0]} ||= [{ artistid => $cur_row->[0] }];
134
135       $collapse_idx[4]{$cur_row_ids[0]}[1]{cds} ||= [];
136       push @{$collapse_idx[4]{$cur_row_ids[0]}[1]{cds}}, $collapse_idx[5]{$cur_row_ids[3]} ||= [{ cdid => $cur_row->[3] }]
137         unless $collapse_idx[5]{$cur_row_ids[3]};
138
139       $collapse_idx[5]{$cur_row_ids[3]}[1]{tracks} ||= [];
140       push @{$collapse_idx[5]{$cur_row_ids[3]}[1]{tracks}}, $collapse_idx[6]{$cur_row_ids[2]}{$cur_row_ids[3]} ||= [{ title => $cur_row->[2] }]
141         unless $collapse_idx[6]{$cur_row_ids[2]}{$cur_row_ids[3]};
142
143       $_[0][$result_pos++] = $collapse_idx[1]{$cur_row_ids[4]}{$cur_row_ids[5]}
144         if $is_new_res;
145     }
146     splice @{$_[0]}, $result_pos;
147   ',
148   'Same 1:1 descending terminating with chained 1:M:M but with collapse',
149 );
150
151 $infmap = [qw/
152   tracks.lyrics.lyric_versions.text
153   existing_single_track.cd.artist.artistid
154   existing_single_track.cd.artist.cds.year
155   year
156   genreid
157   tracks.title
158   existing_single_track.cd.artist.cds.cdid
159   latest_cd
160   existing_single_track.cd.artist.cds.tracks.title
161   existing_single_track.cd.artist.cds.genreid
162 /];
163
164 is_deeply (
165   $schema->source('CD')->_resolve_collapse({map { $infmap->[$_] => $_ } 0 .. $#$infmap}),
166   {
167     -node_index => 1,
168     -node_id => [ 1 ], # existing_single_track.cd.artist.artistid
169     -branch_id => [ 0, 1, 5, 6, 8 ],
170
171     existing_single_track => {
172       -node_index => 2,
173       -node_id => [ 1 ], # existing_single_track.cd.artist.artistid
174       -branch_id => [ 1, 6, 8 ],
175       -is_single => 1,
176
177       cd => {
178         -node_index => 3,
179         -node_id => [ 1 ], # existing_single_track.cd.artist.artistid
180         -branch_id => [ 1, 6, 8 ],
181         -is_single => 1,
182
183         artist => {
184           -node_index => 4,
185           -node_id => [ 1 ], # existing_single_track.cd.artist.artistid
186           -branch_id => [ 1, 6, 8 ],
187           -is_single => 1,
188
189           cds => {
190             -node_index => 5,
191             -node_id => [ 6 ], # existing_single_track.cd.artist.cds.cdid
192             -branch_id => [ 6, 8 ],
193             -is_optional => 1,
194
195             tracks => {
196               -node_index => 6,
197               -node_id => [ 6, 8 ], # existing_single_track.cd.artist.cds.cdid, existing_single_track.cd.artist.cds.tracks.title
198               -branch_id => [ 6, 8 ],
199               -is_optional => 1,
200             }
201           }
202         }
203       }
204     },
205     tracks => {
206       -node_index => 7,
207       -node_id => [ 1, 5 ], # existing_single_track.cd.artist.artistid, tracks.title
208       -branch_id => [ 0, 1, 5 ],
209       -is_optional => 1,
210
211       lyrics => {
212         -node_index => 8,
213         -node_id => [ 1, 5 ], # existing_single_track.cd.artist.artistid, tracks.title
214         -branch_id => [ 0, 1, 5 ],
215         -is_single => 1,
216         -is_optional => 1,
217
218         lyric_versions => {
219           -node_index => 9,
220           -node_id => [ 0, 1, 5 ], # tracks.lyrics.lyric_versions.text, existing_single_track.cd.artist.artistid, tracks.title
221           -branch_id => [ 0, 1, 5 ],
222           -is_optional => 1,
223         },
224       },
225     }
226   },
227   'Correct collapse map constructed',
228 );
229
230 is_same_src (
231   $schema->source ('CD')->_mk_row_parser({
232     inflate_map => $infmap,
233     collapse => 1,
234   }),
235   ' my ($rows_pos, $result_pos, $cur_row, @cur_row_ids, @collapse_idx, $is_new_res) = (0,0);
236
237     while ($cur_row = (
238       ( $rows_pos >= 0 and $_[0][$rows_pos++] ) or do { $rows_pos = -1; undef } )
239         ||
240       ( $_[1] and $_[1]->() )
241     ) {
242
243       $cur_row_ids[$_] = defined $cur_row->[$_] ? $cur_row->[$_] : "\xFF\xFFN\xFFU\xFFL\xFFL\xFF\xFF"
244         for (0, 1, 5, 6, 8);
245
246       $is_new_res = ! $collapse_idx[1]{$cur_row_ids[1]} and (
247         $_[1] and $result_pos and (unshift @{$_[2]}, $cur_row) and last
248       );
249
250       $collapse_idx[1]{$cur_row_ids[1]} ||= [{ latest_cd => $cur_row->[7], year => $cur_row->[3], genreid => $cur_row->[4] }];
251
252       $collapse_idx[1]{$cur_row_ids[1]}[1]{existing_single_track} ||= $collapse_idx[2]{$cur_row_ids[1]};
253       $collapse_idx[2]{$cur_row_ids[1]}[1]{cd} ||= $collapse_idx[3]{$cur_row_ids[1]};
254       $collapse_idx[3]{$cur_row_ids[1]}[1]{artist} ||= $collapse_idx[4]{$cur_row_ids[1]} ||= [{ artistid => $cur_row->[1] }];
255
256       $collapse_idx[4]{$cur_row_ids[1]}[1]{cds} ||= [];
257       push @{ $collapse_idx[4]{$cur_row_ids[1]}[1]{cds} }, $collapse_idx[5]{$cur_row_ids[6]} ||= [{ cdid => $cur_row->[6], genreid => $cur_row->[9], year => $cur_row->[2] }]
258         unless $collapse_idx[5]{$cur_row_ids[6]};
259
260       $collapse_idx[5]{$cur_row_ids[6]}[1]{tracks} ||= [];
261       push @{ $collapse_idx[5]{$cur_row_ids[6]}[1]{tracks} }, $collapse_idx[6]{$cur_row_ids[6]}{$cur_row_ids[8]} ||= [{ title => $cur_row->[8] }]
262         unless $collapse_idx[6]{$cur_row_ids[6]}{$cur_row_ids[8]};
263
264       $collapse_idx[1]{$cur_row_ids[1]}[1]{tracks} ||= [];
265       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] }]
266         unless $collapse_idx[7]{$cur_row_ids[1]}{$cur_row_ids[5]};
267
268       $collapse_idx[7]{$cur_row_ids[1]}{$cur_row_ids[5]}[1]{lyrics} ||= $collapse_idx[8]{$cur_row_ids[1]}{$cur_row_ids[5] };
269
270       $collapse_idx[8]{$cur_row_ids[1]}{$cur_row_ids[5]}[1]{lyric_versions} ||= [];
271       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] }]
272         unless $collapse_idx[9]{$cur_row_ids[0]}{$cur_row_ids[1]}{$cur_row_ids[5]};
273
274       $_[0][$result_pos++] = $collapse_idx[1]{$cur_row_ids[1]}
275         if $is_new_res;
276     }
277
278     splice @{$_[0]}, $result_pos;
279   ',
280   'Multiple has_many on multiple branches torture test',
281 );
282
283 done_testing;
284
285 my $deparser;
286 sub is_same_src {
287   $deparser ||= B::Deparse->new;
288   local $Test::Builder::Level = $Test::Builder::Level + 1;
289
290   my ($got, $expect) = map {
291     my $cref = eval "sub { $_ }" or do {
292       fail "Coderef does not compile!\n\n$@\n\n$_";
293       return undef;
294     };
295     $deparser->coderef2text($cref);
296   } @_[0,1];
297
298   is ($got, $expect, $_[2]||() )
299     or note ("Originals source:\n\n$_[0]\n\n$_[1]\n");
300 }
301