Move the *preliminary* multicol IN support to the sqlmaker
Peter Rabbitson [Fri, 11 Jan 2013 18:00:20 +0000 (19:00 +0100)]
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.

lib/DBIx/Class/ResultSet.pm
lib/DBIx/Class/SQLMaker.pm
t/resultset/update_delete.t

index 3cc24b1..18ff813 100644 (file)
@@ -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 {
index cfb3e4c..c5b0888 100644 (file)
@@ -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
index d7fbb63..62081bf 100644 (file)
@@ -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);