X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FData%2FQuery%2FRenderer%2FSQL%2FSlice%2FFetchFirst.pm;h=d885fb469bcf8255626a6d56bb1e7a2089857e43;hb=cc5ec83c718d51d26c26c6beef0c16cadf92dadf;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..d885fb4 100644 --- a/lib/Data/Query/Renderer/SQL/Slice/FetchFirst.pm +++ b/lib/Data/Query/Renderer/SQL/Slice/FetchFirst.pm @@ -1,155 +1,156 @@ 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; +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_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; + unless is_Select my $orig_select = $dq->{from}; + die "Slice's Select not followed by Order but order_is_stable set" + unless is_Order $orig_select->{from}; + 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; - } + + my @inside_select_list = map { + if (is_Alias) { + $_; + } elsif (is_Identifier) { + my @el = @{$_->{elements}}; + if (@el == 2 and $el[0] eq ($default_inside_alias ||= $el[0])) { + $_; + } else { + Alias(join('__', @el), $_); } - $name ||= join('__', @{$s->{elements}}); - $key = join('.', @{$s->{elements}}); } else { - die "XXX not implemented yet"; + Alias(sprintf("GENSYM__%03i",++$gensym_count), $_); } - $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}; + } @{$orig_select->{select}}; + + my %alias_map = map { + if (is_Alias and is_Identifier $_->{from}) { + +(join('.',@{$_->{from}{elements}}) => Identifier($_->{to})) + } elsif (is_Identifier) { + +(join('.',@{$_->{elements}}) => $_) + } else { + +() + } + } @inside_select_list; + + my @outside_select_list = map { + if (is_Alias) { + Identifier($_->{to}); + } else { + $_; + } + } @inside_select_list; + + my @order_nodes; + my $inner_body = do { + my $order = $orig_select->{from}; + while (is_Order $order) { + push @order_nodes, $order; + $order = $order->{from}; + } + $order; + }; + my $order_gensym_count; - 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} + my @mapped_order = map { + my $by = $_->{by}; + if (is_Identifier $by) { + $default_inside_alias ||= $by->{elements}[0] + if @{$by->{elements}} == 2; + my $mapped_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 ], - }; + if ( + @{$by->{elements}} == 2 + and $by->{elements}[0] eq $default_inside_alias + ) { + $by; + } else { + my $name = sprintf("ORDER__BY__%03i",++$order_gensym_count); + push @inside_select_list, Alias($name, $by); + Identifier($name); + } }; + Order($mapped_by, $_->{reverse}); } 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, - }; + } @order_nodes; + + $default_inside_alias ||= 'me'; + 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}, $a) + } ( + @mapped_order, + Alias($default_inside_alias, $_) + ) + ) + : $_ + } ( + Slice( + undef, $dq->{limit}, + Select( + [ + @outside_select_list, + $dq->{preserve_order} + ? (grep @{$_->{elements}} == 1, + map $_->{by}, @mapped_order) + : (), + ], + compose { + Order($b->{by}, !$b->{reverse}, $a) + } ( + @mapped_order, + Alias( + $default_inside_alias, + Slice( + undef, $limit_plus_offset, + Select( + \@inside_select_list, + compose { + Order($b->{by}, $b->{reverse}, $a) + } @order_nodes, $inner_body + ) + ) + ) + ) + ) + ) + ) + ); } 1;