Refactor ::DBIHacks::_extract_fixed_condition_columns (sequel to 8d005ad9)
[dbsrgits/DBIx-Class-Historic.git] / lib / DBIx / Class / ResultSource.pm
index 4f45c58..886f47e 100644 (file)
@@ -9,7 +9,7 @@ use DBIx::Class::ResultSet;
 use DBIx::Class::ResultSourceHandle;
 
 use DBIx::Class::Carp;
-use DBIx::Class::_Util 'is_literal_value';
+use DBIx::Class::_Util 'UNRESOLVABLE_CONDITION';
 use Devel::GlobalDestruction;
 use Try::Tiny;
 use List::Util 'first';
@@ -1704,11 +1704,10 @@ sub _resolve_condition {
     else {
       $res_args[$_] ||= {};
 
+      # hate everywhere - have to pass in as a plain hash
+      # pretending to be an object at least for now
       $self->throw_exception("Unsupported object-like structure encountered: $res_args[$_]")
         unless ref $res_args[$_] eq 'HASH';
-
-      # hate everywhere
-      $res_args[$_] = $self->relationship_info($rel_name)->{source}->result_class->new($res_args[$_]);
     }
   }
 
@@ -1717,10 +1716,15 @@ sub _resolve_condition {
 
   my $args = {
     condition => $cond,
-    rel_name => $rel_name,
-    $is_objlike[1] ? ( self_alias => $res_args[0], foreign_alias => 'me',         self_resultobj    => $res_args[1] )
-  : $is_objlike[0] ? ( self_alias => 'me',         foreign_alias => $res_args[1], foreign_resultobj => $res_args[0] )
-  :                  ( self_alias => $res_args[1], foreign_alias => $res_args[0] )
+
+    # 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_resultobj    => $res_args[1] )
+    : $is_objlike[0] ? ( rel_name => $res_args[1], self_alias => 'me',         foreign_alias => $res_args[1], foreign_resultobj => $res_args[0] )
+    :                  ( rel_name => $res_args[0], self_alias => $res_args[1], foreign_alias => $res_args[0]                                    )
+    ),
+
+    ( $rel_name ? ( rel_name => $rel_name ) : () ),
   };
 #######################
 
@@ -1741,7 +1745,14 @@ sub _resolve_condition {
   return wantarray ? @res : $res[0];
 }
 
-our $UNRESOLVABLE_CONDITION = \ '1 = 0';
+# Keep this indefinitely. There is evidence of both CPAN and
+# darkpan using it, and there isn't much harm in an extra var
+# anyway.
+our $UNRESOLVABLE_CONDITION = UNRESOLVABLE_CONDITION;
+# YES I KNOW THIS IS EVIL
+# it is there to save darkpan from themselves, since internally
+# we are moving to a constant
+Internals::SvREADONLY($UNRESOLVABLE_CONDITION => 1);
 
 # Resolves the passed condition to a concrete query fragment and a flag
 # indicating whether this is a cross-table condition. Also an optional
@@ -1776,46 +1787,60 @@ sub _resolve_relationship_condition {
       self_resultsource => $self,
       self_alias => $args->{self_alias},
       foreign_alias => $args->{foreign_alias},
-      self_resultobj => defined $args->{self_resultobj} ? $args->{self_resultobj} : undef,
+      self_resultobj => $args->{self_resultobj},
+      foreign_resultobj => $args->{foreign_resultobj},
     };
 
     # legacy - never remove these!!!
     $cref_args->{foreign_relname} = $cref_args->{rel_name};
     $cref_args->{self_rowobj} = $cref_args->{self_resultobj};
 
-    my ($crosstable_cond, $joinfree_cond) = $args->{condition}->($cref_args);
+    my ($crosstable_cond, $joinfree_cond, @extra) = $args->{condition}->($cref_args);
+
+    # FIXME sanity check
+    carp_unique('A custom condition coderef can return at most 2 conditions: extra return values discarded')
+      if @extra;
 
     my @nonvalue_cols;
     if ($joinfree_cond) {
 
+      my ($joinfree_alias, $joinfree_source);
+      if (defined $args->{self_resultobj}) {
+        $joinfree_alias = $args->{foreign_alias};
+        $joinfree_source = $self->related_source($args->{rel_name});
+      }
+      elsif (defined $args->{foreign_resultobj}) {
+        $joinfree_alias = $args->{self_alias};
+        $joinfree_source = $self;
+      }
+
       # FIXME sanity check until things stabilize, remove at some point
       $self->throw_exception (
         "A join-free condition returned for relationship '$args->{rel_name}' without a result object to chain from"
-      ) unless defined $args->{self_resultobj};
+      ) unless $joinfree_alias;
 
-      my $foreign_src_fq_col_list = { map { ( "$args->{foreign_alias}.$_" => 1 ) } $self->related_source($args->{rel_name})->columns };
+      my $fq_col_list = { map { ( "$joinfree_alias.$_" => 1 ) } $joinfree_source->columns };
 
       # FIXME another sanity check
       if (
         ref $joinfree_cond ne 'HASH'
           or
-        grep { ! $foreign_src_fq_col_list->{$_} } keys %$joinfree_cond
+        grep { ! $fq_col_list->{$_} } keys %$joinfree_cond
       ) {
         $self->throw_exception (
           "The join-free condition returned for relationship '$args->{rel_name}' must be a hash "
-         .'reference with all keys being fully qualified column names of the foreign source'
+         .'reference with all keys being fully qualified column names of the corresponding source'
         );
       }
 
       # see which parts of the joinfree cond are *NOT* foreign-source-column equalities
-      my $joinfree_cond_equality_columns = { map
-        {( $_ => 1 )}
-        @{ $self->schema->storage->_extract_fixed_condition_columns($joinfree_cond) }
-      };
+      my $joinfree_cond_equality_columns =
+        $self->schema->storage->_extract_fixed_condition_columns($joinfree_cond, 'consider_nulls');
+
       @nonvalue_cols = map
-        { $_ =~ /^\Q$args->{foreign_alias}.\E(.+)/ }
+        { $_ =~ /^\Q$joinfree_alias.\E(.+)/ }
         grep
-          { ! $joinfree_cond_equality_columns->{$_} }
+          { ! exists $joinfree_cond_equality_columns->{$_} }
           keys %$joinfree_cond;
 
       return ($joinfree_cond, 0, (@nonvalue_cols ? \@nonvalue_cols : undef));
@@ -1861,7 +1886,16 @@ sub _resolve_relationship_condition {
       ;
 
       for my $i (0..$#$obj_cols) {
-        if (defined $args->{self_resultobj} and ! $obj->has_column_loaded($obj_cols->[$i])) {
+
+        # FIXME - temp shim
+        if (! blessed $obj) {
+          $cond->{"$plain_alias.$plain_cols->[$i]"} = $obj->{$obj_cols->[$i]};
+        }
+        elsif (
+          defined $args->{self_resultobj}
+            and
+          ! $obj->has_column_loaded($obj_cols->[$i])
+        ) {
 
           $self->throw_exception(sprintf
             "Unable to resolve relationship '%s' from object '%s': column '%s' not "
@@ -1873,7 +1907,7 @@ sub _resolve_relationship_condition {
             $obj_cols->[$i],
           ) if $obj->in_storage;
 
-          return $UNRESOLVABLE_CONDITION;
+          return UNRESOLVABLE_CONDITION;
         }
         else {
           $cond->{"$plain_alias.$plain_cols->[$i]"} = $obj->get_column($obj_cols->[$i]);
@@ -1885,7 +1919,7 @@ sub _resolve_relationship_condition {
   }
   elsif (ref $args->{condition} eq 'ARRAY') {
     if (@{$args->{condition}} == 0) {
-      return $UNRESOLVABLE_CONDITION;
+      return UNRESOLVABLE_CONDITION;
     }
     elsif (@{$args->{condition}} == 1) {
       return $self->_resolve_relationship_condition({