Fix $object->search_related aliasing, change semantics of _resolve_condition
[dbsrgits/DBIx-Class-Historic.git] / lib / DBIx / Class / SQLMaker.pm
index 1dd46f5..570af4d 100644 (file)
@@ -1,5 +1,8 @@
 package DBIx::Class::SQLMaker;
 
+use strict;
+use warnings;
+
 =head1 NAME
 
 DBIx::Class::SQLMaker - An SQL::Abstract-based SQL maker class
@@ -38,8 +41,7 @@ use base qw/
   Class::Accessor::Grouped
 /;
 use mro 'c3';
-use strict;
-use warnings;
+
 use Sub::Name 'subname';
 use Carp::Clan qw/^DBIx::Class|^SQL::Abstract|^Try::Tiny/;
 use namespace::clean;
@@ -215,7 +217,7 @@ sub select {
 
 sub _assemble_binds {
   my $self = shift;
-  return map { @{ (delete $self->{"${_}_bind"}) || [] } } (qw/from where having order/);
+  return map { @{ (delete $self->{"${_}_bind"}) || [] } } (qw/select from where group having order/);
 }
 
 my $for_syntax = {
@@ -295,7 +297,8 @@ sub _recurse_fields {
   }
   # Is the second check absolutely necessary?
   elsif ( $ref eq 'REF' and ref($$fields) eq 'ARRAY' ) {
-    return $self->_fold_sqlbind( $fields );
+    push @{$self->{select_bind}}, @{$$fields}[1..$#$$fields];
+    return $$fields->[0];
   }
   else {
     croak($ref . qq{ unexpected in _recurse_fields()})
@@ -316,8 +319,13 @@ sub _parse_rs_attrs {
 
   my $sql = '';
 
-  if (my $g = $self->_recurse_fields($arg->{group_by}) ) {
-    $sql .= $self->_sqlcase(' group by ') . $g;
+  if ($arg->{group_by}) {
+    # horible horrible, waiting for refactor
+    local $self->{select_bind};
+    if (my $g = $self->_recurse_fields($arg->{group_by}) ) {
+      $sql .= $self->_sqlcase(' group by ') . $g;
+      push @{$self->{group_bind} ||= []}, @{$self->{select_bind}||[]};
+    }
   }
 
   if (defined $arg->{having}) {
@@ -355,7 +363,7 @@ sub _table {
       return $_[0]->_recurse_from(@{$_[1]});
     }
     elsif ($ref eq 'HASH') {
-      return $_[0]->_make_as($_[1]);
+      return $_[0]->_recurse_from($_[1]);
     }
   }
 
@@ -366,17 +374,17 @@ sub _generate_join_clause {
     my ($self, $join_type) = @_;
 
     return sprintf ('%s JOIN ',
-      $join_type ?  ' ' . uc($join_type) : ''
+      $join_type ?  ' ' . $self->_sqlcase($join_type) : ''
     );
 }
 
 sub _recurse_from {
   my ($self, $from, @join) = @_;
   my @sqlf;
-  push(@sqlf, $self->_make_as($from));
-  foreach my $j (@join) {
-    my ($to, $on) = @$j;
+  push @sqlf, $self->_from_chunk_to_sql($from);
 
+  for (@join) {
+    my ($to, $on) = @$_;
 
     # check whether a join type exists
     my $to_jt = ref($to) eq 'ARRAY' ? $to->[0] : $to;
@@ -393,60 +401,76 @@ sub _recurse_from {
     if (ref $to eq 'ARRAY') {
       push(@sqlf, '(', $self->_recurse_from(@$to), ')');
     } else {
-      push(@sqlf, $self->_make_as($to));
+      push(@sqlf, $self->_from_chunk_to_sql($to));
     }
-    push(@sqlf, ' ON ', $self->_join_condition($on));
+
+    my ($sql, @bind) = $self->_join_condition($on);
+    push(@sqlf, ' ON ', $sql);
+    push @{$self->{from_bind}}, @bind;
   }
+
   return join('', @sqlf);
 }
 
-sub _fold_sqlbind {
-  my ($self, $sqlbind) = @_;
-
-  my @sqlbind = @$$sqlbind; # copy
-  my $sql = shift @sqlbind;
-  push @{$self->{from_bind}}, @sqlbind;
-
-  return $sql;
-}
+sub _from_chunk_to_sql {
+  my ($self, $fromspec) = @_;
+
+  return join (' ', $self->_SWITCH_refkind($fromspec, {
+    SCALARREF => sub {
+      $$fromspec;
+    },
+    ARRAYREFREF => sub {
+      push @{$self->{from_bind}}, @{$$fromspec}[1..$#$$fromspec];
+      $$fromspec->[0];
+    },
+    HASHREF => sub {
+      my ($as, $table, $toomuch) = ( map
+        { $_ => $fromspec->{$_} }
+        ( grep { $_ !~ /^\-/ } keys %$fromspec )
+      );
 
-sub _make_as {
-  my ($self, $from) = @_;
-  return join(' ', map { (ref $_ eq 'SCALAR' ? $$_
-                        : ref $_ eq 'REF'    ? $self->_fold_sqlbind($_)
-                        : $self->_quote($_))
-                       } reverse each %{$self->_skip_options($from)});
-}
+      croak "Only one table/as pair expected in from-spec but an exra '$toomuch' key present"
+        if defined $toomuch;
 
-sub _skip_options {
-  my ($self, $hash) = @_;
-  my $clean_hash = {};
-  $clean_hash->{$_} = $hash->{$_}
-    for grep {!/^-/} keys %$hash;
-  return $clean_hash;
+      ($self->_from_chunk_to_sql($table), $self->_quote($as) );
+    },
+    SCALAR => sub {
+      $self->_quote($fromspec);
+    },
+  }));
 }
 
 sub _join_condition {
   my ($self, $cond) = @_;
-  if (ref $cond eq 'HASH') {
-    my %j;
-    for (keys %$cond) {
-      my $v = $cond->{$_};
-      if (ref $v) {
-        croak (ref($v) . qq{ reference arguments are not supported in JOINS - try using \"..." instead'})
-            if ref($v) ne 'SCALAR';
-        $j{$_} = $v;
-      }
-      else {
-        my $x = '= '.$self->_quote($v); $j{$_} = \$x;
-      }
-    };
-    return scalar($self->_recurse_where(\%j));
-  } elsif (ref $cond eq 'ARRAY') {
-    return join(' OR ', map { $self->_join_condition($_) } @$cond);
-  } else {
-    croak "Can't handle this yet!";
+
+  # Backcompat for the old days when a plain hashref
+  # { 't1.col1' => 't2.col2' } meant ON t1.col1 = t2.col2
+  # Once things settle we should start warning here so that
+  # folks unroll their hacks
+  if (
+    ref $cond eq 'HASH'
+      and
+    keys %$cond == 1
+      and
+    (keys %$cond)[0] =~ /\./
+      and
+    ! ref ( (values %$cond)[0] )
+  ) {
+    $cond = { keys %$cond => { -ident => values %$cond } }
   }
+  elsif ( ref $cond eq 'ARRAY' ) {
+    # do our own ORing so that the hashref-shim above is invoked
+    my @parts;
+    my @binds;
+    foreach my $c (@$cond) {
+      my ($sql, @bind) = $self->_join_condition($c);
+      push @binds, @bind;
+      push @parts, $sql;
+    }
+    return join(' OR ', @parts), @binds;
+  }
+
+  return $self->_recurse_where($cond);
 }
 
 1;