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