Repository: gluesql/gluesql
Pull request #59 (2020-09-05 04:39:31)

filter: support is null and not null expr
implement #50


Patch:
diff --git a/src/executor/evaluate/evaluated.rs b/src/executor/evaluate/evaluated.rs
index 29d8a028..d6d3cd23 100644
--- a/src/executor/evaluate/evaluated.rs
+++ b/src/executor/evaluate/evaluated.rs
@@ -246,6 +246,16 @@ impl<'a> Evaluated<'a> {
             StringRef(_) => unreachable(),
         }
     }
+
+    pub fn is_some(&self) -> bool {
+        match self {
+            Evaluated::ValueRef(v) => v.is_some(),
+            Evaluated::Value(v) => v.is_some(),
+            Evaluated::Literal(v) => v != &AstValue::Null,
+            Evaluated::LiteralRef(v) => v != &&AstValue::Null,
+            Evaluated::StringRef(_v) => true,
+        }
+    }
 }
 
 fn literal_add(a: &AstValue, b: &AstValue) -> Result<AstValue> {
diff --git a/src/executor/filter.rs b/src/executor/filter.rs
index 71e66f33..674ad9b6 100644
--- a/src/executor/filter.rs
+++ b/src/executor/filter.rs
@@ -166,6 +166,8 @@ fn check_expr<'a, T: 'static + Debug>(
             Ok(negated ^ (evaluate(low)? <= target && target <= evaluate(high)?))
         }
         Expr::Exists(query) => Ok(select(storage, query, filter_context)?.next().is_some()),
+        Expr::IsNull(expr) => Ok(!evaluate(expr)?.is_some()),
+        Expr::IsNotNull(expr) => Ok(evaluate(expr)?.is_some()),
         _ => Err(FilterError::Unimplemented.into()),
     }
 }


Test patch:
diff --git a/src/tests/macros.rs b/src/tests/macros.rs
index 5551b865..beb6aad3 100644
--- a/src/tests/macros.rs
+++ b/src/tests/macros.rs
@@ -7,6 +7,15 @@ macro_rules! row {
 
 #[macro_export]
 macro_rules! select {
+    () => (
+        Payload::Select(vec![])
+    );
+    ( $( $t:path )*) => (
+        select!()
+    );
+    ( $( $t:path )* ;) => (
+        select!()
+    );
     ( $( $t:path )* ; $( $v: expr )* ) => (
         Payload::Select(vec![
             row!($( $t )* ; $( $v )* )
diff --git a/src/tests/nullable.rs b/src/tests/nullable.rs
index d959cc13..9534efa2 100644
--- a/src/tests/nullable.rs
+++ b/src/tests/nullable.rs
@@ -15,36 +15,141 @@ CREATE TABLE Test (
 
     use Value::*;
 
-    let found = tester
-        .run("SELECT id, num, name FROM Test")
-        .expect("select");
-    let expected = select!(
-        OptI64  I64 Str;
-        None    2   "Hello".to_owned();
-        Some(1) 9   "World".to_owned();
-        Some(3) 4   "Great".to_owned()
-    );
-    assert_eq!(expected, found);
+    let mut run = |sql| tester.run(sql).expect("select");
 
-    let found = tester
-        .run("SELECT id, num FROM Test WHERE id = NULL AND name = \'Hello\'")
-        .expect("select");
-    let expected = select!(OptI64 I64; None 2);
-    assert_eq!(expected, found);
+    let test_cases = vec![
+        (
+            "SELECT id, num, name FROM Test",
+            select!(
+                OptI64  I64 Str;
+                None    2   "Hello".to_owned();
+                Some(1) 9   "World".to_owned();
+                Some(3) 4   "Great".to_owned()
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE id = NULL AND name = \'Hello\'",
+            select!(
+                OptI64 I64;
+                None   2
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE id IS NULL",
+            select!(
+                OptI64 I64;
+                None   2
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE id IS NOT NULL",
+            select!(
+                OptI64  I64;
+                Some(1) 9;
+                Some(3) 4
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE id + 1 IS NULL",
+            select!(OptI64 I64),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE id + 1 IS NOT NULL",
+            select!(
+                OptI64  I64;
+                None    2;
+                Some(1) 9;
+                Some(3) 4
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE 100 IS NULL",
+            select!(OptI64 I64),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE 100 IS NOT NULL",
+            select!(
+                OptI64  I64;
+                None    2;
+                Some(1) 9;
+                Some(3) 4
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE 8 + 3 IS NULL",
+            select!(OptI64 I64),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE 8 + 3 IS NOT NULL",
+            select!(
+                OptI64  I64;
+                None    2;
+                Some(1) 9;
+                Some(3) 4
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE NULL IS NULL",
+            select!(
+                OptI64  I64;
+                None    2;
+                Some(1) 9;
+                Some(3) 4
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE NULL IS NOT NULL",
+            select!(OptI64 I64),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE \"NULL\" IS NULL",
+            select!(OptI64 I64),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE \"NULL\" IS NOT NULL",
+            select!(
+                OptI64  I64;
+                None    2;
+                Some(1) 9;
+                Some(3) 4
+            ),
+        ),
+    ];
+    test_cases
+        .into_iter()
+        .for_each(|(sql, expected)| assert_eq!(expected, run(sql)));
 
     tester.run_and_print("UPDATE Test SET id = 2");
 
-    let found = tester.run("SELECT id FROM Test").expect("select");
-    let expected = select!(OptI64; Some(2); Some(2); Some(2));
-    assert_eq!(expected, found);
+    let mut run = |sql| tester.run(sql);
 
-    let found = tester.run("SELECT id, num FROM Test").expect("select");
-    let expected = select!(OptI64 I64; Some(2) 2; Some(2) 9; Some(2) 4);
-    assert_eq!(expected, found);
-
-    let found = tester.run("INSERT INTO Test VALUES (1, NULL)");
-    let expected = Err(ValueError::NullValueOnNotNullField.into());
-    assert_eq!(expected, found);
+    let test_cases = vec![
+        (
+            "SELECT id FROM Test",
+            Ok(select!(
+                OptI64;
+                Some(2);
+                Some(2);
+                Some(2)
+            )),
+        ),
+        (
+            "SELECT id, num FROM Test",
+            Ok(select!(
+                OptI64  I64;
+                Some(2) 2;
+                Some(2) 9;
+                Some(2) 4
+            )),
+        ),
+        (
+            "INSERT INTO Test VALUES (1, NULL)",
+            Err(ValueError::NullValueOnNotNullField.into()),
+        ),
+    ];
+    test_cases
+        .into_iter()
+        .for_each(|(sql, expected)| assert_eq!(expected, run(sql)));
 }
 
 pub fn nullable_text(mut tester: impl tests::Tester) {