separate to_sql mechanism
[dbsrgits/Data-Query.git] / lib / Data / Query / Renderer / SQL / Slice / RowNumberOver.pm
1 package Data::Query::Renderer::SQL::Slice::RowNumberOver;
2
3 use Data::Query::Constants;
4 use Data::Query::ExprHelpers;
5 use Moo::Role;
6
7 with 'Data::Query::Renderer::SQL::Slice::SubqueryRemap';
8
9 sub _render_slice {
10   my ($self, $dq) = @_;
11   die "Slice's inner is not a Select"
12     unless (my $orig_select = $dq->{from})->{type} eq DQ_SELECT;
13
14   my %remapped = $self->_subquery_remap($orig_select);
15
16   my @inside_select_list = @{$remapped{inside_select_list}};
17   my @outside_select_list = @{$remapped{outside_select_list}};
18   my @inside_order = @{$remapped{inside_order}};
19   my @outside_order = @{$remapped{outside_order}};
20   my $default_inside_alias = $remapped{default_inside_alias};
21   my $inner_body = $remapped{inner_body};
22
23   my $rno_name = 'rno__row__index';
24
25   my $order = compose { Order($b->{by}, $b->{reverse}, $a) }
26                 @outside_order, undef;
27
28   my $rno_node = Alias($rno_name, $self->_rno_literal($order));
29
30   my $limit_plus_offset = +{
31     %{$dq->{limit}}, value => ($dq->{limit}{value}||0) + ($dq->{offset}{value}||0)
32   };
33
34   my $offset_plus = +{
35     %{$dq->{limit}}, value => ($dq->{offset}{value}||0)+1
36   };
37
38   return $self->_render(
39     Select(
40       \@outside_select_list,
41       Where(
42         Operator(
43           { 'SQL.Naive' => 'AND' },
44           [
45             Operator(
46               { 'SQL.Naive' => '>=' },
47               [ Identifier($rno_name), $offset_plus ],
48             ),
49             Operator(
50               { 'SQL.Naive' => '<=' },
51               [ Identifier($rno_name), $limit_plus_offset ],
52             ),
53           ]
54         ),
55         Alias(
56           $default_inside_alias,
57           Select(
58             [ @outside_select_list, $rno_node ],
59             Alias(
60               $default_inside_alias,
61               Select(
62                 \@inside_select_list,
63                 $inner_body
64               ),
65             ),
66           ),
67         )
68       )
69     )
70   );
71 }
72
73 sub _rno_literal {
74   my ($self, $order) = @_;
75   my ($order_str, @order_bind) = (
76     $order
77       ? @{$self->render($order)}
78       : ('')
79   );
80   return +{
81     type => DQ_LITERAL,
82     subtype => 'SQL',
83     literal => "ROW_NUMBER() OVER( $order_str )",
84     values => \@order_bind
85   };
86 }
87
88 1;