Commit | Line | Data |
77585983 |
1 | package Data::Query::Renderer::SQL::Slice::RowNumberOver; |
2 | |
3 | use Data::Query::Constants qw( |
4 | DQ_SELECT DQ_ALIAS DQ_IDENTIFIER DQ_ORDER DQ_SLICE DQ_WHERE DQ_OPERATOR |
5 | DQ_LITERAL |
6 | ); |
7 | use Moo::Role; |
8 | |
9 | sub _render_slice { |
10 | my ($self, $dq) = @_; |
11 | die "Slice's inner is not a Select" |
12 | unless (my $orig_select = $dq->{from})->{type} eq DQ_SELECT; |
13 | my %alias_map; |
14 | my $gensym_count; |
15 | my (@inside_select_list, @outside_select_list); |
16 | my $default_inside_alias; |
17 | SELECT: foreach my $s (@{$orig_select->{select}}) { |
18 | my $name; |
19 | if ($s->{type} eq DQ_ALIAS) { |
20 | $name = $s->{to}; |
21 | $s = $s->{from}; |
22 | } |
23 | my $key; |
24 | if ($s->{type} eq DQ_IDENTIFIER) { |
25 | if (!$name and @{$s->{elements}} == 2) { |
26 | $default_inside_alias ||= $s->{elements}[0]; |
27 | if ($s->{elements}[0] eq $default_inside_alias) { |
28 | $alias_map{join('.',@{$s->{elements}})} = $s; |
29 | push @inside_select_list, $s; |
30 | push @outside_select_list, $s; |
31 | next SELECT; |
32 | } |
33 | } |
34 | $name ||= join('__', @{$s->{elements}}); |
35 | $key = join('.', @{$s->{elements}}); |
36 | } else { |
37 | die "XXX not implemented yet" unless $name; |
38 | $key = "$s"; |
39 | } |
40 | $name ||= sprintf("GENSYM__%03i",++$gensym_count); |
41 | push @inside_select_list, +{ |
42 | type => DQ_ALIAS, |
43 | from => $s, |
44 | to => $name, |
45 | }; |
46 | push @outside_select_list, $alias_map{$key} = +{ |
47 | type => DQ_IDENTIFIER, |
48 | elements => [ $name ] |
49 | }; |
50 | } |
51 | my $order = $orig_select->{from}; |
52 | my $order_gensym_count; |
53 | my (@order_nodes, %order_map); |
54 | while ($order->{type} eq DQ_ORDER) { |
55 | my $by = $order->{by}; |
56 | if ($by->{type} eq DQ_IDENTIFIER) { |
57 | $default_inside_alias ||= $by->{elements}[0] |
58 | if @{$by->{elements}} == 2; |
59 | $order_map{$by} |
60 | = $alias_map{join('.', @{$by->{elements}})} |
61 | ||= do { |
62 | if ( |
63 | @{$by->{elements}} == 2 |
64 | and $by->{elements}[0] eq $default_inside_alias |
65 | ) { |
66 | $by; |
67 | } else { |
68 | my $name = sprintf("ORDER__BY__%03i",++$order_gensym_count); |
69 | push @inside_select_list, +{ |
70 | type => DQ_ALIAS, |
71 | from => $by, |
72 | to => $name |
73 | }; |
74 | +{ |
75 | type => DQ_IDENTIFIER, |
76 | elements => [ $name ], |
77 | }; |
78 | } |
79 | }; |
80 | } else { |
81 | die "XXX not implemented yet"; |
82 | } |
83 | push @order_nodes, $order; |
84 | $order = $order->{from}; |
85 | } |
86 | my $inside_order = $order; |
87 | $inside_order = +{ |
88 | type => DQ_ORDER, |
89 | by => $_->{by}, |
90 | reverse => $_->{reverse}, |
91 | from => $inside_order |
92 | } for reverse @order_nodes; |
93 | my $inside_select = +{ |
94 | type => DQ_SELECT, |
95 | select => \@inside_select_list, |
96 | from => $inside_order, |
97 | }; |
98 | $default_inside_alias ||= 'me'; |
99 | my $bridge_from = +{ |
100 | type => DQ_ALIAS, |
101 | from => $inside_select, |
102 | to => $default_inside_alias, |
103 | }; |
104 | my $outside_order; |
105 | $outside_order = +{ |
106 | type => DQ_ORDER, |
107 | by => $order_map{$_->{by}}, |
108 | reverse => !$_->{reverse}, |
109 | from => $outside_order |
110 | } for reverse @order_nodes; |
111 | my $outside_select = +{ |
112 | type => DQ_SELECT, |
113 | select => [ |
114 | @outside_select_list, |
115 | { |
116 | type => DQ_ALIAS, |
117 | from => $self->_rno_literal($outside_order), |
118 | to => 'rno__row__index', |
119 | } |
120 | ], |
121 | from => $bridge_from, |
122 | }; |
123 | my $idx_name = +{ |
124 | type => DQ_IDENTIFIER, |
125 | elements => [ 'rno__row__index' ], |
126 | }; |
127 | my $offset_value = $dq->{offset} ? $dq->{offset}{value} : 0; |
128 | my $final = +{ |
129 | type => DQ_WHERE, |
130 | where => { |
131 | type => DQ_OPERATOR, |
132 | operator => { 'SQL.Naive' => 'AND' }, |
133 | args => [ |
134 | { |
135 | type => DQ_OPERATOR, |
136 | operator => { 'SQL.Naive' => '>=' }, |
137 | args => [ |
138 | $idx_name, |
139 | { %{$dq->{limit}}, value => $offset_value + 1 }, |
140 | ] |
141 | }, |
142 | { |
143 | type => DQ_OPERATOR, |
144 | operator => { 'SQL.Naive' => '<=' }, |
145 | args => [ |
146 | $idx_name, |
147 | { %{$dq->{limit}}, value => $dq->{limit}{value} + $offset_value }, |
148 | ] |
149 | }, |
150 | ], |
151 | }, |
152 | from => { |
153 | type => DQ_SELECT, |
154 | select => \@outside_select_list, |
155 | from => { |
156 | type => DQ_ALIAS, |
157 | from => $outside_select, |
158 | to => $default_inside_alias, |
159 | }, |
160 | } |
161 | }; |
162 | return $self->_render($final); |
163 | } |
164 | |
165 | sub _rno_literal { |
166 | my ($self, $order) = @_; |
167 | my ($order_str, @order_bind) = ( |
168 | $order |
3d84f4a9 |
169 | ? @{$self->render($order)} |
77585983 |
170 | : ('') |
171 | ); |
172 | return +{ |
173 | type => DQ_LITERAL, |
174 | subtype => 'SQL', |
175 | literal => "ROW_NUMBER() OVER( $order_str )", |
176 | values => \@order_bind |
177 | }; |
178 | } |
179 | |
180 | 1; |