File Coverage

blib/lib/DBIx/Class/ResultSource/RowParser.pm
Criterion Covered Total %
statement 513 513 100.0
branch 72 92 78.2
condition 47 62 75.8
subroutine 171 171 100.0
pod n/a
total 803 838 95.8


line stmt bran cond sub pod time code
1             package # hide from the pauses
2             DBIx::Class::ResultSource::RowParser;
3              
4 436     436   3934 use strict;
  436         1464  
  436         12993  
5 436     436   2660 use warnings;
  436         1387  
  436         12927  
6              
7 436     436   2616 use base 'DBIx::Class';
  436         1524  
  436         59251  
8              
9 419     419   3737 use Try::Tiny;
  419         1466  
  419         27605  
10 419     419   3240 use List::Util qw(first max);
  419         1409  
  419         32524  
11              
12 419         38558 use DBIx::Class::ResultSource::RowParser::Util qw(
13             assemble_simple_parser
14             assemble_collapsing_parser
15 419     419   185343 );
  419         1454  
16              
17 381     381   3360 use namespace::clean;
  381         1278  
  381         2881  
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   2847 my ($self, $pre, $alias, $alias_map, $order, $pref_path) = @_;
25 966   100     3031 $pref_path ||= [];
26              
27 966 100 100     5765 if (not defined $pre or not length $pre) {
    100          
    100          
    50          
28 99         350 return ();
29             }
30             elsif( ref $pre eq 'ARRAY' ) {
31             return
32 362         974 map { $self->_resolve_prefetch( $_, $alias, $alias_map, $order, [ @$pref_path ] ) }
  422         2568  
33             @$pre;
34             }
35             elsif( ref $pre eq 'HASH' ) {
36             my @ret =
37             map {
38 98         315 $self->_resolve_prefetch($_, $alias, $alias_map, $order, [ @$pref_path ] ),
39             $self->related_source($_)->_resolve_prefetch(
40 97         517 $pre->{$_}, "${alias}.$_", $alias_map, $order, [ @$pref_path, $_] )
41             } keys %$pre;
42 98         672 return @ret;
43             }
44             elsif( ref $pre ) {
45 1         4 $self->throw_exception(
46             "don't know how to resolve prefetch reftype ".ref($pre));
47             }
48             else {
49 411         820 my $p = $alias_map;
50 411         1567 $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     1640 ) if (ref $p->{-join_aliases} ne 'ARRAY' or not @{$p->{-join_aliases}} );
  411         1611  
56              
57 411         1101 my $as = shift @{$p->{-join_aliases}};
  411         1080  
58              
59 411         1445 my $rel_info = $self->relationship_info( $pre );
60 411 50       1194 $self->throw_exception( $self->source_name . " has no such relationship '$pre'" )
61             unless $rel_info;
62              
63 411 100       2024 my $as_prefix = ($alias =~ /^.*?\.(.+)$/ ? $1.'.' : '');
64              
65 411         1410 return map { [ "${as}.$_", "${as_prefix}${pre}.$_", ] }
  1849         8794  
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   859 my ($self, $args, $attrs) = @_;
98              
99             die "HRI without pruning makes zero sense"
100 234 50 66     1163 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         5690 { $args->{inflate_map}[$_] => $_ }
107 234         1273 ( 0 .. $#{$args->{inflate_map}} )
  234         847  
108             },
109             );
110              
111 234         840 my $check_null_columns;
112              
113 234 100       1113 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       5415 { ref $attrs->{select}[$common{val_index}{$_}] ? () : ( $_ => $common{val_index}{$_} ) }
125 166         799 keys %{$common{val_index}}
126             },
127             premultiplied => $args->{premultiplied},
128 166         462 });
129              
130             $check_null_columns = $collapse_map->{-identifying_columns}
131 166 100       836 if @{$collapse_map->{-identifying_columns}};
  166         901  
132              
133 166         1464 assemble_collapsing_parser({
134             %common,
135             collapse_map => $collapse_map,
136             });
137             };
138              
139 234         934 utf8::upgrade($src)
140             if DBIx::Class::_ENV_::STRESSTEST_UTF8_UPGRADE_GENERATED_COLLAPSER_SOURCE;
141              
142             return (
143 234 100 50 41   24640 $args->{eval} ? ( eval "sub $src" || die $@ ) : $src,
  41     41   411  
  41     41   105  
  41     31   1455  
  41     31   331  
  41     31   147  
  41     1   2170  
  41     1   415  
  41     1   121  
  41     1   14831  
  31     1   355  
  31     1   100  
  31     1   1035  
  31     1   227  
  31     1   82  
  31     1   1375  
  31     1   397  
  31     1   85  
  31     1   9421  
  1     1   26  
  1     1   4  
  1     1   3  
  1     1   36  
  1     1   4  
  1     1   2  
  1     1   265  
  1     1   8  
  1     1   2  
  1     1   25  
  1     1   5  
  1     1   2  
  1     1   35  
  1     1   5  
  1     1   2  
  1     1   235  
  1     1   9  
  1     1   2  
  1     1   26  
  1     1   5  
  1     1   2  
  1     1   36  
  1     1   4  
  1     1   3  
  1     1   239  
  1     1   8  
  1     1   3  
  1     1   24  
  1     1   5  
  1     1   2  
  1     1   35  
  1     1   5  
  1     1   2  
  1     1   266  
  1     1   9  
  1     1   2  
  1     1   25  
  1     1   5  
  1     1   2  
  1     1   33  
  1     1   4  
  1     1   2  
  1     1   115  
  1     1   8  
  1     1   2  
  1     1   25  
  1     1   4  
  1     1   2  
  1     1   34  
  1     1   4  
  1     1   2  
  1     1   180  
  1     1   8  
  1     1   2  
  1     1   25  
  1     1   5  
  1     1   2  
  1     1   35  
  1     1   5  
  1     1   1  
  1     1   148  
  1     1   8  
  1     1   2  
  1     1   23  
  1     1   4  
  1     1   2  
  1     1   35  
  1     1   5  
  1     1   2  
  1     1   178  
  1     1   9  
  1     1   2  
  1     1   28  
  1     1   5  
  1     1   2  
  1     1   37  
  1     1   4  
  1     1   2  
  1     1   316  
  1     1   8  
  1     1   3  
  1     1   26  
  1     1   5  
  1     1   1  
  1     1   37  
  1     1   5  
  1     1   2  
  1     1   310  
  1     1   10  
  1     1   2  
  1     1   25  
  1     1   5  
  1     1   2  
  1     1   38  
  1     1   4  
  1     1   2  
  1     1   275  
  1     1   9  
  1     1   2  
  1     1   25  
  1     1   5  
  1     1   2  
  1     1   34  
  1     1   4  
  1     1   2  
  1     1   120  
  1     1   8  
  1     1   3  
  1     1   26  
  1     1   5  
  1     1   438  
  1     1   49  
  1     1   6  
  1     1   2  
  1     1   271  
  1     1   8  
  1     1   3  
  1     1   25  
  1     1   4  
  1     1   2  
  1     1   35  
  1     1   5  
  1     1   2  
  1     1   270  
  1     1   8  
  1     1   2  
  1     1   25  
  1     1   6  
  1     1   2  
  1     1   36  
  1     1   5  
  1     1   1  
  1     1   148  
  1     1   10  
  1     1   2  
  1     1   25  
  1     1   5  
  1     1   2  
  1         37  
  1         5  
  1         1  
  1         278  
  1         8  
  1         2  
  1         25  
  1         5  
  1         1  
  1         37  
  1         5  
  1         2  
  1         185  
  1         7  
  1         2  
  1         26  
  1         4  
  1         2  
  1         34  
  1         4  
  1         2  
  1         231  
  1         8  
  1         1  
  1         24  
  1         5  
  1         1  
  1         33  
  1         5  
  1         1  
  1         230  
  1         8  
  1         2  
  1         25  
  1         4  
  1         1  
  1         35  
  1         4  
  1         3  
  1         270  
  1         8  
  1         3  
  1         25  
  1         4  
  1         3  
  1         35  
  1         5  
  1         2  
  1         149  
  1         8  
  1         2  
  1         27  
  1         4  
  1         3  
  1         35  
  1         5  
  1         2  
  1         196  
  1         8  
  1         2  
  1         57  
  1         6  
  1         3  
  1         35  
  1         5  
  1         2  
  1         119  
  1         8  
  1         2  
  1         26  
  1         4  
  1         2  
  1         34  
  1         6  
  1         2  
  1         145  
  1         9  
  1         2  
  1         26  
  1         5  
  1         1  
  1         36  
  1         5  
  1         2  
  1         327  
  1         8  
  1         2  
  1         24  
  1         5  
  1         2  
  1         34  
  1         5  
  1         1  
  1         208  
  1         7  
  1         2  
  1         23  
  1         5  
  1         2  
  1         32  
  1         4  
  1         2  
  1         112  
  1         7  
  1         2  
  1         23  
  1         5  
  1         2  
  1         32  
  1         4  
  1         2  
  1         222  
  1         8  
  1         2  
  1         26  
  1         5  
  1         1  
  1         32  
  1         5  
  1         2  
  1         115  
  1         7  
  1         3  
  1         25  
  1         5  
  1         3  
  1         34  
  1         4  
  1         2  
  1         245  
  1         7  
  1         2  
  1         25  
  1         4  
  1         2  
  1         31  
  1         4  
  1         2  
  1         110  
  1         8  
  1         2  
  1         25  
  1         5  
  1         2  
  1         35  
  1         4  
  1         2  
  1         226  
  1         9  
  1         1  
  1         25  
  1         5  
  1         1  
  1         33  
  1         4  
  1         2  
  1         172  
  1         6  
  1         2  
  1         21  
  1         5  
  1         1  
  1         26  
  1         4  
  1         1  
  1         226  
  1         10  
  1         3  
  1         29  
  1         6  
  1         5  
  1         41  
  1         5  
  1         2  
  1         144  
  1         12  
  1         3  
  1         31  
  1         6  
  1         3  
  1         40  
  1         6  
  1         4  
  1         327  
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   1724 my ($self, $args, $common_args) = @_;
157              
158             # for comprehensible error messages put ourselves at the head of the relationship chain
159 561   100     2657 $args->{_rel_chain} ||= [ $self->source_name ];
160              
161             # record top-level fully-qualified column index, signify toplevelness
162 561 100       1863 unless ($common_args->{_as_fq_idx}) {
163 169         384 $common_args->{_as_fq_idx} = { %{$args->{as}} };
  169         1230  
164 169         688 $args->{_is_top_level} = 1;
165             };
166              
167 561         1090 my ($my_cols, $rel_cols);
168 561         927 for (keys %{$args->{as}}) {
  561         2266  
169 3675 100       10196 if ($_ =~ /^ ([^\.]+) \. (.+) /x) {
170 1953         5886 $rel_cols->{$1}{$2} = 1;
171             }
172             else {
173 1723         3918 $my_cols->{$_} = {}; # important for ||='s below
174             }
175             }
176              
177 561         1278 my $relinfo;
178             # run through relationships, collect metadata
179 561         1664 for my $rel (keys %$rel_cols) {
180 379         1703 my $inf = $self->relationship_info ($rel);
181              
182             $relinfo->{$rel} = {
183             is_single => ( $inf->{attrs}{accessor} && $inf->{attrs}{accessor} ne 'multi' ),
184 379   66     3883 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         1925 my $cond = $inf->{cond};
190              
191 379 50 66     4003 if (
      66        
      33        
192             ref $cond eq 'HASH'
193             and
194             keys %$cond
195             and
196 378     378   3327 ! defined first { $_ !~ /^foreign\./ } (keys %$cond)
197             and
198 378     378   1830 ! defined first { $_ !~ /^self\./ } (values %$cond)
199             ) {
200 378         1135 for my $f (keys %$cond) {
201 378         948 my $s = $cond->{$f};
202 378         2947 $_ =~ s/^ (?: foreign | self ) \.//x for ($f, $s);
203 378         2547 $relinfo->{$rel}{fk_map}{$s} = $f;
204             }
205             }
206             }
207              
208             # inject non-left fk-bridges from *INNER-JOINED* children (if any)
209 561         1867 for my $rel (grep { $relinfo->{$_}{is_inner} } keys %$relinfo) {
  379         1269  
210 124         316 my $ri = $relinfo->{$rel};
211 124         236 for (keys %{$ri->{fk_map}} ) {
  124         407  
212             # need to know source from *our* pov, hence $rel.col
213             $my_cols->{$_} ||= { via_fk => "$rel.$ri->{fk_map}{$_}" }
214 124 100 100     789 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         1088 my $assumed_from_parent;
225 561 100 100     2710 if ( ! $args->{_parent_info}{underdefined} and ! $args->{_parent_info}{rev_rel_is_optional} ) {
226 395 100       878 for my $col ( values %{$args->{_parent_info}{rel_condition} || {}} ) {
  395         1754  
227 227 100       762 next if exists $my_cols->{$col};
228 54         201 $my_cols->{$col} = { via_collapse => $args->{_parent_info}{collapse_on_idcols} };
229 54         210 $assumed_from_parent->{columns}{$col}++;
230             }
231             }
232              
233             # get colinfo for everything
234 561 100       1611 if ($my_cols) {
235 541         2011 my $ci = $self->columns_info;
236 541         3702 $my_cols->{$_}{colinfo} = $ci->{$_} for keys %$my_cols;
237             }
238              
239 561         1156 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       1768 unless ($collapse_map->{-identifying_columns}) {
244             $collapse_map->{-identifying_columns} = $args->{_parent_info}{collapse_on_idcols}
245 561 100       1628 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     3019 if (
      100        
250             ! $collapse_map->{-identifying_columns}
251             and
252             $my_cols
253             and
254 1541         4963 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         1032 my @reduced_set = grep { ! $assumed_from_parent->{columns}{$_} } @$idset;
  526         2142  
259              
260             $collapse_map->{-identifying_columns} = [ __unique_numlist(
261 423 100       1840 @{ $args->{_parent_info}{collapse_on_idcols}||[] },
262              
263             (map
264             {
265 423         860 my $fqc = join ('.',
266 496         2904 @{$args->{_rel_chain}}[1 .. $#{$args->{_rel_chain}}],
  496         1143  
267 496   66     1023 ( $my_cols->{$_}{via_fk} || $_ ),
268             );
269              
270 496         2063 $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       2585 unless ($collapse_map->{-identifying_columns}) {
281 42         96 my @candidates;
282              
283 42         120 for my $rel (keys %$relinfo) {
284 53 100 100     300 next unless ($relinfo->{$rel}{is_single} && $relinfo->{$rel}{is_inner});
285              
286 28 50       74 if ( my $rel_collapse = $relinfo->{$rel}{rsrc}->_resolve_collapse ({
287             as => $rel_cols->{$rel},
288 28         258 _rel_chain => [ @{$args->{_rel_chain}}, $rel ],
289             _parent_info => { underdefined => 1 },
290             }, $common_args)) {
291 28         307 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       142 if (@candidates) {
299 28         83 ($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     2066 if (
      66        
311             ! $collapse_map->{-identifying_columns}
312             and
313             ! $args->{premultiplied}
314             and
315             $args->{_is_top_level}
316             ) {
317 15         40 my (@collapse_sets, $uncollapsible_chain);
318              
319 15         78 for my $rel (keys %$relinfo) {
320              
321             # we already looked at these higher up
322 23 50 66     93 next if ($relinfo->{$rel}{is_single} && $relinfo->{$rel}{is_inner});
323              
324 23 50       62 if (my $clps = $relinfo->{$rel}{rsrc}->_resolve_collapse ({
325             as => $rel_cols->{$rel},
326 23         433 _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       99 if ($relinfo->{$rel}{is_single}) {
    50          
332 9         51 push @collapse_sets, $clps->{-identifying_columns};
333             }
334             elsif (! $relinfo->{$rel}{fk_map}) {
335 1         28 $uncollapsible_chain = 1;
336 1         5 last;
337             }
338             else {
339 15         32 my $defined_cols_parent_side;
340              
341 15         65 for my $fq_col ( grep { /^$rel\.[^\.]+$/ } keys %{$args->{as}} ) {
  96         585  
  15         59  
342 39         494 my ($col) = $fq_col =~ /([^\.]+)$/;
343              
344 39         81 $defined_cols_parent_side->{$_} = $args->{as}{$fq_col} for grep
345 39         176 { $relinfo->{$rel}{fk_map}{$_} eq $col }
346 39         127 keys %{$relinfo->{$rel}{fk_map}}
347             ;
348             }
349              
350 15 50       106 if (my $set = $self->_identifying_column_set([ keys %$defined_cols_parent_side ]) ) {
351 15         63 push @collapse_sets, [ sort map { $defined_cols_parent_side->{$_} } @$set ];
  15         168  
352             }
353             else {
354 1         5 $uncollapsible_chain = 1;
355 1         3 last;
356             }
357             }
358             }
359             else {
360 1         368 $uncollapsible_chain = 1;
361 1         8 last;
362             }
363             }
364              
365 15 50       58 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         64 $collapse_map->{-identifying_columns} = [];
373             $collapse_map->{-identifying_columns_variants} = [ sort {
374 15 0       53 (scalar @$a) <=> (scalar @$b) or max(@$a) <=> max(@$b)
  9         89  
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       1993 if ($args->{_parent_info}{underdefined}) {
    50          
383 50 50       372 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         178 @{$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       1775 @{ $args->{_parent_info}{collapse_on_idcols}||[] },
402 512         895 @{ $collapse_map->{-identifying_columns} },
  512         1442  
403             )];
404              
405 512         1285 my @id_sets;
406 512         1605 for my $rel (sort keys %$relinfo) {
407              
408             $collapse_map->{$rel} = $relinfo->{$rel}{rsrc}->_resolve_collapse ({
409 1820         3603 as => { map { $_ => 1 } ( keys %{$rel_cols->{$rel}} ) },
  344         1426  
410 344         1104 _rel_chain => [ @{$args->{_rel_chain}}, $rel],
411             _parent_info => {
412             # shallow copy
413 344         1801 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 430 50 100 430   7455 { ref $_->{cond} eq 'HASH' and ($_->{attrs}{join_type}||'') !~ /^left/i }
423 344         1930 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     890 @{$collapse_map->{-identifying_columns}}
    100          
434             ) ? 1 : 0,
435             },
436             }, $common_args );
437              
438 344 100       2704 $collapse_map->{$rel}{-is_single} = 1 if $relinfo->{$rel}{is_single};
439 344 100 50     1674 $collapse_map->{$rel}{-is_optional} ||= 1 unless $relinfo->{$rel}{is_inner};
440             }
441              
442 512         3246 return $collapse_map;
443             }
444              
445             # adding a dep on MoreUtils *just* for this is retarded
446             sub __unique_numlist {
447 934     934   1444 sort { $a <=> $b } keys %{ {map { $_ => 1 } @_ }}
  1132         4133  
  934         1707  
  2283         8048  
448             }
449              
450             1;