DBIx-Class
view release on metacpan or search on metacpan
lib/DBIx/Class/ResultSet.pm view on Meta::CPAN
or
ref $data->[$i]{$_} eq 'HASH'
or
( defined blessed $data->[$i]{$_} and $data->[$i]{$_}->isa('DBIx::Class::Row') )
)
and
1
)) {
# moar sanity check... sigh
for ( ref $data->[$i]{$_} eq 'ARRAY' ? @{$data->[$i]{$_}} : $data->[$i]{$_} ) {
if ( defined blessed $_ and $_->isa('DBIx::Class::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 DBIC_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/DBIx/Class/ResultSet.pm view on Meta::CPAN
=head2 as_subselect_rs
=over 4
=item Arguments: none
=item Return Value: L<$resultset|/search>
=back
Act as a barrier to SQL symbols. The resultset provided will be made into a
"virtual view" by including it as a subquery within the from clause. From this
point on, any joined tables are inaccessible to ->search on the resultset (as if
it were simply where-filtered without joins). For example:
my $rs = $schema->resultset('Bar')->search({'x.name' => 'abc'},{ join => 'x' });
# 'x' now pollutes the query namespace
# So the following works as expected
my $ok_rs = $rs->search({'x.other' => 1});
# But this doesn't: instead of finding a 'Bar' related to two x rows (abc and
# def) we look for one row with contradictory terms and join in another table
# (aliased 'x_2') which we never use
my $broken_rs = $rs->search({'x.name' => 'def'});
my $rs2 = $rs->as_subselect_rs;
# doesn't work - 'x' is no longer accessible in $rs2, having been sealed away
my $not_joined_rs = $rs2->search({'x.other' => 1});
# works as expected: finds a 'table' row related to two x rows (abc and def)
my $correctly_joined_rs = $rs2->search({'x.name' => 'def'});
Another example of when one might use this would be to select a subset of
columns in a group by clause:
my $rs = $schema->resultset('Bar')->search(undef, {
group_by => [qw{ id foo_id baz_id }],
})->as_subselect_rs->search(undef, {
columns => [qw{ id foo_id }]
});
In the above example normally columns would have to be equal to the group by,
but because we isolated the group by into a subselect the above works.
=cut
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 0.478 second using v1.01-cache-2.11-cpan-39bf76dae61 )