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