diff --git a/dozer-sql/expression/src/builder.rs b/dozer-sql/expression/src/builder.rs index 19a6d25028..cefcda0039 100644 --- a/dozer-sql/expression/src/builder.rs +++ b/dozer-sql/expression/src/builder.rs @@ -185,6 +185,14 @@ impl ExpressionBuilder { ) .await } + SqlExpr::IsNull(expr) => { + self.parse_sql_isnull_operator(parse_aggregations, &false, expr, schema, udfs) + .await + } + SqlExpr::IsNotNull(expr) => { + self.parse_sql_isnull_operator(parse_aggregations, &true, expr, schema, udfs) + .await + } _ => Err(Error::UnsupportedExpression(expression.clone())), } } @@ -1014,6 +1022,25 @@ impl ExpressionBuilder { Ok(in_list_expression) } + + async fn parse_sql_isnull_operator( + &mut self, + parse_aggregations: bool, + negated: &bool, + expr: &Expr, + schema: &Schema, + udfs: &[UdfConfig], + ) -> Result { + let arg = self + .parse_sql_expression(parse_aggregations, expr, schema, udfs) + .await?; + + if *negated { + Ok(Expression::IsNotNull { arg: Box::new(arg) }) + } else { + Ok(Expression::IsNull { arg: Box::new(arg) }) + } + } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] diff --git a/dozer-sql/expression/src/execution.rs b/dozer-sql/expression/src/execution.rs index 09ce5c9853..b9a642fd39 100644 --- a/dozer-sql/expression/src/execution.rs +++ b/dozer-sql/expression/src/execution.rs @@ -4,6 +4,7 @@ use crate::conditional::{get_conditional_expr_type, ConditionalExpressionType}; use crate::datetime::{get_datetime_function_type, DateTimeFunctionType}; use crate::error::Error; use crate::geo::common::{get_geo_function_type, GeoFunctionType}; +use crate::is_null::{evaluate_is_not_null, evaluate_is_null}; use crate::json_functions::JsonFunctionType; use crate::operator::{BinaryOperatorType, UnaryOperatorType}; use crate::scalar::common::{get_scalar_function_type, ScalarFunctionType}; @@ -84,6 +85,12 @@ pub enum Expression { results: Vec, else_result: Option>, }, + IsNull { + arg: Box, + }, + IsNotNull { + arg: Box, + }, #[cfg(feature = "python")] PythonUDF { name: String, @@ -278,6 +285,8 @@ impl Expression { } #[cfg(feature = "javascript")] Expression::JavaScriptUdf(udf) => udf.to_string(schema), + Expression::IsNull { arg } => arg.to_string(schema) + " IS NULL ", + Expression::IsNotNull { arg } => arg.to_string(schema) + " IS NOT NULL ", } } } @@ -365,6 +374,8 @@ impl Expression { results, else_result, } => evaluate_case(schema, operand, conditions, results, else_result, record), + Expression::IsNull { arg } => evaluate_is_null(schema, arg, record), + Expression::IsNotNull { arg } => evaluate_is_not_null(schema, arg, record), #[cfg(feature = "javascript")] Expression::JavaScriptUdf(udf) => udf.evaluate(record, schema), } @@ -476,6 +487,18 @@ impl Expression { )), #[cfg(feature = "javascript")] Expression::JavaScriptUdf(udf) => Ok(udf.get_type()), + Expression::IsNull { arg: _ } => Ok(ExpressionType::new( + FieldType::Boolean, + false, + SourceDefinition::Dynamic, + false, + )), + Expression::IsNotNull { arg: _ } => Ok(ExpressionType::new( + FieldType::Boolean, + false, + SourceDefinition::Dynamic, + false, + )), } } } diff --git a/dozer-sql/expression/src/is_null.rs b/dozer-sql/expression/src/is_null.rs new file mode 100644 index 0000000000..5d682991f7 --- /dev/null +++ b/dozer-sql/expression/src/is_null.rs @@ -0,0 +1,50 @@ +use dozer_types::types::Record; +use dozer_types::types::{Field, Schema}; + +use crate::error::Error; +use crate::execution::Expression; + +pub(crate) fn evaluate_is_null( + schema: &Schema, + expr: &mut Expression, + record: &Record, +) -> Result { + let field = expr.evaluate(record, schema)?; + Ok(Field::Boolean(field == Field::Null)) +} + +pub(crate) fn evaluate_is_not_null( + schema: &Schema, + expr: &mut Expression, + record: &Record, +) -> Result { + let field = expr.evaluate(record, schema)?; + Ok(Field::Boolean(field != Field::Null)) +} + +#[test] +fn test_is_null() { + let mut value = Box::new(Expression::Literal(Field::Int(65))); + assert_eq!( + evaluate_is_null(&Schema::default(), &mut value, &Record::new(vec![])).unwrap(), + Field::Boolean(false) + ); + + let mut value = Box::new(Expression::Literal(Field::Null)); + assert_eq!( + evaluate_is_null(&Schema::default(), &mut value, &Record::new(vec![])).unwrap(), + Field::Boolean(true) + ); + + let mut value = Box::new(Expression::Literal(Field::Int(65))); + assert_eq!( + evaluate_is_not_null(&Schema::default(), &mut value, &Record::new(vec![])).unwrap(), + Field::Boolean(true) + ); + + let mut value = Box::new(Expression::Literal(Field::Null)); + assert_eq!( + evaluate_is_not_null(&Schema::default(), &mut value, &Record::new(vec![])).unwrap(), + Field::Boolean(false) + ); +} diff --git a/dozer-sql/expression/src/lib.rs b/dozer-sql/expression/src/lib.rs index 8ad5fda4a6..c94463f9d9 100644 --- a/dozer-sql/expression/src/lib.rs +++ b/dozer-sql/expression/src/lib.rs @@ -10,6 +10,7 @@ pub mod error; pub mod execution; mod geo; mod in_list; +mod is_null; mod json_functions; mod logical; mod mathematical;