functionalise order mapping
[dbsrgits/Data-Query.git] / lib / Data / Query / Renderer / SQL / Slice / FetchFirst.pm
CommitLineData
0446ca9c 1package Data::Query::Renderer::SQL::Slice::FetchFirst;
2
6b45ffe4 3use Data::Query::ExprHelpers;
0446ca9c 4use Moo::Role;
5
3482f7c8 6sub _render_slice_limit {
7 my ($self, $dq) = @_;
8 return [
9 ($dq->{from} ? $self->_render($dq->{from}) : ()),
10 $self->_format_keyword('FETCH FIRST'),
11 sprintf("%i", $dq->{limit}{value}),
12 $self->_format_keyword('ROWS ONLY')
13 ];
14}
15
16sub _slice_type { 'FetchFirst' }
17
0446ca9c 18sub _render_slice {
19 my ($self, $dq) = @_;
20 unless ($dq->{offset}) {
3482f7c8 21 return $self->_render_slice_limit($dq);
0446ca9c 22 }
23 unless ($dq->{order_is_stable}) {
3482f7c8 24 die $self->_slice_type." limit style requires a stable order";
0446ca9c 25 }
26 die "Slice's inner is not a Select"
6b45ffe4 27 unless is_Select my $orig_select = $dq->{from};
22f934ee 28 die "Slice's Select not followed by Order but order_is_stable set"
29 unless is_Order $orig_select->{from};
30
0446ca9c 31 my $gensym_count;
0446ca9c 32 my $default_inside_alias;
22f934ee 33
34 my @inside_select_list = map {
35 if (is_Alias) {
36 $_;
37 } elsif (is_Identifier) {
38 my @el = @{$_->{elements}};
39 if (@el == 2 and $el[0] eq ($default_inside_alias ||= $el[0])) {
40 $_;
41 } else {
42 Alias(join('__', @el), $_);
0446ca9c 43 }
0446ca9c 44 } else {
22f934ee 45 Alias(sprintf("GENSYM__%03i",++$gensym_count), $_);
0446ca9c 46 }
22f934ee 47 } @{$orig_select->{select}};
48
49 my %alias_map = map {
50 if (is_Alias and is_Identifier $_->{from}) {
51 +(join('.',@{$_->{from}{elements}}) => Identifier($_->{to}))
52 } elsif (is_Identifier) {
53 +(join('.',@{$_->{elements}}) => $_)
54 } else {
55 +()
56 }
57 } @inside_select_list;
58
59 my @outside_select_list = map {
60 if (is_Alias) {
61 Identifier($_->{to});
62 } else {
63 $_;
64 }
65 } @inside_select_list;
66
67 my @order_nodes;
68 my $inner_body = do {
69 my $order = $orig_select->{from};
70 while (is_Order $order) {
71 push @order_nodes, $order;
72 $order = $order->{from};
73 }
74 $order;
75 };
76
0446ca9c 77 my $order_gensym_count;
e82795ba 78 my @mapped_order = map {
79 my $by = $_->{by};
6b45ffe4 80 if (is_Identifier $by) {
3482f7c8 81 $default_inside_alias ||= $by->{elements}[0]
82 if @{$by->{elements}} == 2;
e82795ba 83 my $mapped_by
0446ca9c 84 = $alias_map{join('.', @{$by->{elements}})}
85 ||= do {
3482f7c8 86 if (
87 @{$by->{elements}} == 2
88 and $by->{elements}[0] eq $default_inside_alias
89 ) {
90 $by;
91 } else {
92 my $name = sprintf("ORDER__BY__%03i",++$order_gensym_count);
6b45ffe4 93 push @inside_select_list, Alias($name, $by);
94 Identifier($name);
3482f7c8 95 }
0446ca9c 96 };
e82795ba 97 Order($mapped_by, $_->{reverse});
0446ca9c 98 } else {
99 die "XXX not implemented yet";
100 }
e82795ba 101 } @order_nodes;
22f934ee 102
9fcc2256 103 $default_inside_alias ||= 'me';
22f934ee 104
0446ca9c 105 my $limit_plus_offset = +{
106 %{$dq->{limit}}, value => $dq->{limit}{value} + $dq->{offset}{value}
107 };
22f934ee 108
9fcc2256 109 return $self->_render(
110 map {
0446ca9c 111 $dq->{preserve_order}
9fcc2256 112 ? Select(
113 \@outside_select_list,
114 compose {
e82795ba 115 Order($b->{by}, $b->{reverse}, $a)
60cbee33 116 } (
e82795ba 117 @mapped_order,
60cbee33 118 Alias($default_inside_alias, $_)
119 )
9fcc2256 120 )
121 : $_
60cbee33 122 } (
123 Slice(
9fcc2256 124 undef, $dq->{limit},
125 Select(
126 [
1bfa648a 127 @outside_select_list,
9fcc2256 128 $dq->{preserve_order}
129 ? (grep @{$_->{elements}} == 1,
e82795ba 130 map $_->{by}, @mapped_order)
9fcc2256 131 : (),
132 ],
133 compose {
e82795ba 134 Order($b->{by}, !$b->{reverse}, $a)
9fcc2256 135 } (
e82795ba 136 @mapped_order,
9fcc2256 137 Alias(
138 $default_inside_alias,
139 Slice(
140 undef, $limit_plus_offset,
141 Select(
142 \@inside_select_list,
143 compose {
144 Order($b->{by}, $b->{reverse}, $a)
145 } @order_nodes, $inner_body
146 )
147 )
148 )
149 )
150 )
151 )
60cbee33 152 )
6b45ffe4 153 );
0446ca9c 154}
155
1561;