package DBIx::Class::SQLMaker;
+use strict;
+use warnings;
+
=head1 NAME
DBIx::Class::SQLMaker - An SQL::Abstract-based SQL maker class
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;
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 = {
}
# 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()})
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}) {
return $_[0]->_recurse_from(@{$_[1]});
}
elsif ($ref eq 'HASH') {
- return $_[0]->_make_as($_[1]);
+ return $_[0]->_recurse_from($_[1]);
}
}
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;
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;