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 | { |
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 | |
106 | is_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 | |
168 | is_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 | |
215 | is_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 | |
276 | is_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 | |
312 | is_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 |
370 | done_testing; |
371 | |
372 | my $deparser; |
373 | sub 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 | |