introduce SELECT capability and skeleton test
Matt S Trout [Sat, 16 Oct 2010 01:51:57 +0000 (02:51 +0100)]
lib/Data/Query/Constants.pm
lib/Data/Query/ExprHelpers.pm
lib/Data/Query/Renderer/SQL/Naive.pm
t/expr.include
t/sql.t

index 1a9debc..7730cde 100644 (file)
@@ -8,6 +8,7 @@ use constant +{
     DQ_IDENTIFIER => 'Identifier',
     DQ_OPERATOR => 'Operator',
     DQ_VALUE => 'Value',
+    DQ_SELECT => 'Select',
   ))
 };
 
index 1ba349e..d194313 100644 (file)
@@ -1,17 +1,17 @@
 package Data::Query::ExprHelpers;
 
 use strictures 1;
-use Data::Query::Constants qw(DQ_VALUE DQ_OPERATOR);
+use Data::Query::Constants qw(DQ_VALUE DQ_OPERATOR DQ_IDENTIFIER);
 
 use base qw(Exporter);
 
-our @EXPORT_OK = qw(perl_scalar_value perl_operator);
+our @EXPORT_OK = qw(perl_scalar_value perl_operator identifier);
 
 sub perl_scalar_value {
   +{
-      type => DQ_VALUE,
-      subtype => { Perl => 'Scalar' },
-      value => $_[0]
+    type => DQ_VALUE,
+    subtype => { Perl => 'Scalar' },
+    value => $_[0]
   }
 }
 
@@ -24,4 +24,11 @@ sub perl_operator {
   }
 }
 
+sub identifier {
+  +{
+    type => DQ_IDENTIFIER,
+    elements => [ @_ ]
+  }
+}
+
 1;
index cbfc1e9..d8c5870 100644 (file)
@@ -48,6 +48,13 @@ sub _flatten_structure {
   } @$struct), @bind ];
 }
 
+# I present this to permit strange people to easily supply a patch to lc()
+# their keywords, as I have heard many desire to do, lest they infect me
+# with whatever malady caused this desire by their continued proximity for
+# want of such a feature.
+
+sub _format_keyword { $_[1] }
+
 sub _render {
   $_[0]->${\"_render_${\lc($_[1]->{type})}"}($_[1]);
 }
@@ -154,5 +161,36 @@ sub _convert_op {
   }
   die "Can't convert non-perl op yet";
 }
-  
+
+sub _render_select {
+  my ($self, $dq) = @_;
+  die "Empty select list" unless @{$dq->{select}};
+
+  # it is, in fact, completely valid for there to be nothing for us
+  # to project from since many databases handle 'SELECT 1;' fine
+
+  my @select = map {
+    # we should perhaps validate that what we've been handed
+    # is an expression and possibly an identifier - at least a
+    # debugging mode that does such is almost certainly worthwhile;
+    # but for present I'm focusing on making this work.
+    my $e = $self->_render($_->{expr});
+    $_->{name} ? [ $e, 'AS', $self->_render($_->{name}), ',' ] : [ $e, ',' ]
+  } @{$dq->{select}};
+
+  # we put the commas inside the [] for each entry as a hint to the pretty
+  # printer downstreamso now we need to eliminate the comma from the last
+  # entry - we know there always is one due to the die guard at the top
+
+  pop @{$select[-1]};
+
+  return [
+    $self->_format_keyword('SELECT'),
+    \@select,
+    # if present this may be a bare FROM, a FROM+WHERE, or a FROM+WHERE+GROUP
+    # since we're the SELECT and therefore always come first, we don't care.
+    $dq->{from} ? @{$self->_render($dq->{from})} : ()
+  ];
+}
+
 1;
index f124223..0521b29 100644 (file)
@@ -1,5 +1,7 @@
+use strictures 1;
 use Data::Query::ExprBuilder::Identifier;
-use Data::Query::Constants qw(DQ_IDENTIFIER DQ_OPERATOR DQ_VALUE);
+use Data::Query::Constants qw(DQ_SELECT);
+use Data::Query::ExprHelpers qw(perl_scalar_value identifier);
 
 sub expr (&) {
   _mk_expr($_[0]);
@@ -7,12 +9,42 @@ sub expr (&) {
 
 sub _mk_expr {
   local $_ = Data::Query::ExprBuilder::Identifier->new({
-    expr => {
-      type => DQ_IDENTIFIER,
-      elements => [],
-    },
+    expr => identifier()
   });
   $_[0]->()->{expr};
 }
 
+sub AS {
+  my $as = shift;
+  (bless(\$as, 'LIES::AS'), @_);
+}
+
+sub SELECT (&;@) {
+  my @select = map +(
+    ref()
+      ? $_
+      : { expr => perl_scalar_value($_) }
+  ), do {
+    local $_ = Data::Query::ExprBuilder::Identifier->new({
+      expr => identifier()
+    });
+    $_[0]->();
+  };
+  my @final;
+  while (@select) {
+    my $e = shift @select;
+    my $res = push @final, +{ expr => $e->{expr} };
+    if (ref($select[0]) eq 'LIES::AS') {
+      $res->{name} = identifier(shift @select);
+    }
+  }
+      
+  return +{
+    expr => {
+      type => DQ_SELECT,
+      select => \@final
+    },
+  };
+}
+
 1;
diff --git a/t/sql.t b/t/sql.t
index 64e2f2c..57cb03b 100644 (file)
--- a/t/sql.t
+++ b/t/sql.t
@@ -3,6 +3,7 @@ use Test::More qw(no_plan);
 
 use Devel::Dwarn;
 use Data::Query::Renderer::SQL::Naive;
+use Data::Query::ExprHelpers qw(perl_scalar_value);
 
 BEGIN { require 't/expr.include' }
 
@@ -91,3 +92,13 @@ expr_sql_is { ($_->foo == 1) & ($_->bar eq "foo") & ($_->baz > 3) }
 expr_sql_is { !$_->foo }
   [ "NOT foo" ],
   "Unary expression ok";
+
+expr_sql_is { SELECT { $_->foo } }
+  [ "SELECT foo" ],
+  "Simple identifier";
+
+expr_sql_is { SELECT { $_->foo, 1 } }
+  # the extra space here is a little icky but Naive's _flatten_structure
+  # will need rewriting to fix it - commit bits available if you do it first
+  [ "SELECT foo , ?", perl_scalar_value(1) ],
+  "Identifier and literal";