Remove array expansion in Literal()
[dbsrgits/Data-Query.git] / lib / Data / Query / Renderer / SQL / Slice / FetchFirst.pm
index e3d8372..d885fb4 100644 (file)
@@ -1,8 +1,6 @@
 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 {
@@ -26,56 +24,63 @@ sub _render_slice {
     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;
-  my %alias_map;
+    unless is_Select my $orig_select = $dq->{from};
+  die "Slice's Select not followed by Order but order_is_stable set"
+    unless is_Order $orig_select->{from};
+
   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 (!$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;
-          push @inside_select_list, $s;
-          push @outside_select_list, $s;
-          next SELECT;
-        }
+
+  my @inside_select_list = map {
+    if (is_Alias) {
+      $_;
+    } elsif (is_Identifier) {
+      my @el = @{$_->{elements}};
+      if (@el == 2 and $el[0] eq ($default_inside_alias ||= $el[0])) {
+        $_;
+      } else {
+        Alias(join('__', @el), $_);
       }
-      $name ||= join('__', @{$s->{elements}});
-      $key = join('.', @{$s->{elements}});
     } else {
-      die "XXX not implemented yet" unless $name;
-      $key = "$s";
+      Alias(sprintf("GENSYM__%03i",++$gensym_count), $_);
     }
-    $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 ]
-    };
-  }
-  my $order = $orig_select->{from};
+  } @{$orig_select->{select}};
+
+  my %alias_map = map {
+    if (is_Alias and is_Identifier $_->{from}) {
+      +(join('.',@{$_->{from}{elements}}) => Identifier($_->{to}))
+    } elsif (is_Identifier) {
+      +(join('.',@{$_->{elements}}) => $_)
+    } else {
+      +()
+    }
+  } @inside_select_list;
+
+  my @outside_select_list = map {
+    if (is_Alias) {
+      Identifier($_->{to});
+    } else {
+      $_;
+    }
+  } @inside_select_list;
+
+  my @order_nodes;
+  my $inner_body = do {
+    my $order = $orig_select->{from};
+    while (is_Order $order) {
+      push @order_nodes, $order;
+      $order = $order->{from};
+    }
+    $order;
+  };
+
   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) {
+  my @mapped_order = map {
+    my $by = $_->{by};
+    if (is_Identifier $by) {
       $default_inside_alias ||= $by->{elements}[0]
         if @{$by->{elements}} == 2;
-      $order_map{$by}
+      my $mapped_by
         = $alias_map{join('.', @{$by->{elements}})}
           ||= do {
                 if (
@@ -85,91 +90,67 @@ sub _render_slice {
                   $by;
                 } else {
                   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 ],
-                  };
+                  push @inside_select_list, Alias($name, $by);
+                  Identifier($name);
                 }
               };
+      Order($mapped_by, $_->{reverse});
     } 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,
-  };
+  } @order_nodes;
+
+  $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 => (
+
+  return $self->_render(
+    map {
       $dq->{preserve_order}
-        ? [
+        ? Select(
+          \@outside_select_list,
+          compose {
+            Order($b->{by}, $b->{reverse}, $a)
+          } (
+            @mapped_order,
+            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,
+                  map $_->{by}, @mapped_order)
+              : (),
+          ],
+          compose {
+            Order($b->{by}, !$b->{reverse}, $a)
+          } (
+            @mapped_order,
+            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;