add slice stability requirement code
[dbsrgits/Data-Query.git] / lib / Data / Query / Renderer / SQL / Slice / RowNum.pm
1 package Data::Query::Renderer::SQL::Slice::RowNum;
2
3 use Data::Query::ExprHelpers;
4 use Moo::Role;
5
6 with 'Data::Query::Renderer::SQL::Slice::SubqueryRemap';
7
8 sub slice_stability {
9   (offset => 'check');
10 }
11
12 sub _render_slice {
13   my ($self, $dq) = @_;
14   die "Slice's inner is not a Select"
15     unless is_Select my $orig_select = $dq->{from};
16   my %remapped = $self->_subquery_remap_select($orig_select);
17   my $inside_select = Alias(
18     $remapped{default_inside_alias},
19     Select($remapped{inside_select_list}, $orig_select->{from}),
20   );
21   unless ($dq->{offset}) {
22     return $self->render(
23       Select(
24         $remapped{outside_select_list},
25         Where(
26           Operator(
27             { 'SQL.Naive' => '<=' },
28             [
29               Literal(SQL => 'ROWNUM'),
30               $dq->{limit}
31             ]
32           ),
33           $inside_select
34         )
35       )
36     );
37   }
38   my ($limit_plus_offset, $offset_plus) = (
39     { %{$dq->{limit}}, value => $dq->{limit}{value}+$dq->{offset}{value} },
40     { %{$dq->{limit}}, value => $dq->{offset}{value}+1 }
41   );
42
43   my $rownum_name = 'rownum__index';
44
45   if ($dq->{order_is_stable}) {
46     return $self->render(
47       Select(
48         $remapped{outside_select_list},
49         Where(
50           Operator(
51             { 'SQL.Naive' => '>=' },
52             [ Identifier($rownum_name), $offset_plus ]
53           ),
54           Alias(
55             $remapped{default_inside_alias},
56             Select(
57               [ @{$remapped{outside_select_list}},
58                 Alias($rownum_name, Literal(SQL => 'ROWNUM')) ],
59               Where(
60                 Operator(
61                   { 'SQL.Naive' => '<=' },
62                   [ Literal(SQL => 'ROWNUM'), $limit_plus_offset ]
63                 ),
64                 $inside_select,
65               )
66             )
67           )
68         )
69       )
70     );
71   } else {
72     return $self->render(
73       Select(
74         $remapped{outside_select_list},
75         Where(
76           Operator(
77             { 'SQL.Naive' => 'BETWEEN' },
78             [ Identifier($rownum_name), $offset_plus, $limit_plus_offset ]
79           ),
80           Alias(
81             $remapped{default_inside_alias},
82             Select(
83               [ @{$remapped{outside_select_list}},
84                 Alias($rownum_name, Literal(SQL => 'ROWNUM')) ],
85               $inside_select
86             )
87           )
88         )
89       )
90     );
91   }
92 }
93
94 1;