use SQL::ReservedWords;
use Data::Query::Constants qw(
DQ_IDENTIFIER DQ_OPERATOR DQ_VALUE DQ_JOIN DQ_ALIAS DQ_ORDER DQ_LITERAL
- DQ_GROUP
+ DQ_GROUP DQ_SELECT DQ_SLICE
);
use Moo;
($dq->{from}
? ($self->_format_keyword('FROM'), @{$self->_render($dq->{from})})
: ()
- )
+ ),
];
}
}
}
}
- return [
- $self->_render($dq->{from}),
+ my %parenthesise = map +($_ => 1), DQ_SELECT, DQ_SLICE;
+ return [ # XXX not sure this is the right place to detect this
+ ($parenthesise{$dq->{from}{type}}
+ ? [ '(', $self->_render($dq->{from}), ')' ]
+ : $self->_render($dq->{from})),
$as || ' ',
$self->_render_identifier({ elements => [ $dq->{to} ] })
];
my @ret = (
$self->_format_keyword('ORDER BY'),
$self->_render($dq->{by}),
- ($dq->{direction} ? $self->_format_keyword($dq->{direction}) : ())
+ (defined $dq->{reverse}
+ ? $self->_format_keyword($dq->{reverse} ? 'DESC' : 'ASC')
+ : ())
);
my $from;
while ($from = $dq->{from}) {
push @ret, (
',',
$self->_render($dq->{by}),
- ($dq->{direction} ? $self->_format_keyword($dq->{direction}) : ())
+ (exists $dq->{reverse}
+ ? $self->_format_keyword($dq->{reverse} ? 'DESC' : 'ASC')
+ : ())
);
}
unshift @ret, $self->_render($from) if $from;
--- /dev/null
+package Data::Query::Renderer::SQL::Slice::FetchFirst;
+
+use Data::Query::Constants qw(
+ DQ_SELECT DQ_ALIAS DQ_IDENTIFIER DQ_ORDER DQ_SLICE
+);
+use Moo::Role;
+
+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')
+ ];
+ }
+ unless ($dq->{order_is_stable}) {
+ die "FetchFirst limit style requires a stable order";
+ }
+ 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 (@{$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";
+ }
+ $name ||= 'GENSYM__'.++$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;
+ die "Slice's Select not followed by Order but order_is_stable set"
+ unless $order->{type} eq DQ_ORDER;
+ my (@order_nodes, %order_map);
+ while ($order->{type} eq DQ_ORDER) {
+ my $by = $order->{by};
+ if ($by->{type} eq DQ_IDENTIFIER) {
+ $order_map{$by}
+ = $alias_map{join('.', @{$by->{elements}})}
+ ||= do {
+ my $name = 'ORDER__BY__'.++$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,
+ };
+ 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 => (
+ $dq->{preserve_order}
+ ? [ @outside_select_list, @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);
+}
+
+1;