Repository: gluesql/gluesql (rust)

Issue:
NULL + 1 should be NULL
https://github.com/gluesql/gluesql/pull/59#issuecomment-687709531

Current: 
`NULL + 1 => 1`

Expected:
`NULL + 1 => NULL`

Pull request description:
change the behavior about null
fix #64

Patch:
diff --git a/src/data/value.rs b/src/data/value.rs
index 6155f42b..706304d4 100644
--- a/src/data/value.rs
+++ b/src/data/value.rs
@@ -247,11 +247,16 @@ impl Value {
             (I64(a), I64(b)) => Ok(I64(a + b)),
             (I64(a), OptI64(Some(b))) | (OptI64(Some(a)), I64(b)) => Ok(OptI64(Some(a + b))),
             (OptI64(Some(a)), OptI64(Some(b))) => Ok(OptI64(Some(a + b))),
-            (OptI64(None), OptI64(Some(a))) | (OptI64(Some(a)), OptI64(None)) => {
-                Ok(OptI64(Some(*a)))
-            }
             (F64(a), F64(b)) => Ok(F64(a + b)),
             (F64(a), OptF64(Some(b))) | (OptF64(Some(a)), F64(b)) => Ok(OptF64(Some(a + b))),
+            (OptI64(None), OptI64(_))
+            | (OptI64(_), OptI64(None))
+            | (OptI64(None), I64(_))
+            | (I64(_), OptI64(None)) => Ok(OptI64(None)),
+            (OptF64(_), OptF64(None))
+            | (OptF64(None), OptF64(_))
+            | (F64(_), OptF64(None))
+            | (OptF64(None), F64(_)) => Ok(OptF64(None)),
             _ => Err(ValueError::AddOnNonNumeric.into()),
         }
     }
@@ -264,6 +269,14 @@ impl Value {
             (I64(a), OptI64(Some(b))) | (OptI64(Some(a)), I64(b)) => Ok(OptI64(Some(a - b))),
             (F64(a), F64(b)) => Ok(F64(a - b)),
             (F64(a), OptF64(Some(b))) | (OptF64(Some(a)), F64(b)) => Ok(OptF64(Some(a - b))),
+            (OptI64(None), OptI64(_))
+            | (OptI64(_), OptI64(None))
+            | (OptI64(None), I64(_))
+            | (I64(_), OptI64(None)) => Ok(OptI64(None)),
+            (OptF64(_), OptF64(None))
+            | (OptF64(None), OptF64(_))
+            | (F64(_), OptF64(None))
+            | (OptF64(None), F64(_)) => Ok(OptF64(None)),
             _ => Err(ValueError::SubtractOnNonNumeric.into()),
         }
     }
@@ -276,6 +289,14 @@ impl Value {
             (I64(a), OptI64(Some(b))) | (OptI64(Some(a)), I64(b)) => Ok(OptI64(Some(a * b))),
             (F64(a), F64(b)) => Ok(F64(a * b)),
             (F64(a), OptF64(Some(b))) | (OptF64(Some(a)), F64(b)) => Ok(OptF64(Some(a * b))),
+            (OptI64(None), OptI64(_))
+            | (OptI64(_), OptI64(None))
+            | (OptI64(None), I64(_))
+            | (I64(_), OptI64(None)) => Ok(OptI64(None)),
+            (OptF64(_), OptF64(None))
+            | (OptF64(None), OptF64(_))
+            | (F64(_), OptF64(None))
+            | (OptF64(None), F64(_)) => Ok(OptF64(None)),
             _ => Err(ValueError::MultiplyOnNonNumeric.into()),
         }
     }
@@ -288,6 +309,14 @@ impl Value {
             (I64(a), OptI64(Some(b))) | (OptI64(Some(a)), I64(b)) => Ok(OptI64(Some(a / b))),
             (F64(a), F64(b)) => Ok(F64(a / b)),
             (F64(a), OptF64(Some(b))) | (OptF64(Some(a)), F64(b)) => Ok(OptF64(Some(a / b))),
+            (OptI64(None), OptI64(_))
+            | (OptI64(_), OptI64(None))
+            | (OptI64(None), I64(_))
+            | (I64(_), OptI64(None)) => Ok(OptI64(None)),
+            (OptF64(_), OptF64(None))
+            | (OptF64(None), OptF64(_))
+            | (F64(_), OptF64(None))
+            | (OptF64(None), F64(_)) => Ok(OptF64(None)),
             _ => Err(ValueError::DivideOnNonNumeric.into()),
         }
     }
diff --git a/src/executor/evaluate/evaluated.rs b/src/executor/evaluate/evaluated.rs
index d6d3cd23..dc3cd022 100644
--- a/src/executor/evaluate/evaluated.rs
+++ b/src/executor/evaluate/evaluated.rs
@@ -264,6 +264,9 @@ fn literal_add(a: &AstValue, b: &AstValue) -> Result<AstValue> {
             (Ok(a), Ok(b)) => Ok(AstValue::Number((a + b).to_string())),
             _ => panic!(),
         },
+        (AstValue::Null, AstValue::Number(_)) | (AstValue::Number(_), AstValue::Null) => {
+            Ok(AstValue::Null)
+        }
         _ => Err(EvaluateError::UnreachableLiteralArithmetic.into()),
     }
 }
@@ -274,6 +277,9 @@ fn literal_subtract(a: &AstValue, b: &AstValue) -> Result<AstValue> {
             (Ok(a), Ok(b)) => Ok(AstValue::Number((a - b).to_string())),
             _ => panic!(),
         },
+        (AstValue::Null, AstValue::Number(_)) | (AstValue::Number(_), AstValue::Null) => {
+            Ok(AstValue::Null)
+        }
         _ => Err(EvaluateError::UnreachableLiteralArithmetic.into()),
     }
 }
@@ -284,6 +290,9 @@ fn literal_multiply(a: &AstValue, b: &AstValue) -> Result<AstValue> {
             (Ok(a), Ok(b)) => Ok(AstValue::Number((a * b).to_string())),
             _ => panic!(),
         },
+        (AstValue::Null, AstValue::Number(_)) | (AstValue::Number(_), AstValue::Null) => {
+            Ok(AstValue::Null)
+        }
         _ => Err(EvaluateError::UnreachableLiteralArithmetic.into()),
     }
 }
@@ -294,6 +303,9 @@ fn literal_divide(a: &AstValue, b: &AstValue) -> Result<AstValue> {
             (Ok(a), Ok(b)) => Ok(AstValue::Number((a / b).to_string())),
             _ => panic!(),
         },
+        (AstValue::Null, AstValue::Number(_)) | (AstValue::Number(_), AstValue::Null) => {
+            Ok(AstValue::Null)
+        }
         _ => Err(EvaluateError::UnreachableLiteralArithmetic.into()),
     }
 }


Test patch:
diff --git a/src/tests/aggregate.rs b/src/tests/aggregate.rs
index f46f727d..2816584f 100644
--- a/src/tests/aggregate.rs
+++ b/src/tests/aggregate.rs
@@ -42,13 +42,13 @@ pub fn aggregate(mut tester: impl tests::Tester) {
         (
             "SELECT SUM(age), MAX(age), MIN(age) FROM Item",
             select!(
-                OptI64    OptI64   OptI64;
-                Some(104) Some(90) Some(3)
+                OptI64 OptI64   OptI64;
+                None   Some(90) Some(3)
             ),
         ),
         (
             "SELECT SUM(age) + SUM(quantity) FROM Item",
-            select!(OptI64; Some(151)),
+            select!(OptI64; None),
         ),
         (
             "SELECT COUNT(age), COUNT(quantity) FROM Item",
diff --git a/src/tests/nullable.rs b/src/tests/nullable.rs
index 9534efa2..d90a0df0 100644
--- a/src/tests/nullable.rs
+++ b/src/tests/nullable.rs
@@ -51,13 +51,15 @@ CREATE TABLE Test (
         ),
         (
             "SELECT id, num FROM Test WHERE id + 1 IS NULL",
-            select!(OptI64 I64),
+            select!(
+                OptI64  I64;
+                None    2
+            ),
         ),
         (
             "SELECT id, num FROM Test WHERE id + 1 IS NOT NULL",
             select!(
                 OptI64  I64;
-                None    2;
                 Some(1) 9;
                 Some(3) 4
             ),
@@ -114,6 +116,69 @@ CREATE TABLE Test (
                 Some(3) 4
             ),
         ),
+        (
+            "SELECT id, num FROM Test WHERE id = NULL + 1;",
+            select!(
+                OptI64  I64;
+                None    2
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE id = 1 + NULL;",
+            select!(
+                OptI64  I64;
+                None    2
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE id = NULL - 1;",
+            select!(
+                OptI64  I64;
+                None    2
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE id = 1 - NULL;",
+            select!(
+                OptI64  I64;
+                None    2
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE id = NULL * 1;",
+            select!(
+                OptI64  I64;
+                None    2
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE id = 1 * NULL;",
+            select!(
+                OptI64  I64;
+                None    2
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE id = NULL / 1;",
+            select!(
+                OptI64  I64;
+                None    2
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE id = 1 / NULL;",
+            select!(
+                OptI64  I64;
+                None    2
+            ),
+        ),
+        (
+            "SELECT id + 1, 1 + id, id - 1, 1 - id, id * 1, 1 * id, id / 1, 1 / id FROM Test WHERE id = NULL;",
+            select!(
+                OptI64 OptI64 OptI64 OptI64 OptI64 OptI64 OptI64 OptI64;
+                None   None   None   None   None   None   None   None
+            ),
+        ),
     ];
     test_cases
         .into_iter()