Fix trailing whitespace
[dbsrgits/Data-Query.git] / lib / Data / Query / Renderer / SQL / Slice / GenericSubquery.pm
index c01c48c..b3793e2 100644 (file)
@@ -5,27 +5,100 @@ use Moo::Role;
 
 with 'Data::Query::Renderer::SQL::Slice::SubqueryRemap';
 
+sub slice_subquery {
+  (limit => 1, offset => 1);
+}
+
+sub slice_stability {
+  (limit => 'requires', offset => 'requires');
+}
+
 sub _render_slice {
   my ($self, $dq) = @_;
   die "Slice's inner is not a Select"
     unless is_Select my $orig_select = $dq->{from};
   my %remapped = $self->_subquery_remap($orig_select);
   my $first_from = $remapped{inner_body};
-  $first_from = $first_from->{from} if is_Where($first_from);
-  while (is_Join $first_from) {
-    $first_from = $first_from->{left};
+  # Should we simply strip until we reach a join/alias/etc. here?
+  STRIP: while ($first_from) {
+    if (is_Group($first_from)) {
+      $first_from = $first_from->{from};
+      next STRIP;
+    } elsif (is_Where($first_from)) {
+      $first_from = $first_from->{from};
+      next STRIP;
+    } elsif (is_Join($first_from)) {
+      $first_from = $first_from->{left};
+      next STRIP;
+    }
+    last STRIP;
   }
+  die "WHAT" unless $first_from;
   $first_from = $first_from->{from} if is_Alias($first_from);
-  my $first_order = $remapped{inside_order}[0]{by};
-  my $count_col = $first_order->{elements}[-1];
+  my @main_order;
+  foreach my $i (0..$#{$remapped{inside_order}}) {
+    my $order = $remapped{inside_order}[$i];
+    my $outside = $remapped{outside_order}[$i];
+    if (is_Identifier($order->{by})
+        and (
+          (@{$order->{by}{elements}} == 2
+          and $order->{by}{elements}[0] eq $remapped{default_inside_alias})
+        or (@{$order->{by}{elements}} == 1))
+    ) {
+      push @main_order, [
+        $outside->{by}, $order->{by}{elements}[-1], $order->{reverse},
+        $order->{nulls}
+      ];
+    } else {
+      last;
+    }
+  }
+
   my $count_alias = 'rownum__emulation';
+  my ($op_and, $op_or) = map +{ 'SQL.Naive' => $_ }, qw(AND OR);
+  my $count_cond = compose {
+    my $lhs = $b->[0];
+    my $rhs = Identifier($count_alias, $b->[1]);
+    ($lhs, $rhs) = ($rhs, $lhs) if $b->[2];
+    my $no_nulls = ($b->[3]||'') eq 'none';
+    my ($this) = map {
+      $no_nulls
+        ? $_
+        : Operator($op_or, [
+            Operator($op_and, [
+              Operator({ 'SQL.Naive' => 'IS NOT NULL' }, [ $lhs ]),
+              Operator({ 'SQL.Naive' => 'IS NULL' }, [ $rhs ]),
+            ]),
+            $_
+          ])
+    } Operator({ 'SQL.Naive' => '>' }, [ $lhs, $rhs ]);
+    my $final = (
+      $a
+        ? Operator($op_or, [
+            $this,
+            Operator($op_and, [
+              (map {
+                $no_nulls
+                  ? $_
+                  : Operator($op_or, [
+                      Operator($op_and, [
+                        map Operator({ 'SQL.Naive' => 'IS NULL' }, [ $_ ]),
+                          $lhs, $rhs
+                      ]),
+                      $_,
+                    ])
+              } Operator({ 'SQL.Naive' => '=' }, [ $lhs, $rhs ])),
+              $a
+            ])
+          ])
+        : $this
+    );
+    $final;
+  } @main_order, undef;
   my $count_sel = Select(
     [ Operator({ 'SQL.Naive' => 'apply' }, [ Identifier('COUNT'), Identifier('*') ]) ],
     Where(
-      Operator(
-        { 'SQL.Naive' => ($first_order->{reverse} ? '>' : '<') },
-        [ Identifier($count_alias, $count_col), $first_order ]
-      ),
+      $count_cond,
       Alias($count_alias, $first_from)
     )
   );
@@ -47,7 +120,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,