Filename | /usr/share/perl5/DBIx/Class/ResultSource/RowParser.pm |
Statements | Executed 15 statements in 3.05ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 2.00ms | 2.23ms | BEGIN@12 | DBIx::Class::ResultSource::RowParser::
1 | 1 | 1 | 29µs | 46µs | BEGIN@10 | DBIx::Class::ResultSource::RowParser::
1 | 1 | 1 | 23µs | 30µs | BEGIN@4 | DBIx::Class::ResultSource::RowParser::
1 | 1 | 1 | 14µs | 182µs | BEGIN@17 | DBIx::Class::ResultSource::RowParser::
1 | 1 | 1 | 12µs | 16µs | BEGIN@5 | DBIx::Class::ResultSource::RowParser::
1 | 1 | 1 | 10µs | 46µs | BEGIN@9 | DBIx::Class::ResultSource::RowParser::
1 | 1 | 1 | 8µs | 49µs | BEGIN@7 | DBIx::Class::ResultSource::RowParser::
0 | 0 | 0 | 0s | 0s | __ANON__[:196] | DBIx::Class::ResultSource::RowParser::
0 | 0 | 0 | 0s | 0s | __ANON__[:198] | DBIx::Class::ResultSource::RowParser::
0 | 0 | 0 | 0s | 0s | __ANON__[:422] | DBIx::Class::ResultSource::RowParser::
0 | 0 | 0 | 0s | 0s | __unique_numlist | DBIx::Class::ResultSource::RowParser::
0 | 0 | 0 | 0s | 0s | _mk_row_parser | DBIx::Class::ResultSource::RowParser::
0 | 0 | 0 | 0s | 0s | _resolve_collapse | DBIx::Class::ResultSource::RowParser::
0 | 0 | 0 | 0s | 0s | _resolve_prefetch | DBIx::Class::ResultSource::RowParser::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | package # hide from the pauses | ||||
2 | DBIx::Class::ResultSource::RowParser; | ||||
3 | |||||
4 | 2 | 44µs | 2 | 37µs | # spent 30µs (23+7) within DBIx::Class::ResultSource::RowParser::BEGIN@4 which was called:
# once (23µs+7µs) by base::import at line 4 # spent 30µs making 1 call to DBIx::Class::ResultSource::RowParser::BEGIN@4
# spent 7µs making 1 call to strict::import |
5 | 2 | 38µs | 2 | 21µs | # spent 16µs (12+4) within DBIx::Class::ResultSource::RowParser::BEGIN@5 which was called:
# once (12µs+4µs) by base::import at line 5 # spent 16µs making 1 call to DBIx::Class::ResultSource::RowParser::BEGIN@5
# spent 4µs making 1 call to warnings::import |
6 | |||||
7 | 2 | 70µs | 2 | 49µs | # spent 49µs (8+41) within DBIx::Class::ResultSource::RowParser::BEGIN@7 which was called:
# once (8µs+41µs) by base::import at line 7 # spent 49µs making 1 call to DBIx::Class::ResultSource::RowParser::BEGIN@7
# spent 41µs making 1 call to base::import, recursion: max depth 2, sum of overlapping time 41µs |
8 | |||||
9 | 2 | 68µs | 2 | 81µs | # spent 46µs (10+36) within DBIx::Class::ResultSource::RowParser::BEGIN@9 which was called:
# once (10µs+36µs) by base::import at line 9 # spent 46µs making 1 call to DBIx::Class::ResultSource::RowParser::BEGIN@9
# spent 36µs making 1 call to Exporter::import |
10 | 2 | 73µs | 2 | 62µs | # spent 46µs (29+17) within DBIx::Class::ResultSource::RowParser::BEGIN@10 which was called:
# once (29µs+17µs) by base::import at line 10 # spent 46µs making 1 call to DBIx::Class::ResultSource::RowParser::BEGIN@10
# spent 17µs making 1 call to List::Util::import |
11 | |||||
12 | 1 | 67µs | 1 | 48µs | # spent 2.23ms (2.00+236µs) within DBIx::Class::ResultSource::RowParser::BEGIN@12 which was called:
# once (2.00ms+236µs) by base::import at line 15 # spent 48µs making 1 call to Exporter::import |
13 | assemble_simple_parser | ||||
14 | assemble_collapsing_parser | ||||
15 | 1 | 181µs | 1 | 2.23ms | ); # spent 2.23ms making 1 call to DBIx::Class::ResultSource::RowParser::BEGIN@12 |
16 | |||||
17 | 2 | 2.51ms | 2 | 351µs | # spent 182µs (14+168) within DBIx::Class::ResultSource::RowParser::BEGIN@17 which was called:
# once (14µs+168µs) by base::import at line 17 # spent 182µs making 1 call to DBIx::Class::ResultSource::RowParser::BEGIN@17
# spent 168µs making 1 call to namespace::clean::import |
18 | |||||
19 | # Accepts one or more relationships for the current source and returns an | ||||
20 | # array of column names for each of those relationships. Column names are | ||||
21 | # prefixed relative to the current source, in accordance with where they appear | ||||
22 | # in the supplied relationships. | ||||
23 | sub _resolve_prefetch { | ||||
24 | my ($self, $pre, $alias, $alias_map, $order, $pref_path) = @_; | ||||
25 | $pref_path ||= []; | ||||
26 | |||||
27 | if (not defined $pre or not length $pre) { | ||||
28 | return (); | ||||
29 | } | ||||
30 | elsif( ref $pre eq 'ARRAY' ) { | ||||
31 | return | ||||
32 | map { $self->_resolve_prefetch( $_, $alias, $alias_map, $order, [ @$pref_path ] ) } | ||||
33 | @$pre; | ||||
34 | } | ||||
35 | elsif( ref $pre eq 'HASH' ) { | ||||
36 | my @ret = | ||||
37 | map { | ||||
38 | $self->_resolve_prefetch($_, $alias, $alias_map, $order, [ @$pref_path ] ), | ||||
39 | $self->related_source($_)->_resolve_prefetch( | ||||
40 | $pre->{$_}, "${alias}.$_", $alias_map, $order, [ @$pref_path, $_] ) | ||||
41 | } keys %$pre; | ||||
42 | return @ret; | ||||
43 | } | ||||
44 | elsif( ref $pre ) { | ||||
45 | $self->throw_exception( | ||||
46 | "don't know how to resolve prefetch reftype ".ref($pre)); | ||||
47 | } | ||||
48 | else { | ||||
49 | my $p = $alias_map; | ||||
50 | $p = $p->{$_} for (@$pref_path, $pre); | ||||
51 | |||||
52 | $self->throw_exception ( | ||||
53 | "Unable to resolve prefetch '$pre' - join alias map does not contain an entry for path: " | ||||
54 | . join (' -> ', @$pref_path, $pre) | ||||
55 | ) if (ref $p->{-join_aliases} ne 'ARRAY' or not @{$p->{-join_aliases}} ); | ||||
56 | |||||
57 | my $as = shift @{$p->{-join_aliases}}; | ||||
58 | |||||
59 | my $rel_info = $self->relationship_info( $pre ); | ||||
60 | $self->throw_exception( $self->source_name . " has no such relationship '$pre'" ) | ||||
61 | unless $rel_info; | ||||
62 | |||||
63 | my $as_prefix = ($alias =~ /^.*?\.(.+)$/ ? $1.'.' : ''); | ||||
64 | |||||
65 | return map { [ "${as}.$_", "${as_prefix}${pre}.$_", ] } | ||||
66 | $self->related_source($pre)->columns; | ||||
67 | } | ||||
68 | } | ||||
69 | |||||
70 | # Takes an arrayref of {as} dbic column aliases and the collapse and select | ||||
71 | # attributes from the same $rs (the selector requirement is a temporary | ||||
72 | # workaround... I hope), and returns a coderef capable of: | ||||
73 | # my $me_pref_clps = $coderef->([$rs->cursor->next/all]) | ||||
74 | # Where the $me_pref_clps arrayref is the future argument to inflate_result() | ||||
75 | # | ||||
76 | # For an example of this coderef in action (and to see its guts) look at | ||||
77 | # t/resultset/rowparser_internals.t | ||||
78 | # | ||||
79 | # This is a huge performance win, as we call the same code for every row | ||||
80 | # returned from the db, thus avoiding repeated method lookups when traversing | ||||
81 | # relationships | ||||
82 | # | ||||
83 | # Also since the coderef is completely stateless (the returned structure is | ||||
84 | # always fresh on every new invocation) this is a very good opportunity for | ||||
85 | # memoization if further speed improvements are needed | ||||
86 | # | ||||
87 | # The way we construct this coderef is somewhat fugly, although the result is | ||||
88 | # really worth it. The final coderef does not perform any kind of recursion - | ||||
89 | # the entire nested structure constructor is rolled out into a single scope. | ||||
90 | # | ||||
91 | # In any case - the output of this thing is meticulously micro-tested, so | ||||
92 | # any sort of adjustment/rewrite should be relatively easy (fsvo relatively) | ||||
93 | # | ||||
94 | sub _mk_row_parser { | ||||
95 | # $args and $attrs are separated to delineate what is core collapser stuff and | ||||
96 | # what is dbic $rs specific | ||||
97 | my ($self, $args, $attrs) = @_; | ||||
98 | |||||
99 | die "HRI without pruning makes zero sense" | ||||
100 | if ( $args->{hri_style} && ! $args->{prune_null_branches} ); | ||||
101 | |||||
102 | my %common = ( | ||||
103 | hri_style => $args->{hri_style}, | ||||
104 | prune_null_branches => $args->{prune_null_branches}, | ||||
105 | val_index => { map | ||||
106 | { $args->{inflate_map}[$_] => $_ } | ||||
107 | ( 0 .. $#{$args->{inflate_map}} ) | ||||
108 | }, | ||||
109 | ); | ||||
110 | |||||
111 | my $check_null_columns; | ||||
112 | |||||
113 | my $src = (! $args->{collapse} ) ? assemble_simple_parser(\%common) : do { | ||||
114 | my $collapse_map = $self->_resolve_collapse ({ | ||||
115 | # FIXME | ||||
116 | # only consider real columns (not functions) during collapse resolution | ||||
117 | # this check shouldn't really be here, as fucktards are not supposed to | ||||
118 | # alias random crap to existing column names anyway, but still - just in | ||||
119 | # case | ||||
120 | # FIXME !!!! - this does not yet deal with unbalanced selectors correctly | ||||
121 | # (it is now trivial as the attrs specify where things go out of sync | ||||
122 | # needs MOAR tests) | ||||
123 | as => { map | ||||
124 | { ref $attrs->{select}[$common{val_index}{$_}] ? () : ( $_ => $common{val_index}{$_} ) } | ||||
125 | keys %{$common{val_index}} | ||||
126 | }, | ||||
127 | premultiplied => $args->{premultiplied}, | ||||
128 | }); | ||||
129 | |||||
130 | $check_null_columns = $collapse_map->{-identifying_columns} | ||||
131 | if @{$collapse_map->{-identifying_columns}}; | ||||
132 | |||||
133 | assemble_collapsing_parser({ | ||||
134 | %common, | ||||
135 | collapse_map => $collapse_map, | ||||
136 | }); | ||||
137 | }; | ||||
138 | |||||
139 | utf8::upgrade($src) | ||||
140 | if DBIx::Class::_ENV_::STRESSTEST_UTF8_UPGRADE_GENERATED_COLLAPSER_SOURCE; | ||||
141 | |||||
142 | return ( | ||||
143 | $args->{eval} ? ( eval "sub $src" || die $@ ) : $src, | ||||
144 | $check_null_columns, | ||||
145 | ); | ||||
146 | } | ||||
147 | |||||
148 | |||||
149 | # Takes an arrayref selection list and generates a collapse-map representing | ||||
150 | # row-object fold-points. Every relationship is assigned a set of unique, | ||||
151 | # non-nullable columns (which may *not even be* from the same resultset) | ||||
152 | # and the collapser will use this information to correctly distinguish | ||||
153 | # data of individual to-be-row-objects. See t/resultset/rowparser_internals.t | ||||
154 | # for extensive RV examples | ||||
155 | sub _resolve_collapse { | ||||
156 | my ($self, $args, $common_args) = @_; | ||||
157 | |||||
158 | # for comprehensible error messages put ourselves at the head of the relationship chain | ||||
159 | $args->{_rel_chain} ||= [ $self->source_name ]; | ||||
160 | |||||
161 | # record top-level fully-qualified column index, signify toplevelness | ||||
162 | unless ($common_args->{_as_fq_idx}) { | ||||
163 | $common_args->{_as_fq_idx} = { %{$args->{as}} }; | ||||
164 | $args->{_is_top_level} = 1; | ||||
165 | }; | ||||
166 | |||||
167 | my ($my_cols, $rel_cols); | ||||
168 | for (keys %{$args->{as}}) { | ||||
169 | if ($_ =~ /^ ([^\.]+) \. (.+) /x) { | ||||
170 | $rel_cols->{$1}{$2} = 1; | ||||
171 | } | ||||
172 | else { | ||||
173 | $my_cols->{$_} = {}; # important for ||='s below | ||||
174 | } | ||||
175 | } | ||||
176 | |||||
177 | my $relinfo; | ||||
178 | # run through relationships, collect metadata | ||||
179 | for my $rel (keys %$rel_cols) { | ||||
180 | my $inf = $self->relationship_info ($rel); | ||||
181 | |||||
182 | $relinfo->{$rel} = { | ||||
183 | is_single => ( $inf->{attrs}{accessor} && $inf->{attrs}{accessor} ne 'multi' ), | ||||
184 | is_inner => ( ( $inf->{attrs}{join_type} || '' ) !~ /^left/i), | ||||
185 | rsrc => $self->related_source($rel), | ||||
186 | }; | ||||
187 | |||||
188 | # FIME - need to use _resolve_cond here instead | ||||
189 | my $cond = $inf->{cond}; | ||||
190 | |||||
191 | if ( | ||||
192 | ref $cond eq 'HASH' | ||||
193 | and | ||||
194 | keys %$cond | ||||
195 | and | ||||
196 | ! defined first { $_ !~ /^foreign\./ } (keys %$cond) | ||||
197 | and | ||||
198 | ! defined first { $_ !~ /^self\./ } (values %$cond) | ||||
199 | ) { | ||||
200 | for my $f (keys %$cond) { | ||||
201 | my $s = $cond->{$f}; | ||||
202 | $_ =~ s/^ (?: foreign | self ) \.//x for ($f, $s); | ||||
203 | $relinfo->{$rel}{fk_map}{$s} = $f; | ||||
204 | } | ||||
205 | } | ||||
206 | } | ||||
207 | |||||
208 | # inject non-left fk-bridges from *INNER-JOINED* children (if any) | ||||
209 | for my $rel (grep { $relinfo->{$_}{is_inner} } keys %$relinfo) { | ||||
210 | my $ri = $relinfo->{$rel}; | ||||
211 | for (keys %{$ri->{fk_map}} ) { | ||||
212 | # need to know source from *our* pov, hence $rel.col | ||||
213 | $my_cols->{$_} ||= { via_fk => "$rel.$ri->{fk_map}{$_}" } | ||||
214 | if defined $rel_cols->{$rel}{$ri->{fk_map}{$_}} # in fact selected | ||||
215 | } | ||||
216 | } | ||||
217 | |||||
218 | # if the parent is already defined *AND* we have an inner reverse relationship | ||||
219 | # (i.e. do not exist without it) , assume all of its related FKs are selected | ||||
220 | # (even if they in fact are NOT in the select list). Keep a record of what we | ||||
221 | # assumed, and if any such phantom-column becomes part of our own collapser, | ||||
222 | # throw everything assumed-from-parent away and replace with the collapser of | ||||
223 | # the parent (whatever it may be) | ||||
224 | my $assumed_from_parent; | ||||
225 | if ( ! $args->{_parent_info}{underdefined} and ! $args->{_parent_info}{rev_rel_is_optional} ) { | ||||
226 | for my $col ( values %{$args->{_parent_info}{rel_condition} || {}} ) { | ||||
227 | next if exists $my_cols->{$col}; | ||||
228 | $my_cols->{$col} = { via_collapse => $args->{_parent_info}{collapse_on_idcols} }; | ||||
229 | $assumed_from_parent->{columns}{$col}++; | ||||
230 | } | ||||
231 | } | ||||
232 | |||||
233 | # get colinfo for everything | ||||
234 | if ($my_cols) { | ||||
235 | my $ci = $self->columns_info; | ||||
236 | $my_cols->{$_}{colinfo} = $ci->{$_} for keys %$my_cols; | ||||
237 | } | ||||
238 | |||||
239 | my $collapse_map; | ||||
240 | |||||
241 | # first try to reuse the parent's collapser (i.e. reuse collapser over 1:1) | ||||
242 | # (makes for a leaner coderef later) | ||||
243 | unless ($collapse_map->{-identifying_columns}) { | ||||
244 | $collapse_map->{-identifying_columns} = $args->{_parent_info}{collapse_on_idcols} | ||||
245 | if $args->{_parent_info}{collapser_reusable}; | ||||
246 | } | ||||
247 | |||||
248 | # Still don't know how to collapse - try to resolve based on our columns (plus already inserted FK bridges) | ||||
249 | if ( | ||||
250 | ! $collapse_map->{-identifying_columns} | ||||
251 | and | ||||
252 | $my_cols | ||||
253 | and | ||||
254 | my $idset = $self->_identifying_column_set ({map { $_ => $my_cols->{$_}{colinfo} } keys %$my_cols}) | ||||
255 | ) { | ||||
256 | # see if the resulting collapser relies on any implied columns, | ||||
257 | # and fix stuff up if this is the case | ||||
258 | my @reduced_set = grep { ! $assumed_from_parent->{columns}{$_} } @$idset; | ||||
259 | |||||
260 | $collapse_map->{-identifying_columns} = [ __unique_numlist( | ||||
261 | @{ $args->{_parent_info}{collapse_on_idcols}||[] }, | ||||
262 | |||||
263 | (map | ||||
264 | { | ||||
265 | my $fqc = join ('.', | ||||
266 | @{$args->{_rel_chain}}[1 .. $#{$args->{_rel_chain}}], | ||||
267 | ( $my_cols->{$_}{via_fk} || $_ ), | ||||
268 | ); | ||||
269 | |||||
270 | $common_args->{_as_fq_idx}->{$fqc}; | ||||
271 | } | ||||
272 | @reduced_set | ||||
273 | ), | ||||
274 | )]; | ||||
275 | } | ||||
276 | |||||
277 | # Stil don't know how to collapse - keep descending down 1:1 chains - if | ||||
278 | # a related non-LEFT 1:1 is resolvable - its condition will collapse us | ||||
279 | # too | ||||
280 | unless ($collapse_map->{-identifying_columns}) { | ||||
281 | my @candidates; | ||||
282 | |||||
283 | for my $rel (keys %$relinfo) { | ||||
284 | next unless ($relinfo->{$rel}{is_single} && $relinfo->{$rel}{is_inner}); | ||||
285 | |||||
286 | if ( my $rel_collapse = $relinfo->{$rel}{rsrc}->_resolve_collapse ({ | ||||
287 | as => $rel_cols->{$rel}, | ||||
288 | _rel_chain => [ @{$args->{_rel_chain}}, $rel ], | ||||
289 | _parent_info => { underdefined => 1 }, | ||||
290 | }, $common_args)) { | ||||
291 | push @candidates, $rel_collapse->{-identifying_columns}; | ||||
292 | } | ||||
293 | } | ||||
294 | |||||
295 | # get the set with least amount of columns | ||||
296 | # FIXME - maybe need to implement a data type order as well (i.e. prefer several ints | ||||
297 | # to a single varchar) | ||||
298 | if (@candidates) { | ||||
299 | ($collapse_map->{-identifying_columns}) = sort { scalar @$a <=> scalar @$b } (@candidates); | ||||
300 | } | ||||
301 | } | ||||
302 | |||||
303 | # Stil don't know how to collapse, and we are the root node. Last ditch | ||||
304 | # effort in case we are *NOT* premultiplied. | ||||
305 | # Run through *each multi* all the way down, left or not, and all | ||||
306 | # *left* singles (a single may become a multi underneath) . When everything | ||||
307 | # gets back see if all the rels link to us definitively. If this is the | ||||
308 | # case we are good - either one of them will define us, or if all are NULLs | ||||
309 | # we know we are "unique" due to the "non-premultiplied" check | ||||
310 | if ( | ||||
311 | ! $collapse_map->{-identifying_columns} | ||||
312 | and | ||||
313 | ! $args->{premultiplied} | ||||
314 | and | ||||
315 | $args->{_is_top_level} | ||||
316 | ) { | ||||
317 | my (@collapse_sets, $uncollapsible_chain); | ||||
318 | |||||
319 | for my $rel (keys %$relinfo) { | ||||
320 | |||||
321 | # we already looked at these higher up | ||||
322 | next if ($relinfo->{$rel}{is_single} && $relinfo->{$rel}{is_inner}); | ||||
323 | |||||
324 | if (my $clps = $relinfo->{$rel}{rsrc}->_resolve_collapse ({ | ||||
325 | as => $rel_cols->{$rel}, | ||||
326 | _rel_chain => [ @{$args->{_rel_chain}}, $rel ], | ||||
327 | _parent_info => { underdefined => 1 }, | ||||
328 | }, $common_args) ) { | ||||
329 | |||||
330 | # for singles use the idcols wholesale (either there or not) | ||||
331 | if ($relinfo->{$rel}{is_single}) { | ||||
332 | push @collapse_sets, $clps->{-identifying_columns}; | ||||
333 | } | ||||
334 | elsif (! $relinfo->{$rel}{fk_map}) { | ||||
335 | $uncollapsible_chain = 1; | ||||
336 | last; | ||||
337 | } | ||||
338 | else { | ||||
339 | my $defined_cols_parent_side; | ||||
340 | |||||
341 | for my $fq_col ( grep { /^$rel\.[^\.]+$/ } keys %{$args->{as}} ) { | ||||
342 | my ($col) = $fq_col =~ /([^\.]+)$/; | ||||
343 | |||||
344 | $defined_cols_parent_side->{$_} = $args->{as}{$fq_col} for grep | ||||
345 | { $relinfo->{$rel}{fk_map}{$_} eq $col } | ||||
346 | keys %{$relinfo->{$rel}{fk_map}} | ||||
347 | ; | ||||
348 | } | ||||
349 | |||||
350 | if (my $set = $self->_identifying_column_set([ keys %$defined_cols_parent_side ]) ) { | ||||
351 | push @collapse_sets, [ sort map { $defined_cols_parent_side->{$_} } @$set ]; | ||||
352 | } | ||||
353 | else { | ||||
354 | $uncollapsible_chain = 1; | ||||
355 | last; | ||||
356 | } | ||||
357 | } | ||||
358 | } | ||||
359 | else { | ||||
360 | $uncollapsible_chain = 1; | ||||
361 | last; | ||||
362 | } | ||||
363 | } | ||||
364 | |||||
365 | unless ($uncollapsible_chain) { | ||||
366 | # if we got here - we are good to go, but the construction is tricky | ||||
367 | # since our children will want to include our collapse criteria - we | ||||
368 | # don't give them anything (safe, since they are all collapsible on their own) | ||||
369 | # in addition we record the individual collapse possibilities | ||||
370 | # of all left children node collapsers, and merge them in the rowparser | ||||
371 | # coderef later | ||||
372 | $collapse_map->{-identifying_columns} = []; | ||||
373 | $collapse_map->{-identifying_columns_variants} = [ sort { | ||||
374 | (scalar @$a) <=> (scalar @$b) or max(@$a) <=> max(@$b) | ||||
375 | } @collapse_sets ]; | ||||
376 | } | ||||
377 | } | ||||
378 | |||||
379 | # stop descending into children if we were called by a parent for first-pass | ||||
380 | # and don't despair if nothing was found (there may be other parallel branches | ||||
381 | # to dive into) | ||||
382 | if ($args->{_parent_info}{underdefined}) { | ||||
383 | return $collapse_map->{-identifying_columns} ? $collapse_map : undef | ||||
384 | } | ||||
385 | # nothing down the chain resolved - can't calculate a collapse-map | ||||
386 | elsif (! $collapse_map->{-identifying_columns}) { | ||||
387 | $self->throw_exception ( sprintf | ||||
388 | "Unable to calculate a definitive collapse column set for %s%s: fetch more unique non-nullable columns", | ||||
389 | $self->source_name, | ||||
390 | @{$args->{_rel_chain}} > 1 | ||||
391 | ? sprintf (' (last member of the %s chain)', join ' -> ', @{$args->{_rel_chain}} ) | ||||
392 | : '' | ||||
393 | , | ||||
394 | ); | ||||
395 | } | ||||
396 | |||||
397 | # If we got that far - we are collapsable - GREAT! Now go down all children | ||||
398 | # a second time, and fill in the rest | ||||
399 | |||||
400 | $collapse_map->{-identifying_columns} = [ __unique_numlist( | ||||
401 | @{ $args->{_parent_info}{collapse_on_idcols}||[] }, | ||||
402 | @{ $collapse_map->{-identifying_columns} }, | ||||
403 | )]; | ||||
404 | |||||
405 | my @id_sets; | ||||
406 | for my $rel (sort keys %$relinfo) { | ||||
407 | |||||
408 | $collapse_map->{$rel} = $relinfo->{$rel}{rsrc}->_resolve_collapse ({ | ||||
409 | as => { map { $_ => 1 } ( keys %{$rel_cols->{$rel}} ) }, | ||||
410 | _rel_chain => [ @{$args->{_rel_chain}}, $rel], | ||||
411 | _parent_info => { | ||||
412 | # shallow copy | ||||
413 | collapse_on_idcols => [ @{$collapse_map->{-identifying_columns}} ], | ||||
414 | |||||
415 | rel_condition => $relinfo->{$rel}{fk_map}, | ||||
416 | |||||
417 | is_optional => ! $relinfo->{$rel}{is_inner}, | ||||
418 | |||||
419 | # if there is at least one *inner* reverse relationship which is HASH-based (equality only) | ||||
420 | # we can safely assume that the child can not exist without us | ||||
421 | rev_rel_is_optional => ( first | ||||
422 | { ref $_->{cond} eq 'HASH' and ($_->{attrs}{join_type}||'') !~ /^left/i } | ||||
423 | values %{ $self->reverse_relationship_info($rel) }, | ||||
424 | ) ? 0 : 1, | ||||
425 | |||||
426 | # if this is a 1:1 our own collapser can be used as a collapse-map | ||||
427 | # (regardless of left or not) | ||||
428 | collapser_reusable => ( | ||||
429 | $relinfo->{$rel}{is_single} | ||||
430 | && | ||||
431 | $relinfo->{$rel}{is_inner} | ||||
432 | && | ||||
433 | @{$collapse_map->{-identifying_columns}} | ||||
434 | ) ? 1 : 0, | ||||
435 | }, | ||||
436 | }, $common_args ); | ||||
437 | |||||
438 | $collapse_map->{$rel}{-is_single} = 1 if $relinfo->{$rel}{is_single}; | ||||
439 | $collapse_map->{$rel}{-is_optional} ||= 1 unless $relinfo->{$rel}{is_inner}; | ||||
440 | } | ||||
441 | |||||
442 | return $collapse_map; | ||||
443 | } | ||||
444 | |||||
445 | # adding a dep on MoreUtils *just* for this is retarded | ||||
446 | sub __unique_numlist { | ||||
447 | sort { $a <=> $b } keys %{ {map { $_ => 1 } @_ }} | ||||
448 | } | ||||
449 | |||||
450 | 1 | 4µs | 1 | 233µs | 1; # spent 233µs making 1 call to B::Hooks::EndOfScope::XS::__ANON__ |