diff --git a/lib/Data/ObjectDriver/SQL.pm b/lib/Data/ObjectDriver/SQL.pm index 068d39e..e6d72b5 100644 --- a/lib/Data/ObjectDriver/SQL.pm +++ b/lib/Data/ObjectDriver/SQL.pm @@ -247,25 +247,65 @@ sub _mk_term { $term = join " $logic ", @terms; } else { $col = $m->($col) if $m = $stmt->column_mutator; - $term = "$col IN (".join(',', ('?') x scalar @$val).')'; + $term = $stmt->_mk_term_arrayref($col, 'IN', $val); @bind = @$val; } } elsif (ref($val) eq 'HASH') { my $c = $val->{column} || $col; $c = $m->($c) if $m = $stmt->column_mutator; - $term = "$c $val->{op} ?"; - push @bind, $val->{value}; + my $op = uc $val->{op}; + if (($op eq 'IN' or $op eq 'NOT IN') and ref $val->{value} eq 'ARRAY') { + $term = $stmt->_mk_term_arrayref($c, $op, $val->{value}); + push @bind, @{$val->{value}}; + } elsif (($op eq 'IN' or $op eq 'NOT IN') and ref $val->{value} eq 'REF') { + my @values = @{${$val->{value}}}; + $term = "$c $op (" . (shift @values) . ")"; + push @bind, @values; + } elsif ($op eq 'BETWEEN' and ref $val->{value} eq 'ARRAY') { + Carp::croak "USAGE: foo => {op => 'BETWEEN', value => [\$a, \$b]}" if @{$val->{value}} != 2; + $term = "$c $op ? AND ?"; + push @bind, @{$val->{value}}; + } else { + if (ref $val->{value} eq 'SCALAR') { + $term = "$c $val->{op} " . ${$val->{value}}; + } else { + $term = "$c $val->{op} ?"; + push @bind, $val->{value}; + } + } } elsif (ref($val) eq 'SCALAR') { $col = $m->($col) if $m = $stmt->column_mutator; $term = "$col $$val"; + } elsif (ref($val) eq 'REF') { + $col = $m->($col) if $m = $stmt->column_mutator; + my @values = @{$$val}; + $term = "$col " . (shift @values); + push @bind, @values; } else { $col = $m->($col) if $m = $stmt->column_mutator; - $term = "$col = ?"; - push @bind, $val; + if (defined $val) { + $term = "$col = ?"; + push @bind, $val; + } else { + $term = "$col IS NULL"; + } } ($term, \@bind, $col); } +sub _mk_term_arrayref { + my ($stmt, $col, $op, $val) = @_; + if (@$val) { + return "$col $op (".join(',', ('?') x scalar @$val).')'; + } else { + if ($op eq 'IN') { + return '0 = 1'; + } elsif ($op eq 'NOT IN') { + return '1 = 1'; + } + } +} + sub _add_index_hint { my $stmt = shift; my ($tbl_name) = @_; diff --git a/t/11-sql.t b/t/11-sql.t index 38c4666..d82ee2e 100644 --- a/t/11-sql.t +++ b/t/11-sql.t @@ -3,7 +3,7 @@ use strict; use Data::ObjectDriver::SQL; -use Test::More tests => 68; +use Test::More tests => 93; my $stmt = ns(); ok($stmt, 'Created SQL object'); @@ -128,6 +128,27 @@ is(scalar @{ $stmt->bind }, 2); is($stmt->bind->[0], 'bar'); is($stmt->bind->[1], 'baz'); +$stmt = ns(); $stmt->add_where(foo => { op => 'IN', value => ['bar'] }); +is($stmt->as_sql_where, "WHERE (foo IN (?))\n"); +is(scalar @{ $stmt->bind }, 1); +is($stmt->bind->[0], 'bar'); + +$stmt = ns(); $stmt->add_where(foo => { op => 'NOT IN', value => ['bar'] }); +is($stmt->as_sql_where, "WHERE (foo NOT IN (?))\n"); +is(scalar @{ $stmt->bind }, 1); +is($stmt->bind->[0], 'bar'); + +$stmt = ns(); $stmt->add_where(foo => { op => 'BETWEEN', value => ['bar', 'baz'] }); +is($stmt->as_sql_where, "WHERE (foo BETWEEN ? AND ?)\n"); +is(scalar @{ $stmt->bind }, 2); +is($stmt->bind->[0], 'bar'); +is($stmt->bind->[1], 'baz'); + +$stmt = ns(); $stmt->add_where(foo => { op => 'LIKE', value => 'bar%' }); +is($stmt->as_sql_where, "WHERE (foo LIKE ?)\n"); +is(scalar @{ $stmt->bind }, 1); +is($stmt->bind->[0], 'bar%'); + $stmt = ns(); $stmt->add_where(foo => { op => '!=', value => 'bar' }); is($stmt->as_sql_where, "WHERE (foo != ?)\n"); is(scalar @{ $stmt->bind }, 1); @@ -174,6 +195,34 @@ is($stmt->bind->[0], 'foo'); is($stmt->bind->[1], 'bar'); is($stmt->bind->[2], 'baz'); +$stmt = ns(); +$stmt->add_where(foo => \['IN (SELECT foo FROM bar WHERE t=?)', 'foo']); +is($stmt->as_sql_where, "WHERE (foo IN (SELECT foo FROM bar WHERE t=?))\n"); +is(scalar @{ $stmt->bind }, 1); +is($stmt->bind->[0], 'foo'); + +$stmt = ns(); +$stmt->add_where(foo => { op => 'IN', value => \['(SELECT foo FROM bar WHERE t=?)', 'foo']}); +is($stmt->as_sql_where, "WHERE (foo IN ((SELECT foo FROM bar WHERE t=?)))\n"); +is(scalar @{ $stmt->bind }, 1); +is($stmt->bind->[0], 'foo'); + +$stmt = ns(); +$stmt->add_where(foo => { op => 'IN', value => \'(SELECT foo FROM bar)'}); +is($stmt->as_sql_where, "WHERE (foo IN (SELECT foo FROM bar))\n"); +is(scalar @{ $stmt->bind }, 0); + +$stmt = ns(); +$stmt->add_where(foo => undef); +is($stmt->as_sql_where, "WHERE (foo IS NULL)\n"); +is(scalar @{ $stmt->bind }, 0); + +## avoid syntax error +$stmt = ns(); +$stmt->add_where(foo => []); +is($stmt->as_sql_where, "WHERE (0 = 1)\n"); # foo IN () +is(scalar @{ $stmt->bind }, 0); + ## regression bug. modified parameters my %terms = ( foo => [-and => 'foo', 'bar', 'baz']); $stmt = ns();