use Try::Tiny;
use List::Util qw(first max);
-use B 'perlstring';
use DBIx::Class::ResultSource::RowParser::Util qw(
assemble_simple_parser
# any sort of adjustment/rewrite should be relatively easy (fsvo relatively)
#
sub _mk_row_parser {
- my ($self, $args) = @_;
+ # $args and $attrs are separated to delineate what is core collapser stuff and
+ # what is dbic $rs specific
+ my ($self, $args, $attrs) = @_;
- my $val_index = { map
- { $args->{inflate_map}[$_] => $_ }
- ( 0 .. $#{$args->{inflate_map}} )
- };
+ die "HRI without pruning makes zero sense"
+ if ( $args->{hri_style} && ! $args->{prune_null_branches} );
- if (! $args->{collapse} ) {
- return assemble_simple_parser({
- val_index => $val_index,
- });
- }
- else {
+ my %common = (
+ hri_style => $args->{hri_style},
+ prune_null_branches => $args->{prune_null_branches},
+ val_index => { map
+ { $args->{inflate_map}[$_] => $_ }
+ ( 0 .. $#{$args->{inflate_map}} )
+ },
+ );
+
+ my $check_null_columns;
+
+ my $src = (! $args->{collapse} ) ? assemble_simple_parser(\%common) : do {
my $collapse_map = $self->_resolve_collapse ({
- premultiplied => $args->{premultiplied},
# FIXME
# only consider real columns (not functions) during collapse resolution
# this check shouldn't really be here, as fucktards are not supposed to
# (it is now trivial as the attrs specify where things go out of sync
# needs MOAR tests)
as => { map
- { ref $args->{selection}[$val_index->{$_}] ? () : ( $_ => $val_index->{$_} ) }
- keys %$val_index
- }
+ { ref $attrs->{select}[$common{val_index}{$_}] ? () : ( $_ => $common{val_index}{$_} ) }
+ keys %{$common{val_index}}
+ },
+ premultiplied => $args->{premultiplied},
});
- return assemble_collapsing_parser({
- val_index => $val_index,
+ $check_null_columns = $collapse_map->{-identifying_columns}
+ if @{$collapse_map->{-identifying_columns}};
+
+ assemble_collapsing_parser({
+ %common,
collapse_map => $collapse_map,
});
- }
+ };
+
+ utf8::upgrade($src)
+ if DBIx::Class::_ENV_::STRESSTEST_UTF8_UPGRADE_GENERATED_COLLAPSER_SOURCE;
+
+ return (
+ $args->{eval} ? ( eval "sub $src" || die $@ ) : $src,
+ $check_null_columns,
+ );
}
}
}
- # if the parent is already defined, assume all of its related FKs are selected
+ # if the parent is already defined *AND* we have an inner reverse relationship
+ # (i.e. do not exist without it) , assume all of its related FKs are selected
# (even if they in fact are NOT in the select list). Keep a record of what we
# assumed, and if any such phantom-column becomes part of our own collapser,
# throw everything assumed-from-parent away and replace with the collapser of
# the parent (whatever it may be)
my $assumed_from_parent;
- unless ($args->{_parent_info}{underdefined}) {
+ if ( ! $args->{_parent_info}{underdefined} and ! $args->{_parent_info}{rev_rel_is_optional} ) {
for my $col ( values %{$args->{_parent_info}{rel_condition} || {}} ) {
next if exists $my_cols->{$col};
$my_cols->{$col} = { via_collapse => $args->{_parent_info}{collapse_on_idcols} };
if $args->{_parent_info}{collapser_reusable};
}
-
- # Still dont know how to collapse - try to resolve based on our columns (plus already inserted FK bridges)
+ # Still don't know how to collapse - try to resolve based on our columns (plus already inserted FK bridges)
if (
! $collapse_map->{-identifying_columns}
and
# if we got here - we are good to go, but the construction is tricky
# since our children will want to include our collapse criteria - we
# don't give them anything (safe, since they are all collapsible on their own)
- # in addition we record the individual collapse posibilities
+ # in addition we record the individual collapse possibilities
# of all left children node collapsers, and merge them in the rowparser
# coderef later
$collapse_map->{-identifying_columns} = [];
# If we got that far - we are collapsable - GREAT! Now go down all children
# a second time, and fill in the rest
- $collapse_map->{-is_optional} = 1 if $args->{_parent_info}{is_optional};
-
+ $collapse_map->{-identifying_columns} = [ __unique_numlist(
+ @{ $args->{_parent_info}{collapse_on_idcols}||[] },
+ @{ $collapse_map->{-identifying_columns} },
+ )];
my @id_sets;
for my $rel (sort keys %$relinfo) {
rel_condition => $relinfo->{$rel}{fk_map},
- is_optional => $collapse_map->{-is_optional},
+ is_optional => ! $relinfo->{$rel}{is_inner},
+
+ # if there is at least one *inner* reverse relationship which is HASH-based (equality only)
+ # we can safely assume that the child can not exist without us
+ rev_rel_is_optional => ( first
+ { ref $_->{cond} eq 'HASH' and ($_->{attrs}{join_type}||'') !~ /^left/i }
+ values %{ $self->reverse_relationship_info($rel) },
+ ) ? 0 : 1,
# if this is a 1:1 our own collapser can be used as a collapse-map
# (regardless of left or not)
- collapser_reusable => @{$collapse_map->{-identifying_columns}} && $relinfo->{$rel}{is_single},
+ collapser_reusable => (
+ $relinfo->{$rel}{is_single}
+ &&
+ $relinfo->{$rel}{is_inner}
+ &&
+ @{$collapse_map->{-identifying_columns}}
+ ) ? 1 : 0,
},
}, $common_args );