Centralize specification of expected Result class base in the codebase
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSet.pm
index 1231a07..bf7e88f 100644 (file)
@@ -4,17 +4,16 @@ use strict;
 use warnings;
 
 use base 'DBIx::Class';
-use mro 'c3';
 
 use DBIx::Class::Carp;
 use DBIx::Class::ResultSetColumn;
 use DBIx::Class::ResultClass::HashRefInflator;
 use Scalar::Util qw( blessed reftype );
 use DBIx::Class::_Util qw(
-  dbic_internal_try dump_value
+  dbic_internal_try dbic_internal_catch dump_value emit_loud_diag
   fail_on_internal_wantarray fail_on_internal_call UNRESOLVABLE_CONDITION
 );
-use Try::Tiny;
+use DBIx::Class::SQLMaker::Util qw( normalize_sqla_condition extract_equality_conditions );
 
 BEGIN {
   # De-duplication in _merge_attr() is disabled, but left in for reference
@@ -654,17 +653,15 @@ sub _stack_cond {
     (ref $_ eq 'HASH' and ! keys %$_)
   ) and $_ = undef for ($left, $right);
 
-  # either one of the two undef
-  if ( (defined $left) xor (defined $right) ) {
-    return defined $left ? $left : $right;
-  }
-  # both undef
-  elsif ( ! defined $left ) {
-    return undef
-  }
-  else {
-    return $self->result_source->schema->storage->_collapse_cond({ -and => [$left, $right] });
-  }
+  return(
+    # either one of the two undef
+    ( (defined $left) xor (defined $right) )  ? ( defined $left ? $left : $right )
+
+    # both undef
+  : ( ! defined $left )                       ? undef
+
+                                              : { -and => [$left, $right] }
+  );
 }
 
 =head2 search_literal
@@ -777,7 +774,7 @@ See also L</find_or_create> and L</update_or_create>.
 
 sub find {
   my $self = shift;
-  my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
+  my $attrs = (@_ > 1 && ref $_[-1] eq 'HASH' ? pop(@_) : {});
 
   my $rsrc = $self->result_source;
 
@@ -821,7 +818,7 @@ sub find {
       my $relinfo = $rsrc->relationship_info($key)
         and
       # implicitly skip has_many's (likely MC)
-      (ref (my $val = delete $call_cond->{$key}) ne 'ARRAY' )
+      ( ref( my $val = delete $call_cond->{$key} ) ne 'ARRAY' )
     ) {
       my ($rel_cond, $crosstable) = $rsrc->_resolve_condition(
         $relinfo->{cond}, $val, $key, $key
@@ -886,7 +883,7 @@ sub find {
           $alias
         );
       }
-      catch {
+      dbic_internal_catch {
         push @fc_exceptions, $_ if $_ =~ /\bFilterColumn\b/;
       };
     }
@@ -987,7 +984,8 @@ See also L</search_related_rs>.
 
 =cut
 
-sub search_related {
+sub search_related :DBIC_method_is_indirect_sugar {
+  DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call;
   return shift->related_resultset(shift)->search(@_);
 }
 
@@ -998,7 +996,8 @@ it guarantees a resultset, even in list context.
 
 =cut
 
-sub search_related_rs {
+sub search_related_rs :DBIC_method_is_indirect_sugar {
+  DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call;
   return shift->related_resultset(shift)->search_rs(@_);
 }
 
@@ -1022,7 +1021,7 @@ sub cursor {
 
   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
     );
   };
@@ -1095,7 +1094,7 @@ sub single {
     }
   }
 
-  my $data = [ $self->result_source->storage->select_single(
+  my $data = [ $self->result_source->schema->storage->select_single(
     $attrs->{from}, $attrs->{select},
     $attrs->{where}, $attrs
   )];
@@ -1122,9 +1121,7 @@ Returns a L<DBIx::Class::ResultSetColumn> instance for a column of the ResultSet
 =cut
 
 sub get_column {
-  my ($self, $column) = @_;
-  my $new = DBIx::Class::ResultSetColumn->new($self, $column);
-  return $new;
+  DBIx::Class::ResultSetColumn->new(@_);
 }
 
 =head2 search_like
@@ -1164,7 +1161,7 @@ sub search_like {
    .' Instead use ->search({ x => { -like => "y%" } })'
    .' (note the outer pair of {}s - they are important!)'
   );
-  my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
+  my $attrs = (@_ > 1 && ref $_[-1] eq 'HASH' ? pop(@_) : {});
   my $query = ref $_[0] eq 'HASH' ? { %{shift()} }: {@_};
   $query->{$_} = { 'like' => $query->{$_} } for keys %$query;
   return $class->search($query, { %$attrs });
@@ -1649,7 +1646,7 @@ sub _count_rs {
   # 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');
 }
@@ -1680,7 +1677,7 @@ sub _count_subq_rs {
   # 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;
@@ -1747,7 +1744,7 @@ sub _count_subq_rs {
   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');
 }
 
@@ -1770,7 +1767,10 @@ with the passed arguments, then L</count>.
 
 =cut
 
-sub count_literal { shift->search_literal(@_)->count; }
+sub count_literal :DBIC_method_is_indirect_sugar {
+  DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call;
+  shift->search_literal(@_)->count
+}
 
 =head2 all
 
@@ -1847,7 +1847,8 @@ an object for the first result (or C<undef> if the resultset is empty).
 
 =cut
 
-sub first {
+sub first :DBIC_method_is_indirect_sugar {
+  DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call;
   return $_[0]->reset->next;
 }
 
@@ -1904,7 +1905,7 @@ sub _rs_update_delete {
     # 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}) ];
     };
@@ -2287,7 +2288,18 @@ sub populate {
                 or
               ref $data->[$i][$_->{pos}] eq 'HASH'
                 or
-              ( defined blessed $data->[$i][$_->{pos}] and $data->[$i][$_->{pos}]->isa('DBIx::Class::Row') )
+              (
+                defined blessed $data->[$i][$_->{pos}]
+                  and
+                $data->[$i][$_->{pos}]->isa(
+                  $DBIx::Class::ResultSource::__expected_result_class_isa
+                    ||
+                  emit_loud_diag(
+                    confess => 1,
+                    msg => 'Global $DBIx::Class::ResultSource::__expected_result_class_isa unexpectedly unset...'
+                  )
+                )
+              )
             )
               and
             1
@@ -2295,7 +2307,18 @@ sub populate {
 
             # moar sanity check... sigh
             for ( ref $data->[$i][$_->{pos}] eq 'ARRAY' ? @{$data->[$i][$_->{pos}]} : $data->[$i][$_->{pos}] ) {
-              if ( defined blessed $_ and $_->isa('DBIx::Class::Row' ) ) {
+              if (
+                defined blessed $_
+                  and
+                $_->isa(
+                  $DBIx::Class::ResultSource::__expected_result_class_isa
+                    ||
+                  emit_loud_diag(
+                    confess => 1,
+                    msg => 'Global $DBIx::Class::ResultSource::__expected_result_class_isa unexpectedly unset...'
+                  )
+                )
+              ) {
                 carp_unique("Fast-path populate() with supplied related objects is not possible - falling back to regular create()");
                 return my $throwaway = $self->populate(@_);
               }
@@ -2337,7 +2360,18 @@ sub populate {
               or
             ref $data->[$i]{$_} eq 'HASH'
               or
-            ( defined blessed $data->[$i]{$_} and $data->[$i]{$_}->isa('DBIx::Class::Row') )
+            (
+              defined blessed $data->[$i]{$_}
+                and
+              $data->[$i]{$_}->isa(
+                $DBIx::Class::ResultSource::__expected_result_class_isa
+                  ||
+                emit_loud_diag(
+                  confess => 1,
+                  msg => 'Global $DBIx::Class::ResultSource::__expected_result_class_isa unexpectedly unset...'
+                )
+              )
+            )
           )
             and
           1
@@ -2345,7 +2379,18 @@ sub populate {
 
           # moar sanity check... sigh
           for ( ref $data->[$i]{$_} eq 'ARRAY' ? @{$data->[$i]{$_}} : $data->[$i]{$_} ) {
-            if ( defined blessed $_ and $_->isa('DBIx::Class::Row' ) ) {
+            if (
+              defined blessed $_
+                and
+              $_->isa(
+                $DBIx::Class::ResultSource::__expected_result_class_isa
+                  ||
+                emit_loud_diag(
+                  confess => 1,
+                  msg => 'Global $DBIx::Class::ResultSource::__expected_result_class_isa unexpectedly unset...'
+                )
+              )
+            ) {
               carp_unique("Fast-path populate() with supplied related objects is not possible - falling back to regular create()");
               return my $throwaway = $self->populate(@_);
             }
@@ -2413,7 +2458,7 @@ sub populate {
 ### 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 {
@@ -2564,11 +2609,8 @@ Passes the hashref of input on to L<DBIx::Class::Row/new>.
 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);
 
@@ -2618,7 +2660,7 @@ sub _merge_with_rscond {
     @cols_from_relations = keys %{ $implied_data || {} };
   }
   else {
-    my $eqs = $self->result_source->schema->storage->_extract_fixed_condition_columns($self->{cond}, 'consider_nulls');
+    my $eqs = extract_equality_conditions( $self->{cond}, 'consider_nulls' );
     $implied_data = { map {
       ( ($eqs->{$_}||'') eq UNRESOLVABLE_CONDITION ) ? () : ( $_ => $eqs->{$_} )
     } keys %$eqs };
@@ -2732,7 +2774,7 @@ sub as_query {
 
   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
   );
 
@@ -2778,7 +2820,7 @@ all in the call to C<find_or_new>, even when set to C<undef>.
 
 sub find_or_new {
   my $self     = shift;
-  my $attrs    = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
+  my $attrs    = (@_ > 1 && ref $_[-1] eq 'HASH' ? pop(@_) : {});
   my $hash     = ref $_[0] eq 'HASH' ? shift : {@_};
   if (keys %$hash and my $row = $self->find($hash, $attrs) ) {
     return $row;
@@ -2867,7 +2909,7 @@ L</new>.
 
 =cut
 
-sub create {
+sub create :DBIC_method_is_indirect_sugar {
   #my ($self, $col_data) = @_;
   DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call;
   return shift->new_result(shift)->insert;
@@ -2947,7 +2989,7 @@ database!
 
 sub find_or_create {
   my $self     = shift;
-  my $attrs    = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
+  my $attrs    = (@_ > 1 && ref $_[-1] eq 'HASH' ? pop(@_) : {});
   my $hash     = ref $_[0] eq 'HASH' ? shift : {@_};
   if (keys %$hash and my $row = $self->find($hash, $attrs) ) {
     return $row;
@@ -3013,7 +3055,7 @@ database!
 
 sub update_or_create {
   my $self = shift;
-  my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
+  my $attrs = (@_ > 1 && ref $_[-1] eq 'HASH' ? pop(@_) : {});
   my $cond = ref $_[0] eq 'HASH' ? shift : {@_};
 
   my $row = $self->find($cond, $attrs);
@@ -3076,7 +3118,7 @@ See also L</find>, L</find_or_create> and L</find_or_new>.
 
 sub update_or_new {
     my $self  = shift;
-    my $attrs = ( @_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {} );
+    my $attrs = ( @_ > 1 && ref $_[-1] eq 'HASH' ? pop(@_) : {} );
     my $cond  = ref $_[0] eq 'HASH' ? shift : {@_};
 
     my $row = $self->find( $cond, $attrs );
@@ -3185,7 +3227,7 @@ sub is_paged {
 
 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
@@ -3528,6 +3570,9 @@ sub _resolved_attrs {
       if ( $attrs->{rows} =~ /[^0-9]/ or $attrs->{rows} <= 0 );
   }
 
+  # normalize where condition
+  $attrs->{where} = normalize_sqla_condition( $attrs->{where} )
+    if $attrs->{where};
 
   # default selection list
   $attrs->{columns} = [ $source->columns ]
@@ -3714,7 +3759,7 @@ sub _resolved_attrs {
     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