DBIO
view release on metacpan or search on metacpan
lib/DBIO/ResultSet.pm view on Meta::CPAN
# if all else fails - get all primary keys and operate over a ORed set
# wrap in a transaction for consistency
# this is where the group_by/multiplication starts to matter
if (
$existing_group_by
or
# we do not need to check pre-multipliers, since if the premulti is there, its
# parent (who is multi) will be there too
keys %{ $join_classifications->{multiplying} || {} }
) {
# make sure if there is a supplied group_by it matches the columns compiled above
# perfectly. Anything else can not be sanely executed on most databases so croak
# right then and there
if ($existing_group_by) {
my @current_group_by = map
{ $_ =~ /\./ ? $_ : "$attrs->{alias}.$_" }
@$existing_group_by
;
if (
join ("\x00", sort @current_group_by)
ne
join ("\x00", sort @{$attrs->{columns}} )
) {
$self->throw_exception (
"You have just attempted a $op operation on a resultset which does group_by"
. ' on columns other than the primary keys, while DBIO internally needs to retrieve'
. ' the primary keys in a subselect. All sane RDBMS engines do not support this'
. ' kind of queries. Please retry the operation with a modified group_by or'
. ' without using one at all.'
);
}
}
$subrs = $subrs->search({}, { group_by => $attrs->{columns} });
}
$guard = $storage->txn_scope_guard;
for my $row ($subrs->cursor->all) {
push @$cond, { map
{ $idcols->[$_] => $row->[$_] }
(0 .. $#$idcols)
};
}
}
}
my $res = $cond ? $storage->$op (
$rsrc,
$op eq 'update' ? $values : (),
$cond,
) : '0E0';
$guard->commit if $guard;
return $res;
}
sub update {
my ($self, $values) = @_;
$self->throw_exception('Values for update must be a hash')
unless ref $values eq 'HASH';
return $self->_rs_update_delete ('update', $values);
}
sub update_all {
my ($self, $values) = @_;
$self->throw_exception('Values for update_all must be a hash')
unless ref $values eq 'HASH';
my $guard = $self->result_source->schema->txn_scope_guard;
$_->update({%$values}) for $self->all; # shallow copy - update will mangle it
$guard->commit;
return 1;
}
sub delete {
my $self = shift;
$self->throw_exception('delete does not accept any arguments')
if @_;
return $self->_rs_update_delete ('delete');
}
sub delete_all {
my $self = shift;
$self->throw_exception('delete_all does not accept any arguments')
if @_;
my $guard = $self->result_source->schema->txn_scope_guard;
$_->delete for $self->all;
$guard->commit;
return 1;
}
sub populate {
my $self = shift;
# this is naive and just a quick check
# the types will need to be checked more thoroughly when the
# multi-source populate gets added
my $data = (
ref $_[0] eq 'ARRAY'
and
( @{$_[0]} or return )
and
( ref $_[0][0] eq 'HASH' or ref $_[0][0] eq 'ARRAY' )
and
$_[0]
) or $self->throw_exception('Populate expects an arrayref of hashrefs or arrayref of arrayrefs');
# FIXME - no cref handling
# At this point assume either hashes or arrays
if(defined wantarray) {
my (@results, $guard);
if (ref $data->[0] eq 'ARRAY') {
# column names only, nothing to do
return if @$data == 1;
$guard = $self->result_source->schema->storage->txn_scope_guard
if @$data > 2;
lib/DBIO/ResultSet.pm view on Meta::CPAN
my ($self, $query, $alias) = @_;
my %orig = %{ $query || {} };
my %unaliased;
foreach my $key (keys %orig) {
if ($key !~ /\./) {
$unaliased{$key} = $orig{$key};
next;
}
$unaliased{$1} = $orig{$key}
if $key =~ m/^(?:\Q$alias\E\.)?([^.]+)$/;
}
return \%unaliased;
}
sub as_query {
my $self = shift;
my $attrs = { %{ $self->_resolved_attrs } };
my $aq = $self->result_source->storage->_select_args_to_query (
$attrs->{from}, $attrs->{select}, $attrs->{where}, $attrs
);
$aq;
}
sub find_or_new {
my $self = shift;
my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
my $hash = ref $_[0] eq 'HASH' ? shift : {@_};
if (keys %$hash and my $row = $self->find($hash, $attrs) ) {
return $row;
}
return $self->new_result($hash);
}
sub create {
#my ($self, $col_data) = @_;
assert_no_internal_indirect_calls and fail_on_internal_call;
return shift->new_result(shift)->insert;
}
sub find_or_create {
my $self = shift;
my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
my $hash = ref $_[0] eq 'HASH' ? shift : {@_};
if (keys %$hash and my $row = $self->find($hash, $attrs) ) {
return $row;
}
return $self->new_result($hash)->insert;
}
sub update_or_create {
my $self = shift;
my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
my $cond = ref $_[0] eq 'HASH' ? shift : {@_};
my ($main_cond, $related_subconds) = $self->_split_related_update_conds($cond);
# When related sub-structures are peeled off to be applied after the main
# row, wrap the whole operation in a transaction so a partially-built graph
# never survives a failure -- mirroring the multi-create guarantee in
# DBIO::Row::insert. No transaction is opened for the common no-relation case.
my $guard = keys %$related_subconds
? $self->result_source->schema->txn_scope_guard
: undef;
my $row = $self->find($main_cond, $attrs);
if (defined $row) {
$row->update($main_cond);
}
else {
$row = $self->new_result($main_cond)->insert;
}
# DWIM: apply each peeled-off relation through the (now stored) main row's
# related resultset. The main-table 'key' constraint applies only to the main
# table, so it is intentionally NOT propagated to these calls.
for my $rel_name (keys %$related_subconds) {
$row->related_resultset($rel_name)
->update_or_create($related_subconds->{$rel_name});
}
$guard->commit if $guard;
return $row;
}
# update_or_create() DWIM: split a mixed condition into ( \%main_cond,
# \%related_subconds ). A top-level key is peeled off into %related_subconds
# only when it is a relationship name whose value is a plain (unblessed) hashref
# that does NOT reduce to a *concrete* foreign-key condition on the main table.
#
# A resolvable belongs_to (FK-bearing hashref, or a blessed related object)
# reduces join-free to { self_fk => <defined value> } and STAYS in %main_cond --
# find()/update() handle it exactly as before. A has_many / has_one / might_have
# searched by its own non-key columns either is crosstable, or resolves join-free
# to { self_pk => undef } (which would silently clobber the main key with an
# undef FK -- the original defect this DWIM fixes). Those are peeled off and
# applied to their own related resultset after the main row exists.
#
# has_many arrayrefs (multi) and blessed/other non-plain-hashref relation values
# are left untouched in %main_cond: the main create/multi-create or update path
# already handles or rejects them, unchanged. See ADR 0033.
sub _split_related_update_conds {
my ($self, $cond) = @_;
my $rsrc = $self->result_source;
my %main_cond = %$cond;
my %related_subconds;
for my $key (keys %main_cond) {
next unless ref $main_cond{$key} eq 'HASH';
next unless $rsrc->relationship_info($key);
my ($rel_cond, $crosstable)
= $self->_resolve_related_cond($rsrc, $key, $main_cond{$key});
# Keep in the main search only when the relation reduces to a concrete,
# fully-defined foreign-key condition on this table (resolvable belongs_to).
my $concrete_fk =
! $crosstable
&& ref $rel_cond eq 'HASH'
&& keys %$rel_cond
&& ! grep {
! defined $_ or ( ! ref($_) && $_ eq UNRESOLVABLE_CONDITION )
} values %$rel_cond;
next if $concrete_fk;
$related_subconds{$key} = delete $main_cond{$key};
}
return (\%main_cond, \%related_subconds);
}
sub update_or_new {
my $self = shift;
my $attrs = ( @_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {} );
my $cond = ref $_[0] eq 'HASH' ? shift : {@_};
my $row = $self->find( $cond, $attrs );
if ( defined $row ) {
$row->update($cond);
return $row;
}
return $self->new_result($cond);
}
sub get_cache {
shift->{all_cache};
}
sub set_cache {
my ( $self, $data ) = @_;
$self->throw_exception("set_cache requires an arrayref")
if defined($data) && (ref $data ne 'ARRAY');
# If set_cache is called after a related resultset has been created,
# we expect subsequent calls to related_resultset to include cached
# results (if they are present on the objects in $data).
#
# This won't be true if we've cached the related_resultsets already,
# so clear them.
$self->{related_resultsets} = {};
$self->{all_cache} = $data;
}
sub clear_cache {
shift->set_cache(undef);
}
sub is_paged {
my ($self) = @_;
return !!$self->{attrs}{page};
}
sub is_ordered {
my ($self) = @_;
return scalar $self->result_source->storage->_extract_order_criteria($self->{attrs}{order_by});
}
sub related_resultset {
my ($self, $rel) = @_;
return $self->{related_resultsets}{$rel}
if defined $self->{related_resultsets}{$rel};
return $self->{related_resultsets}{$rel} = do {
my $rsrc = $self->result_source;
( run in 1.056 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )