Convert (in)equality with undef to (not) defined/IS NOT NULL ops
Dagfinn Ilmari Mannsåker [Thu, 9 Jan 2014 17:34:53 +0000 (17:34 +0000)]
We can't overload defined(), so this is the only way to do it.
We're not using IS NULL, since the converter can only hande 1:1
mapping between ops.

lib/Data/Query/ExprBuilder.pm
lib/Data/Query/Renderer/SQL/Naive.pm
t/expr.t
t/sql.t

index 35c0187..3c62973 100644 (file)
@@ -32,11 +32,36 @@ use overload (
       });
     }
   }
-    qw(+ - * / % ** << >> . < > == != lt le gt ge eq ne),
+    qw(+ - * / % ** << >> . < > lt le gt ge ),
 
     # since 'and' and 'or' aren't operators we borrow the bitwise ops
     [ '&' => 'and' ], [ '|' => 'or' ],
   ),
+
+  # equality operators (need undef maping)
+  (map {
+    my ($overload, $as) = ref($_) ? @$_ : ($_, $_);
+    $overload => sub {
+      Data::Query::ExprBuilder->new({
+        expr => grep(!defined, @_[0,1])
+          ?  (map { $overload =~ /==|eq/ ? perl_operator(not => $_) : $_ }
+              perl_operator(defined => map { defined($_) ? $_->{expr} : () } @_[0,1]))
+          :  perl_operator(
+               $as,
+               map {
+                 (Scalar::Util::blessed($_)
+                 && $_->isa('Data::Query::ExprBuilder'))
+                   ? $_->{expr}
+                   : perl_scalar_value($_)
+                  # we're called with ($left, $right, 0) or ($right, $left, 1)
+                } $_[2] ? @_[1,0] : @_[0,1]
+              )
+      });
+    }
+  }
+    qw(== != eq ne)
+
+  ),
   # unsupported
   (map {
     my $op = $_;
index a84981b..82e4e16 100644 (file)
@@ -268,6 +268,7 @@ sub _convert_op {
       $_ eq '==' and return '=';
       $_ eq 'eq' and return '=';
       $_ eq '!' and return 'NOT';
+      $_ eq 'defined' and return 'IS NOT NULL';
     }
     return uc $perl_op; # hope!
   }
index f53f61f..15e87ce 100644 (file)
--- a/t/expr.t
+++ b/t/expr.t
@@ -103,3 +103,27 @@ expr_is { !$_->foo }
     args => [ expr { $_->foo } ],
   },
   '! ok';
+
+expr_is { $_->foo != undef }
+  {
+    type => DQ_OPERATOR,
+    operator => { Perl => 'defined' },
+    args => [ expr { $_->foo } ],
+  },
+  '!= undef => defined';
+
+expr_is { $_->foo ne undef }
+  {
+    type => DQ_OPERATOR,
+    operator => { Perl => 'defined' },
+    args => [ expr { $_->foo } ],
+  },
+  'ne undef => defined';
+
+expr_is { $_->foo == undef }
+  expr { !($_->foo != undef) },
+  '== undef => not defined';
+
+expr_is { $_->foo eq undef }
+  expr { !($_->foo ne undef) },
+  'eq undef => not defined';
diff --git a/t/sql.t b/t/sql.t
index 6bf46e8..b5c9294 100644 (file)
--- a/t/sql.t
+++ b/t/sql.t
@@ -49,6 +49,14 @@ expr_sql_is { !$_->foo } # XXX revisit this why are the parens here
   [ "( NOT foo )" ],
   "Unary expression ok";
 
+expr_sql_is { $_->foo != undef }
+  [ "foo IS NOT NULL" ],
+  "!= undef => IS NOT NULL";
+
+expr_sql_is { $_->foo == undef }
+  [ "( NOT foo IS NOT NULL )" ],
+  "== undef => ( NOT foo IS NOT NULL )";
+
 expr_sql_is { SELECT { $_->foo } }
   [ "SELECT foo" ],
   "Simple identifier";
@@ -66,3 +74,4 @@ expr_sql_is { SELECT { $_->foo => AS("foom"), 1 } }
 expr_sql_is { SELECT { $_->foo => AS "foom", 1 } }
   [ "SELECT foo AS foom, ?", binding(1) ],
   "AS without parens";
+