rewrite GenericSubquery to handle multiple order columns
Matt S Trout [Fri, 1 Nov 2013 01:45:55 +0000 (01:45 +0000)]
lib/Data/Query/Renderer/SQL/Slice/GenericSubquery.pm

index eb83c4c..5320cbb 100644 (file)
@@ -20,25 +20,71 @@ sub _render_slice {
   my %remapped = $self->_subquery_remap($orig_select);
   my $first_from = $remapped{inner_body};
   # Should we simply strip until we reach a join/alias/etc. here?
-  $first_from = $first_from->{from}{from} if is_Having($first_from);
-  $first_from = $first_from->{from} if is_Where($first_from);
-  while (is_Join $first_from) {
-    $first_from = $first_from->{left};
+  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}
+      ];
+    } 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 $this = Operator($op_or, [
+      Operator($op_and, [
+        Operator({ 'SQL.Naive' => 'IS NOT NULL' }, [ $lhs ]),
+        Operator({ 'SQL.Naive' => 'IS NULL' }, [ $rhs ]),
+      ]),
+      Operator({ 'SQL.Naive' => '>' }, [ $lhs, $rhs ]),
+    ]);
+    ($a
+      ? Operator($op_or, [
+          $this,
+          Operator($op_and, [
+            Operator($op_or, [
+              Operator($op_and, [
+                map Operator({ 'SQL.Naive' => 'IS NULL' }, [ $_ ]), $lhs, $rhs
+              ]),
+              Operator({ 'SQL.Naive' => '=' }, [ $lhs, $rhs ])
+            ]),
+            $a
+          ])
+        ])
+      : $this)
+  } @main_order, undef;
   my $count_sel = Select(
     [ Operator({ 'SQL.Naive' => 'apply' }, [ Identifier('COUNT'), Identifier('*') ]) ],
     Where(
-      Operator(
-        { 'SQL.Naive' => ($remapped{inside_order}[0]{reverse} ? '>' : '<') },
-        [
-          Identifier($count_alias, $count_col),
-          $remapped{outside_order}[0]{by}
-        ]
-      ),
+      $count_cond,
       Alias($count_alias, $first_from)
     )
   );