use strict;
use warnings;
-use base qw/DBIx::Class/;
+
+use base 'DBIx::Class';
+use mro 'c3';
+
use DBIx::Class::Carp;
use DBIx::Class::ResultSetColumn;
-use Scalar::Util qw/blessed weaken reftype/;
+use DBIx::Class::ResultClass::HashRefInflator;
+use Scalar::Util qw( blessed reftype );
use DBIx::Class::_Util qw(
+ dbic_internal_try dump_value
fail_on_internal_wantarray fail_on_internal_call UNRESOLVABLE_CONDITION
);
use Try::Tiny;
-# not importing first() as it will clash with our own method
-use List::Util ();
-
BEGIN {
# De-duplication in _merge_attr() is disabled, but left in for reference
# (the merger is used for other things that ought not to be de-duped)
$call_cond = shift;
}
# fish out attrs in the ($condref, $attr) case
- elsif (@_ == 2 and ( ! defined $_[0] or (ref $_[0]) ne '') ) {
+ elsif (@_ == 2 and ( ! defined $_[0] or length ref $_[0] ) ) {
($call_cond, $call_attrs) = @_;
}
elsif (@_ % 2) {
for my $i (0 .. $#_) {
next if $i % 2;
$self->throw_exception ('All keys in condition key/value pairs must be plain scalars')
- if (! defined $_[$i] or ref $_[$i] ne '');
+ if (! defined $_[$i] or length ref $_[$i] );
}
$call_cond = { @_ };
# see if we can keep the cache (no $rs changes)
my $cache;
my %safe = (alias => 1, cache => 1);
- if ( ! List::Util::first { !$safe{$_} } keys %$call_attrs and (
+ if ( ! grep { !$safe{$_} } keys %$call_attrs and (
! defined $call_cond
or
ref $call_cond eq 'HASH' && ! keys %$call_cond
my @selector_attrs = qw/select as columns cols +select +as +columns include_columns/;
# reset the current selector list if new selectors are supplied
- if (List::Util::first { exists $call_attrs->{$_} } qw/columns cols select as/) {
- delete @{$old_attrs}{(@selector_attrs, '_dark_selector')};
- }
+ delete @{$old_attrs}{(@selector_attrs, '_dark_selector')}
+ if grep { exists $call_attrs->{$_} } qw(columns cols select as);
# Normalize the new selector list (operates on the passed-in attr structure)
# Need to do it on every chain instead of only once on _resolved_attrs, in
return $rs;
}
-my $dark_sel_dumper;
sub _normalize_selection {
my ($self, $attrs) = @_;
else {
$attrs->{_dark_selector} = {
plus_stage => $pref,
- string => ($dark_sel_dumper ||= do {
- require Data::Dumper::Concise;
- Data::Dumper::Concise::DumperObject()->Indent(0);
- })->Values([$_])->Dump
- ,
+ string => do {
+ local $Data::Dumper::Indent = 0;
+ dump_value $_;
+ },
};
last SELECTOR;
}
if (defined $constraint_name) {
$final_cond = $self->_qualify_cond_columns (
- $self->result_source->_minimal_valueset_satisfying_constraint(
+ $rsrc->_minimal_valueset_satisfying_constraint(
constraint_name => $constraint_name,
values => ($self->_merge_with_rscond($call_cond))[0],
carp_on_nulls => 1,
join "\x00", sort $rsrc->unique_constraint_columns($c_name)
}++;
- try {
+ dbic_internal_try {
push @unique_queries, $self->_qualify_cond_columns(
- $self->result_source->_minimal_valueset_satisfying_constraint(
+ $rsrc->_minimal_valueset_satisfying_constraint(
constraint_name => $c_name,
values => ($self->_merge_with_rscond($call_cond))[0],
- columns_info => ($ci ||= $self->result_source->columns_info),
+ columns_info => ($ci ||= $rsrc->columns_info),
),
$alias
);
=cut
sub search_related {
+ DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call;
return shift->related_resultset(shift)->search(@_);
}
=cut
sub search_related_rs {
+ DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call;
return shift->related_resultset(shift)->search_rs(@_);
}
return $self->{cursor} ||= do {
my $attrs = $self->_resolved_attrs;
- $self->result_source->storage->select(
+ $self->result_source->schema->storage->select(
$attrs->{from}, $attrs->{select}, $attrs->{where}, $attrs
);
};
}
}
- my $data = [ $self->result_source->storage->select_single(
+ my $data = [ $self->result_source->schema->storage->select_single(
$attrs->{from}, $attrs->{select},
$attrs->{where}, $attrs
)];
=cut
sub get_column {
- my ($self, $column) = @_;
- my $new = DBIx::Class::ResultSetColumn->new($self, $column);
- return $new;
+ DBIx::Class::ResultSetColumn->new(@_);
}
=head2 search_like
$self->{_result_inflator}{is_hri} = ( (
! $self->{_result_inflator}{is_core_row}
and
- $inflator_cref == (
- require DBIx::Class::ResultClass::HashRefInflator
- &&
- DBIx::Class::ResultClass::HashRefInflator->can('inflate_result')
- )
+ $inflator_cref == \&DBIx::Class::ResultClass::HashRefInflator::inflate_result
) ? 1 : 0 ) unless defined $self->{_result_inflator}{is_hri};
: '@$rows = map { $inflator_cref->($res_class, $rsrc, { %s } ) } @$rows'
),
( join (', ', map { "\$infmap->[$_] => \$_->[$_]" } 0..$#$infmap ) )
- );
+ ) . '; 1' or die;
}
}
else {
: 'classic_nonpruning'
;
- # $args and $attrs to _mk_row_parser are separated to delineate what is
- # core collapser stuff and what is dbic $rs specific
- @{$self->{_row_parser}{$parser_type}}{qw(cref nullcheck)} = $rsrc->_mk_row_parser({
- eval => 1,
- inflate_map => $infmap,
- collapse => $attrs->{collapse},
- premultiplied => $attrs->{_main_source_premultiplied},
- hri_style => $self->{_result_inflator}{is_hri},
- prune_null_branches => $self->{_result_inflator}{is_hri} || $self->{_result_inflator}{is_core_row},
- }, $attrs) unless $self->{_row_parser}{$parser_type}{cref};
-
- # column_info metadata historically hasn't been too reliable.
- # We need to start fixing this somehow (the collapse resolver
- # can't work without it). Add an explicit check for the *main*
- # result, hopefully this will gradually weed out such errors
- #
- # FIXME - this is a temporary kludge that reduces performance
- # It is however necessary for the time being
- my ($unrolled_non_null_cols_to_check, $err);
+ unless( $self->{_row_parser}{$parser_type}{cref} ) {
- if (my $check_non_null_cols = $self->{_row_parser}{$parser_type}{nullcheck} ) {
+ # $args and $attrs to _mk_row_parser are separated to delineate what is
+ # core collapser stuff and what is dbic $rs specific
+ $self->{_row_parser}{$parser_type}{src} = $rsrc->_mk_row_parser({
+ inflate_map => $infmap,
+ collapse => $attrs->{collapse},
+ premultiplied => $attrs->{_main_source_premultiplied},
+ hri_style => $self->{_result_inflator}{is_hri},
+ prune_null_branches => $self->{_result_inflator}{is_hri} || $self->{_result_inflator}{is_core_row},
+ }, $attrs);
- $err =
- 'Collapse aborted due to invalid ResultSource metadata - the following '
- . 'selections are declared non-nullable but NULLs were retrieved: '
- ;
+ $self->{_row_parser}{$parser_type}{cref} = do {
+ package # hide form PAUSE
+ DBIx::Class::__GENERATED_ROW_PARSER__;
- my @violating_idx;
- COL: for my $i (@$check_non_null_cols) {
- ! defined $_->[$i] and push @violating_idx, $i and next COL for @$rows;
- }
-
- $self->throw_exception( $err . join (', ', map { "'$infmap->[$_]'" } @violating_idx ) )
- if @violating_idx;
-
- $unrolled_non_null_cols_to_check = join (',', @$check_non_null_cols);
+ eval $self->{_row_parser}{$parser_type}{src};
+ } || die $@;
}
- my $next_cref =
- ($did_fetch_all or ! $attrs->{collapse}) ? undef
- : defined $unrolled_non_null_cols_to_check ? eval sprintf <<'EOS', $unrolled_non_null_cols_to_check
-sub {
- # FIXME SUBOPTIMAL - we can do better, cursor->next/all (well diff. methods) should return a ref
- my @r = $cursor->next or return;
- if (my @violating_idx = grep { ! defined $r[$_] } (%s) ) {
- $self->throw_exception( $err . join (', ', map { "'$infmap->[$_]'" } @violating_idx ) )
- }
- \@r
-}
-EOS
- : sub {
+ # this needs to close over the *current* cursor, hence why it is not cached above
+ my $next_cref = ($did_fetch_all or ! $attrs->{collapse})
+ ? undef
+ : sub {
# FIXME SUBOPTIMAL - we can do better, cursor->next/all (well diff. methods) should return a ref
my @r = $cursor->next or return;
\@r
$self->{_row_parser}{$parser_type}{cref}->(
$rows,
- $next_cref ? ( $next_cref, $self->{_stashed_rows} = [] ) : (),
+ $next_cref,
+ ( $self->{_stashed_rows} = [] ),
+ ( my $null_violations = {} ),
);
+ $self->throw_exception(
+ 'Collapse aborted - the following columns are declared (or defaulted to) '
+ . 'non-nullable within DBIC but NULLs were retrieved from storage: '
+ . join( ', ', map { "'$infmap->[$_]'" } sort { $a <=> $b } keys %$null_violations )
+ . ' within data row ' . dump_value({
+ map {
+ $infmap->[$_] =>
+ ( ! defined $self->{_stashed_rows}[0][$_] or length $self->{_stashed_rows}[0][$_] < 50 )
+ ? $self->{_stashed_rows}[0][$_]
+ : substr( $self->{_stashed_rows}[0][$_], 0, 50 ) . '...'
+ } 0 .. $#{$self->{_stashed_rows}[0]}
+ })
+ ) if keys %$null_violations;
+
# simple in-place substitution, does not regrow $rows
if ($self->{_result_inflator}{is_core_row}) {
$_ = $inflator_cref->($res_class, $rsrc, @$_) for @$rows
# overwrite the selector (supplied by the storage)
$rsrc->resultset_class->new($rsrc, {
%$tmp_attrs,
- select => $rsrc->storage->_count_select ($rsrc, $attrs),
+ select => $rsrc->schema->storage->_count_select ($rsrc, $attrs),
as => 'count',
})->get_column ('count');
}
# Calculate subquery selector
if (my $g = $sub_attrs->{group_by}) {
- my $sql_maker = $rsrc->storage->sql_maker;
+ my $sql_maker = $rsrc->schema->storage->sql_maker;
# necessary as the group_by may refer to aliased functions
my $sel_index;
return $rsrc->resultset_class
->new ($rsrc, $sub_attrs)
->as_subselect_rs
- ->search ({}, { columns => { count => $rsrc->storage->_count_select ($rsrc, $attrs) } })
+ ->search ({}, { columns => { count => $rsrc->schema->storage->_count_select ($rsrc, $attrs) } })
->get_column ('count');
}
=cut
-sub count_literal { shift->search_literal(@_)->count; }
+sub count_literal {
+ DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call;
+ shift->search_literal(@_)->count
+}
=head2 all
=cut
sub first {
+ DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call;
return $_[0]->reset->next;
}
$storage->_prune_unused_joins ($attrs);
# any non-pruneable non-local restricting joins imply subq
- $needs_subq = defined List::Util::first { $_ ne $attrs->{alias} } keys %{ $join_classifications->{restricting} || {} };
+ $needs_subq = grep { $_ ne $attrs->{alias} } keys %{ $join_classifications->{restricting} || {} };
}
# check if the head is composite (by now all joins are thrown out unless $needs_subq)
# a condition containing 'me' or other table prefixes will not work
# at all. Tell SQLMaker to dequalify idents via a gross hack.
$cond = do {
- my $sqla = $rsrc->storage->sql_maker;
+ my $sqla = $rsrc->schema->storage->sql_maker;
local $sqla->{_dequalify_idents} = 1;
\[ $sqla->_recurse_where($self->{cond}) ];
};
# FIXME - no cref handling
# At this point assume either hashes or arrays
+ my $rsrc = $self->result_source;
+
if(defined wantarray) {
my (@results, $guard);
# column names only, nothing to do
return if @$data == 1;
- $guard = $self->result_source->schema->storage->txn_scope_guard
+ $guard = $rsrc->schema->storage->txn_scope_guard
if @$data > 2;
@results = map
}
else {
- $guard = $self->result_source->schema->storage->txn_scope_guard
+ $guard = $rsrc->schema->storage->txn_scope_guard
if @$data > 1;
@results = map { $self->new_result($_)->insert } @$data;
# this means we have to walk the data structure twice
# whether we want this or not
# jnap, I hate you ;)
- my $rsrc = $self->result_source;
my $rel_info = { map { $_ => $rsrc->relationship_info($_) } $rsrc->relationships };
my ($colinfo, $colnames, $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->schema->storage->_insert_bulk(
$rsrc,
[ @$colnames, sort keys %$rs_data ],
[ map {
sub new_result {
my ($self, $values) = @_;
- $self->throw_exception( "new_result takes only one argument - a hashref of values" )
- if @_ > 2;
-
- $self->throw_exception( "Result object instantiation requires a hashref as argument" )
- unless (ref $values eq 'HASH');
+ $self->throw_exception( "Result object instantiation requires a single hashref argument" )
+ if @_ > 2 or ref $values ne 'HASH';
my ($merged_cond, $cols_from_relations) = $self->_merge_with_rscond($values);
my $attrs = { %{ $self->_resolved_attrs } };
- my $aq = $self->result_source->storage->_select_args_to_query (
+ my $aq = $self->result_source->schema->storage->_select_args_to_query (
$attrs->{from}, $attrs->{select}, $attrs->{where}, $attrs
);
sub is_ordered {
my ($self) = @_;
- return scalar $self->result_source->storage->_extract_order_criteria($self->{attrs}{order_by});
+ return scalar $self->result_source->schema->storage->_extract_order_criteria($self->{attrs}{order_by});
}
=head2 related_resultset
=cut
sub related_resultset {
- my ($self, $rel) = @_;
+ $_[0]->throw_exception(
+ 'Extra arguments to $rs->related_resultset() were always quietly '
+ . 'discarded without consideration, you need to switch to '
+ . '...->related_resultset( $relname )->search_rs( $search, $args ) instead.'
+ ) if @_ > 2;
- return $self->{related_resultsets}{$rel}
- if defined $self->{related_resultsets}{$rel};
+ return $_[0]->{related_resultsets}{$_[1]}
+ if defined $_[0]->{related_resultsets}{$_[1]};
+
+ my ($self, $rel) = @_;
return $self->{related_resultsets}{$rel} = do {
my $rsrc = $self->result_source;
my $attrs = $self->_chain_relationship($rel);
- my $join_count = $attrs->{seen_join}{$rel};
+ my $storage = $rsrc->schema->storage;
- my $alias = $self->result_source->storage
- ->relname_to_table_alias($rel, $join_count);
+ # Previously this atribute was deleted (instead of being set as it is now)
+ # Doing so seems to be harmless in all available test permutations
+ # See also 01d59a6a6 and mst's comment below
+ #
+ $attrs->{alias} = $storage->relname_to_table_alias(
+ $rel,
+ $attrs->{seen_join}{$rel}
+ );
# 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);
-
+ $attrs->{from} = $storage->_inner_join_to_node( $attrs->{from}, $attrs->{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);
+ delete $attrs->{result_class};
my $new = do {
# 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},
- });
+ # -- mst ~ 2007 (01d59a6a6)
+ #
+ # FIXME - this seems to be no longer neccessary (perhaps due to the
+ # advances in relcond resolution. Testing DBIC::S::RWO and its only
+ # dependent (as of Jun 2015 ) does not yield any difference with or
+ # without this line. Nevertheless keep it as is for now, to minimize
+ # churn, there is enough potential for breakage in 0.0829xx as it is
+ # -- ribasushi Jun 2015
+ #
+ my $rel_source = $rsrc->related_source($rel);
+ local $rel_source->resultset_attributes->{alias} = $attrs->{alias};
+
+ $rel_source->resultset->search_rs( undef, $attrs );
};
if (my $cache = $self->get_cache) {
});
}
+The alias of L<newly created resultsets|/search> can be altered by the
+L<alias attribute|/alias>.
+
=cut
sub current_source_alias {
$self->throw_exception("Specifying distinct => 1 in conjunction with collapse => 1 is unsupported")
if $attrs->{collapse} and $attrs->{distinct};
+
+ # Sanity check the paging attributes
+ # SQLMaker does it too, but in case of a software_limit we'll never get there
+ if (defined $attrs->{offset}) {
+ $self->throw_exception('A supplied offset attribute must be a non-negative integer')
+ if ( $attrs->{offset} =~ /[^0-9]/ or $attrs->{offset} < 0 );
+ }
+ if (defined $attrs->{rows}) {
+ $self->throw_exception("The rows attribute must be a positive integer if present")
+ if ( $attrs->{rows} =~ /[^0-9]/ or $attrs->{rows} <= 0 );
+ }
+
+
# default selection list
$attrs->{columns} = [ $source->columns ]
- unless List::Util::first { exists $attrs->{$_} } qw/columns cols select as/;
+ unless grep { exists $attrs->{$_} } qw/columns cols select as/;
# merge selectors together
for (qw/columns select as/) {
];
}
+
for my $attr (qw(order_by group_by)) {
if ( defined $attrs->{$attr} ) {
}
}
- # generate selections based on the prefetch helper
- my ($prefetch, @prefetch_select, @prefetch_as);
- $prefetch = $self->_merge_joinpref_attr( {}, delete $attrs->{prefetch} )
- if defined $attrs->{prefetch};
-
- if ($prefetch) {
-
- $self->throw_exception("Unable to prefetch, resultset contains an unnamed selector $attrs->{_dark_selector}{string}")
- if $attrs->{_dark_selector};
+ # set collapse default based on presence of prefetch
+ my $prefetch;
+ if (
+ defined $attrs->{prefetch}
+ and
+ $prefetch = $self->_merge_joinpref_attr( {}, delete $attrs->{prefetch} )
+ ) {
$self->throw_exception("Specifying prefetch in conjunction with an explicit collapse => 0 is unsupported")
if defined $attrs->{collapse} and ! $attrs->{collapse};
$attrs->{collapse} = 1;
-
- # this is a separate structure (we don't look in {from} directly)
- # as the resolver needs to shift things off the lists to work
- # properly (identical-prefetches on different branches)
- my $join_map = {};
- if (ref $attrs->{from} eq 'ARRAY') {
-
- my $start_depth = $attrs->{seen_join}{-relation_chain_depth} || 0;
-
- for my $j ( @{$attrs->{from}}[1 .. $#{$attrs->{from}} ] ) {
- next unless $j->[0]{-alias};
- next unless $j->[0]{-join_path};
- next if ($j->[0]{-relation_chain_depth} || 0) < $start_depth;
-
- my @jpath = map { keys %$_ } @{$j->[0]{-join_path}};
-
- my $p = $join_map;
- $p = $p->{$_} ||= {} for @jpath[ ($start_depth/2) .. $#jpath]; #only even depths are actual jpath boundaries
- push @{$p->{-join_aliases} }, $j->[0]{-alias};
- }
- }
-
- my @prefetch = $source->_resolve_prefetch( $prefetch, $alias, $join_map );
-
- # save these for after distinct resolution
- @prefetch_select = map { $_->[0] } @prefetch;
- @prefetch_as = map { $_->[1] } @prefetch;
}
+
# run through the resulting joinstructure (starting from our current slot)
# and unset collapse if proven unnecessary
#
if (
! $attrs->{_main_source_premultiplied}
and
- ! List::Util::first { ! $_->[0]{-is_single} } @fromlist
+ ! grep { ! $_->[0]{-is_single} } @fromlist
) {
$attrs->{collapse} = 0;
}
}
}
+
# generate the distinct induced group_by before injecting the prefetched select/as parts
if (delete $attrs->{distinct}) {
if ($attrs->{group_by}) {
else {
$attrs->{_grouped_by_distinct} = 1;
# distinct affects only the main selection part, not what prefetch may add below
- ($attrs->{group_by}, my $new_order) = $source->storage->_group_over_selection($attrs);
+ ($attrs->{group_by}, my $new_order) = $source->schema->storage->_group_over_selection($attrs);
# FIXME possibly ignore a rewritten order_by (may turn out to be an issue)
# The thinking is: if we are collapsing the subquerying prefetch engine will
}
}
- # inject prefetch-bound selection (if any)
- push @{$attrs->{select}}, @prefetch_select;
- push @{$attrs->{as}}, @prefetch_as;
+
+ # generate selections based on the prefetch helper
+ if ($prefetch) {
+
+ $self->throw_exception("Unable to prefetch, resultset contains an unnamed selector $attrs->{_dark_selector}{string}")
+ if $attrs->{_dark_selector};
+
+ # this is a separate structure (we don't look in {from} directly)
+ # as the resolver needs to shift things off the lists to work
+ # properly (identical-prefetches on different branches)
+ my $joined_node_aliases_map = {};
+ if (ref $attrs->{from} eq 'ARRAY') {
+
+ my $start_depth = $attrs->{seen_join}{-relation_chain_depth} || 0;
+
+ for my $j ( @{$attrs->{from}}[1 .. $#{$attrs->{from}} ] ) {
+ next unless $j->[0]{-alias};
+ next unless $j->[0]{-join_path};
+ next if ($j->[0]{-relation_chain_depth} || 0) < $start_depth;
+
+ my @jpath = map { keys %$_ } @{$j->[0]{-join_path}};
+
+ my $p = $joined_node_aliases_map;
+ $p = $p->{$_} ||= {} for @jpath[ ($start_depth/2) .. $#jpath]; #only even depths are actual jpath boundaries
+ push @{$p->{-join_aliases} }, $j->[0]{-alias};
+ }
+ }
+
+ ( push @{$attrs->{select}}, $_->[0] ) and ( push @{$attrs->{as}}, $_->[1] )
+ for $source->_resolve_selection_from_prefetch( $prefetch, $joined_node_aliases_map );
+ }
+
$attrs->{_simple_passthrough_construction} = !(
$attrs->{collapse}
grep { $_ =~ /\./ } @{$attrs->{as}}
);
+
# if both page and offset are specified, produce a combined offset
# even though it doesn't make much sense, this is what pre 081xx has
# been doing
if (ref $b eq 'HASH') {
my ($b_key) = keys %{$b};
+ $b_key = '' if ! defined $b_key;
if (ref $a eq 'HASH') {
my ($a_key) = keys %{$a};
+ $a_key = '' if ! defined $a_key;
if ($a_key eq $b_key) {
return (1 + $self->_calculate_score( $a->{$a_key}, $b->{$b_key} ));
} else {
},
ARRAY => sub {
return $_[1] if !defined $_[0];
- return $_[1] if __HM_DEDUP and List::Util::first { $_ eq $_[0] } @{$_[1]};
+ return $_[1] if __HM_DEDUP and grep { $_ eq $_[0] } @{$_[1]};
return [$_[0], @{$_[1]}]
},
HASH => sub {
ARRAY => {
SCALAR => sub {
return $_[0] if !defined $_[1];
- return $_[0] if __HM_DEDUP and List::Util::first { $_ eq $_[1] } @{$_[0]};
+ return $_[0] if __HM_DEDUP and grep { $_ eq $_[1] } @{$_[0]};
return [@{$_[0]}, $_[1]]
},
ARRAY => sub {
HASH => sub {
return [ $_[1] ] if ! @{$_[0]};
return $_[0] if !keys %{$_[1]};
- return $_[0] if __HM_DEDUP and List::Util::first { $_ eq $_[1] } @{$_[0]};
+ return $_[0] if __HM_DEDUP and grep { $_ eq $_[1] } @{$_[0]};
return [ @{$_[0]}, $_[1] ];
},
},
return [] if !keys %{$_[0]} and !@{$_[1]};
return [ $_[0] ] if !@{$_[1]};
return $_[1] if !keys %{$_[0]};
- return $_[1] if __HM_DEDUP and List::Util::first { $_ eq $_[0] } @{$_[1]};
+ return $_[1] if __HM_DEDUP and grep { $_ eq $_[0] } @{$_[1]};
return [ $_[0], @{$_[1]} ];
},
HASH => sub {
as => [qw(some_column dbic_slot)]
If you want to individually retrieve related columns (in essence perform
-manual prefetch) you have to make sure to specify the correct inflation slot
+manual L</prefetch>) you have to make sure to specify the correct inflation slot
chain such that it matches existing relationships:
my $rs = $schema->resultset('Artist')->search({}, {
# required to tell DBIC to collapse has_many relationships
collapse => 1,
- join => { cds => 'tracks'},
+ join => { cds => 'tracks' },
'+columns' => {
'cds.cdid' => 'cds.cdid',
'cds.tracks.title' => 'tracks.title',
},
});
+Like elsewhere, literal SQL or literal values can be included by using a
+scalar reference or a literal bind value, and these values will be available
+in the result with C<get_column> (see also
+L<SQL::Abstract/Literal SQL and value type operators>):
+
+ # equivalent SQL: SELECT 1, 'a string', IF(my_column,?,?) ...
+ # bind values: $true_value, $false_value
+ columns => [
+ {
+ foo => \1,
+ bar => \q{'a string'},
+ baz => \[ 'IF(my_column,?,?)', $true_value, $false_value ],
+ }
+ ]
+
=head2 +columns
B<NOTE:> You B<MUST> explicitly quote C<'+columns'> when using this attribute.
=head2 where
-=over 4
-
-Adds to the WHERE clause.
+Adds extra conditions to the resultset, combined with the preexisting C<WHERE>
+conditions, same as the B<first> argument to the L<search operator|/search>
# only return rows WHERE deleted IS NULL for all searches
__PACKAGE__->resultset_attributes({ where => { deleted => undef } });
-Can be overridden by passing C<< { where => undef } >> as an attribute
-to a resultset.
-
-For more complicated where clauses see L<SQL::Abstract/WHERE CLAUSES>.
-
-=back
+Note that the above example is
+L<strongly discouraged|DBIx::Class::ResultSource/resultset_attributes>.
=head2 cache
... do stuff ...
}
- $rs->first; # without cache, this would issue a query
+ $resultset->first; # without cache, this would issue a query
By default, searches are not cached.
[ undef, $val ] === [ {}, $val ]
$val === [ {}, $val ]
-=head1 AUTHOR AND CONTRIBUTORS
+=head1 FURTHER QUESTIONS?
-See L<AUTHOR|DBIx::Class/AUTHOR> and L<CONTRIBUTORS|DBIx::Class/CONTRIBUTORS> in DBIx::Class
+Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
-=head1 LICENSE
+=head1 COPYRIGHT AND LICENSE
-You may distribute this code under the same terms as Perl itself.
+This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
+by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
+redistribute it and/or modify it under the same terms as the
+L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.
+=cut