import cleanups
[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};
0446ca9c 28 my %alias_map;
29 my $gensym_count;
30 my (@inside_select_list, @outside_select_list);
31 my $default_inside_alias;
32 SELECT: foreach my $s (@{$orig_select->{select}}) {
33 my $name;
6b45ffe4 34 if (is_Alias $s) {
0446ca9c 35 $name = $s->{to};
36 $s = $s->{from};
37 }
38 my $key;
6b45ffe4 39 if (is_Identifier $s) {
3482f7c8 40 if (!$name and @{$s->{elements}} == 2) {
0446ca9c 41 $default_inside_alias ||= $s->{elements}[0];
42 if ($s->{elements}[0] eq $default_inside_alias) {
43 $alias_map{join('.',@{$s->{elements}})} = $s;
44 push @inside_select_list, $s;
45 push @outside_select_list, $s;
46 next SELECT;
47 }
48 }
49 $name ||= join('__', @{$s->{elements}});
50 $key = join('.', @{$s->{elements}});
51 } else {
3482f7c8 52 die "XXX not implemented yet" unless $name;
53 $key = "$s";
0446ca9c 54 }
1bfa648a 55 $name ||= sprintf("GENSYM__%03i",++$gensym_count);
6b45ffe4 56 push @inside_select_list, Alias($name, $s);
57 push @outside_select_list, $alias_map{$key} = Identifier($name);
0446ca9c 58 }
59 my $order = $orig_select->{from};
60 my $order_gensym_count;
61 die "Slice's Select not followed by Order but order_is_stable set"
6b45ffe4 62 unless is_Order $order;
0446ca9c 63 my (@order_nodes, %order_map);
6b45ffe4 64 while (is_Order $order) {
0446ca9c 65 my $by = $order->{by};
6b45ffe4 66 if (is_Identifier $by) {
3482f7c8 67 $default_inside_alias ||= $by->{elements}[0]
68 if @{$by->{elements}} == 2;
0446ca9c 69 $order_map{$by}
70 = $alias_map{join('.', @{$by->{elements}})}
71 ||= do {
3482f7c8 72 if (
73 @{$by->{elements}} == 2
74 and $by->{elements}[0] eq $default_inside_alias
75 ) {
76 $by;
77 } else {
78 my $name = sprintf("ORDER__BY__%03i",++$order_gensym_count);
6b45ffe4 79 push @inside_select_list, Alias($name, $by);
80 Identifier($name);
3482f7c8 81 }
0446ca9c 82 };
83 } else {
84 die "XXX not implemented yet";
85 }
86 push @order_nodes, $order;
87 $order = $order->{from};
88 }
89 my $inside_order = $order;
6b45ffe4 90 $inside_order = Order($_->{by}, $_->{reverse}, $inside_order)
91 for reverse @order_nodes;
92 my $inside_select = Select(\@inside_select_list, $inside_order);
0446ca9c 93 my $limit_plus_offset = +{
94 %{$dq->{limit}}, value => $dq->{limit}{value} + $dq->{offset}{value}
95 };
96 $default_inside_alias ||= 'me';
6b45ffe4 97 my $bridge_from = Alias(
98 $default_inside_alias,
99 Slice(undef, $limit_plus_offset, $inside_select)
100 );
0446ca9c 101 my $outside_order = $bridge_from;
6b45ffe4 102 $outside_order = Order($order_map{$_->{by}}, !$_->{reverse}, $outside_order)
103 for reverse @order_nodes;
104 my $outside_select = Select(
105 (
0446ca9c 106 $dq->{preserve_order}
1bfa648a 107 ? [
108 @outside_select_list,
109 grep @{$_->{elements}} == 1, @order_map{map $_->{by}, @order_nodes}
110 ]
0446ca9c 111 : \@outside_select_list,
112 ),
6b45ffe4 113 $outside_order,
114 );
115 my $final = Slice(undef, $dq->{limit}, $outside_select);
0446ca9c 116 if ($dq->{preserve_order}) {
6b45ffe4 117 $final = Alias($default_inside_alias, $final);
118 $final = Order($order_map{$_->{by}}, $_->{reverse}, $final)
119 for reverse @order_nodes;
120 $final = Select(\@outside_select_list, $final);
0446ca9c 121 }
122 return $self->_render($final);
123}
124
1251;