package Data::Query::Renderer::SQL::Slice::FetchFirst;
-use Data::Query::Constants qw(
- DQ_SELECT DQ_ALIAS DQ_IDENTIFIER DQ_ORDER DQ_SLICE
-);
+use Data::Query::ExprHelpers;
use Moo::Role;
+sub _render_slice_limit {
+ my ($self, $dq) = @_;
+ return [
+ ($dq->{from} ? $self->_render($dq->{from}) : ()),
+ $self->_format_keyword('FETCH FIRST'),
+ sprintf("%i", $dq->{limit}{value}),
+ $self->_format_keyword('ROWS ONLY')
+ ];
+}
+
+sub _slice_type { 'FetchFirst' }
+
sub _render_slice {
my ($self, $dq) = @_;
unless ($dq->{offset}) {
- return [
- ($dq->{from} ? $self->_render($dq->{from}) : ()),
- $self->_format_keyword('FETCH FIRST'),
- sprintf("%i", $dq->{limit}{value}),
- $self->_format_keyword('ROWS ONLY')
- ];
+ return $self->_render_slice_limit($dq);
}
unless ($dq->{order_is_stable}) {
- die "FetchFirst limit style requires a stable order";
+ die $self->_slice_type." limit style requires a stable order";
}
die "Slice's inner is not a Select"
- unless (my $orig_select = $dq->{from})->{type} eq DQ_SELECT;
+ unless is_Select my $orig_select = $dq->{from};
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) {
+ if (is_Alias $s) {
$name = $s->{to};
$s = $s->{from};
}
my $key;
- if ($s->{type} eq DQ_IDENTIFIER) {
- if (@{$s->{elements}} == 2) {
+ if (is_Identifier $s) {
+ 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;
$name ||= join('__', @{$s->{elements}});
$key = join('.', @{$s->{elements}});
} else {
- die "XXX not implemented yet";
+ 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 ]
- };
+ push @inside_select_list, Alias($name, $s);
+ push @outside_select_list, $alias_map{$key} = Identifier($name);
}
my $order = $orig_select->{from};
my $order_gensym_count;
die "Slice's Select not followed by Order but order_is_stable set"
- unless $order->{type} eq DQ_ORDER;
+ unless is_Order $order;
my (@order_nodes, %order_map);
- while ($order->{type} eq DQ_ORDER) {
+ while (is_Order $order) {
my $by = $order->{by};
- if ($by->{type} eq DQ_IDENTIFIER) {
+ if (is_Identifier $by) {
+ $default_inside_alias ||= $by->{elements}[0]
+ if @{$by->{elements}} == 2;
$order_map{$by}
= $alias_map{join('.', @{$by->{elements}})}
||= do {
- 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 ],
- };
+ 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);
+ }
};
} 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 $limit_plus_offset = +{
%{$dq->{limit}}, value => $dq->{limit}{value} + $dq->{offset}{value}
};
- $default_inside_alias ||= 'me';
- my $bridge_from = +{
- type => DQ_ALIAS,
- to => $default_inside_alias,
- from => {
- type => DQ_SLICE,
- limit => $limit_plus_offset,
- from => $inside_select,
- },
- };
- my $outside_order = $bridge_from;
- $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 => (
+ my $inner_body = $order;
+ return $self->_render(
+ map {
$dq->{preserve_order}
- ? [
+ ? Select(
+ \@outside_select_list,
+ compose {
+ Order($order_map{$b->{by}}, $b->{reverse}, $a)
+ } @order_nodes, Alias($default_inside_alias, $_)
+ )
+ : $_
+ } Slice(
+ undef, $dq->{limit},
+ Select(
+ [
@outside_select_list,
- grep @{$_->{elements}} == 1, @order_map{map $_->{by}, @order_nodes}
- ]
- : \@outside_select_list,
- ),
- from => $outside_order,
- };
- my $final = {
- type => DQ_SLICE,
- limit => $dq->{limit},
- from => $outside_select
- };
- if ($dq->{preserve_order}) {
- $final = {
- type => DQ_ALIAS,
- from => $final,
- to => $default_inside_alias,
- };
- $final = +{
- type => DQ_ORDER,
- by => $order_map{$_->{by}},
- reverse => $_->{reverse},
- from => $final
- } for reverse @order_nodes;
- $final = {
- type => DQ_SELECT,
- select => \@outside_select_list,
- from => $final,
- };
- }
- return $self->_render($final);
+ $dq->{preserve_order}
+ ? (grep @{$_->{elements}} == 1,
+ @order_map{map $_->{by}, @order_nodes})
+ : (),
+ ],
+ compose {
+ Order($order_map{$b->{by}}, !$b->{reverse}, $a)
+ } (
+ @order_nodes,
+ Alias(
+ $default_inside_alias,
+ Slice(
+ undef, $limit_plus_offset,
+ Select(
+ \@inside_select_list,
+ compose {
+ Order($b->{by}, $b->{reverse}, $a)
+ } @order_nodes, $inner_body
+ )
+ )
+ )
+ )
+ )
+ )
+ );
}
1;