X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FData%2FQuery%2FRenderer%2FSQL%2FSlice%2FFetchFirst.pm;h=3cea06e2f4ddcdaf543094407bb16387c202e0a8;hb=6b9e9259c9a2e2475e63db7756d0c844ebac54f6;hp=6dd07febf2be92df3987df01551f1268df4f4fd1;hpb=0446ca9c18ac30c9b39b0baac8b09e77adcbcd56;p=dbsrgits%2FData-Query.git diff --git a/lib/Data/Query/Renderer/SQL/Slice/FetchFirst.pm b/lib/Data/Query/Renderer/SQL/Slice/FetchFirst.pm index 6dd07fe..3cea06e 100644 --- a/lib/Data/Query/Renderer/SQL/Slice/FetchFirst.pm +++ b/lib/Data/Query/Renderer/SQL/Slice/FetchFirst.pm @@ -1,155 +1,101 @@ package Data::Query::Renderer::SQL::Slice::FetchFirst; -use Data::Query::Constants qw( - DQ_SELECT DQ_ALIAS DQ_IDENTIFIER DQ_ORDER DQ_SLICE -); +use Data::Query::ExprHelpers; use Moo::Role; +with 'Data::Query::Renderer::SQL::Slice::SubqueryRemap'; + +sub _render_slice_limit { + my ($self, $dq) = @_; + return [ + ($dq->{from} ? $self->_render($dq->{from}) : ()), + $self->_format_keyword('FETCH FIRST'), + sprintf("%i", $dq->{limit}{value}), + $self->_format_keyword('ROWS ONLY') + ]; +} + +sub slice_subquery { + (offset => 1); +} + +sub slice_stability { + (offset => 'requires'); +} + +sub _slice_type { 'FetchFirst' } + sub _render_slice { my ($self, $dq) = @_; unless ($dq->{offset}) { - return [ - ($dq->{from} ? $self->_render($dq->{from}) : ()), - $self->_format_keyword('FETCH FIRST'), - sprintf("%i", $dq->{limit}{value}), - $self->_format_keyword('ROWS ONLY') - ]; + return $self->_render_slice_limit($dq); } unless ($dq->{order_is_stable}) { - die "FetchFirst limit style requires a stable order"; + die $self->_slice_type." limit style requires a stable order"; } die "Slice's inner is not a Select" - unless (my $orig_select = $dq->{from})->{type} eq DQ_SELECT; - my %alias_map; - my $gensym_count; - my (@inside_select_list, @outside_select_list); - my $default_inside_alias; - SELECT: foreach my $s (@{$orig_select->{select}}) { - my $name; - if ($s->{type} eq DQ_ALIAS) { - $name = $s->{to}; - $s = $s->{from}; - } - my $key; - if ($s->{type} eq DQ_IDENTIFIER) { - if (@{$s->{elements}} == 2) { - $default_inside_alias ||= $s->{elements}[0]; - if ($s->{elements}[0] eq $default_inside_alias) { - $alias_map{join('.',@{$s->{elements}})} = $s; - push @inside_select_list, $s; - push @outside_select_list, $s; - next SELECT; - } - } - $name ||= join('__', @{$s->{elements}}); - $key = join('.', @{$s->{elements}}); - } else { - die "XXX not implemented yet"; - } - $name ||= 'GENSYM__'.++$gensym_count; - push @inside_select_list, +{ - type => DQ_ALIAS, - from => $s, - to => $name, - }; - push @outside_select_list, $alias_map{$key} = +{ - type => DQ_IDENTIFIER, - elements => [ $name ] - }; - } - my $order = $orig_select->{from}; - my $order_gensym_count; + unless is_Select my $orig_select = $dq->{from}; die "Slice's Select not followed by Order but order_is_stable set" - unless $order->{type} eq DQ_ORDER; - my (@order_nodes, %order_map); - while ($order->{type} eq DQ_ORDER) { - my $by = $order->{by}; - if ($by->{type} eq DQ_IDENTIFIER) { - $order_map{$by} - = $alias_map{join('.', @{$by->{elements}})} - ||= do { - my $name = 'ORDER__BY__'.++$order_gensym_count; - push @inside_select_list, +{ - type => DQ_ALIAS, - from => $by, - to => $name - }; - +{ - type => DQ_IDENTIFIER, - elements => [ $name ], - }; - }; - } else { - die "XXX not implemented yet"; - } - push @order_nodes, $order; - $order = $order->{from}; - } - my $inside_order = $order; - $inside_order = +{ - type => DQ_ORDER, - by => $_->{by}, - reverse => $_->{reverse}, - from => $inside_order - } for reverse @order_nodes; - my $inside_select = +{ - type => DQ_SELECT, - select => \@inside_select_list, - from => $inside_order, - }; + unless is_Order $orig_select->{from}; + + my %remapped = $self->_subquery_remap($orig_select); + + my @inside_select_list = @{$remapped{inside_select_list}}; + my @outside_select_list = @{$remapped{outside_select_list}}; + my @inside_order = @{$remapped{inside_order}}; + my @outside_order = @{$remapped{outside_order}}; + my $default_inside_alias = $remapped{default_inside_alias}; + my $inner_body = $remapped{inner_body}; + my $limit_plus_offset = +{ %{$dq->{limit}}, value => $dq->{limit}{value} + $dq->{offset}{value} }; - $default_inside_alias ||= 'me'; - my $bridge_from = +{ - type => DQ_ALIAS, - to => $default_inside_alias, - from => { - type => DQ_SLICE, - limit => $limit_plus_offset, - from => $inside_select, - }, - }; - my $outside_order = $bridge_from; - $outside_order = +{ - type => DQ_ORDER, - by => $order_map{$_->{by}}, - reverse => !$_->{reverse}, - from => $outside_order - } for reverse @order_nodes; - my $outside_select = +{ - type => DQ_SELECT, - select => ( + + return $self->_render( + map { $dq->{preserve_order} - ? [ @outside_select_list, @order_map{map $_->{by}, @order_nodes} ] - : \@outside_select_list, - ), - from => $outside_order, - }; - my $final = { - type => DQ_SLICE, - limit => $dq->{limit}, - from => $outside_select - }; - if ($dq->{preserve_order}) { - $final = { - type => DQ_ALIAS, - from => $final, - to => $default_inside_alias, - }; - $final = +{ - type => DQ_ORDER, - by => $order_map{$_->{by}}, - reverse => $_->{reverse}, - from => $final - } for reverse @order_nodes; - $final = { - type => DQ_SELECT, - select => \@outside_select_list, - from => $final, - }; - } - return $self->_render($final); + ? Select( + \@outside_select_list, + compose { + Order($b->{by}, $b->{reverse}, $b->{nulls}, $a) + } ( + @outside_order, + Alias($default_inside_alias, $_) + ) + ) + : $_ + } ( + Slice( + undef, $dq->{limit}, + Select( + [ + @outside_select_list, + $dq->{preserve_order} + ? (grep @{$_->{elements}} == 1, + map $_->{by}, @outside_order) + : (), + ], + compose { + Order($b->{by}, !$b->{reverse}, -($b->{nulls}||0), $a) + } ( + @outside_order, + Alias( + $default_inside_alias, + Slice( + undef, $limit_plus_offset, + Select( + \@inside_select_list, + compose { + Order($b->{by}, $b->{reverse}, $b->{nulls}, $a) + } @inside_order, $inner_body + ) + ) + ) + ) + ) + ) + ) + ); } 1;