diff --git a/datafusion/src/logical_plan/expr.rs b/datafusion/src/logical_plan/expr.rs index 318d73fe35117..04e95e73a297e 100644 --- a/datafusion/src/logical_plan/expr.rs +++ b/datafusion/src/logical_plan/expr.rs @@ -1972,10 +1972,27 @@ fn create_name(e: &Expr, input_schema: &DFSchema) -> Result { Ok(format!("{} IN ({:?})", expr, list)) } } - other => Err(DataFusionError::NotImplemented(format!( - "Create name does not support logical expression {:?}", - other - ))), + Expr::Between { + expr, + negated, + low, + high, + } => { + let expr = create_name(expr, input_schema)?; + let low = create_name(low, input_schema)?; + let high = create_name(high, input_schema)?; + if *negated { + Ok(format!("{} NOT BETWEEN {} AND {}", expr, low, high)) + } else { + Ok(format!("{} BETWEEN {} AND {}", expr, low, high)) + } + } + Expr::Sort { .. } => Err(DataFusionError::Internal( + "Create name does not support sort expression".to_string(), + )), + Expr::Wildcard => Err(DataFusionError::Internal( + "Create name does not support wildcard".to_string(), + )), } } diff --git a/datafusion/src/physical_plan/planner.rs b/datafusion/src/physical_plan/planner.rs index fd0421b5e0bad..006d86c42ab94 100644 --- a/datafusion/src/physical_plan/planner.rs +++ b/datafusion/src/physical_plan/planner.rs @@ -182,10 +182,27 @@ fn create_physical_name(e: &Expr, is_first_expr: bool) -> Result { Ok(format!("{} IN ({:?})", expr, list)) } } - other => Err(DataFusionError::NotImplemented(format!( - "Cannot derive physical field name for logical expression {:?}", - other - ))), + Expr::Between { + expr, + negated, + low, + high, + } => { + let expr = create_physical_name(expr, false)?; + let low = create_physical_name(low, false)?; + let high = create_physical_name(high, false)?; + if *negated { + Ok(format!("{} NOT BETWEEN {} AND {}", expr, low, high)) + } else { + Ok(format!("{} BETWEEN {} AND {}", expr, low, high)) + } + } + Expr::Sort { .. } => Err(DataFusionError::Internal( + "Create physical name does not support sort expression".to_string(), + )), + Expr::Wildcard => Err(DataFusionError::Internal( + "Create physical name does not support wildcard".to_string(), + )), } } diff --git a/datafusion/tests/sql.rs b/datafusion/tests/sql.rs index 6cd1d3822bce9..fe894d1761de2 100644 --- a/datafusion/tests/sql.rs +++ b/datafusion/tests/sql.rs @@ -5397,6 +5397,54 @@ async fn case_with_bool_type_result() -> Result<()> { Ok(()) } +#[tokio::test] +async fn use_between_expression_in_select_query() -> Result<()> { + let mut ctx = ExecutionContext::new(); + + let sql = "SELECT 1 NOT BETWEEN 3 AND 5"; + let actual = execute_to_batches(&mut ctx, sql).await; + let expected = vec![ + "+---------------+", + "| Boolean(true) |", + "+---------------+", + "| true |", + "+---------------+", + ]; + assert_batches_eq!(expected, &actual); + + let input = Int64Array::from(vec![1, 2, 3, 4]); + let batch = RecordBatch::try_from_iter(vec![("c1", Arc::new(input) as _)]).unwrap(); + let table = MemTable::try_new(batch.schema(), vec![vec![batch]])?; + ctx.register_table("test", Arc::new(table))?; + + let sql = "SELECT abs(c1) BETWEEN 0 AND LoG(c1 * 100 ) FROM test"; + let actual = execute_to_batches(&mut ctx, sql).await; + // Expect field name to be correctly converted for expr, low and high. + let expected = vec![ + "+--------------------------------------------------------------------+", + "| abs(test.c1) BETWEEN Int64(0) AND log(test.c1 Multiply Int64(100)) |", + "+--------------------------------------------------------------------+", + "| true |", + "| true |", + "| false |", + "| false |", + "+--------------------------------------------------------------------+", + ]; + assert_batches_eq!(expected, &actual); + + let sql = "EXPLAIN SELECT c1 BETWEEN 2 AND 3 FROM test"; + let actual = execute_to_batches(&mut ctx, sql).await; + let formatted = arrow::util::pretty::pretty_format_batches(&actual).unwrap(); + + // Only test that the projection exprs arecorrect, rather than entire output + let needle = "ProjectionExec: expr=[c1@0 >= 2 AND c1@0 <= 3 as test.c1 BETWEEN Int64(2) AND Int64(3)]"; + assert_contains!(&formatted, needle); + let needle = "Projection: #test.c1 BETWEEN Int64(2) AND Int64(3)"; + assert_contains!(&formatted, needle); + + Ok(()) +} + #[tokio::test] async fn query_get_indexed_field() -> Result<()> { let mut ctx = ExecutionContext::new();