First part of changes for better unexpected NULL reporting
[dbsrgits/DBIx-Class.git] / t / resultset / rowparser_internals.t
1 BEGIN { do "./t/lib/ANFANG.pm" or die ( $@ || $! ) }
2
3 use strict;
4 use warnings;
5
6 use Test::More;
7
8 use DBICTest;
9 use B::Deparse;
10 use DBIx::Class::_Util 'perlstring';
11
12 my $schema = DBICTest->init_schema(no_deploy => 1);
13 my $infmap = [qw/
14   single_track.cd.artist.name
15   year
16 /];
17
18 is_same_src (
19   ($schema->source ('CD')->_mk_row_parser({
20     inflate_map => $infmap,
21   }))[0],
22   '$_ = [
23     { year => $_->[1] },
24     { single_track => ( ! defined( $_->[0]) )
25       ? bless( [
26         undef,
27         { cd => [
28           undef,
29           { artist => [
30             { name  => $_->[0] },
31           ] },
32         ] },
33       ], __NBC__ )
34       : [
35         undef,
36         { cd => [
37           undef,
38           { artist => [
39             { name  => $_->[0] },
40           ] },
41         ] },
42       ]
43     },
44   ] for @{$_[0]}',
45   'Simple 1:1 descending non-collapsing parser',
46 );
47
48 $infmap = [qw/
49   single_track.cd.artist.cds.tracks.title
50   single_track.cd.artist.artistid
51   year
52   single_track.cd.artist.cds.cdid
53   title
54   artist
55 /];
56
57 is_same_src (
58   ($schema->source ('CD')->_mk_row_parser({
59     inflate_map => $infmap,
60   }))[0],
61   '$_ = [
62     { artist => $_->[5], title => $_->[4], year => $_->[2] },
63     {
64       single_track => ( (! defined $_->[0] ) && (! defined $_->[1]) && (! defined $_->[3] ) )
65         ? bless( [
66           undef,
67           {
68             cd => [
69               undef,
70               {
71                 artist => [
72                   { artistid => $_->[1] },
73                   {
74                     cds => ( (! defined $_->[0] ) && ( ! defined $_->[3] ) )
75                       ? bless ([
76                         { cdid => $_->[3] },
77                         {
78                           tracks => ( ! defined $_->[0] )
79                             ? bless ( [{ title => $_->[0] }], __NBC__ )
80                             : [{ title => $_->[0] }]
81                         }
82                       ], __NBC__)
83                       : [
84                         { cdid => $_->[3] },
85                         {
86                           tracks => ( ! defined $_->[0] )
87                             ? bless ( [{ title => $_->[0] }], __NBC__ )
88                             : [{ title => $_->[0] }]
89                         }
90                       ]
91                   }
92                 ]
93               }
94             ]
95           }
96         ], __NBC__)
97         : [
98           undef,
99           {
100             cd => [
101               undef,
102               {
103                 artist => [
104                   { artistid => $_->[1] },
105                   {
106                     cds => ( (! defined $_->[0] ) && ( ! defined $_->[3] ) )
107                       ? bless ([
108                         { cdid => $_->[3] },
109                         {
110                           tracks => ( ! defined $_->[0] )
111                             ? bless ( [{ title => $_->[0] }], __NBC__ )
112                             : [{ title => $_->[0] }]
113                         }
114                       ], __NBC__)
115                       : [
116                         { cdid => $_->[3] },
117                         {
118                           tracks => ( ! defined $_->[0] )
119                             ? bless ( [{ title => $_->[0] }], __NBC__ )
120                             : [{ title => $_->[0] }]
121                         }
122                       ]
123                   }
124                 ]
125               }
126             ]
127           }
128         ]
129     }
130   ] for @{$_[0]}',
131   '1:1 descending non-collapsing parser terminating with chained 1:M:M',
132 );
133
134 is_same_src (
135   ($schema->source ('CD')->_mk_row_parser({
136     prune_null_branches => 1,
137     inflate_map => $infmap,
138   }))[0],
139   '$_ = [
140     { artist => $_->[5], title => $_->[4], year => $_->[2] },
141     {
142       single_track => ( (! defined $_->[0] ) && (! defined $_->[1]) && (! defined $_->[3] ) ) ? undef : [
143         undef,
144         {
145           cd => [
146             undef,
147             {
148               artist => [
149                 { artistid => $_->[1] },
150                 {
151                   cds => ( (! defined $_->[0] ) && ( ! defined $_->[3] ) ) ? undef : [
152                     { cdid => $_->[3] },
153                     {
154                       tracks => ( ! defined $_->[0] ) ? undef : [
155                         { title => $_->[0] },
156                       ]
157                     }
158                   ]
159                 }
160               ]
161             }
162           ]
163         }
164       ]
165     }
166   ] for @{$_[0]}',
167   '1:1 descending non-collapsing pruning parser terminating with chained 1:M:M',
168 );
169
170 is_same_src (
171   ($schema->source ('CD')->_mk_row_parser({
172     hri_style => 1,
173     prune_null_branches => 1,
174     inflate_map => $infmap,
175   }))[0],
176   '$_ = {
177       artist => $_->[5], title => $_->[4], year => $_->[2],
178
179       ( single_track => ( (! defined $_->[0] ) && (! defined $_->[1]) && (! defined $_->[3] ) )
180         ? undef
181         : {
182             cd =>
183               {
184                 artist => {
185                     artistid => $_->[1],
186                     ( cds => ( (! defined $_->[0] ) && ( ! defined $_->[3] ) )
187                       ? undef
188                       : {
189                           cdid => $_->[3],
190                           ( tracks => ( ! defined $_->[0] )
191                             ? undef
192                             : { title => $_->[0] }
193                           )
194                         }
195                     )
196                   }
197               }
198           }
199       )
200     } for @{$_[0]}',
201   '1:1 descending non-collapsing HRI-direct parser terminating with chained 1:M:M',
202 );
203
204
205
206 is_deeply (
207   ($schema->source('CD')->_resolve_collapse({ as => {map { $infmap->[$_] => $_ } 0 .. $#$infmap} })),
208   {
209     -identifying_columns => [ 4, 5 ],
210
211     single_track => {
212       -identifying_columns => [ 1, 4, 5 ],
213       -is_optional => 1,
214       -is_single => 1,
215
216       cd => {
217         -identifying_columns => [ 1, 4, 5 ],
218         -is_single => 1,
219
220         artist => {
221           -identifying_columns => [ 1, 4, 5 ],
222           -is_single => 1,
223
224           cds => {
225             -identifying_columns => [ 1, 3, 4, 5 ],
226             -is_optional => 1,
227
228             tracks => {
229               -identifying_columns => [ 0, 1, 3, 4, 5 ],
230               -is_optional => 1,
231             },
232           },
233         },
234       },
235     },
236   },
237   'Correct collapse map for 1:1 descending chain terminating with chained 1:M:M'
238 );
239
240 is_same_src (
241   ($schema->source ('CD')->_mk_row_parser({
242     inflate_map => $infmap,
243     collapse => 1,
244   }))[0],
245   ' my $rows_pos = 0;
246     my ($result_pos, @collapse_idx, $cur_row_data, %cur_row_ids);
247
248     while ($cur_row_data = (
249       (
250         $rows_pos >= 0
251           and
252         (
253           $_[0][$rows_pos++]
254             or
255           ( ($rows_pos = -1), undef )
256         )
257       )
258         or
259       ( $_[1] and $_[1]->() )
260     ) ) {
261
262       ( @cur_row_ids{0,1,3,4,5} = (
263         ( $cur_row_data->[0] // "\0NULL\xFF$rows_pos\xFF0\0" ),
264         ( $cur_row_data->[1] // "\0NULL\xFF$rows_pos\xFF1\0" ),
265         ( $cur_row_data->[3] // "\0NULL\xFF$rows_pos\xFF3\0" ),
266         ( $cur_row_data->[4] ),
267         ( $cur_row_data->[5] ),
268       ) ),
269
270       # a present cref in $_[1] implies lazy prefetch, implies a supplied stash in $_[2]
271       ( $_[1] and $result_pos and ! $collapse_idx[0]{$cur_row_ids{4}}{$cur_row_ids{5}} and (unshift @{$_[2]}, $cur_row_data) and last ),
272
273       # the rowdata itself for root node
274       ( $collapse_idx[0]{$cur_row_ids{4}}{$cur_row_ids{5}} //= $_[0][$result_pos++] = [{ artist => $cur_row_data->[5], title => $cur_row_data->[4], year => $cur_row_data->[2] }] ),
275
276       # prefetch data of single_track (placed in root)
277       ( $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}} = [] ),
278       ( defined($cur_row_data->[1]) or bless( $collapse_idx[0]{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{single_track}, __NBC__ ) ),
279
280       # prefetch data of cd (placed in single_track)
281       ( $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}} = [] ),
282
283       # prefetch data of artist ( placed in single_track->cd)
284       ( $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] }] ),
285
286       # prefetch data of cds (if available)
287       (
288         (! $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}} )
289           and
290         push @{$collapse_idx[3]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{cds}}, (
291           $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}} = [{ cdid => $cur_row_data->[3] }]
292         )
293       ),
294       ( defined($cur_row_data->[3]) or bless( $collapse_idx[3]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{cds}, __NBC__ ) ),
295
296       # prefetch data of tracks (if available)
297       (
298         (! $collapse_idx[5]{$cur_row_ids{0}}{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}} )
299           and
300         push @{$collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{tracks}}, (
301           $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] }]
302         )
303       ),
304       ( defined($cur_row_data->[0]) or bless( $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{tracks}, __NBC__ ) ),
305
306     }
307     $#{$_[0]} = $result_pos - 1;
308   ',
309   'Same 1:1 descending terminating with chained 1:M:M but with collapse',
310 );
311
312 is_same_src (
313   ($schema->source ('CD')->_mk_row_parser({
314     inflate_map => $infmap,
315     collapse => 1,
316     hri_style => 1,
317     prune_null_branches => 1,
318   }))[0],
319   ' my $rows_pos = 0;
320     my ($result_pos, @collapse_idx, $cur_row_data, %cur_row_ids);
321
322     while ($cur_row_data = (
323       (
324         $rows_pos >= 0
325           and
326         (
327           $_[0][$rows_pos++]
328             or
329           ( ($rows_pos = -1), undef )
330         )
331       )
332         or
333       ( $_[1] and $_[1]->() )
334     ) ) {
335
336       ( @cur_row_ids{0, 1, 3, 4, 5} = @{$cur_row_data}[0, 1, 3, 4, 5] ),
337
338       # a present cref in $_[1] implies lazy prefetch, implies a supplied stash in $_[2]
339       ( $_[1] and $result_pos and ! $collapse_idx[0]{$cur_row_ids{4}}{$cur_row_ids{5}} and (unshift @{$_[2]}, $cur_row_data) and last ),
340
341       # the rowdata itself for root node
342       ( $collapse_idx[0]{$cur_row_ids{4}}{$cur_row_ids{5}} //= $_[0][$result_pos++] = { artist => $cur_row_data->[5], title => $cur_row_data->[4], year => $cur_row_data->[2] } ),
343
344       # prefetch data of single_track (placed in root)
345       ( (! defined($cur_row_data->[1]) ) ? $collapse_idx[0]{$cur_row_ids{4}}{$cur_row_ids{5}}{single_track} = undef : do {
346         ( $collapse_idx[0]{$cur_row_ids{4}}{$cur_row_ids{5}}{single_track} //= $collapse_idx[1]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}} = {} ),
347
348         # prefetch data of cd (placed in single_track)
349         ( $collapse_idx[1]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}}{cd} //= $collapse_idx[2]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}} = {} ),
350
351         # prefetch data of artist ( placed in single_track->cd)
352         ( $collapse_idx[2]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}}{artist} //= $collapse_idx[3]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}} = { artistid => $cur_row_data->[1] } ),
353
354         # prefetch data of cds (if available)
355         ( (! defined $cur_row_data->[3] ) ? $collapse_idx[3]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}}{cds} = [] : do {
356
357           (
358             (! $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}} )
359               and
360             push @{$collapse_idx[3]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}}{cds}}, (
361               $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}} = { cdid => $cur_row_data->[3] }
362             )
363           ),
364
365           # prefetch data of tracks (if available)
366           (( ! defined $cur_row_data->[0] ) ? $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}}{tracks} = [] : do {
367
368             (
369               (! $collapse_idx[5]{$cur_row_ids{0}}{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}} )
370                 and
371               push @{$collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}}{tracks}}, (
372                 $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] }
373               )
374             ),
375           } ),
376         } ),
377       } ),
378     }
379     $#{$_[0]} = $result_pos - 1;
380   ',
381   'Same 1:1 descending terminating with chained 1:M:M but with collapse, HRI-direct',
382 );
383
384 $infmap = [qw/
385   tracks.lyrics.existing_lyric_versions.text
386   existing_single_track.cd.artist.artistid
387   existing_single_track.cd.artist.cds.year
388   year
389   genreid
390   tracks.title
391   existing_single_track.cd.artist.cds.cdid
392   latest_cd
393   existing_single_track.cd.artist.cds.tracks.title
394   existing_single_track.cd.artist.cds.genreid
395   tracks.lyrics.existing_lyric_versions.lyric_id
396 /];
397
398 is_deeply (
399   $schema->source('CD')->_resolve_collapse({ as => {map { $infmap->[$_] => $_ } 0 .. $#$infmap} }),
400   {
401     -identifying_columns => [ 1 ], # existing_single_track.cd.artist.artistid
402
403     existing_single_track => {
404       -identifying_columns => [ 1 ], # existing_single_track.cd.artist.artistid
405       -is_single => 1,
406
407       cd => {
408         -identifying_columns => [ 1 ], # existing_single_track.cd.artist.artistid
409         -is_single => 1,
410
411         artist => {
412           -identifying_columns => [ 1 ], # existing_single_track.cd.artist.artistid
413           -is_single => 1,
414
415           cds => {
416             -identifying_columns => [ 1, 6 ], # existing_single_track.cd.artist.cds.cdid
417             -is_optional => 1,
418
419             tracks => {
420               -identifying_columns => [ 1, 6, 8 ], # existing_single_track.cd.artist.cds.cdid, existing_single_track.cd.artist.cds.tracks.title
421               -is_optional => 1,
422             }
423           }
424         }
425       }
426     },
427     tracks => {
428       -identifying_columns => [ 1, 5 ], # existing_single_track.cd.artist.artistid, tracks.title
429       -is_optional => 1,
430
431       lyrics => {
432         -identifying_columns => [ 1, 5, 10 ], # existing_single_track.cd.artist.artistid, tracks.title, tracks.lyrics.existing_lyric_versions.lyric_id
433         -is_single => 1,
434         -is_optional => 1,
435
436         existing_lyric_versions => {
437           -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
438         },
439       },
440     }
441   },
442   'Correct collapse map constructed',
443 );
444
445 is_same_src (
446   ($schema->source ('CD')->_mk_row_parser({
447     inflate_map => $infmap,
448     collapse => 1,
449   }))[0],
450   ' my $rows_pos = 0;
451     my ($result_pos, @collapse_idx, $cur_row_data, %cur_row_ids);
452
453     while ($cur_row_data = (
454       (
455         $rows_pos >= 0
456           and
457         (
458           $_[0][$rows_pos++]
459             or
460           ( ($rows_pos = -1), undef )
461         )
462       )
463         or
464       ( $_[1] and $_[1]->() )
465     ) ) {
466
467       ( @cur_row_ids{0, 1, 5, 6, 8, 10} = (
468         $cur_row_data->[0] // "\0NULL\xFF$rows_pos\xFF0\0",
469         $cur_row_data->[1],
470         $cur_row_data->[5] // "\0NULL\xFF$rows_pos\xFF5\0",
471         $cur_row_data->[6] // "\0NULL\xFF$rows_pos\xFF6\0",
472         $cur_row_data->[8] // "\0NULL\xFF$rows_pos\xFF8\0",
473         $cur_row_data->[10] // "\0NULL\xFF$rows_pos\xFF10\0",
474       ) ),
475
476       # a present cref in $_[1] implies lazy prefetch, implies a supplied stash in $_[2]
477       ( $_[1] and $result_pos and ! $collapse_idx[0]{$cur_row_ids{1}} and (unshift @{$_[2]}, $cur_row_data) and last ),
478
479       ( $collapse_idx[0]{$cur_row_ids{1}} //= $_[0][$result_pos++] = [{ genreid => $cur_row_data->[4], latest_cd => $cur_row_data->[7], year => $cur_row_data->[3] }] ),
480
481       ( $collapse_idx[0]{$cur_row_ids{1}}[1]{existing_single_track} //= $collapse_idx[1]{$cur_row_ids{1}} = [] ),
482       ( $collapse_idx[1]{$cur_row_ids{1}}[1]{cd} //= $collapse_idx[2]{$cur_row_ids{1}} = [] ),
483       ( $collapse_idx[2]{$cur_row_ids{1}}[1]{artist} //= $collapse_idx[3]{$cur_row_ids{1}} = [{ artistid => $cur_row_data->[1] }] ),
484
485       (
486         (! $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{6}} )
487           and
488         push @{ $collapse_idx[3]{$cur_row_ids{1}}[1]{cds} }, (
489           $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] }]
490         )
491       ),
492       ( defined($cur_row_data->[6]) or bless( $collapse_idx[3]{$cur_row_ids{1}}[1]{cds}, __NBC__ ) ),
493
494       (
495         (! $collapse_idx[5]{$cur_row_ids{1}}{$cur_row_ids{6}}{$cur_row_ids{8}} )
496           and
497         push @{ $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{6}}[1]{tracks} }, (
498           $collapse_idx[5]{$cur_row_ids{1}}{$cur_row_ids{6}}{$cur_row_ids{8}} = [{ title => $cur_row_data->[8] }]
499         )
500       ),
501       ( defined($cur_row_data->[8]) or bless( $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{6}}[1]{tracks}, __NBC__ ) ),
502
503       (
504         (! $collapse_idx[6]{$cur_row_ids{1}}{$cur_row_ids{5}} )
505           and
506         push @{ $collapse_idx[0]{$cur_row_ids{1}}[1]{tracks} }, (
507           $collapse_idx[6]{$cur_row_ids{1}}{$cur_row_ids{5}} = [{ title => $cur_row_data->[5] }]
508         )
509       ),
510       ( defined($cur_row_data->[5]) or bless( $collapse_idx[0]{$cur_row_ids{1}}[1]{tracks}, __NBC__ ) ),
511
512       ( $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}} = [] ),
513       ( defined($cur_row_data->[10]) or bless( $collapse_idx[6]{$cur_row_ids{1}}{$cur_row_ids{5}}[1]{lyrics}, __NBC__ ) ),
514
515       (
516         (! $collapse_idx[8]{$cur_row_ids{0}}{$cur_row_ids{1}}{$cur_row_ids{5}}{$cur_row_ids{10}} )
517           and
518         push @{ $collapse_idx[7]{$cur_row_ids{1}}{$cur_row_ids{5}}{$cur_row_ids{10}}[1]{existing_lyric_versions} }, (
519           $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] }]
520         )
521       ),
522     }
523
524     $#{$_[0]} = $result_pos - 1;
525   ',
526   'Multiple has_many on multiple branches torture test',
527 );
528
529 is_same_src (
530   ($schema->source ('CD')->_mk_row_parser({
531     inflate_map => $infmap,
532     collapse => 1,
533     prune_null_branches => 1,
534   }))[0],
535   ' my $rows_pos = 0;
536     my ($result_pos, @collapse_idx, $cur_row_data, %cur_row_ids);
537
538     while ($cur_row_data = (
539       (
540         $rows_pos >= 0
541           and
542         (
543           $_[0][$rows_pos++]
544             or
545           ( ($rows_pos = -1), undef )
546         )
547       )
548         or
549       ( $_[1] and $_[1]->() )
550     ) ) {
551
552       ( @cur_row_ids{( 0, 1, 5, 6, 8, 10 )} = @{$cur_row_data}[( 0, 1, 5, 6, 8, 10 )] ),
553
554       # a present cref in $_[1] implies lazy prefetch, implies a supplied stash in $_[2]
555       ( $_[1] and $result_pos and ! $collapse_idx[0]{$cur_row_ids{1}} and (unshift @{$_[2]}, $cur_row_data) and last ),
556
557       ( $collapse_idx[0]{$cur_row_ids{1}} //= $_[0][$result_pos++] = [{ genreid => $cur_row_data->[4], latest_cd => $cur_row_data->[7], year => $cur_row_data->[3] }] ),
558
559       ( $collapse_idx[0]{$cur_row_ids{1}}[1]{existing_single_track} //= $collapse_idx[1]{$cur_row_ids{1}} = [] ),
560       ( $collapse_idx[1]{$cur_row_ids{1}}[1]{cd} //= $collapse_idx[2]{$cur_row_ids{1}} = [] ),
561       ( $collapse_idx[2]{$cur_row_ids{1}}[1]{artist} //= $collapse_idx[3]{$cur_row_ids{1}} = [{ artistid => $cur_row_data->[1] }] ),
562
563       ( (! defined($cur_row_data->[6])) ? $collapse_idx[3]{$cur_row_ids{1}}[1]{cds} = [] : do {
564         (
565           (! $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{6}} )
566             and
567           push @{ $collapse_idx[3]{$cur_row_ids{1}}[1]{cds} }, (
568             $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] }]
569           )
570         ),
571
572         ( (! defined($cur_row_data->[8]) ) ? $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{6}}[1]{tracks} = [] : do {
573           (
574             (! $collapse_idx[5]{$cur_row_ids{1}}{$cur_row_ids{6}}{$cur_row_ids{8}} )
575               and
576             push @{ $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{6}}[1]{tracks} }, (
577               $collapse_idx[5]{$cur_row_ids{1}}{$cur_row_ids{6}}{$cur_row_ids{8}} = [{ title => $cur_row_data->[8] }]
578             )
579           ),
580         } ),
581       } ),
582
583       ( (! defined($cur_row_data->[5]) ) ? $collapse_idx[0]{$cur_row_ids{1}}[1]{tracks} = [] : do {
584
585         (
586           (! $collapse_idx[6]{$cur_row_ids{1}}{$cur_row_ids{5}} )
587             and
588           push @{ $collapse_idx[0]{$cur_row_ids{1}}[1]{tracks} }, (
589             $collapse_idx[6]{$cur_row_ids{1}}{$cur_row_ids{5}} = [{ title => $cur_row_data->[5] }]
590           )
591         ),
592
593         ( (! defined($cur_row_data->[10]) ) ? $collapse_idx[6]{$cur_row_ids{1}}{$cur_row_ids{5}}[1]{lyrics} = [] : do {
594
595           ( $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}} = [] ),
596
597           (
598             (! $collapse_idx[8]{$cur_row_ids{0}}{$cur_row_ids{1}}{$cur_row_ids{5}}{$cur_row_ids{10}} )
599               and
600             push @{ $collapse_idx[7]{$cur_row_ids{1}}{$cur_row_ids{5}}{$cur_row_ids{10}}[1]{existing_lyric_versions} }, (
601               $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] }]
602             )
603           ),
604         } ),
605       } ),
606     }
607
608     $#{$_[0]} = $result_pos - 1;
609   ',
610   'Multiple has_many on multiple branches with branch pruning torture test',
611 );
612
613 $infmap = [
614   'single_track.trackid',                   # (0) definitive link to root from 1:1:1:1:M:M chain
615   'year',                                   # (1) non-unique
616   'tracks.cd',                              # (2) \ together both uniqueness for second multirel
617   'tracks.title',                           # (3) / and definitive link back to root
618   'single_track.cd.artist.cds.cdid',        # (4) to give uniquiness to ...tracks.title below
619   'single_track.cd.artist.cds.year',        # (5) non-unique
620   'single_track.cd.artist.artistid',        # (6) uniqufies entire parental chain
621   'single_track.cd.artist.cds.genreid',     # (7) nullable
622   'single_track.cd.artist.cds.tracks.title',# (8) unique when combined with ...cds.cdid above
623 ];
624
625 is_deeply (
626   $schema->source('CD')->_resolve_collapse({ as => {map { $infmap->[$_] => $_ } 0 .. $#$infmap} }),
627   {
628     -identifying_columns => [],
629     -identifying_columns_variants => [
630       [ 0 ], [ 2 ],
631     ],
632     single_track => {
633       -identifying_columns => [ 0 ],
634       -is_optional => 1,
635       -is_single => 1,
636       cd => {
637         -identifying_columns => [ 0 ],
638         -is_single => 1,
639         artist => {
640           -identifying_columns => [ 0 ],
641           -is_single => 1,
642           cds => {
643             -identifying_columns => [ 0, 4 ],
644             -is_optional => 1,
645             tracks => {
646               -identifying_columns => [ 0, 4, 8 ],
647               -is_optional => 1,
648             }
649           }
650         }
651       }
652     },
653     tracks => {
654       -identifying_columns => [ 2, 3 ],
655       -is_optional => 1,
656     }
657   },
658   'Correct underdefined root collapse map constructed'
659 );
660
661 is_same_src (
662   ($schema->source ('CD')->_mk_row_parser({
663     inflate_map => $infmap,
664     collapse => 1,
665   }))[0],
666   ' my $rows_pos = 0;
667     my ($result_pos, @collapse_idx, $cur_row_data, %cur_row_ids);
668
669     while ($cur_row_data = (
670       (
671         $rows_pos >= 0
672           and
673         (
674           $_[0][$rows_pos++]
675             or
676           ( ($rows_pos = -1), undef )
677         )
678       )
679         or
680       ( $_[1] and $_[1]->() )
681     ) ) {
682
683       ( @cur_row_ids{( 0, 2, 3, 4, 8 )} = (
684         $cur_row_data->[0] // "\0NULL\xFF$rows_pos\xFF0\0",
685         $cur_row_data->[2] // "\0NULL\xFF$rows_pos\xFF2\0",
686         $cur_row_data->[3] // "\0NULL\xFF$rows_pos\xFF3\0",
687         $cur_row_data->[4] // "\0NULL\xFF$rows_pos\xFF4\0",
688         $cur_row_data->[8] // "\0NULL\xFF$rows_pos\xFF8\0",
689       )),
690
691       # cache expensive set of ops in a non-existent rowid slot
692       ( $cur_row_ids{10} = (
693         ( ( defined $cur_row_data->[0] ) && (join "\xFF", q{}, $cur_row_ids{0}, q{} ))
694           or
695         ( ( defined $cur_row_data->[2] ) && (join "\xFF", q{}, $cur_row_ids{2}, q{} ))
696           or
697         "\0$rows_pos\0"
698       )),
699
700       # a present cref in $_[1] implies lazy prefetch, implies a supplied stash in $_[2]
701       ( $_[1] and $result_pos and ! $collapse_idx[0]{$cur_row_ids{10}} and (unshift @{$_[2]}, $cur_row_data) and last ),
702
703       ( $collapse_idx[0]{$cur_row_ids{10}} //= $_[0][$result_pos++] = [{ year => $$cur_row_data[1] }] ),
704
705       ( $collapse_idx[0]{$cur_row_ids{10}}[1]{single_track} //= ($collapse_idx[1]{$cur_row_ids{0}} = [{ trackid => $cur_row_data->[0] }]) ),
706       ( defined($cur_row_data->[0]) or bless ( $collapse_idx[0]{$cur_row_ids{10}}[1]{single_track}, __NBC__ ) ),
707
708       ( $collapse_idx[1]{$cur_row_ids{0}}[1]{cd} //= $collapse_idx[2]{$cur_row_ids{0}} = [] ),
709
710       ( $collapse_idx[2]{$cur_row_ids{0}}[1]{artist} //= ($collapse_idx[3]{$cur_row_ids{0}} = [{ artistid => $cur_row_data->[6] }]) ),
711
712       (
713         (! $collapse_idx[4]{$cur_row_ids{0}}{$cur_row_ids{4}} )
714           and
715         push @{$collapse_idx[3]{$cur_row_ids{0}}[1]{cds}}, (
716           $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] }]
717         )
718       ),
719       ( defined($cur_row_data->[4]) or bless ( $collapse_idx[3]{$cur_row_ids{0}}[1]{cds}, __NBC__ ) ),
720
721       (
722         (! $collapse_idx[5]{$cur_row_ids{0}}{$cur_row_ids{4}}{$cur_row_ids{8}} )
723           and
724         push @{$collapse_idx[4]{$cur_row_ids{0}}{$cur_row_ids{4}}[1]{tracks}}, (
725             $collapse_idx[5]{$cur_row_ids{0}}{$cur_row_ids{4}}{$cur_row_ids{8}} = [{ title => $cur_row_data->[8] }]
726         )
727       ),
728       ( defined($cur_row_data->[8]) or bless ( $collapse_idx[4]{$cur_row_ids{0}}{$cur_row_ids{4}}[1]{tracks}, __NBC__ ) ),
729
730       (
731         (! $collapse_idx[6]{$cur_row_ids{2}}{$cur_row_ids{3}} )
732           and
733         push @{$collapse_idx[0]{$cur_row_ids{10}}[1]{tracks}}, (
734           $collapse_idx[6]{$cur_row_ids{2}}{$cur_row_ids{3}} = [{ cd => $$cur_row_data[2], title => $cur_row_data->[3] }]
735         )
736       ),
737       ( defined($cur_row_data->[2]) or bless ( $collapse_idx[0]{$cur_row_ids{10}}[1]{tracks}, __NBC__ ) ),
738     }
739
740     $#{$_[0]} = $result_pos - 1;
741   ',
742   'Multiple has_many on multiple branches with underdefined root torture test',
743 );
744
745 is_same_src (
746   ($schema->source ('CD')->_mk_row_parser({
747     inflate_map => $infmap,
748     collapse => 1,
749     hri_style => 1,
750     prune_null_branches => 1,
751   }))[0],
752   ' my $rows_pos = 0;
753     my ($result_pos, @collapse_idx, $cur_row_data, %cur_row_ids);
754
755     while ($cur_row_data = (
756       (
757         $rows_pos >= 0
758           and
759         (
760           $_[0][$rows_pos++]
761             or
762           ( ($rows_pos = -1), undef )
763         )
764       )
765         or
766       ( $_[1] and $_[1]->() )
767     ) ) {
768
769       # do not care about nullability here
770       ( @cur_row_ids{( 0, 2, 3, 4, 8 )} = @{$cur_row_data}[( 0, 2, 3, 4, 8 )] ),
771
772       # cache expensive set of ops in a non-existent rowid slot
773       ( $cur_row_ids{10} = (
774         ( ( defined $cur_row_data->[0] ) && (join "\xFF", q{}, $cur_row_ids{0}, q{} ))
775           or
776         ( ( defined $cur_row_data->[2] ) && (join "\xFF", q{}, $cur_row_ids{2}, q{} ))
777           or
778         "\0$rows_pos\0"
779       )),
780
781       # a present cref in $_[1] implies lazy prefetch, implies a supplied stash in $_[2]
782       ( $_[1] and $result_pos and ! $collapse_idx[0]{$cur_row_ids{10}} and (unshift @{$_[2]}, $cur_row_data) and last ),
783
784       ( $collapse_idx[0]{$cur_row_ids{10}} //= $_[0][$result_pos++] = { year => $$cur_row_data[1] } ),
785
786       ( (! defined $cur_row_data->[0] ) ? $collapse_idx[0]{$cur_row_ids{10}}{single_track} = undef : do {
787
788         ( $collapse_idx[0]{$cur_row_ids{10}}{single_track} //= ($collapse_idx[1]{$cur_row_ids{0}} = { trackid => $$cur_row_data[0] }) ),
789
790         ( $collapse_idx[1]{$cur_row_ids{0}}{cd} //= $collapse_idx[2]{$cur_row_ids{0}} = {} ),
791
792         ( $collapse_idx[2]{$cur_row_ids{0}}{artist} //= ($collapse_idx[3]{$cur_row_ids{0}} = { artistid => $$cur_row_data[6] }) ),
793
794         ( (! defined $cur_row_data->[4] ) ? $collapse_idx[3]{$cur_row_ids{0}}{cds} = [] : do {
795
796           (
797             (! $collapse_idx[4]{$cur_row_ids{0}}{$cur_row_ids{4}} )
798               and
799             push @{$collapse_idx[3]{$cur_row_ids{0}}{cds}}, (
800                 $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] }
801             )
802           ),
803
804           ( (! defined $cur_row_data->[8] ) ? $collapse_idx[4]{$cur_row_ids{0}}{$cur_row_ids{4}}{tracks} = [] : do {
805
806             (! $collapse_idx[5]{$cur_row_ids{0}}{$cur_row_ids{4}}{$cur_row_ids{8}} )
807               and
808             push @{$collapse_idx[4]{$cur_row_ids{0}}{$cur_row_ids{4}}{tracks}}, (
809                 $collapse_idx[5]{$cur_row_ids{0}}{$cur_row_ids{4}}{$cur_row_ids{8}} = { title => $$cur_row_data[8] }
810             ),
811           } ),
812         } ),
813       } ),
814
815       ( (! defined $cur_row_data->[2] ) ? $collapse_idx[0]{$cur_row_ids{10}}{tracks} = [] : do {
816         (
817           (! $collapse_idx[6]{$cur_row_ids{2}}{$cur_row_ids{3}} )
818             and
819           push @{$collapse_idx[0]{$cur_row_ids{10}}{tracks}}, (
820             $collapse_idx[6]{$cur_row_ids{2}}{$cur_row_ids{3}} = { cd => $$cur_row_data[2], title => $$cur_row_data[3] }
821           )
822         ),
823       } ),
824     }
825
826     $#{$_[0]} = $result_pos - 1;
827   ',
828   'Multiple has_many on multiple branches with underdefined root, HRI-direct torture test',
829 );
830
831
832 $infmap = [
833   'single_track.lyrics.track_id',             # (0) random optional 1:1:1 chain
834   'year',                                     # (1) non-unique
835   'tracks.cd',                                # (2) \ together both uniqueness for second multirel
836   'tracks.title',                             # (3) / and definitive link back to root
837   'single_track.cd.artist.cds.cdid',          # (4) to give uniquiness to ...tracks.title below
838   'single_track.cd.artist.cds.year',          # (5) non-unique
839   'single_track.cd.artist.artistid',          # (6) uniqufies entire parental chain
840   'single_track.cd.artist.cds.genreid',       # (7) nullable
841   'single_track.cd.artist.cds.tracks.title',  # (8) unique when combined with ...cds.cdid above
842   'single_track.lyrics.lyric_versions.text',  # (9) unique combined with the single_track.lyrics 1:1:1
843 ];
844
845 is_deeply (
846   $schema->source('CD')->_resolve_collapse({ as => {map { $infmap->[$_] => $_ } 0 .. $#$infmap} }),
847   {
848     -identifying_columns => [],
849     -identifying_columns_variants => [
850       [ 2 ], [ 6 ],
851     ],
852     single_track => {
853       -identifying_columns => [ 6 ],
854       -is_optional => 1,
855       -is_single => 1,
856       cd => {
857         -identifying_columns => [ 6 ],
858         -is_single => 1,
859         artist => {
860           -identifying_columns => [ 6 ],
861           -is_single => 1,
862           cds => {
863             -identifying_columns => [ 4, 6 ],
864             -is_optional => 1,
865             tracks => {
866               -identifying_columns => [ 4, 6, 8 ],
867               -is_optional => 1,
868             }
869           }
870         }
871       },
872       lyrics => {
873         -identifying_columns => [ 0, 6 ],
874         -is_optional => 1,
875         -is_single => 1,
876         lyric_versions => {
877           -identifying_columns => [ 0, 6, 9 ],
878           -is_optional => 1,
879         },
880       },
881     },
882     tracks => {
883       -identifying_columns => [ 2, 3 ],
884       -is_optional => 1,
885     }
886   },
887   'Correct underdefined root tripple-has-many-torture collapse map constructed'
888 );
889
890 is_same_src (
891   ($schema->source ('CD')->_mk_row_parser({
892     inflate_map => $infmap,
893     collapse => 1,
894     hri_style => 1,
895     prune_null_branches => 1,
896   }))[0],
897   ' my $rows_pos = 0;
898     my ($result_pos, @collapse_idx, $cur_row_data, %cur_row_ids);
899
900     while ($cur_row_data = (
901       (
902         $rows_pos >= 0
903           and
904         (
905           $_[0][$rows_pos++]
906             or
907           ( ($rows_pos = -1), undef )
908         )
909       )
910         or
911       ( $_[1] and $_[1]->() )
912     ) ) {
913
914       # do not care about nullability here
915       ( @cur_row_ids{( 0, 2, 3, 4, 6, 8, 9 )} = @{$cur_row_data}[( 0, 2, 3, 4, 6, 8, 9 )] ),
916
917       # cache expensive set of ops in a non-existent rowid slot
918       ( $cur_row_ids{11} = (
919         ( ( defined $cur_row_data->[2] ) && (join "\xFF", q{}, $cur_row_ids{2}, q{} ))
920           or
921         ( ( defined $cur_row_data->[6] ) && (join "\xFF", q{}, $cur_row_ids{6}, q{} ))
922           or
923         "\0$rows_pos\0"
924       )),
925
926       # a present cref in $_[1] implies lazy prefetch, implies a supplied stash in $_[2]
927       ( $_[1] and $result_pos and ! $collapse_idx[0]{$cur_row_ids{11}} and (unshift @{$_[2]}, $cur_row_data) and last ),
928
929       ( $collapse_idx[0]{$cur_row_ids{11}} //= $_[0][$result_pos++] = { year => $$cur_row_data[1] } ),
930
931       ( (! defined $cur_row_data->[6] ) ? $collapse_idx[0]{$cur_row_ids{11}}{single_track} = undef : do {
932
933         ( $collapse_idx[0]{$cur_row_ids{11}}{single_track} //= ( $collapse_idx[1]{$cur_row_ids{6}} = {} ) ),
934
935         ( $collapse_idx[1]{$cur_row_ids{6}}{cd} //= $collapse_idx[2]{$cur_row_ids{6}} = {} ),
936
937         ( $collapse_idx[2]{$cur_row_ids{6}}{artist} //= ($collapse_idx[3]{$cur_row_ids{6}} = { artistid => $$cur_row_data[6] }) ),
938
939         ( (! defined $cur_row_data->[4] ) ? $collapse_idx[3]{$cur_row_ids{6}}{cds} = [] : do {
940
941           (
942             (! $collapse_idx[4]{$cur_row_ids{4}}{$cur_row_ids{6}} )
943               and
944             push @{$collapse_idx[3]{$cur_row_ids{6}}{cds}}, (
945                 $collapse_idx[4]{$cur_row_ids{4}}{$cur_row_ids{6}} = { cdid => $$cur_row_data[4], genreid => $$cur_row_data[7], year => $$cur_row_data[5] }
946             )
947           ),
948
949           ( (! defined $cur_row_data->[8] ) ? $collapse_idx[4]{$cur_row_ids{4}}{$cur_row_ids{6}}{tracks} = [] : do {
950
951             (! $collapse_idx[5]{$cur_row_ids{4}}{$cur_row_ids{6}}{$cur_row_ids{8}} )
952               and
953             push @{$collapse_idx[4]{$cur_row_ids{4}}{$cur_row_ids{6}}{tracks}}, (
954                 $collapse_idx[5]{$cur_row_ids{4}}{$cur_row_ids{6}}{$cur_row_ids{8}} = { title => $$cur_row_data[8] }
955             ),
956           } ),
957         } ),
958
959         ( ( ! defined $cur_row_data->[0] ) ? $collapse_idx[1]{ $cur_row_ids{6} }{"lyrics"} = undef : do {
960
961           ( $collapse_idx[1]{ $cur_row_ids{6} }{"lyrics"} //= ( $collapse_idx[6]{ $cur_row_ids{0} }{ $cur_row_ids{6} } = { "track_id" => $cur_row_data->[0] } ) ),
962
963           ( ( ! defined $cur_row_data->[9] ) ? $collapse_idx[6]{ $cur_row_ids{0} }{ $cur_row_ids{6} }{"lyric_versions"} = [] : do {
964             (
965               (! $collapse_idx[7]{ $cur_row_ids{0} }{ $cur_row_ids{6} }{ $cur_row_ids{9} })
966                 and
967               push @{$collapse_idx[6]{ $cur_row_ids{0} }{ $cur_row_ids{6} }{"lyric_versions"}}, (
968                 $collapse_idx[7]{ $cur_row_ids{0} }{ $cur_row_ids{6} }{ $cur_row_ids{9} } = { "text" => $cur_row_data->[9] }
969               ),
970             ),
971           } ),
972         } ),
973       } ),
974
975       ( (! defined $cur_row_data->[2] ) ? $collapse_idx[0]{$cur_row_ids{11}}{tracks} = [] : do {
976         (
977           (! $collapse_idx[8]{$cur_row_ids{2}}{$cur_row_ids{3}} )
978             and
979           push @{$collapse_idx[0]{$cur_row_ids{11}}{tracks}}, (
980             $collapse_idx[8]{$cur_row_ids{2}}{$cur_row_ids{3}} = { cd => $$cur_row_data[2], title => $$cur_row_data[3] }
981           )
982         ),
983       } ),
984     }
985
986     $#{$_[0]} = $result_pos - 1;
987   ',
988   'Tripple multiple has_many on multiple branches with underdefined root, HRI-direct torture test',
989 );
990
991 is_same_src (
992   ($schema->source ('Owners')->_mk_row_parser({
993     inflate_map => [qw( books.title books.owner )],
994     collapse => 1,
995     prune_null_branches => 1,
996   }))[0],
997   ' my $rows_pos = 0;
998     my ($result_pos, @collapse_idx, $cur_row_data, %cur_row_ids );
999
1000     while ($cur_row_data = (
1001       (
1002         $rows_pos >= 0
1003           and
1004         (
1005           $_[0][$rows_pos++]
1006             or
1007           ( ($rows_pos = -1), undef )
1008         )
1009       )
1010         or
1011       ( $_[1] and $_[1]->() )
1012     ) ) {
1013
1014       ( @cur_row_ids{0,1} = @{$cur_row_data}[0,1] ),
1015
1016       ( $cur_row_ids{3} = (
1017         ( ( defined $cur_row_data->[1] ) && (join "\xFF", q{}, $cur_row_ids{1}, q{} ))
1018           or
1019         "\0${rows_pos}\0"
1020       )),
1021
1022       ( $_[1] and $result_pos and ! $collapse_idx[0]{$cur_row_ids{3}} and (unshift @{$_[2]}, $cur_row_data) and last ),
1023
1024       # empty data for the root node
1025       ( $collapse_idx[0]{$cur_row_ids{3}} //= $_[0][$result_pos++] = [] ),
1026
1027       ( ( ! defined $cur_row_data->[0] ) ? $collapse_idx[0]{$cur_row_ids{3}}[1]{"books"} = [] : do {
1028         ( ! $collapse_idx[1]{$cur_row_ids{0}} )
1029           and
1030         push @{$collapse_idx[0]{$cur_row_ids{3}}[1]{books}},
1031           $collapse_idx[1]{$cur_row_ids{0}} = [ { owner => $cur_row_data->[1], title => $cur_row_data->[0] } ]
1032       } ),
1033     }
1034
1035     $#{$_[0]} = $result_pos - 1; # truncate the passed in array to where we filled it with results
1036   ',
1037   'Non-premultiplied implicit collapse with missing join columns',
1038 );
1039
1040 done_testing;
1041
1042 my $deparser;
1043 sub is_same_src { SKIP: {
1044
1045   skip "Skipping comparison of unicode-poisoned source", 1
1046     if DBIx::Class::_ENV_::STRESSTEST_UTF8_UPGRADE_GENERATED_COLLAPSER_SOURCE;
1047
1048   $deparser ||= B::Deparse->new;
1049   local $Test::Builder::Level = $Test::Builder::Level + 1;
1050
1051   my ($got, $expect) = @_;
1052
1053   skip "Not testing equality of source containing defined-or operator on this perl $]", 1
1054     if ( "$]" < 5.010 and $expect =~ m!\Q//=! );
1055
1056   $expect =~ s/__NBC__/perlstring($DBIx::Class::ResultSource::RowParser::Util::null_branch_class)/ge;
1057
1058   $expect = "  { use strict; use warnings FATAL => 'uninitialized';\n$expect\n  }";
1059
1060   my @normalized = map {
1061     my $cref = eval "sub { $_ }" or do {
1062       fail "Coderef does not compile!\n\n$@\n\n$_";
1063       return undef;
1064     };
1065     $deparser->coderef2text($cref);
1066   } ($got, $expect);
1067
1068   &is (@normalized, $_[2]||() ) or do {
1069     eval { require Test::Differences }
1070       ? &Test::Differences::eq_or_diff( @normalized, $_[2]||() )
1071       : note ("Original sources:\n\n$got\n\n$expect\n")
1072     ;
1073     exit 1;
1074   };
1075 } }