Add support for NULLS FIRST/LAST in ORDER BY
Dagfinn Ilmari Mannsåker [Sat, 26 Jan 2013 19:16:39 +0000 (19:16 +0000)]
lib/Data/Query/ExprDeclare.pm
lib/Data/Query/ExprHelpers.pm
lib/Data/Query/Renderer/SQL/Naive.pm
lib/Data/Query/Renderer/SQL/Slice/FetchFirst.pm
lib/Data/Query/Renderer/SQL/Slice/GenericSubquery.pm
lib/Data/Query/Renderer/SQL/Slice/RowNumberOver.pm
t/example.t

index 8cb1fbc..ad8e360 100644 (file)
@@ -9,7 +9,7 @@ use Safe::Isa;
 use base qw(Exporter);
 
 our @EXPORT = qw(
-  expr SELECT AS FROM BY JOIN ON LEFT WHERE ORDER GROUP DESC LIMIT OFFSET
+  expr SELECT AS FROM BY JOIN ON LEFT WHERE ORDER GROUP DESC LIMIT OFFSET NULLS FIRST LAST
 );
 
 sub expr (&) {
@@ -84,7 +84,7 @@ sub FROM (&;@) {
   }
   while (is_Order($_[0])) {
     my $order = shift;
-    $from_dq = Order($order->{by}, $order->{reverse}, $from_dq);
+    $from_dq = Order($order->{by}, $order->{reverse}, $order->{nulls}, $from_dq);
   }
   return ($from_dq, @_);
 }
@@ -119,6 +119,9 @@ sub WHERE (&;@) {
 }
 
 sub DESC { bless({}, 'LIES::DESC'), @_ }
+sub NULLS { bless(\shift, 'LIES::NULLS'), @_ }
+sub FIRST { 1, @_ }
+sub LAST { -1, @_ }
 
 sub ORDER {
   my @order = map _value($_), _run_expr(shift);
@@ -129,7 +132,9 @@ sub ORDER {
       0;
     }
   };
-  return ((compose { Order($b, $reverse, $a) } @order, undef), @_);
+  my $nulls = $_[0]->$_isa('LIES::NULLS') ? ${+shift} : undef;
+
+  return ((compose { Order($b, $reverse, $nulls, $a) } @order, undef), @_);
 }
 
 sub LIMIT (&;@) {
index 6020be6..be56f2f 100644 (file)
@@ -31,7 +31,7 @@ my %map = (
   Operator => [ qw(operator args) ],
   Select => [ qw(select from) ],
   Where => [ qw(where from) ],
-  Order => [ qw(by reverse from) ],
+  Order => [ qw(by reverse nulls from) ],
   Group => [ qw(by from) ],
   Delete => [ qw(where target) ],
   Update => [ qw(set where target) ],
index 19c2288..aac3c73 100644 (file)
@@ -371,14 +371,24 @@ sub _render_where {
   ]
 }
 
-sub _render_order {
+sub _order_chunk {
   my ($self, $dq) = @_;
-  my @ret = (
-    $self->_format_keyword('ORDER BY'),
+  return +(
     $self->_render($dq->{by}),
     ($dq->{reverse}
       ? $self->_format_keyword('DESC')
-      : ())
+      : ()),
+    ($dq->{nulls}
+      ? $self->_format_keyword('NULLS '.('', qw(FIRST LAST))[$dq->{nulls}])
+      : ()),
+  );
+}
+
+sub _render_order {
+  my ($self, $dq) = @_;
+  my @ret = (
+    $self->_format_keyword('ORDER BY'),
+    $self->_order_chunk($dq),
   );
   my $from;
   while ($from = $dq->{from}) {
@@ -386,10 +396,7 @@ sub _render_order {
     $dq = $from;
     push @ret, (
       ',',
-      $self->_render($dq->{by}),
-      ($dq->{reverse}
-        ? $self->_format_keyword('DESC')
-        : ())
+      $self->_order_chunk($dq),
     );
   }
   unshift @ret, $self->_render($from) if $from;
index 466258b..0bb96e0 100644 (file)
@@ -53,7 +53,7 @@ sub _render_slice {
         ? Select(
           \@outside_select_list,
           compose {
-            Order($b->{by}, $b->{reverse}, $a)
+            Order($b->{by}, $b->{reverse}, $b->{nulls}, $a)
           } (
             @outside_order,
             Alias($default_inside_alias, $_)
@@ -72,7 +72,7 @@ sub _render_slice {
               : (),
           ],
           compose {
-            Order($b->{by}, !$b->{reverse}, $a)
+            Order($b->{by}, !$b->{reverse}, -$b->{nulls}, $a)
           } (
             @outside_order,
             Alias(
@@ -82,7 +82,7 @@ sub _render_slice {
                 Select(
                   \@inside_select_list,
                   compose {
-                    Order($b->{by}, $b->{reverse}, $a)
+                    Order($b->{by}, $b->{reverse}, $b->{nulls}, $a)
                   } @inside_order, $inner_body
                 )
               )
index 67b7b93..c10a890 100644 (file)
@@ -56,7 +56,7 @@ sub _render_slice {
   return $self->render(
     Select(
       $remapped{outside_select_list},
-      (compose { no warnings 'once'; Order($b->{by}, $b->{reverse}, $a) }
+      (compose { no warnings 'once'; Order($b->{by}, $b->{reverse}, $b->{nulls}, $a) }
         @{$remapped{outside_order}},
         Where(
           $count_where,
index 47b65bc..4f27fc6 100644 (file)
@@ -24,7 +24,7 @@ sub _render_slice {
 
   my $rno_name = 'rno__row__index';
 
-  my $order = compose { Order($b->{by}, $b->{reverse}, $a) }
+  my $order = compose { Order($b->{by}, $b->{reverse}, $b->{nulls}, $a) }
                 @outside_order, undef;
 
   my $rno_node = Alias($rno_name, $self->_rno_literal($order));
index 4458a8b..9a8a6ff 100644 (file)
@@ -51,7 +51,7 @@ DwarnL to_sql(
 );
 
 DwarnL to_sql(
-  SELECT { $_->cd->name } FROM { $_->cds, AS 'cd' } ORDER BY { $_->year }
+  SELECT { $_->cd->name } FROM { $_->cds, AS 'cd' } ORDER BY { $_->year } DESC NULLS LAST
 );
 
 my $lo =