nested where nodes
[dbsrgits/Data-Query.git] / lib / Data / Query / Renderer / SQL / Slice / GenericSubquery.pm
1 package Data::Query::Renderer::SQL::Slice::GenericSubquery;
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   (limit => 'requires', offset => 'requires');
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($orig_select);
17   my $first_from = $remapped{inner_body};
18   # Should we simply strip until we reach a join/alias/etc. here?
19   $first_from = $first_from->{from}{from} if is_Having($first_from);
20   $first_from = $first_from->{from} if is_Where($first_from);
21   while (is_Join $first_from) {
22     $first_from = $first_from->{left};
23   }
24   $first_from = $first_from->{from} if is_Alias($first_from);
25   my $first_order = $remapped{inside_order}[0]{by};
26   my $count_col = $first_order->{elements}[-1];
27   my $count_alias = 'rownum__emulation';
28   my $count_sel = Select(
29     [ Operator({ 'SQL.Naive' => 'apply' }, [ Identifier('COUNT'), Identifier('*') ]) ],
30     Where(
31       Operator(
32         { 'SQL.Naive' => ($remapped{inside_order}[0]{reverse} ? '>' : '<') },
33         [
34           Identifier($count_alias, $count_col),
35           $remapped{outside_order}[0]{by}
36         ]
37       ),
38       Alias($count_alias, $first_from)
39     )
40   );
41   my $count_where = Operator(
42     { 'SQL.Naive' => ($dq->{offset} ? 'BETWEEN' : '<') },
43     [ $count_sel, (
44         $dq->{offset}
45           ? (
46               $dq->{offset},
47               {
48                 %{$dq->{limit}},
49                 value => $dq->{limit}{value}+$dq->{offset}{value}-1
50               }
51             )
52           : ($dq->{limit})
53       )
54     ]
55   );
56   return $self->render(
57     Select(
58       $remapped{outside_select_list},
59       (compose { no warnings 'once'; Order($b->{by}, $b->{reverse}, $a) }
60         @{$remapped{outside_order}},
61         Where(
62           $count_where,
63           Alias(
64             $remapped{default_inside_alias},
65             Select(
66               $remapped{inside_select_list},
67               $remapped{inner_body},
68             )
69           )
70         )
71       )
72     )
73   );
74 }
75
76 1;