move select arg handling to _select_to_dq to enable creating $rs->_as_select_dq
Matt S Trout [Fri, 1 Nov 2013 09:37:23 +0000 (09:37 +0000)]
lib/DBIx/Class/ResultSet.pm
lib/DBIx/Class/SQLMaker.pm
lib/DBIx/Class/SQLMaker/Converter.pm

index cddf84c..bde773e 100644 (file)
@@ -1967,7 +1967,7 @@ sub _rs_update_delete {
     my $subrs = (ref $self)->new($rsrc, $attrs);
 
     if (@$idcols == 1) {
-      $cond = { $idcols->[0] => { -in => $subrs->as_query } };
+      $cond = { $idcols->[0] => { -in => \$subrs->_as_select_dq } };
     }
     elsif ($storage->_use_multicolumn_in) {
       # no syntax for calling this properly yet
@@ -2684,6 +2684,19 @@ sub as_query {
   $aq;
 }
 
+sub _as_select_dq {
+  my $self = shift;
+  my $attrs = { %{ $self->_resolved_attrs } };
+  my $storage = $self->result_source->storage;
+  my (undef, $ident, @args) = $storage->_select_args(
+    $attrs->{from}, $attrs->{select}, $attrs->{where}, $attrs
+  );
+  $ident = $ident->from if blessed($ident);
+  $storage->sql_maker->converter->_select_to_dq(
+    $ident, @args
+  );
+}
+
 =head2 find_or_new
 
 =over 4
index dac76df..d8255d6 100644 (file)
@@ -144,79 +144,23 @@ sub _where_op_NEST {
   shift->next::method(@_);
 }
 
-# Handle limit-dialect selection
-sub select {
-  my ($self, $table, $fields, $where, $rs_attrs, $limit, $offset) = @_;
-
-  if (defined $offset) {
-    $self->throw_exception('A supplied offset must be a non-negative integer')
-      if ( $offset =~ /\D/ or $offset < 0 );
-  }
-  $offset ||= 0;
-
-  if (defined $limit) {
-    $self->throw_exception('A supplied limit must be a positive integer')
-      if ( $limit =~ /\D/ or $limit <= 0 );
-  }
-  elsif ($offset) {
-    $limit = $self->__max_int;
+around _converter_args => sub {
+  my ($orig, $self) = (shift, shift);
+  +{
+    %{$self->$orig(@_)},
+    name_sep => $self->name_sep,
+    limit_dialect => $self->limit_dialect,
+    slice_stability => { $self->renderer->slice_stability },
+    slice_subquery => { $self->renderer->slice_subquery },
   }
+};
 
-  my %final_attrs = (%{$rs_attrs||{}}, limit => $limit, offset => $offset);
-
-  if ($limit or $offset) {
-    my %slice_stability = $self->renderer->slice_stability;
-
-    if (my $stability = $slice_stability{$offset ? 'offset' : 'limit'}) {
-      my $source = $rs_attrs->{_rsroot_rsrc};
-      unless (
-        $final_attrs{order_is_stable}
-        = $final_attrs{preserve_order}
-        = $source->schema->storage
-                 ->_order_by_is_stable(
-                     @final_attrs{qw(from order_by where)}
-                   )
-      ) {
-        if ($stability eq 'requires') {
-          if ($self->converter->_order_by_to_dq($final_attrs{order_by})) {
-            $self->throw_exception(
-                $self->limit_dialect.' limit/offset implementation requires a stable order for '.($offset ? 'offset' : 'limit').' but you gave me '.$self->_render_sqla(order_by => $final_attrs{order_by})
-            );
-          }
-          if (my $ident_cols = $source->_identifying_column_set) {
-            $final_attrs{order_by} = [
-                map "$final_attrs{alias}.$_", @$ident_cols
-            ];
-            $final_attrs{order_is_stable} = 1;
-          } else {
-            $self->throw_exception(sprintf(
-              'Unable to auto-construct stable order criteria for "skimming type" 
-  limit '
-              . "dialect based on source '%s'", $source->name) );
-          }
-        }
-      }
-
-    }
-
-    my %slice_subquery = $self->renderer->slice_subquery;
-
-    if (my $subquery = $slice_subquery{$offset ? 'offset' : 'limit'}) {
-      $fields = [ map {
-        my $f = $fields->[$_];
-        if (ref $f) {
-          $f = { '' => $f } unless ref($f) eq 'HASH';
-          ($f->{-as} ||= $final_attrs{as}[$_]) =~ s/\Q${\$self->name_sep}/__/g;
-        } elsif ($f !~ /^\Q$final_attrs{alias}${\$self->name_sep}/) {
-          $f = { '' => $f };
-          ($f->{-as} ||= $final_attrs{as}[$_]) =~ s/\Q${\$self->name_sep}/__/g;
-        }
-        $f;
-        } 0 .. $#$fields ];
-    }
-  }
+# Handle limit-dialect selection
+sub select {
+  my $self = shift;
+  my ($table, $fields, $where, $rs_attrs, $limit, $offset) = @_;
 
-  my ($sql, @bind) = $self->next::method ($table, $fields, $where, $final_attrs{order_by}, \%final_attrs );
+  my ($sql, @bind) = $self->next::method(@_);
 
   $sql .= $self->_lock_select ($rs_attrs->{for})
     if $rs_attrs->{for};
index d904a1f..131041f 100644 (file)
@@ -6,10 +6,93 @@ use namespace::clean;
 
 extends 'SQL::Abstract::Converter';
 
+has limit_dialect => (is => 'ro', required => 1);
+has name_sep => (is => 'ro', required => 1);
+has slice_stability => (is => 'ro', required => 1);
+has slice_subquery => (is => 'ro', required => 1);
+
+sub __max_int () { 0x7FFFFFFF }
+
+# Handle limit-dialect selection
+sub _select_attrs {
+  my ($self, $table, $fields, $where, $rs_attrs, $limit, $offset) = @_;
+
+  if (defined $offset) {
+    die('A supplied offset must be a non-negative integer')
+      if ( $offset =~ /\D/ or $offset < 0 );
+  }
+  $offset ||= 0;
+
+  if (defined $limit) {
+    die('A supplied limit must be a positive integer')
+      if ( $limit =~ /\D/ or $limit <= 0 );
+  }
+  elsif ($offset) {
+    $limit = $self->__max_int;
+  }
+
+  my %final_attrs = (%{$rs_attrs||{}}, limit => $limit, offset => $offset);
+
+  if ($limit or $offset) {
+    my %slice_stability = %{$self->slice_stability};
+
+    if (my $stability = $slice_stability{$offset ? 'offset' : 'limit'}) {
+      my $source = $rs_attrs->{_rsroot_rsrc};
+      unless (
+        $final_attrs{order_is_stable}
+        = $final_attrs{preserve_order}
+        = $source->schema->storage
+                 ->_order_by_is_stable(
+                     @final_attrs{qw(from order_by where)}
+                   )
+      ) {
+        if ($stability eq 'requires') {
+          if ($self->_order_by_to_dq($final_attrs{order_by})) {
+            die(
+                $self->limit_dialect.' limit/offset implementation requires a stable order for '.($offset ? 'offset' : 'limit')
+            );
+          }
+          if (my $ident_cols = $source->_identifying_column_set) {
+            $final_attrs{order_by} = [
+                map "$final_attrs{alias}.$_", @$ident_cols
+            ];
+            $final_attrs{order_is_stable} = 1;
+          } else {
+            die(sprintf(
+              'Unable to auto-construct stable order criteria for "skimming type" 
+  limit '
+              . "dialect based on source '%s'", $source->name) );
+          }
+        }
+      }
+
+    }
+
+    my %slice_subquery = %{$self->slice_subquery};
+
+    if (my $subquery = $slice_subquery{$offset ? 'offset' : 'limit'}) {
+      $fields = [ map {
+        my $f = $fields->[$_];
+        if (ref $f) {
+          $f = { '' => $f } unless ref($f) eq 'HASH';
+          ($f->{-as} ||= $final_attrs{as}[$_]) =~ s/\Q${\$self->name_sep}/__/g;
+        } elsif ($f !~ /^\Q$final_attrs{alias}${\$self->name_sep}/) {
+          $f = { '' => $f };
+          ($f->{-as} ||= $final_attrs{as}[$_]) =~ s/\Q${\$self->name_sep}/__/g;
+        }
+        $f;
+        } 0 .. $#$fields ];
+    }
+  }
+
+  return ($fields, \%final_attrs);
+}
+
 around _select_to_dq => sub {
   my ($orig, $self) = (shift, shift);
-  my $attrs = $_[4];
-  my $orig_dq = $self->$orig(@_);
+  my ($table, undef, $where) = @_;
+  my ($fields, $attrs) = $self->_select_attrs(@_);
+  my $orig_dq = $self->$orig($table, $fields, $where, $attrs->{order_by}, $attrs);
   return $orig_dq unless $attrs->{limit};
   +{
     type => DQ_SLICE,