DBIO
view release on metacpan or search on metacpan
lib/DBIO/ResultSet.pm view on Meta::CPAN
or
ref $data->[$i]{$_} eq 'HASH'
or
( defined blessed $data->[$i]{$_} and $data->[$i]{$_}->isa('DBIO::Row') )
)
and
1
)) {
# moar sanity check... sigh
for ( ref $data->[$i]{$_} eq 'ARRAY' ? @{$data->[$i]{$_}} : $data->[$i]{$_} ) {
if ( defined blessed $_ and $_->isa('DBIO::Row' ) ) {
carp_unique("Fast-path populate() with supplied related objects is not possible - falling back to regular create()");
return my $throwaway = $self->populate(@_);
}
}
push @$current_slice_seen_rel_infos, $rel_info->{$_};
}
}
if ($current_slice_seen_rel_infos) {
push @$slices_with_rels, $data->[$i];
# this is needed further down to decide whether or not to fallback to create()
$colinfo->{$_}{seen_null} ||= ! defined $data->[$i]{$_}
for keys %{$data->[$i]};
}
}
else {
$self->throw_exception('Unexpected populate() data structure member type: ' . ref $data->[$i] );
}
if ( grep
{ $_->{attrs}{is_depends_on} }
@{ $current_slice_seen_rel_infos || [] }
) {
carp_unique("Fast-path populate() of belongs_to relationship data is not possible - falling back to regular create()");
return my $throwaway = $self->populate(@_);
}
}
if( $slices_with_rels ) {
# need to exclude the rel "columns"
$colnames = [ grep { ! $colinfo->{$_}{is_rel} } @$colnames ];
# extra sanity check - ensure the main source is in fact identifiable
# the localizing of nullability is insane, but oh well... the use-case is legit
my $ci = $rsrc->columns_info($colnames);
$ci->{$_} = { %{$ci->{$_}}, is_nullable => 0 }
for grep { ! $colinfo->{$_}{seen_null} } keys %$ci;
unless( $rsrc->_identifying_column_set($ci) ) {
carp_unique("Fast-path populate() of non-uniquely identifiable rows with related data is not possible - falling back to regular create()");
return my $throwaway = $self->populate(@_);
}
}
### inherit the data locked in the conditions of the resultset
my ($rs_data) = $self->_merge_with_rscond({});
delete @{$rs_data}{@$colnames}; # passed-in stuff takes precedence
# if anything left - decompose rs_data
my $rs_data_vals;
if (keys %$rs_data) {
push @$rs_data_vals, $rs_data->{$_}
for sort keys %$rs_data;
}
### start work
my $guard;
$guard = $rsrc->schema->storage->txn_scope_guard
if $slices_with_rels;
### main source data
# FIXME - need to switch entirely to a coderef-based thing,
# so that large sets aren't copied several times... I think
$rsrc->storage->_insert_bulk(
$rsrc,
[ @$colnames, sort keys %$rs_data ],
[ map {
ref $data->[$_] eq 'ARRAY'
? (
$slices_with_rels ? [ @{$data->[$_]}[0..$#$colnames], @{$rs_data_vals||[]} ] # the collist changed
: $rs_data_vals ? [ @{$data->[$_]}, @$rs_data_vals ]
: $data->[$_]
)
: [ @{$data->[$_]}{@$colnames}, @{$rs_data_vals||[]} ]
} $data_start .. $#$data ],
);
### do the children relationships
if ( $slices_with_rels ) {
my @rels = grep { $colinfo->{$_}{is_rel} } keys %$colinfo
or die 'wtf... please report a bug with DBIO_TRACE=1 output (stacktrace)';
for my $sl (@$slices_with_rels) {
my ($main_proto, $main_proto_rs);
for my $rel (@rels) {
next unless defined $sl->{$rel};
$main_proto ||= {
%$rs_data,
(map { $_ => $sl->{$_} } @$colnames),
};
unless (defined $colinfo->{$rel}{rs}) {
$colinfo->{$rel}{rs} = $rsrc->related_source($rel)->resultset;
$colinfo->{$rel}{fk_map} = { reverse %{ $rsrc->_resolve_relationship_condition(
rel_name => $rel,
self_alias => "\xFE", # irrelevant
foreign_alias => "\xFF", # irrelevant
)->{identity_map} || {} } };
}
lib/DBIO/ResultSet.pm view on Meta::CPAN
# since this is search_related, and we already slid the select window inwards
# (the select/as attrs were deleted in the beginning), we need to flip all
# left joins to inner, so we get the expected results
# read the comment on top of the actual function to see what this does
$attrs->{from} = $rsrc->schema->storage->_inner_join_to_node ($attrs->{from}, $alias);
#XXX - temp fix for result_class bug. There likely is a more elegant fix -groditi
delete @{$attrs}{qw(result_class alias)};
my $rel_source = $rsrc->related_source($rel);
my $new = do {
# The reason we do this now instead of passing the alias to the
# search_rs below is that if you wrap/overload resultset on the
# source you need to know what alias it's -going- to have for things
# to work sanely (e.g. RestrictWithObject wants to be able to add
# extra query restrictions, and these may need to be $alias.)
my $rel_attrs = $rel_source->resultset_attributes;
local $rel_attrs->{alias} = $alias;
$rel_source->resultset
->search_rs(
undef, {
%$attrs,
where => $attrs->{where},
});
};
if (my $cache = $self->get_cache) {
my @related_cache = map
{ $_->related_resultset($rel)->get_cache || () }
@$cache
;
$new->set_cache([ map @$_, @related_cache ]) if @related_cache == @$cache;
}
$new;
};
}
sub current_source_alias {
return (shift->{attrs} || {})->{alias} || 'me';
}
sub as_subselect_rs {
my $self = shift;
my $attrs = $self->_resolved_attrs;
my $fresh_rs = (ref $self)->new (
$self->result_source,
{},
);
# these pieces will be locked in the subquery
delete $fresh_rs->{cond};
delete @{$fresh_rs->{attrs}}{qw/where bind/};
return $fresh_rs->search( {}, {
from => [{
$attrs->{alias} => $self->as_query,
-alias => $attrs->{alias},
-rsrc => $self->result_source,
}],
alias => $attrs->{alias},
});
}
# This code is called by search_related, and makes sure there
# is clear separation between the joins before, during, and
# after the relationship. This information is needed later
# in order to properly resolve prefetch aliases (any alias
# with a relation_chain_depth less than the depth of the
# current prefetch is not considered)
#
# The increments happen twice per join. An even number means a
# relationship specified via a search_related, whereas an odd
# number indicates a join/prefetch added via attributes
#
# Also this code will wrap the current resultset (the one we
# chain to) in a subselect IFF it contains limiting attributes
sub _chain_relationship {
my ($self, $rel) = @_;
my $source = $self->result_source;
my $attrs = { %{$self->{attrs}||{}} };
# we need to take the prefetch the attrs into account before we
# ->_resolve_join as otherwise they get lost - captainL
my $join = $self->_merge_joinpref_attr( $attrs->{join}, $attrs->{prefetch} );
delete @{$attrs}{qw/join prefetch collapse group_by distinct _grouped_by_distinct select as columns +select +as +columns/};
my $seen = { %{ (delete $attrs->{seen_join}) || {} } };
my $from;
my @force_subq_attrs = qw/offset rows group_by having/;
if (
($attrs->{from} && ref $attrs->{from} ne 'ARRAY')
||
$self->_has_resolved_attr (@force_subq_attrs)
) {
# Nuke the prefetch (if any) before the new $rs attrs
# are resolved (prefetch is useless - we are wrapping
# a subquery anyway).
my $rs_copy = $self->search;
$rs_copy->{attrs}{join} = $self->_merge_joinpref_attr (
$rs_copy->{attrs}{join},
delete $rs_copy->{attrs}{prefetch},
);
$from = [{
-rsrc => $source,
-alias => $attrs->{alias},
$attrs->{alias} => $rs_copy->as_query,
( run in 1.125 second using v1.01-cache-2.11-cpan-5e09290becf )