Commit | Line | Data |
0446ca9c |
1 | package Data::Query::Renderer::SQL::Slice::FetchFirst; |
2 | |
2a54ca0e |
3 | use List::Util qw(reduce); |
6b45ffe4 |
4 | use Data::Query::ExprHelpers; |
0446ca9c |
5 | use Moo::Role; |
6 | |
3482f7c8 |
7 | sub _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 | |
17 | sub _slice_type { 'FetchFirst' } |
18 | |
0446ca9c |
19 | sub _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 | |
128 | 1; |