Optimization - order only on lazy prefetch
[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 {
9f98c4b2 75 -identifying_columns => [ 4, 5 ],
4e9fc3f3 76
77 single_track => {
a0726a33 78 -identifying_columns => [ 1, 4, 5 ],
4e9fc3f3 79 -is_optional => 1,
80 -is_single => 1,
81
82 cd => {
a0726a33 83 -identifying_columns => [ 1, 4, 5 ],
4e9fc3f3 84 -is_single => 1,
85
86 artist => {
a0726a33 87 -identifying_columns => [ 1, 4, 5 ],
4e9fc3f3 88 -is_single => 1,
89
90 cds => {
a0726a33 91 -identifying_columns => [ 1, 3, 4, 5 ],
4e9fc3f3 92 -is_optional => 1,
93
94 tracks => {
a0726a33 95 -identifying_columns => [ 0, 1, 3, 4, 5 ],
4e9fc3f3 96 -is_optional => 1,
97 },
98 },
99 },
100 },
101 },
102 },
103 'Correct collapse map for 1:1 descending chain terminating with chained 1:M:M'
104);
105
106is_same_src (
107 $schema->source ('CD')->_mk_row_parser({
108 inflate_map => $infmap,
109 collapse => 1,
110 }),
9f98c4b2 111 ' my($rows_pos, $result_pos, $cur_row_data, %cur_row_ids, @collapse_idx, $is_new_res) = (0, 0);
4e9fc3f3 112
9f98c4b2 113 while ($cur_row_data = (
4e9fc3f3 114 ( $rows_pos >= 0 and $_[0][$rows_pos++] ) or do { $rows_pos = -1; undef } )
115 ||
116 ( $_[1] and $_[1]->() )
117 ) {
118
9f98c4b2 119 $cur_row_ids{$_} = defined $cur_row_data->[$_] ? $cur_row_data->[$_] : "\0NULL\xFF$rows_pos\xFF$_\0"
a0726a33 120 for (0, 1, 3, 4, 5);
4e9fc3f3 121
3faac878 122 # a present cref in $_[1] implies lazy prefetch, implies a supplied stash in $_[2]
9f98c4b2 123 $_[1] and $result_pos and unshift(@{$_[2]}, $cur_row_data) and last
124 if ( $is_new_res = ! $collapse_idx[0]{$cur_row_ids{4}}{$cur_row_ids{5}} );
4e9fc3f3 125
3faac878 126 # the rowdata itself for root node
9f98c4b2 127 $collapse_idx[0]{$cur_row_ids{4}}{$cur_row_ids{5}} ||= [{ artist => $cur_row_data->[5], title => $cur_row_data->[4], year => $cur_row_data->[2] }];
3faac878 128
129 # prefetch data of single_track (placed in root)
a0726a33 130 $collapse_idx[0]{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{single_track} ||= $collapse_idx[1]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}};
3faac878 131
132 # prefetch data of cd (placed in single_track)
a0726a33 133 $collapse_idx[1]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{cd} ||= $collapse_idx[2]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}};
4e9fc3f3 134
3faac878 135 # prefetch data of artist ( placed in single_track->cd)
a0726a33 136 $collapse_idx[2]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{artist} ||= $collapse_idx[3]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}} ||= [{ artistid => $cur_row_data->[1] }];
3faac878 137
138 # prefetch data of cds (if available)
a0726a33 139 push @{$collapse_idx[3]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{cds}}, $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}} ||= [{ cdid => $cur_row_data->[3] }]
140 unless $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}};
4e9fc3f3 141
3faac878 142 # prefetch data of tracks (if available)
a0726a33 143 push @{$collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{tracks}}, $collapse_idx[5]{$cur_row_ids{0}}{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}} ||= [{ title => $cur_row_data->[0] }]
144 unless $collapse_idx[5]{$cur_row_ids{0}}{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}};
4e9fc3f3 145
9f98c4b2 146 $_[0][$result_pos++] = $collapse_idx[0]{$cur_row_ids{4}}{$cur_row_ids{5}}
4e9fc3f3 147 if $is_new_res;
148 }
149 splice @{$_[0]}, $result_pos;
150 ',
151 'Same 1:1 descending terminating with chained 1:M:M but with collapse',
152);
153
154$infmap = [qw/
3d8caf63 155 tracks.lyrics.existing_lyric_versions.text
4e9fc3f3 156 existing_single_track.cd.artist.artistid
157 existing_single_track.cd.artist.cds.year
158 year
159 genreid
160 tracks.title
161 existing_single_track.cd.artist.cds.cdid
162 latest_cd
163 existing_single_track.cd.artist.cds.tracks.title
164 existing_single_track.cd.artist.cds.genreid
3d8caf63 165 tracks.lyrics.existing_lyric_versions.lyric_id
4e9fc3f3 166/];
167
168is_deeply (
82f0e0aa 169 $schema->source('CD')->_resolve_collapse({ as => {map { $infmap->[$_] => $_ } 0 .. $#$infmap} }),
4e9fc3f3 170 {
9f98c4b2 171 -identifying_columns => [ 1 ], # existing_single_track.cd.artist.artistid
4e9fc3f3 172
173 existing_single_track => {
9f98c4b2 174 -identifying_columns => [ 1 ], # existing_single_track.cd.artist.artistid
4e9fc3f3 175 -is_single => 1,
176
177 cd => {
9f98c4b2 178 -identifying_columns => [ 1 ], # existing_single_track.cd.artist.artistid
4e9fc3f3 179 -is_single => 1,
180
181 artist => {
9f98c4b2 182 -identifying_columns => [ 1 ], # existing_single_track.cd.artist.artistid
4e9fc3f3 183 -is_single => 1,
184
185 cds => {
9f98c4b2 186 -identifying_columns => [ 1, 6 ], # existing_single_track.cd.artist.cds.cdid
4e9fc3f3 187 -is_optional => 1,
188
189 tracks => {
9f98c4b2 190 -identifying_columns => [ 1, 6, 8 ], # existing_single_track.cd.artist.cds.cdid, existing_single_track.cd.artist.cds.tracks.title
4e9fc3f3 191 -is_optional => 1,
192 }
193 }
194 }
195 }
196 },
197 tracks => {
9f98c4b2 198 -identifying_columns => [ 1, 5 ], # existing_single_track.cd.artist.artistid, tracks.title
4e9fc3f3 199 -is_optional => 1,
200
201 lyrics => {
3d8caf63 202 -identifying_columns => [ 1, 5, 10 ], # existing_single_track.cd.artist.artistid, tracks.title, tracks.lyrics.existing_lyric_versions.lyric_id
4e9fc3f3 203 -is_single => 1,
204 -is_optional => 1,
205
3d8caf63 206 existing_lyric_versions => {
207 -identifying_columns => [ 0, 1, 5, 10 ], # tracks.lyrics.existing_lyric_versions.text, existing_single_track.cd.artist.artistid, tracks.title, tracks.lyrics.existing_lyric_versions.lyric_id
4e9fc3f3 208 },
209 },
210 }
211 },
212 'Correct collapse map constructed',
213);
214
215is_same_src (
216 $schema->source ('CD')->_mk_row_parser({
217 inflate_map => $infmap,
218 collapse => 1,
219 }),
9f98c4b2 220 ' my ($rows_pos, $result_pos, $cur_row_data, %cur_row_ids, @collapse_idx, $is_new_res) = (0,0);
4e9fc3f3 221
9f98c4b2 222 while ($cur_row_data = (
4e9fc3f3 223 ( $rows_pos >= 0 and $_[0][$rows_pos++] ) or do { $rows_pos = -1; undef } )
224 ||
225 ( $_[1] and $_[1]->() )
226 ) {
227
9f98c4b2 228 $cur_row_ids{$_} = defined $cur_row_data->[$_] ? $cur_row_data->[$_] : "\0NULL\xFF$rows_pos\xFF$_\0"
3d8caf63 229 for (0, 1, 5, 6, 8, 10);
4e9fc3f3 230
9f98c4b2 231 # a present cref in $_[1] implies lazy prefetch, implies a supplied stash in $_[2]
232 $_[1] and $result_pos and unshift(@{$_[2]}, $cur_row_data) and last
233 if ( $is_new_res = ! $collapse_idx[0]{$cur_row_ids{1}} );
4e9fc3f3 234
9f98c4b2 235 $collapse_idx[0]{$cur_row_ids{1}} ||= [{ genreid => $cur_row_data->[4], latest_cd => $cur_row_data->[7], year => $cur_row_data->[3] }];
4e9fc3f3 236
9f98c4b2 237 $collapse_idx[0]{$cur_row_ids{1}}[1]{existing_single_track} ||= $collapse_idx[1]{$cur_row_ids{1}};
238 $collapse_idx[1]{$cur_row_ids{1}}[1]{cd} ||= $collapse_idx[2]{$cur_row_ids{1}};
239 $collapse_idx[2]{$cur_row_ids{1}}[1]{artist} ||= $collapse_idx[3]{$cur_row_ids{1}} ||= [{ artistid => $cur_row_data->[1] }];
4e9fc3f3 240
9f98c4b2 241 push @{ $collapse_idx[3]{$cur_row_ids{1}}[1]{cds} }, $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{6}} ||= [{ cdid => $cur_row_data->[6], genreid => $cur_row_data->[9], year => $cur_row_data->[2] }]
242 unless $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{6}};
4e9fc3f3 243
9f98c4b2 244 push @{ $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{6}}[1]{tracks} }, $collapse_idx[5]{$cur_row_ids{1}}{$cur_row_ids{6}}{$cur_row_ids{8}} ||= [{ title => $cur_row_data->[8] }]
245 unless $collapse_idx[5]{$cur_row_ids{1}}{$cur_row_ids{6}}{$cur_row_ids{8}};
4e9fc3f3 246
9f98c4b2 247 push @{ $collapse_idx[0]{$cur_row_ids{1}}[1]{tracks} }, $collapse_idx[6]{$cur_row_ids{1}}{$cur_row_ids{5}} ||= [{ title => $cur_row_data->[5] }]
248 unless $collapse_idx[6]{$cur_row_ids{1}}{$cur_row_ids{5}};
4e9fc3f3 249
3d8caf63 250 $collapse_idx[6]{$cur_row_ids{1}}{$cur_row_ids{5}}[1]{lyrics} ||= $collapse_idx[7]{$cur_row_ids{1}}{$cur_row_ids{5}}{$cur_row_ids{10}};
4e9fc3f3 251
3d8caf63 252 push @{ $collapse_idx[7]{$cur_row_ids{1}}{$cur_row_ids{5}}{$cur_row_ids{10}}[1]{existing_lyric_versions} }, $collapse_idx[8]{$cur_row_ids{0}}{$cur_row_ids{1}}{$cur_row_ids{5}}{$cur_row_ids{10}} ||= [{ lyric_id => $cur_row_data->[10], text => $cur_row_data->[0] }]
253 unless $collapse_idx[8]{$cur_row_ids{0}}{$cur_row_ids{1}}{$cur_row_ids{5}}{$cur_row_ids{10}};
4e9fc3f3 254
9f98c4b2 255 $_[0][$result_pos++] = $collapse_idx[0]{$cur_row_ids{1}}
4e9fc3f3 256 if $is_new_res;
257 }
258
259 splice @{$_[0]}, $result_pos;
260 ',
261 'Multiple has_many on multiple branches torture test',
262);
263
fcf32d04 264$infmap = [
265 'single_track.trackid', # (0) definitive link to root from 1:1:1:1:M:M chain
266 'year', # (1) non-unique
267 'tracks.cd', # (2) \ together both uniqueness for second multirel
268 'tracks.title', # (3) / and definitive link back to root
269 'single_track.cd.artist.cds.cdid', # (4) to give uniquiness to ...tracks.title below
270 'single_track.cd.artist.cds.year', # (5) non-unique
271 'single_track.cd.artist.artistid', # (6) uniqufies entire parental chain
272 'single_track.cd.artist.cds.genreid', # (7) nullable
273 'single_track.cd.artist.cds.tracks.title',# (8) unique when combined with ...cds.cdid above
274];
275
276is_deeply (
277 $schema->source('CD')->_resolve_collapse({ as => {map { $infmap->[$_] => $_ } 0 .. $#$infmap} }),
278 {
9f98c4b2 279 -identifying_columns => [],
280 -identifying_columns_variants => [
fcf32d04 281 [ 0 ], [ 2 ],
282 ],
283 single_track => {
9f98c4b2 284 -identifying_columns => [ 0 ],
fcf32d04 285 -is_optional => 1,
286 -is_single => 1,
fcf32d04 287 cd => {
9f98c4b2 288 -identifying_columns => [ 0 ],
fcf32d04 289 -is_single => 1,
fcf32d04 290 artist => {
9f98c4b2 291 -identifying_columns => [ 0 ],
fcf32d04 292 -is_single => 1,
fcf32d04 293 cds => {
9f98c4b2 294 -identifying_columns => [ 0, 4 ],
fcf32d04 295 -is_optional => 1,
fcf32d04 296 tracks => {
9f98c4b2 297 -identifying_columns => [ 0, 4, 8 ],
fcf32d04 298 -is_optional => 1,
fcf32d04 299 }
300 }
301 }
302 }
303 },
304 tracks => {
9f98c4b2 305 -identifying_columns => [ 2, 3 ],
fcf32d04 306 -is_optional => 1,
fcf32d04 307 }
308 },
309 'Correct underdefined root collapse map constructed'
310);
311
312is_same_src (
313 $schema->source ('CD')->_mk_row_parser({
314 inflate_map => $infmap,
315 collapse => 1,
316 }),
9f98c4b2 317 ' my($rows_pos, $result_pos, $cur_row_data, %cur_row_ids, @collapse_idx, $is_new_res) = (0, 0);
fcf32d04 318
9f98c4b2 319 while ($cur_row_data = (
fcf32d04 320 ( $rows_pos >= 0 and $_[0][$rows_pos++] ) or do { $rows_pos = -1; undef } )
321 ||
322 ( $_[1] and $_[1]->() )
323 ) {
324
9f98c4b2 325 $cur_row_ids{$_} = defined $$cur_row_data[$_] ? $$cur_row_data[$_] : "\0NULL\xFF$rows_pos\xFF$_\0"
fcf32d04 326 for (0, 2, 3, 4, 8);
327
328 # cache expensive set of ops in a non-existent rowid slot
9f98c4b2 329 $cur_row_ids{10} = (
330 ( ( defined $cur_row_data->[0] ) && (join "\xFF", q{}, $cur_row_data->[0], q{} ))
fcf32d04 331 or
9f98c4b2 332 ( ( defined $cur_row_data->[2] ) && (join "\xFF", q{}, $cur_row_data->[2], q{} ))
fcf32d04 333 or
334 "\0$rows_pos\0"
335 );
336
9f98c4b2 337 # a present cref in $_[1] implies lazy prefetch, implies a supplied stash in $_[2]
338 $_[1] and $result_pos and unshift(@{$_[2]}, $cur_row_data) and last
339 if ( $is_new_res = ! $collapse_idx[0]{$cur_row_ids{10}} );
fcf32d04 340
9f98c4b2 341 $collapse_idx[0]{$cur_row_ids{10}} ||= [{ year => $$cur_row_data[1] }];
fcf32d04 342
9f98c4b2 343 $collapse_idx[0]{$cur_row_ids{10}}[1]{single_track} ||= ($collapse_idx[1]{$cur_row_ids{0}} ||= [{ trackid => $$cur_row_data[0] }]);
fcf32d04 344
9f98c4b2 345 $collapse_idx[1]{$cur_row_ids{0}}[1]{cd} ||= $collapse_idx[2]{$cur_row_ids{0}};
fcf32d04 346
9f98c4b2 347 $collapse_idx[2]{$cur_row_ids{0}}[1]{artist} ||= ($collapse_idx[3]{$cur_row_ids{0}} ||= [{ artistid => $$cur_row_data[6] }]);
fcf32d04 348
9f98c4b2 349 push @{$collapse_idx[3]{$cur_row_ids{0}}[1]{cds}},
350 $collapse_idx[4]{$cur_row_ids{0}}{$cur_row_ids{4}} ||= [{ cdid => $$cur_row_data[4], genreid => $$cur_row_data[7], year => $$cur_row_data[5] }]
351 unless $collapse_idx[4]{$cur_row_ids{0}}{$cur_row_ids{4}};
fcf32d04 352
9f98c4b2 353 push @{$collapse_idx[4]{$cur_row_ids{0}}{$cur_row_ids{4}}[1]{tracks}},
354 $collapse_idx[5]{$cur_row_ids{0}}{$cur_row_ids{4}}{$cur_row_ids{8}} ||= [{ title => $$cur_row_data[8] }]
355 unless $collapse_idx[5]{$cur_row_ids{0}}{$cur_row_ids{4}}{$cur_row_ids{8}};
fcf32d04 356
9f98c4b2 357 push @{$collapse_idx[0]{$cur_row_ids{10}}[1]{tracks}},
358 $collapse_idx[6]{$cur_row_ids{2}}{$cur_row_ids{3}} ||= [{ cd => $$cur_row_data[2], title => $$cur_row_data[3] }]
359 unless $collapse_idx[6]{$cur_row_ids{2}}{$cur_row_ids{3}};
fcf32d04 360
9f98c4b2 361 $_[0][$result_pos++] = $collapse_idx[0]{$cur_row_ids{10}}
fcf32d04 362 if $is_new_res;
363 }
364
365 splice @{$_[0]}, $result_pos;
366 ',
367 'Multiple has_many on multiple branches with underdefined root torture test',
368);
369
4e9fc3f3 370done_testing;
371
372my $deparser;
373sub is_same_src {
374 $deparser ||= B::Deparse->new;
375 local $Test::Builder::Level = $Test::Builder::Level + 1;
376
377 my ($got, $expect) = map {
378 my $cref = eval "sub { $_ }" or do {
379 fail "Coderef does not compile!\n\n$@\n\n$_";
380 return undef;
381 };
382 $deparser->coderef2text($cref);
383 } @_[0,1];
384
385 is ($got, $expect, $_[2]||() )
386 or note ("Originals source:\n\n$_[0]\n\n$_[1]\n");
387}
388