File Coverage

blib/lib/DBIx/Class/ResultSource/RowParser.pm
Criterion Covered Total %
statement 513 513 100.0
branch 72 92 78.2
condition 45 62 72.5
subroutine 171 171 100.0
pod n/a
total 801 838 95.5


line stmt bran cond sub pod time code
1             package # hide from the pauses
2             DBIx::Class::ResultSource::RowParser;
3              
4 436     436   2514 use strict;
  436         894  
  436         11930  
5 436     436   1736 use warnings;
  436         827  
  436         11826  
6              
7 436     436   1757 use base 'DBIx::Class';
  436         887  
  436         43028  
8              
9 419     419   2156 use Try::Tiny;
  419         926  
  419         26697  
10 419     419   1999 use List::Util qw(first max);
  419         936  
  419         27633  
11              
12 419         36044 use DBIx::Class::ResultSource::RowParser::Util qw(
13             assemble_simple_parser
14             assemble_collapsing_parser
15 419     419   169626 );
  419         991  
16              
17 381     381   2414 use namespace::clean;
  381         734  
  381         2993  
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 966     966   1650 my ($self, $pre, $alias, $alias_map, $order, $pref_path) = @_;
25 966   100     2510 $pref_path ||= [];
26              
27 966 100 100     5729 if (not defined $pre or not length $pre) {
    100          
    100          
    50          
28 99         391 return ();
29             }
30             elsif( ref $pre eq 'ARRAY' ) {
31             return
32 362         747 map { $self->_resolve_prefetch( $_, $alias, $alias_map, $order, [ @$pref_path ] ) }
  422         2147  
33             @$pre;
34             }
35             elsif( ref $pre eq 'HASH' ) {
36             my @ret =
37             map {
38 98         269 $self->_resolve_prefetch($_, $alias, $alias_map, $order, [ @$pref_path ] ),
39             $self->related_source($_)->_resolve_prefetch(
40 97         436 $pre->{$_}, "${alias}.$_", $alias_map, $order, [ @$pref_path, $_] )
41             } keys %$pre;
42 98         596 return @ret;
43             }
44             elsif( ref $pre ) {
45 1         3 $self->throw_exception(
46             "don't know how to resolve prefetch reftype ".ref($pre));
47             }
48             else {
49 411         544 my $p = $alias_map;
50 411         1158 $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 411 50 33     1765 ) if (ref $p->{-join_aliases} ne 'ARRAY' or not @{$p->{-join_aliases}} );
  411         1605  
56              
57 411         735 my $as = shift @{$p->{-join_aliases}};
  411         940  
58              
59 411         1280 my $rel_info = $self->relationship_info( $pre );
60 411 50       996 $self->throw_exception( $self->source_name . " has no such relationship '$pre'" )
61             unless $rel_info;
62              
63 411 100       1768 my $as_prefix = ($alias =~ /^.*?\.(.+)$/ ? $1.'.' : '');
64              
65 411         1173 return map { [ "${as}.$_", "${as_prefix}${pre}.$_", ] }
  1849         7597  
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 234     235   514 my ($self, $args, $attrs) = @_;
98              
99             die "HRI without pruning makes zero sense"
100 234 50 66     1174 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 2215         4466 { $args->{inflate_map}[$_] => $_ }
107 234         1090 ( 0 .. $#{$args->{inflate_map}} )
  234         693  
108             },
109             );
110              
111 234         643 my $check_null_columns;
112              
113 234 100       965 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 1653 100       4921 { ref $attrs->{select}[$common{val_index}{$_}] ? () : ( $_ => $common{val_index}{$_} ) }
125 166         683 keys %{$common{val_index}}
126             },
127             premultiplied => $args->{premultiplied},
128 166         355 });
129              
130             $check_null_columns = $collapse_map->{-identifying_columns}
131 166 100       904 if @{$collapse_map->{-identifying_columns}};
  166         956  
132              
133 166         1294 assemble_collapsing_parser({
134             %common,
135             collapse_map => $collapse_map,
136             });
137             };
138              
139 234         708 utf8::upgrade($src)
140             if DBIx::Class::_ENV_::STRESSTEST_UTF8_UPGRADE_GENERATED_COLLAPSER_SOURCE;
141              
142             return (
143 234 100 50 41   22118 $args->{eval} ? ( eval "sub $src" || die $@ ) : $src,
  41     41   306  
  41     41   71  
  41     31   1306  
  41     31   225  
  41     31   60  
  41     1   1904  
  41     1   351  
  41     1   65  
  41     1   12627  
  31     1   253  
  31     1   59  
  31     1   882  
  31     1   214  
  31     1   49  
  31     1   1304  
  31     1   447  
  31     1   58  
  31     1   8179  
  1     1   43  
  1     1   6  
  1     1   4  
  1     1   62  
  1     1   8  
  1     1   1  
  1     1   328  
  1     1   7  
  1     1   2  
  1     1   26  
  1     1   4  
  1     1   1  
  1     1   38  
  1     1   3  
  1     1   1  
  1     1   202  
  1     1   7  
  1     1   1  
  1     1   26  
  1     1   3  
  1     1   2  
  1     1   35  
  1     1   3  
  1     1   2  
  1     1   254  
  1     1   7  
  1     1   1  
  1     1   24  
  1     1   3  
  1     1   1  
  1     1   32  
  1     1   2  
  1     1   1  
  1     1   114  
  1     1   7  
  1     1   3  
  1     1   25  
  1     1   4  
  1     1   2  
  1     1   37  
  1     1   3  
  1     1   1  
  1     1   245  
  1     1   7  
  1     1   1  
  1     1   24  
  1     1   4  
  1     1   1  
  1     1   42  
  1     1   3  
  1     1   1  
  1     1   207  
  1     1   8  
  1     1   2  
  1     1   28  
  1     1   4  
  1     1   1  
  1     1   40  
  1     1   4  
  1     1   1  
  1     1   239  
  1     1   12  
  1     1   2  
  1     1   47  
  1     1   7  
  1     1   4  
  1     1   67  
  1     1   8  
  1     1   1  
  1     1   568  
  1     1   7  
  1     1   2  
  1     1   29  
  1     1   3  
  1     1   1  
  1     1   39  
  1     1   3  
  1     1   1  
  1     1   159  
  1     1   7  
  1     1   1  
  1     1   26  
  1     1   4  
  1     1   0  
  1     1   38  
  1     1   4  
  1     1   1  
  1     1   132  
  1     1   8  
  1     1   1  
  1     1   26  
  1     1   4  
  1     1   1  
  1     1   37  
  1     1   3  
  1     1   1  
  1     1   130  
  1     1   7  
  1     1   1  
  1     1   25  
  1     1   4  
  1     1   1  
  1     1   37  
  1     1   3  
  1     1   1  
  1     1   248  
  1     1   8  
  1     1   2  
  1     1   25  
  1     1   4  
  1     1   1  
  1     1   36  
  1     1   3  
  1     1   1  
  1     1   158  
  1     1   8  
  1     1   1  
  1     1   30  
  1     1   4  
  1     1   0  
  1     1   35  
  1     1   3  
  1     1   2  
  1     1   117  
  1     1   6  
  1     1   1  
  1     1   25  
  1     1   3  
  1     1   2  
  1     1   48  
  1     1   3  
  1     1   2  
  1     1   207  
  1     1   28  
  1     1   2  
  1     1   32  
  1     1   4  
  1     1   2  
  1         36  
  1         29  
  1         2  
  1         173  
  1         8  
  1         1  
  1         24  
  1         4  
  1         1  
  1         44  
  1         4  
  1         1  
  1         140  
  1         7  
  1         2  
  1         25  
  1         3  
  1         1  
  1         37  
  1         3  
  1         1  
  1         137  
  1         7  
  1         2  
  1         24  
  1         4  
  1         1  
  1         44  
  1         3  
  1         2  
  1         262  
  1         7  
  1         2  
  1         28  
  1         3  
  1         1  
  1         38  
  1         3  
  1         2  
  1         191  
  1         11  
  1         2  
  1         41  
  1         9  
  1         1  
  1         67  
  1         8  
  1         2  
  1         366  
  1         7  
  1         2  
  1         27  
  1         3  
  1         1  
  1         36  
  1         3  
  1         1  
  1         260  
  1         6  
  1         1  
  1         24  
  1         3  
  1         1  
  1         33  
  1         3  
  1         2  
  1         114  
  1         7  
  1         1  
  1         24  
  1         5  
  1         1  
  1         41  
  1         3  
  1         1  
  1         276  
  1         6  
  1         2  
  1         25  
  1         3  
  1         1  
  1         38  
  1         3  
  1         2  
  1         237  
  1         9  
  1         1  
  1         31  
  1         5  
  1         1  
  1         40  
  1         2  
  1         2  
  1         244  
  1         7  
  1         1  
  1         24  
  1         4  
  1         1  
  1         34  
  1         3  
  1         1  
  1         137  
  1         7  
  1         1  
  1         24  
  1         3  
  1         1  
  1         35  
  1         4  
  1         1  
  1         185  
  1         7  
  1         1  
  1         23  
  1         4  
  1         1  
  1         36  
  1         3  
  1         1  
  1         191  
  1         7  
  1         2  
  1         50  
  1         5  
  1         1  
  1         45  
  1         4  
  1         1  
  1         288  
  1         6  
  1         2  
  1         24  
  1         3  
  1         1  
  1         35  
  1         3  
  1         1  
  1         210  
  1         6  
  1         1  
  1         23  
  1         3  
  1         2  
  1         35  
  1         3  
  1         1  
  1         277  
  1         7  
  1         1  
  1         24  
  1         3  
  1         1  
  1         34  
  1         2  
  1         2  
  1         240  
  1         7  
  1         1  
  1         24  
  1         4  
  1         2  
  1         33  
  1         3  
  1         2  
  1         239  
  1         6  
  1         1  
  1         24  
  1         3  
  1         2  
  1         34  
  1         3  
  1         1  
  1         209  
  1         7  
  1         1  
  1         23  
  1         4  
  1         2  
  1         34  
  1         3  
  1         1  
  1         210  
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 561     561   960 my ($self, $args, $common_args) = @_;
157              
158             # for comprehensible error messages put ourselves at the head of the relationship chain
159 561   100     2362 $args->{_rel_chain} ||= [ $self->source_name ];
160              
161             # record top-level fully-qualified column index, signify toplevelness
162 561 100       1497 unless ($common_args->{_as_fq_idx}) {
163 169         254 $common_args->{_as_fq_idx} = { %{$args->{as}} };
  169         1025  
164 169         695 $args->{_is_top_level} = 1;
165             };
166              
167 561         596 my ($my_cols, $rel_cols);
168 561         572 for (keys %{$args->{as}}) {
  561         1963  
169 3675 100       7539 if ($_ =~ /^ ([^\.]+) \. (.+) /x) {
170 1953         4193 $rel_cols->{$1}{$2} = 1;
171             }
172             else {
173 1723         2643 $my_cols->{$_} = {}; # important for ||='s below
174             }
175             }
176              
177 561         833 my $relinfo;
178             # run through relationships, collect metadata
179 561         1219 for my $rel (keys %$rel_cols) {
180 379         1580 my $inf = $self->relationship_info ($rel);
181              
182             $relinfo->{$rel} = {
183             is_single => ( $inf->{attrs}{accessor} && $inf->{attrs}{accessor} ne 'multi' ),
184 379   66     3873 is_inner => ( ( $inf->{attrs}{join_type} || '' ) !~ /^left/i),
      100        
185             rsrc => $self->related_source($rel),
186             };
187              
188             # FIME - need to use _resolve_cond here instead
189 379         1520 my $cond = $inf->{cond};
190              
191 379 50 66     3932 if (
      66        
      33        
192             ref $cond eq 'HASH'
193             and
194             keys %$cond
195             and
196 378     378   3077 ! defined first { $_ !~ /^foreign\./ } (keys %$cond)
197             and
198 378     378   1672 ! defined first { $_ !~ /^self\./ } (values %$cond)
199             ) {
200 378         797 for my $f (keys %$cond) {
201 378         611 my $s = $cond->{$f};
202 378         2528 $_ =~ s/^ (?: foreign | self ) \.//x for ($f, $s);
203 378         2135 $relinfo->{$rel}{fk_map}{$s} = $f;
204             }
205             }
206             }
207              
208             # inject non-left fk-bridges from *INNER-JOINED* children (if any)
209 561         1354 for my $rel (grep { $relinfo->{$_}{is_inner} } keys %$relinfo) {
  379         998  
210 124         196 my $ri = $relinfo->{$rel};
211 124         173 for (keys %{$ri->{fk_map}} ) {
  124         311  
212             # need to know source from *our* pov, hence $rel.col
213             $my_cols->{$_} ||= { via_fk => "$rel.$ri->{fk_map}{$_}" }
214 124 100 100     790 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 561         672 my $assumed_from_parent;
225 561 100 66     2174 if ( ! $args->{_parent_info}{underdefined} and ! $args->{_parent_info}{rev_rel_is_optional} ) {
226 395 100       721 for my $col ( values %{$args->{_parent_info}{rel_condition} || {}} ) {
  395         1724  
227 227 100       701 next if exists $my_cols->{$col};
228 54         179 $my_cols->{$col} = { via_collapse => $args->{_parent_info}{collapse_on_idcols} };
229 54         143 $assumed_from_parent->{columns}{$col}++;
230             }
231             }
232              
233             # get colinfo for everything
234 561 100       1205 if ($my_cols) {
235 541         1863 my $ci = $self->columns_info;
236 541         3214 $my_cols->{$_}{colinfo} = $ci->{$_} for keys %$my_cols;
237             }
238              
239 561         651 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 561 50       2339 unless ($collapse_map->{-identifying_columns}) {
244             $collapse_map->{-identifying_columns} = $args->{_parent_info}{collapse_on_idcols}
245 561 100       1383 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 561 100 100     3241 if (
      100        
250             ! $collapse_map->{-identifying_columns}
251             and
252             $my_cols
253             and
254 1541         3889 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 423         687 my @reduced_set = grep { ! $assumed_from_parent->{columns}{$_} } @$idset;
  526         1960  
259              
260             $collapse_map->{-identifying_columns} = [ __unique_numlist(
261 423 100       1673 @{ $args->{_parent_info}{collapse_on_idcols}||[] },
262              
263             (map
264             {
265 423         517 my $fqc = join ('.',
266 496         2472 @{$args->{_rel_chain}}[1 .. $#{$args->{_rel_chain}}],
  496         852  
267 496   66     636 ( $my_cols->{$_}{via_fk} || $_ ),
268             );
269              
270 496         1558 $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 561 100       2155 unless ($collapse_map->{-identifying_columns}) {
281 42         59 my @candidates;
282              
283 42         75 for my $rel (keys %$relinfo) {
284 53 100 66     257 next unless ($relinfo->{$rel}{is_single} && $relinfo->{$rel}{is_inner});
285              
286 28 50       53 if ( my $rel_collapse = $relinfo->{$rel}{rsrc}->_resolve_collapse ({
287             as => $rel_cols->{$rel},
288 28         211 _rel_chain => [ @{$args->{_rel_chain}}, $rel ],
289             _parent_info => { underdefined => 1 },
290             }, $common_args)) {
291 28         399 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 42 100       109 if (@candidates) {
299 28         63 ($collapse_map->{-identifying_columns}) = sort { scalar @$a <=> scalar @$b } (@candidates);
  1         25  
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 561 50 66     2439 if (
      66        
311             ! $collapse_map->{-identifying_columns}
312             and
313             ! $args->{premultiplied}
314             and
315             $args->{_is_top_level}
316             ) {
317 15         21 my (@collapse_sets, $uncollapsible_chain);
318              
319 15         71 for my $rel (keys %$relinfo) {
320              
321             # we already looked at these higher up
322 23 50 66     75 next if ($relinfo->{$rel}{is_single} && $relinfo->{$rel}{is_inner});
323              
324 23 50       44 if (my $clps = $relinfo->{$rel}{rsrc}->_resolve_collapse ({
325             as => $rel_cols->{$rel},
326 23         298 _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 23 100       81 if ($relinfo->{$rel}{is_single}) {
    50          
332 9         39 push @collapse_sets, $clps->{-identifying_columns};
333             }
334             elsif (! $relinfo->{$rel}{fk_map}) {
335 1         26 $uncollapsible_chain = 1;
336 1         5 last;
337             }
338             else {
339 15         26 my $defined_cols_parent_side;
340              
341 15         67 for my $fq_col ( grep { /^$rel\.[^\.]+$/ } keys %{$args->{as}} ) {
  96         354  
  15         45  
342 39         288 my ($col) = $fq_col =~ /([^\.]+)$/;
343              
344 39         60 $defined_cols_parent_side->{$_} = $args->{as}{$fq_col} for grep
345 39         154 { $relinfo->{$rel}{fk_map}{$_} eq $col }
346 39         112 keys %{$relinfo->{$rel}{fk_map}}
347             ;
348             }
349              
350 15 50       78 if (my $set = $self->_identifying_column_set([ keys %$defined_cols_parent_side ]) ) {
351 15         36 push @collapse_sets, [ sort map { $defined_cols_parent_side->{$_} } @$set ];
  15         147  
352             }
353             else {
354 1         4 $uncollapsible_chain = 1;
355 1         3 last;
356             }
357             }
358             }
359             else {
360 1         164 $uncollapsible_chain = 1;
361 1         7 last;
362             }
363             }
364              
365 15 50       41 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 15         66 $collapse_map->{-identifying_columns} = [];
373             $collapse_map->{-identifying_columns_variants} = [ sort {
374 15 0       54 (scalar @$a) <=> (scalar @$b) or max(@$a) <=> max(@$b)
  9         79  
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 561 100       2069 if ($args->{_parent_info}{underdefined}) {
    50          
383 50 50       338 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 1         195 @{$args->{_rel_chain}} > 1
391 1 0       2 ? sprintf (' (last member of the %s chain)', join ' -> ', @{$args->{_rel_chain}} )
  1         7  
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 512 100       1502 @{ $args->{_parent_info}{collapse_on_idcols}||[] },
402 512         551 @{ $collapse_map->{-identifying_columns} },
  512         1285  
403             )];
404              
405 512         779 my @id_sets;
406 512         1285 for my $rel (sort keys %$relinfo) {
407              
408             $collapse_map->{$rel} = $relinfo->{$rel}{rsrc}->_resolve_collapse ({
409 1820         2317 as => { map { $_ => 1 } ( keys %{$rel_cols->{$rel}} ) },
  344         1101  
410 344         892 _rel_chain => [ @{$args->{_rel_chain}}, $rel],
411             _parent_info => {
412             # shallow copy
413 344         1775 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 431 50 100 431   6871 { ref $_->{cond} eq 'HASH' and ($_->{attrs}{join_type}||'') !~ /^left/i }
423 344         1583 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 344 100 66     629 @{$collapse_map->{-identifying_columns}}
    100          
434             ) ? 1 : 0,
435             },
436             }, $common_args );
437              
438 344 100       2833 $collapse_map->{$rel}{-is_single} = 1 if $relinfo->{$rel}{is_single};
439 344 100 50     3388 $collapse_map->{$rel}{-is_optional} ||= 1 unless $relinfo->{$rel}{is_inner};
440             }
441              
442 512         3115 return $collapse_map;
443             }
444              
445             # adding a dep on MoreUtils *just* for this is retarded
446             sub __unique_numlist {
447 934     934   815 sort { $a <=> $b } keys %{ {map { $_ => 1 } @_ }}
  1104         3668  
  934         1336  
  2283         6780  
448             }
449              
450             1;