clean up FetchFirst more
[dbsrgits/Data-Query.git] / lib / Data / Query / Renderer / SQL / Slice / RowNumberOver.pm
CommitLineData
77585983 1package Data::Query::Renderer::SQL::Slice::RowNumberOver;
2
3use Data::Query::Constants qw(
4 DQ_SELECT DQ_ALIAS DQ_IDENTIFIER DQ_ORDER DQ_SLICE DQ_WHERE DQ_OPERATOR
5 DQ_LITERAL
6);
7use Moo::Role;
8
9sub _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
165sub _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
1801;