From: Matt S Trout Date: Fri, 1 Nov 2013 09:37:23 +0000 (+0000) Subject: move select arg handling to _select_to_dq to enable creating $rs->_as_select_dq X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=28be821e8e72949fac8d5a2c2bb1f517414fe624;p=dbsrgits%2FDBIx-Class.git move select arg handling to _select_to_dq to enable creating $rs->_as_select_dq --- diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index cddf84c..bde773e 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -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 diff --git a/lib/DBIx/Class/SQLMaker.pm b/lib/DBIx/Class/SQLMaker.pm index dac76df..d8255d6 100644 --- a/lib/DBIx/Class/SQLMaker.pm +++ b/lib/DBIx/Class/SQLMaker.pm @@ -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}; diff --git a/lib/DBIx/Class/SQLMaker/Converter.pm b/lib/DBIx/Class/SQLMaker/Converter.pm index d904a1f..131041f 100644 --- a/lib/DBIx/Class/SQLMaker/Converter.pm +++ b/lib/DBIx/Class/SQLMaker/Converter.pm @@ -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,