sub _format_keyword { $_[0]->lc_keywords ? lc($_[1]) : $_[1] }
sub _render {
+ die "Expected hashref, got $_[1]" unless ref($_[1]) eq 'HASH';
$_[0]->${\"_render_${\(lc($_[1]->{type})||'broken')}"}($_[1]);
}
use Data::Query::ExprHelpers;
use Moo::Role;
+with 'Data::Query::Renderer::SQL::Slice::SubqueryRemap';
+
sub _render_slice_limit {
my ($self, $dq) = @_;
return [
die "Slice's Select not followed by Order but order_is_stable set"
unless is_Order $orig_select->{from};
- my $gensym_count;
- my $default_inside_alias;
-
- my @inside_select_list = map {
- if (is_Alias) {
- $_;
- } elsif (is_Identifier) {
- my @el = @{$_->{elements}};
- if (@el == 2 and $el[0] eq ($default_inside_alias ||= $el[0])) {
- $_;
- } else {
- Alias(join('__', @el), $_);
- }
- } else {
- Alias(sprintf("GENSYM__%03i",++$gensym_count), $_);
- }
- } @{$orig_select->{select}};
-
- my %alias_map = map {
- if (is_Alias and is_Identifier $_->{from}) {
- +(join('.',@{$_->{from}{elements}}) => Identifier($_->{to}))
- } elsif (is_Identifier) {
- +(join('.',@{$_->{elements}}) => $_)
- } else {
- +()
- }
- } @inside_select_list;
-
- my @outside_select_list = map {
- if (is_Alias) {
- Identifier($_->{to});
- } else {
- $_;
- }
- } @inside_select_list;
-
- my @order_nodes;
- my $inner_body = do {
- my $order = $orig_select->{from};
- while (is_Order $order) {
- push @order_nodes, $order;
- $order = $order->{from};
- }
- $order;
- };
-
- my $order_gensym_count;
- my @mapped_order = map {
- my $by = $_->{by};
- if (is_Identifier $by) {
- $default_inside_alias ||= $by->{elements}[0]
- if @{$by->{elements}} == 2;
- my $mapped_by
- = $alias_map{join('.', @{$by->{elements}})}
- ||= do {
- if (
- @{$by->{elements}} == 2
- and $by->{elements}[0] eq $default_inside_alias
- ) {
- $by;
- } else {
- my $name = sprintf("ORDER__BY__%03i",++$order_gensym_count);
- push @inside_select_list, Alias($name, $by);
- Identifier($name);
- }
- };
- Order($mapped_by, $_->{reverse});
- } else {
- die "XXX not implemented yet";
- }
- } @order_nodes;
+ my %remapped = $self->_subquery_remap($orig_select);
- $default_inside_alias ||= 'me';
+ my @inside_select_list = @{$remapped{inside_select_list}};
+ my @outside_select_list = @{$remapped{outside_select_list}};
+ my @inside_order = @{$remapped{inside_order}};
+ my @outside_order = @{$remapped{outside_order}};
+ my $default_inside_alias = $remapped{default_inside_alias};
+ my $inner_body = $remapped{inner_body};
my $limit_plus_offset = +{
%{$dq->{limit}}, value => $dq->{limit}{value} + $dq->{offset}{value}
compose {
Order($b->{by}, $b->{reverse}, $a)
} (
- @mapped_order,
+ @outside_order,
Alias($default_inside_alias, $_)
)
)
@outside_select_list,
$dq->{preserve_order}
? (grep @{$_->{elements}} == 1,
- map $_->{by}, @mapped_order)
+ map $_->{by}, @outside_order)
: (),
],
compose {
Order($b->{by}, !$b->{reverse}, $a)
} (
- @mapped_order,
+ @outside_order,
Alias(
$default_inside_alias,
Slice(
\@inside_select_list,
compose {
Order($b->{by}, $b->{reverse}, $a)
- } @order_nodes, $inner_body
+ } @inside_order, $inner_body
)
)
)
package Data::Query::Renderer::SQL::Slice::RowNumberOver;
-use Data::Query::Constants qw(
- DQ_SELECT DQ_ALIAS DQ_IDENTIFIER DQ_ORDER DQ_SLICE DQ_WHERE DQ_OPERATOR
- DQ_LITERAL
-);
+use Data::Query::Constants;
+use Data::Query::ExprHelpers;
use Moo::Role;
+with 'Data::Query::Renderer::SQL::Slice::SubqueryRemap';
+
sub _render_slice {
my ($self, $dq) = @_;
die "Slice's inner is not a Select"
unless (my $orig_select = $dq->{from})->{type} eq DQ_SELECT;
- my %alias_map;
- my $gensym_count;
- my (@inside_select_list, @outside_select_list);
- my $default_inside_alias;
- SELECT: foreach my $s (@{$orig_select->{select}}) {
- my $name;
- if ($s->{type} eq DQ_ALIAS) {
- $name = $s->{to};
- $s = $s->{from};
- }
- my $key;
- if ($s->{type} eq DQ_IDENTIFIER) {
- if (!$name and @{$s->{elements}} == 2) {
- $default_inside_alias ||= $s->{elements}[0];
- if ($s->{elements}[0] eq $default_inside_alias) {
- $alias_map{join('.',@{$s->{elements}})} = $s;
- push @inside_select_list, $s;
- push @outside_select_list, $s;
- next SELECT;
- }
- }
- $name ||= join('__', @{$s->{elements}});
- $key = join('.', @{$s->{elements}});
- } else {
- die "XXX not implemented yet" unless $name;
- $key = "$s";
- }
- $name ||= sprintf("GENSYM__%03i",++$gensym_count);
- push @inside_select_list, +{
- type => DQ_ALIAS,
- from => $s,
- to => $name,
- };
- push @outside_select_list, $alias_map{$key} = +{
- type => DQ_IDENTIFIER,
- elements => [ $name ]
- };
- }
- my $order = $orig_select->{from};
- my $order_gensym_count;
- my (@order_nodes, %order_map);
- while ($order->{type} eq DQ_ORDER) {
- my $by = $order->{by};
- if ($by->{type} eq DQ_IDENTIFIER) {
- $default_inside_alias ||= $by->{elements}[0]
- if @{$by->{elements}} == 2;
- $order_map{$by}
- = $alias_map{join('.', @{$by->{elements}})}
- ||= do {
- if (
- @{$by->{elements}} == 2
- and $by->{elements}[0] eq $default_inside_alias
- ) {
- $by;
- } else {
- my $name = sprintf("ORDER__BY__%03i",++$order_gensym_count);
- push @inside_select_list, +{
- type => DQ_ALIAS,
- from => $by,
- to => $name
- };
- +{
- type => DQ_IDENTIFIER,
- elements => [ $name ],
- };
- }
- };
- } else {
- die "XXX not implemented yet";
- }
- push @order_nodes, $order;
- $order = $order->{from};
- }
- my $inside_order = $order;
- $inside_order = +{
- type => DQ_ORDER,
- by => $_->{by},
- reverse => $_->{reverse},
- from => $inside_order
- } for reverse @order_nodes;
- my $inside_select = +{
- type => DQ_SELECT,
- select => \@inside_select_list,
- from => $inside_order,
- };
- $default_inside_alias ||= 'me';
- my $bridge_from = +{
- type => DQ_ALIAS,
- from => $inside_select,
- to => $default_inside_alias,
- };
- my $outside_order;
- $outside_order = +{
- type => DQ_ORDER,
- by => $order_map{$_->{by}},
- reverse => !$_->{reverse},
- from => $outside_order
- } for reverse @order_nodes;
- my $outside_select = +{
- type => DQ_SELECT,
- select => [
- @outside_select_list,
- {
- type => DQ_ALIAS,
- from => $self->_rno_literal($outside_order),
- to => 'rno__row__index',
- }
- ],
- from => $bridge_from,
+
+ my %remapped = $self->_subquery_remap($orig_select);
+
+ my @inside_select_list = @{$remapped{inside_select_list}};
+ my @outside_select_list = @{$remapped{outside_select_list}};
+ my @inside_order = @{$remapped{inside_order}};
+ my @outside_order = @{$remapped{outside_order}};
+ my $default_inside_alias = $remapped{default_inside_alias};
+ my $inner_body = $remapped{inner_body};
+
+ my $rno_name = 'rno__row__index';
+
+ my $order = compose { Order($b->{by}, $b->{reverse}, $a) }
+ @outside_order, undef;
+
+ my $rno_node = Alias($rno_name, $self->_rno_literal($order));
+
+ my $limit_plus_offset = +{
+ %{$dq->{limit}}, value => ($dq->{limit}{value}||0) + ($dq->{offset}{value}||0)
};
- my $idx_name = +{
- type => DQ_IDENTIFIER,
- elements => [ 'rno__row__index' ],
+
+ my $offset_plus = +{
+ %{$dq->{limit}}, value => ($dq->{offset}{value}||0)+1
};
- my $offset_value = $dq->{offset} ? $dq->{offset}{value} : 0;
- my $final = +{
- type => DQ_WHERE,
- where => {
- type => DQ_OPERATOR,
- operator => { 'SQL.Naive' => 'AND' },
- args => [
- {
- type => DQ_OPERATOR,
- operator => { 'SQL.Naive' => '>=' },
- args => [
- $idx_name,
- { %{$dq->{limit}}, value => $offset_value + 1 },
- ]
- },
- {
- type => DQ_OPERATOR,
- operator => { 'SQL.Naive' => '<=' },
- args => [
- $idx_name,
- { %{$dq->{limit}}, value => $dq->{limit}{value} + $offset_value },
+
+ return $self->_render(
+ Select(
+ \@outside_select_list,
+ Where(
+ Operator(
+ { 'SQL.Naive' => 'AND' },
+ [
+ Operator(
+ { 'SQL.Naive' => '>=' },
+ [ Identifier($rno_name), $offset_plus ],
+ ),
+ Operator(
+ { 'SQL.Naive' => '<=' },
+ [ Identifier($rno_name), $limit_plus_offset ],
+ ),
]
- },
- ],
- },
- from => {
- type => DQ_SELECT,
- select => \@outside_select_list,
- from => {
- type => DQ_ALIAS,
- from => $outside_select,
- to => $default_inside_alias,
- },
- }
- };
- return $self->_render($final);
+ ),
+ Alias(
+ $default_inside_alias,
+ Select(
+ [ @outside_select_list, $rno_node ],
+ Alias(
+ $default_inside_alias,
+ Select(
+ \@inside_select_list,
+ $inner_body
+ ),
+ ),
+ ),
+ )
+ )
+ )
+ );
}
sub _rno_literal {
--- /dev/null
+package Data::Query::Renderer::SQL::Slice::SubqueryRemap;
+
+use Data::Query::ExprHelpers;
+use Moo::Role;
+
+sub _subquery_remap {
+ my ($self, $orig_select) = @_;
+
+ my $gensym_count;
+ my $default_inside_alias;
+
+ my @inside_select_list = map {
+ if (is_Alias) {
+ $_;
+ } elsif (is_Identifier) {
+ my @el = @{$_->{elements}};
+ if (@el == 2 and $el[0] eq ($default_inside_alias ||= $el[0])) {
+ $_;
+ } else {
+ Alias(join('__', @el), $_);
+ }
+ } else {
+ Alias(sprintf("GENSYM__%03i",++$gensym_count), $_);
+ }
+ } @{$orig_select->{select}};
+
+ my %alias_map = map {
+ if (is_Alias and is_Identifier $_->{from}) {
+ +(join('.',@{$_->{from}{elements}}) => Identifier($_->{to}))
+ } elsif (is_Identifier) {
+ +(join('.',@{$_->{elements}}) => $_)
+ } else {
+ +()
+ }
+ } @inside_select_list;
+
+ my @outside_select_list = map {
+ if (is_Alias) {
+ Identifier($_->{to});
+ } else {
+ $_;
+ }
+ } @inside_select_list;
+
+ my @inside_order;
+ my $inner_body = do {
+ my $order = $orig_select->{from};
+ while (is_Order $order) {
+ push @inside_order, $order;
+ $order = $order->{from};
+ }
+ $order;
+ };
+
+ my $order_gensym_count;
+ my @outside_order = map {
+ my $by = $_->{by};
+ if (is_Identifier $by) {
+ $default_inside_alias ||= $by->{elements}[0]
+ if @{$by->{elements}} == 2;
+ my $mapped_by
+ = $alias_map{join('.', @{$by->{elements}})}
+ ||= do {
+ if (
+ @{$by->{elements}} == 2
+ and $by->{elements}[0] eq $default_inside_alias
+ ) {
+ push @inside_select_list, $by;
+ $by;
+ } else {
+ my $name = sprintf("ORDER__BY__%03i",++$order_gensym_count);
+ push @inside_select_list, Alias($name, $by);
+ Identifier($name);
+ }
+ };
+ Order($mapped_by, $_->{reverse});
+ } else {
+ die "XXX not implemented yet";
+ }
+ } @inside_order;
+
+ $default_inside_alias ||= 'me';
+
+ return (
+ inside_select_list => \@inside_select_list,
+ outside_select_list => \@outside_select_list,
+ inside_order => \@inside_order,
+ outside_order => \@outside_order,
+ default_inside_alias => $default_inside_alias,
+ inner_body => $inner_body,
+ );
+}
+
+1;