From: Peter Rabbitson Date: Fri, 11 Jan 2013 18:00:20 +0000 (+0100) Subject: Move the *preliminary* multicol IN support to the sqlmaker X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=66137dffe1bc1763bbdf72223bbb9e0214e3051e;p=dbsrgits%2FDBIx-Class-Historic.git Move the *preliminary* multicol IN support to the sqlmaker It really does not belong in ResultSet.pm, but we do not have an API either. Take a first stab at a sketch, DQ will need to wrangle it to completion. --- diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index 3cc24b1..18ff813 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -1830,18 +1830,15 @@ sub _rs_update_delete { ); } elsif ($storage->_use_multicolumn_in) { - # This is hideously ugly, but SQLA does not understand multicol IN expressions - my $sql_maker = $storage->sql_maker; - my ($sql, @bind) = @${$subrs->as_query}; - $sql = sprintf ('(%s) IN %s', # the as_query already comes with a set of parenthesis - join (', ', map { $sql_maker->_quote ($_) } @$idcols), - $sql, - ); - return $storage->$op ( $rsrc, $op eq 'update' ? $values : (), - \[$sql, @bind], + # no syntax for calling this properly yet + # !!! EXPERIMENTAL API !!! WILL CHANGE !!! + $storage->sql_maker->_where_op_multicolumn_in ( + $idcols, # how do I convey a list of idents...? can binds reside on lhs? + $subrs->as_query + ), ); } else { diff --git a/lib/DBIx/Class/SQLMaker.pm b/lib/DBIx/Class/SQLMaker.pm index cfb3e4c..c5b0888 100644 --- a/lib/DBIx/Class/SQLMaker.pm +++ b/lib/DBIx/Class/SQLMaker.pm @@ -455,6 +455,55 @@ sub _join_condition { return $self->_recurse_where($cond); } +# This is hideously ugly, but SQLA does not understand multicol IN expressions +# FIXME TEMPORARY - DQ should have native syntax for this +# moved here to raise API questions +# +# !!! EXPERIMENTAL API !!! WILL CHANGE !!! +sub _where_op_multicolumn_in { + my ($self, $lhs, $rhs) = @_; + + if (! ref $lhs or ref $lhs eq 'ARRAY') { + my (@sql, @bind); + for (ref $lhs ? @$lhs : $lhs) { + if (! ref $_) { + push @sql, $self->_quote($_); + } + elsif (ref $_ eq 'SCALAR') { + push @sql, $$_; + } + elsif (ref $_ eq 'REF' and ref $$_ eq 'ARRAY') { + my ($s, @b) = @$$_; + push @sql, $s; + push @bind, @b; + } + else { + $self->throw_exception("ARRAY of @{[ ref $_ ]}es unsupported for multicolumn IN lhs..."); + } + } + $lhs = \[ join(', ', @sql), @bind]; + } + elsif (ref $lhs eq 'SCALAR') { + $lhs = \[ $$lhs ]; + } + elsif (ref $lhs eq 'REF' and ref $$lhs eq 'ARRAY' ) { + # noop + } + else { + $self->throw_exception( ref($lhs) . "es unsupported for multicolumn IN lhs..."); + } + + # is this proper...? + $rhs = \[ $self->_recurse_where($rhs) ]; + + for ($lhs, $rhs) { + $$_->[0] = "( $$_->[0] )" + unless $$_->[0] =~ /^ \s* \( .* \) \s* ^/xs; + } + + \[ join( ' IN ', shift @$$lhs, shift @$$rhs ), @$$lhs, @$$rhs ]; +} + 1; =head1 AUTHORS diff --git a/t/resultset/update_delete.t b/t/resultset/update_delete.t index d7fbb63..62081bf 100644 --- a/t/resultset/update_delete.t +++ b/t/resultset/update_delete.t @@ -106,7 +106,8 @@ is ($fc->discard_changes->read_count, 30, 'Update did not touch outlier'); $schema->storage->_use_multicolumn_in (1); $schema->storage->debugobj ($debugobj); $schema->storage->debug (1); -eval { $fks->update ({ read_count => \ 'read_count + 1' }) }; # this can't actually execute, we just need the "as_query" +throws_ok { $fks->update ({ read_count => \ 'read_count + 1' }) } # this can't actually execute, we just need the "as_query" + qr/\Q DBI Exception:/ or do { $sql = ''; @bind = () }; $schema->storage->_use_multicolumn_in (undef); $schema->storage->debugobj ($orig_debugobj); $schema->storage->debug ($orig_debug);