From: Matt S Trout Date: Thu, 16 Aug 2012 16:33:08 +0000 (+0100) Subject: working RowNumberOver X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=fdd10824bebc6621cb33c023c3cbebb8b907376b;p=dbsrgits%2FData-Query.git working RowNumberOver --- diff --git a/lib/Data/Query/Renderer/SQL/Naive.pm b/lib/Data/Query/Renderer/SQL/Naive.pm index 6e16fb8..7b3a941 100644 --- a/lib/Data/Query/Renderer/SQL/Naive.pm +++ b/lib/Data/Query/Renderer/SQL/Naive.pm @@ -79,6 +79,7 @@ sub _flatten_structure { sub _format_keyword { $_[0]->lc_keywords ? lc($_[1]) : $_[1] } sub _render { + die "Expected hashref, got $_[1]" unless ref($_[1]) eq 'HASH'; $_[0]->${\"_render_${\(lc($_[1]->{type})||'broken')}"}($_[1]); } diff --git a/lib/Data/Query/Renderer/SQL/Slice/FetchFirst.pm b/lib/Data/Query/Renderer/SQL/Slice/FetchFirst.pm index d885fb4..1af7f78 100644 --- a/lib/Data/Query/Renderer/SQL/Slice/FetchFirst.pm +++ b/lib/Data/Query/Renderer/SQL/Slice/FetchFirst.pm @@ -3,6 +3,8 @@ package Data::Query::Renderer::SQL::Slice::FetchFirst; use Data::Query::ExprHelpers; use Moo::Role; +with 'Data::Query::Renderer::SQL::Slice::SubqueryRemap'; + sub _render_slice_limit { my ($self, $dq) = @_; return [ @@ -28,79 +30,14 @@ sub _render_slice { die "Slice's Select not followed by Order but order_is_stable set" unless is_Order $orig_select->{from}; - my $gensym_count; - my $default_inside_alias; - - 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), $_); - } - } else { - Alias(sprintf("GENSYM__%03i",++$gensym_count), $_); - } - } @{$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; - 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 { - 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"; - } - } @order_nodes; + my %remapped = $self->_subquery_remap($orig_select); - $default_inside_alias ||= 'me'; + 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} @@ -114,7 +51,7 @@ sub _render_slice { compose { Order($b->{by}, $b->{reverse}, $a) } ( - @mapped_order, + @outside_order, Alias($default_inside_alias, $_) ) ) @@ -127,13 +64,13 @@ sub _render_slice { @outside_select_list, $dq->{preserve_order} ? (grep @{$_->{elements}} == 1, - map $_->{by}, @mapped_order) + map $_->{by}, @outside_order) : (), ], compose { Order($b->{by}, !$b->{reverse}, $a) } ( - @mapped_order, + @outside_order, Alias( $default_inside_alias, Slice( @@ -142,7 +79,7 @@ sub _render_slice { \@inside_select_list, compose { Order($b->{by}, $b->{reverse}, $a) - } @order_nodes, $inner_body + } @inside_order, $inner_body ) ) ) diff --git a/lib/Data/Query/Renderer/SQL/Slice/RowNumberOver.pm b/lib/Data/Query/Renderer/SQL/Slice/RowNumberOver.pm index 53c3562..c3f6764 100644 --- a/lib/Data/Query/Renderer/SQL/Slice/RowNumberOver.pm +++ b/lib/Data/Query/Renderer/SQL/Slice/RowNumberOver.pm @@ -1,165 +1,73 @@ package Data::Query::Renderer::SQL::Slice::RowNumberOver; -use Data::Query::Constants qw( - DQ_SELECT DQ_ALIAS DQ_IDENTIFIER DQ_ORDER DQ_SLICE DQ_WHERE DQ_OPERATOR - DQ_LITERAL -); +use Data::Query::Constants; +use Data::Query::ExprHelpers; use Moo::Role; +with 'Data::Query::Renderer::SQL::Slice::SubqueryRemap'; + sub _render_slice { my ($self, $dq) = @_; 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 (!$name and @{$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" unless $name; - $key = "$s"; - } - $name ||= sprintf("GENSYM__%03i",++$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; - my (@order_nodes, %order_map); - while ($order->{type} eq DQ_ORDER) { - my $by = $order->{by}; - if ($by->{type} eq DQ_IDENTIFIER) { - $default_inside_alias ||= $by->{elements}[0] - if @{$by->{elements}} == 2; - $order_map{$by} - = $alias_map{join('.', @{$by->{elements}})} - ||= do { - 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, +{ - 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, - }; - $default_inside_alias ||= 'me'; - my $bridge_from = +{ - type => DQ_ALIAS, - from => $inside_select, - to => $default_inside_alias, - }; - my $outside_order; - $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 => [ - @outside_select_list, - { - type => DQ_ALIAS, - from => $self->_rno_literal($outside_order), - to => 'rno__row__index', - } - ], - from => $bridge_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 $rno_name = 'rno__row__index'; + + my $order = compose { Order($b->{by}, $b->{reverse}, $a) } + @outside_order, undef; + + my $rno_node = Alias($rno_name, $self->_rno_literal($order)); + + my $limit_plus_offset = +{ + %{$dq->{limit}}, value => ($dq->{limit}{value}||0) + ($dq->{offset}{value}||0) }; - my $idx_name = +{ - type => DQ_IDENTIFIER, - elements => [ 'rno__row__index' ], + + my $offset_plus = +{ + %{$dq->{limit}}, value => ($dq->{offset}{value}||0)+1 }; - my $offset_value = $dq->{offset} ? $dq->{offset}{value} : 0; - my $final = +{ - type => DQ_WHERE, - where => { - type => DQ_OPERATOR, - operator => { 'SQL.Naive' => 'AND' }, - args => [ - { - type => DQ_OPERATOR, - operator => { 'SQL.Naive' => '>=' }, - args => [ - $idx_name, - { %{$dq->{limit}}, value => $offset_value + 1 }, - ] - }, - { - type => DQ_OPERATOR, - operator => { 'SQL.Naive' => '<=' }, - args => [ - $idx_name, - { %{$dq->{limit}}, value => $dq->{limit}{value} + $offset_value }, + + return $self->_render( + Select( + \@outside_select_list, + Where( + Operator( + { 'SQL.Naive' => 'AND' }, + [ + Operator( + { 'SQL.Naive' => '>=' }, + [ Identifier($rno_name), $offset_plus ], + ), + Operator( + { 'SQL.Naive' => '<=' }, + [ Identifier($rno_name), $limit_plus_offset ], + ), ] - }, - ], - }, - from => { - type => DQ_SELECT, - select => \@outside_select_list, - from => { - type => DQ_ALIAS, - from => $outside_select, - to => $default_inside_alias, - }, - } - }; - return $self->_render($final); + ), + Alias( + $default_inside_alias, + Select( + [ @outside_select_list, $rno_node ], + Alias( + $default_inside_alias, + Select( + \@inside_select_list, + $inner_body + ), + ), + ), + ) + ) + ) + ); } sub _rno_literal { diff --git a/lib/Data/Query/Renderer/SQL/Slice/SubqueryRemap.pm b/lib/Data/Query/Renderer/SQL/Slice/SubqueryRemap.pm new file mode 100644 index 0000000..092d726 --- /dev/null +++ b/lib/Data/Query/Renderer/SQL/Slice/SubqueryRemap.pm @@ -0,0 +1,94 @@ +package Data::Query::Renderer::SQL::Slice::SubqueryRemap; + +use Data::Query::ExprHelpers; +use Moo::Role; + +sub _subquery_remap { + my ($self, $orig_select) = @_; + + my $gensym_count; + my $default_inside_alias; + + 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), $_); + } + } else { + Alias(sprintf("GENSYM__%03i",++$gensym_count), $_); + } + } @{$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 @inside_order; + my $inner_body = do { + my $order = $orig_select->{from}; + while (is_Order $order) { + push @inside_order, $order; + $order = $order->{from}; + } + $order; + }; + + my $order_gensym_count; + my @outside_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 { + if ( + @{$by->{elements}} == 2 + and $by->{elements}[0] eq $default_inside_alias + ) { + push @inside_select_list, $by; + $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"; + } + } @inside_order; + + $default_inside_alias ||= 'me'; + + return ( + inside_select_list => \@inside_select_list, + outside_select_list => \@outside_select_list, + inside_order => \@inside_order, + outside_order => \@outside_order, + default_inside_alias => $default_inside_alias, + inner_body => $inner_body, + ); +} + +1;