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