Remove ability to hand an explicit condition to resolve_relationship_condition
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSource.pm
index 2d54ec0..3c45662 100644 (file)
@@ -77,7 +77,7 @@ More specifically, the L<DBIx::Class::Core> base class pulls in the
 L<DBIx::Class::ResultSourceProxy::Table> component, which defines
 the L<table|DBIx::Class::ResultSourceProxy::Table/table> method.
 When called, C<table> creates and stores an instance of
-L<DBIx::Class::ResultSoure::Table>. Luckily, to use tables as result
+L<DBIx::Class::ResultSource::Table>. Luckily, to use tables as result
 sources, you don't need to remember any of this.
 
 Result sources representing select queries, or views, can also be
@@ -86,7 +86,8 @@ created, see L<DBIx::Class::ResultSource::View> for full details.
 =head2 Finding result source objects
 
 As mentioned above, a result source instance is created and stored for
-you when you define a L<result class|DBIx::Class::Manual::Glossary/Result class>.
+you when you define a
+L<Result Class|DBIx::Class::Manual::Glossary/Result Class>.
 
 You can retrieve the result source at runtime in the following ways:
 
@@ -108,7 +109,13 @@ You can retrieve the result source at runtime in the following ways:
 
 =head1 METHODS
 
-=pod
+=head2 new
+
+  $class->new();
+
+  $class->new({attribute_name => value});
+
+Creates a new ResultSource object.  Not normally called directly by end users.
 
 =cut
 
@@ -204,6 +211,12 @@ The length of your column, if it is a column type that can have a size
 restriction. This is currently only used to create tables from your
 schema, see L<DBIx::Class::Schema/deploy>.
 
+   { size => [ 9, 6 ] }
+
+For decimal or float values you can specify an ArrayRef in order to
+control precision, assuming your database's
+L<SQL::Translator::Producer> supports it.
+
 =item is_nullable
 
    { is_nullable => 1 }
@@ -582,7 +595,7 @@ sub remove_column { shift->remove_columns(@_); } # DO NOT CHANGE THIS TO GLOB
 Defines one or more columns as primary key for this source. Must be
 called after L</add_columns>.
 
-Additionally, defines a L<unique constraint|add_unique_constraint>
+Additionally, defines a L<unique constraint|/add_unique_constraint>
 named C<primary>.
 
 Note: you normally do want to define a primary key on your sources
@@ -1180,6 +1193,17 @@ clause contents.
 
 sub from { die 'Virtual method!' }
 
+=head2 source_info
+
+Stores a hashref of per-source metadata.  No specific key names
+have yet been standardized, the examples below are purely hypothetical
+and don't actually accomplish anything on their own:
+
+  __PACKAGE__->source_info({
+    "_tablespace" => 'fast_disk_array_3',
+    "_engine" => 'InnoDB',
+  });
+
 =head2 schema
 
 =over 4
@@ -1331,7 +1355,6 @@ sub add_relationship {
   my %rels = %{ $self->_relationships };
   $rels{$rel} = { class => $f_source_name,
                   source => $f_source_name,
-                  _original_name => $rel,
                   cond  => $cond,
                   attrs => $attrs };
   $self->_relationships(\%rels);
@@ -1787,8 +1810,6 @@ sub _resolve_condition {
   }
 
   my $args = {
-    condition => $cond,
-
     # where-is-waldo block guesses relname, then further down we override it if available
     (
       $is_objlike[1] ? ( rel_name => $res_args[0], self_alias => $res_args[0], foreign_alias => 'me',         self_result_object  => $res_args[1] )
@@ -1798,6 +1819,12 @@ sub _resolve_condition {
 
     ( $rel_name ? ( rel_name => $rel_name ) : () ),
   };
+
+  # Allowing passing relconds different than the relationshup itself is cute,
+  # but likely dangerous. Remove that from the (still unofficial) API of
+  # _resolve_relationship_condition, and instead make it "hard on purpose"
+  local $self->relationship_info( $args->{rel_name} )->{cond} = $cond if defined $cond;
+
 #######################
 
   # now it's fucking easy isn't it?!
@@ -1841,7 +1868,6 @@ Internals::SvREADONLY($UNRESOLVABLE_CONDITION => 1);
 # self_result_object    => (either not supplied or a result object)
 # require_join_free_condition => (boolean, throws on failure to construct a JF-cond)
 # infer_values_based_on => (either not supplied or a hashref, implies require_join_free_condition)
-# condition             => (sqla cond struct, optional, defeaults to from $self->rel_info(rel_name)->{cond})
 #
 ## returns a hash
 # condition           => (a valid *likely fully qualified* sqla cond structure)
@@ -1865,12 +1891,17 @@ sub _resolve_relationship_condition {
   $self->throw_exception("Arguments 'self_alias' and 'foreign_alias' may not be identical")
     if $args->{self_alias} eq $args->{foreign_alias};
 
+# TEMP
+  my $exception_rel_id = "relationship '$args->{rel_name}' on source '@{[ $self->source_name ]}'";
+
   my $rel_info = $self->relationship_info($args->{rel_name})
 # TEMP
 #    or $self->throw_exception( "No such $exception_rel_id" );
     or carp_unique("Requesting resolution on non-existent relationship '$args->{rel_name}' on source '@{[ $self->source_name ]}': fix your code *soon*, as it will break with the next major version");
 
-  my $exception_rel_id = "relationship '$rel_info->{_original_name}' on source '@{[ $self->source_name ]}'";
+# TEMP
+  $exception_rel_id = "relationship '$rel_info->{_original_name}' on source '@{[ $self->source_name ]}'"
+    if $rel_info and exists $rel_info->{_original_name};
 
   $self->throw_exception("No practical way to resolve $exception_rel_id between two data structures")
     if exists $args->{self_result_object} and exists $args->{foreign_values};
@@ -1880,19 +1911,21 @@ sub _resolve_relationship_condition {
 
   $args->{require_join_free_condition} ||= !!$args->{infer_values_based_on};
 
-  $args->{condition} ||= $rel_info->{cond};
-
-  $self->throw_exception( "Argument 'self_result_object' must be an object of class '@{[ $self->result_class ]}'" )
+  $self->throw_exception( "Argument 'self_result_object' must be an object inheriting from DBIx::Class::Row" )
     if (
       exists $args->{self_result_object}
         and
-      ( ! defined blessed $args->{self_result_object} or ! $args->{self_result_object}->isa($self->result_class) )
+      ( ! defined blessed $args->{self_result_object} or ! $args->{self_result_object}->isa('DBIx::Class::Row') )
     )
   ;
 
-  my $rel_rsrc = $self->related_source($args->{rel_name});
+#TEMP
+  my $rel_rsrc;# = $self->related_source($args->{rel_name});
 
   if (exists $args->{foreign_values}) {
+# TEMP
+    $rel_rsrc ||= $self->related_source($args->{rel_name});
+
     if (defined blessed $args->{foreign_values}) {
 
       $self->throw_exception( "Objects supplied as 'foreign_values' ($args->{foreign_values}) must inherit from DBIx::Class::Row" )
@@ -1923,7 +1956,7 @@ sub _resolve_relationship_condition {
 
   my $ret;
 
-  if (ref $args->{condition} eq 'CODE') {
+  if (ref $rel_info->{cond} eq 'CODE') {
 
     my $cref_args = {
       rel_name => $args->{rel_name},
@@ -1942,7 +1975,7 @@ sub _resolve_relationship_condition {
     $cref_args->{self_rowobj} = $cref_args->{self_result_object}
       if exists $cref_args->{self_result_object};
 
-    ($ret->{condition}, $ret->{join_free_condition}, my @extra) = $args->{condition}->($cref_args);
+    ($ret->{condition}, $ret->{join_free_condition}, my @extra) = $rel_info->{cond}->($cref_args);
 
     # sanity check
     $self->throw_exception("A custom condition coderef can return at most 2 conditions, but $exception_rel_id returned extra values: @extra")
@@ -1954,6 +1987,9 @@ sub _resolve_relationship_condition {
         "The join-free condition returned for $exception_rel_id must be a hash reference"
       ) unless ref $jfc eq 'HASH';
 
+# TEMP
+      $rel_rsrc ||= $self->related_source($args->{rel_name});
+
       my ($joinfree_alias, $joinfree_source);
       if (defined $args->{self_result_object}) {
         $joinfree_alias = $args->{foreign_alias};
@@ -1996,14 +2032,14 @@ sub _resolve_relationship_condition {
 
     }
   }
-  elsif (ref $args->{condition} eq 'HASH') {
+  elsif (ref $rel_info->{cond} eq 'HASH') {
 
     # the condition is static - use parallel arrays
     # for a "pivot" depending on which side of the
     # rel did we get as an object
     my (@f_cols, @l_cols);
-    for my $fc (keys %{$args->{condition}}) {
-      my $lc = $args->{condition}{$fc};
+    for my $fc (keys %{ $rel_info->{cond} }) {
+      my $lc = $rel_info->{cond}{$fc};
 
       # FIXME STRICTMODE should probably check these are valid columns
       $fc =~ s/^foreign\.// ||
@@ -2051,35 +2087,36 @@ sub _resolve_relationship_condition {
       }
     }
   }
-  elsif (ref $args->{condition} eq 'ARRAY') {
-    if (@{$args->{condition}} == 0) {
+  elsif (ref $rel_info->{cond} eq 'ARRAY') {
+    if (@{ $rel_info->{cond} } == 0) {
       $ret = {
         condition => UNRESOLVABLE_CONDITION,
         join_free_condition => UNRESOLVABLE_CONDITION,
       };
     }
-    elsif (@{$args->{condition}} == 1) {
-      $ret = $self->_resolve_relationship_condition({
-        %$args,
-        condition => $args->{condition}[0],
-      });
-    }
     else {
-      # we are discarding inferred values here... likely incorrect...
-      # then again - the entire thing is an OR, so we *can't* use them anyway
-      for my $subcond ( map
-        { $self->_resolve_relationship_condition({ %$args, condition => $_ }) }
-        @{$args->{condition}}
-      ) {
-        $self->throw_exception('Either all or none of the OR-condition members must resolve to a join-free condition')
-          if ( $ret and ( $ret->{join_free_condition} xor $subcond->{join_free_condition} ) );
+      my @subconds = map {
+        local $rel_info->{cond} = $_;
+        $self->_resolve_relationship_condition( $args );
+      } @{ $rel_info->{cond} };
+
+      if( @{ $rel_info->{cond} } == 1 ) {
+        $ret = $subconds[0];
+      }
+      else {
+        # we are discarding inferred values here... likely incorrect...
+        # then again - the entire thing is an OR, so we *can't* use them anyway
+        for my $subcond ( @subconds ) {
+          $self->throw_exception('Either all or none of the OR-condition members must resolve to a join-free condition')
+            if ( $ret and ( $ret->{join_free_condition} xor $subcond->{join_free_condition} ) );
 
-        $subcond->{$_} and push @{$ret->{$_}}, $subcond->{$_} for (qw(condition join_free_condition));
+          $subcond->{$_} and push @{$ret->{$_}}, $subcond->{$_} for (qw(condition join_free_condition));
+        }
       }
     }
   }
   else {
-    $self->throw_exception ("Can't handle condition $args->{condition} for $exception_rel_id yet :(");
+    $self->throw_exception ("Can't handle condition $rel_info->{cond} for $exception_rel_id yet :(");
   }
 
   $self->throw_exception(ucfirst "$exception_rel_id does not resolve to a join-free condition fragment") if (
@@ -2092,7 +2129,13 @@ sub _resolve_relationship_condition {
 
   # we got something back - sanity check and infer values if we can
   my @nonvalues;
-  if ( my $jfc = $ret->{join_free_condition} and $ret->{join_free_condition} ne UNRESOLVABLE_CONDITION ) {
+  if (
+    $ret->{join_free_condition}
+      and
+    $ret->{join_free_condition} ne UNRESOLVABLE_CONDITION
+      and
+    my $jfc = $storage->_collapse_cond( $ret->{join_free_condition} )
+  ) {
 
     my $jfc_eqs = $storage->_extract_fixed_condition_columns($jfc, 'consider_nulls');
 
@@ -2141,6 +2184,9 @@ sub _resolve_relationship_condition {
 
       next if $col_eqs->{$lhs} eq UNRESOLVABLE_CONDITION;
 
+# TEMP
+      $rel_rsrc ||= $self->related_source($args->{rel_name});
+
       # there is no way to know who is right and who is left in a cref
       # therefore a full blown resolution call, and figure out the
       # direction a bit further below
@@ -2274,6 +2320,9 @@ sub handle {
 
 my $global_phase_destroy;
 sub DESTROY {
+  ### NO detected_reinvoked_destructor check
+  ### This code very much relies on being called multuple times
+
   return if $global_phase_destroy ||= in_global_destruction;
 
 ######
@@ -2341,25 +2390,6 @@ sub throw_exception {
   ;
 }
 
-=head2 source_info
-
-Stores a hashref of per-source metadata.  No specific key names
-have yet been standardized, the examples below are purely hypothetical
-and don't actually accomplish anything on their own:
-
-  __PACKAGE__->source_info({
-    "_tablespace" => 'fast_disk_array_3',
-    "_engine" => 'InnoDB',
-  });
-
-=head2 new
-
-  $class->new();
-
-  $class->new({attribute_name => value});
-
-Creates a new ResultSource object.  Not normally called directly by end users.
-
 =head2 column_info_from_storage
 
 =over
@@ -2376,14 +2406,16 @@ Enables the on-demand automatic loading of the above column
 metadata from storage as necessary.  This is *deprecated*, and
 should not be used.  It will be removed before 1.0.
 
+=head1 FURTHER QUESTIONS?
 
-=head1 AUTHOR AND CONTRIBUTORS
-
-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