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