try to fold custom join conds back to something DBIC will allow
Matt S Trout [Wed, 16 Oct 2019 01:03:49 +0000 (01:03 +0000)]
Changes
examples/sqla2passthrough.pl
lib/DBIx/Class/SQLMaker/Role/SQLA2Passthrough.pm

diff --git a/Changes b/Changes
index c042198..a65608d 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,7 @@
 Revision history for SQL::Abstract
 
+  - Collapse custom join conditions back to something DBIC might understand
+
 1.90_03 - 2019-10-13
    - Add proof of concept DBIx::Class::SQLMaker::Role::SQLA2Passthrough
    - _where_field_IN/BETWEEN are documented as subclassable; feature restored
index 4cf87b9..8a6d051 100644 (file)
@@ -21,7 +21,8 @@ use With::Roles;
   use DBIx::Class::SQLMaker::Role::SQLA2Passthrough qw(on);
   MySchema->source('Foo')->add_relationship(bars => 'Bar' => on {
     +{ 'foreign.x' => 'self.x',
-       'self.y' => { -between => [ qw(foreign.y1 foreign.y2) ] }
+       'foreign.y1' => { '<=', 'self.y' },
+       'foreign.y2' => { '>=', 'self.y' },
     };
   });
 }
@@ -44,7 +45,7 @@ $s->storage
 warn ref($s->storage->sql_maker);
 
 my $rs2 = $s->resultset('Foo')->search({
-  -op => [ '=', { -ident => 'outer.y' }, { -ident => 'me.x' } ]
+  -op => [ '=', { -ident => 'outer.x' }, { -ident => 'me.y' } ]
 }, {
   'select' => [ 'me.x', { -ident => 'me.z' } ],
   '!with' => [ outer => $rs->get_column('x')->as_query ],
@@ -56,3 +57,11 @@ my $rs3 = $s->resultset('Foo')
             ->search({}, { prefetch => 'bars' });
 
 ::Dwarn(${$rs3->as_query}->[0]);
+
+$s->source('Foo')->result_class('DBIx::Class::Core');
+$s->source('Foo')->set_primary_key('x');
+
+my $rs4 = $s->resultset('Foo')->new_result({ x => 1, y => 2 })
+            ->search_related('bars');
+
+::Dwarn(${$rs4->as_query}->[0]);
index d7db581..04a5434 100644 (file)
@@ -50,19 +50,64 @@ around select => sub {
 
 sub expand_join_condition {
   my ($self, $cond, $args) = @_;
+  my ($type, %known) = do {
+    if (my $obj = $args->{self_result_object}) {
+      (self => $obj->get_columns)
+    } elsif (my $val = $args->{foreign_values}) {
+      (foreign => %$val)
+    } else {
+      ('')
+    }
+  };
+  my $maybe = $type ? 1 : 0;
+  my $outside;
   my $wrap = sub {
     my ($orig) = @_;
+    $outside = $orig;
     sub {
       my $res = $orig->(@_);
-      my ($name, @rest) = @{$res->{-ident}};
+      my ($name, $col) = @{$res->{-ident}};
       if ($name eq 'self' or $name eq 'foreign') {
-        $res->{-ident} = [ $args->{"${name}_alias"}, @rest ];
+        if ($type eq $name) {
+          $maybe = 0 unless exists $known{$col};
+        }
+        return { -ident => [ $args->{"${name}_alias"}, $col ] };
       }
       return $res;
     };
   };
   my $sqla = $self->clone->wrap_op_expander(ident => $wrap);
-  $sqla->expand_expr($cond, -ident);
+  my $aqt = $sqla->expand_expr($cond, -ident);
+  return $aqt unless $maybe;
+  my $inner_wrap = sub {
+    my $res = $outside->(@_);
+    my ($name, $col) = @{$res->{-ident}};
+    if ($name eq 'self' or $name eq 'foreign') {
+      if ($type eq $name) {
+        return { -bind => [ $args->{"${name}_alias"}.'.'.$col, $known{$col} ] };
+      }
+      return { -ident => [ $args->{"${name}_alias"}, $col ] };
+    }
+    return $res;
+  };
+  $sqla->op_expander(ident => $inner_wrap);
+  my $inner_aqt = $self->_collapsify($sqla->expand_expr($cond, -ident));
+  return ($aqt, $inner_aqt);
+}
+
+sub _collapsify {
+  my ($self, $aqt) = @_;
+  return $aqt unless my @opargs = @{$aqt->{-op}};
+  my ($logop, @args) = @opargs;
+  return $aqt unless $logop eq 'and';
+  my %collapsed = map {
+    my $q = $_;
+    return $aqt unless my @opargs = @{$q->{-op}};
+    my ($op, $lhs, @rest) = @opargs;
+    return $aqt unless my @ident = @{$lhs->{-ident}};
+    (join('.', @ident), { $op => \@rest });
+  } @args;
+  return \%collapsed;
 }
 
 1;