diff --git a/src/query/src/range_select/plan_rewrite.rs b/src/query/src/range_select/plan_rewrite.rs index 0113f61b8248..8da43fe8f21c 100644 --- a/src/query/src/range_select/plan_rewrite.rs +++ b/src/query/src/range_select/plan_rewrite.rs @@ -32,8 +32,8 @@ use datafusion_expr::execution_props::ExecutionProps; use datafusion_expr::expr::WildcardOptions; use datafusion_expr::simplify::SimplifyContext; use datafusion_expr::{ - Aggregate, Analyze, Explain, Expr, ExprSchemable, Extension, LogicalPlan, LogicalPlanBuilder, - Projection, + Aggregate, Analyze, Cast, Explain, Expr, ExprSchemable, Extension, LogicalPlan, + LogicalPlanBuilder, Projection, }; use datafusion_optimizer::simplify_expressions::ExprSimplifier; use datatypes::prelude::ConcreteDataType; @@ -548,12 +548,29 @@ fn have_range_in_exprs(exprs: &[Expr]) -> bool { fn interval_only_in_expr(expr: &Expr) -> bool { let mut all_interval = true; let _ = expr.apply(|expr| { + // A cast expression for an interval. + if matches!( + expr, + Expr::Cast(Cast{ + expr, + data_type: DataType::Interval(_) + }) if matches!(&**expr, Expr::Literal(ScalarValue::Utf8(_))) + ) { + // Stop checking the sub `expr`, + // which is a `Utf8` type and has already been tested above. + return Ok(TreeNodeRecursion::Stop); + } + if !matches!( expr, Expr::Literal(ScalarValue::IntervalDayTime(_)) | Expr::Literal(ScalarValue::IntervalMonthDayNano(_)) | Expr::Literal(ScalarValue::IntervalYearMonth(_)) | Expr::BinaryExpr(_) + | Expr::Cast(Cast { + data_type: DataType::Interval(_), + .. + }) ) { all_interval = false; Ok(TreeNodeRecursion::Stop) @@ -561,6 +578,7 @@ fn interval_only_in_expr(expr: &Expr) -> bool { Ok(TreeNodeRecursion::Continue) } }); + all_interval } @@ -569,6 +587,7 @@ mod test { use std::error::Error; + use arrow::datatypes::IntervalUnit; use catalog::memory::MemoryCatalogManager; use catalog::RegisterTableRequest; use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME}; @@ -844,6 +863,15 @@ mod test { parse_duration_expr(&args, 0).unwrap(), parse_duration("1y4w").unwrap() ); + // test cast expression + let args = vec![Expr::Cast(Cast { + expr: Box::new(Expr::Literal(ScalarValue::Utf8(Some("15 minutes".into())))), + data_type: DataType::Interval(IntervalUnit::MonthDayNano), + })]; + assert_eq!( + parse_duration_expr(&args, 0).unwrap(), + parse_duration("15m").unwrap() + ); // test index err assert!(parse_duration_expr(&args, 10).is_err()); // test evaluate expr @@ -958,5 +986,37 @@ mod test { )))), }); assert!(interval_only_in_expr(&expr)); + + let expr = Expr::BinaryExpr(BinaryExpr { + left: Box::new(Expr::Cast(Cast { + expr: Box::new(Expr::Literal(ScalarValue::Utf8(Some( + "15 minute".to_string(), + )))), + data_type: DataType::Interval(IntervalUnit::MonthDayNano), + })), + op: Operator::Minus, + right: Box::new(Expr::Literal(ScalarValue::IntervalDayTime(Some( + IntervalDayTime::new(10, 0).into(), + )))), + }); + assert!(interval_only_in_expr(&expr)); + + let expr = Expr::Cast(Cast { + expr: Box::new(Expr::BinaryExpr(BinaryExpr { + left: Box::new(Expr::Cast(Cast { + expr: Box::new(Expr::Literal(ScalarValue::Utf8(Some( + "15 minute".to_string(), + )))), + data_type: DataType::Interval(IntervalUnit::MonthDayNano), + })), + op: Operator::Minus, + right: Box::new(Expr::Literal(ScalarValue::IntervalDayTime(Some( + IntervalDayTime::new(10, 0).into(), + )))), + })), + data_type: DataType::Interval(IntervalUnit::MonthDayNano), + }); + + assert!(interval_only_in_expr(&expr)); } } diff --git a/tests/cases/standalone/common/function/vector/vector.result b/tests/cases/standalone/common/function/vector/vector.result index 996138dba2db..57a37d638d08 100644 --- a/tests/cases/standalone/common/function/vector/vector.result +++ b/tests/cases/standalone/common/function/vector/vector.result @@ -342,7 +342,7 @@ FROM ( UNION ALL SELECT '[8.0]' AS v ) -WHERE vec_kth_elem(v, 0) > 2.0; +WHERE vec_kth_elem(v, 0) > 2.0 ORDER BY first_elem; +----------------------+------------+ | v | first_elem | diff --git a/tests/cases/standalone/common/function/vector/vector.sql b/tests/cases/standalone/common/function/vector/vector.sql index 05f7b6ee17ca..c441fc14807c 100644 --- a/tests/cases/standalone/common/function/vector/vector.sql +++ b/tests/cases/standalone/common/function/vector/vector.sql @@ -110,7 +110,7 @@ FROM ( UNION ALL SELECT '[8.0]' AS v ) -WHERE vec_kth_elem(v, 0) > 2.0; +WHERE vec_kth_elem(v, 0) > 2.0 ORDER BY first_elem; SELECT vec_to_string(vec_subvector('[1.0,2.0,3.0,4.0,5.0]', 0, 3)); diff --git a/tests/cases/standalone/common/range/interval.result b/tests/cases/standalone/common/range/interval.result index 6dbb7dd720f2..20d61aeb178f 100644 --- a/tests/cases/standalone/common/range/interval.result +++ b/tests/cases/standalone/common/range/interval.result @@ -33,6 +33,63 @@ SELECT ts, host, min(val) RANGE (INTERVAL '1' day) FROM host ALIGN (INTERVAL '1' | 1971-01-02T00:00:00 | host2 | 6 | +---------------------+-------+---------------------------------------------------------------------------------------------------------+ +SELECT ts, host, min(val) RANGE ('1 day'::INTERVAL) FROM host ALIGN ('1 day'::INTERVAL) ORDER BY host, ts; + ++---------------------+-------+-----------------------------------+ +| ts | host | min(host.val) RANGE Utf8("1 day") | ++---------------------+-------+-----------------------------------+ +| 1970-01-01T00:00:00 | host1 | 0 | +| 1971-01-02T00:00:00 | host1 | 2 | +| 1970-01-01T00:00:00 | host2 | 4 | +| 1971-01-02T00:00:00 | host2 | 6 | ++---------------------+-------+-----------------------------------+ + +SELECT ts, host, min(val) RANGE ('1 hour'::INTERVAL) FROM host ALIGN ('1 hour'::INTERVAL) ORDER BY host, ts; + ++---------------------+-------+------------------------------------+ +| ts | host | min(host.val) RANGE Utf8("1 hour") | ++---------------------+-------+------------------------------------+ +| 1970-01-01T01:00:00 | host1 | 0 | +| 1970-01-01T02:00:00 | host1 | 1 | +| 1971-01-02T03:00:00 | host1 | 2 | +| 1971-01-02T04:00:00 | host1 | 3 | +| 1970-01-01T01:00:00 | host2 | 4 | +| 1970-01-01T02:00:00 | host2 | 5 | +| 1971-01-02T03:00:00 | host2 | 6 | +| 1971-01-02T04:00:00 | host2 | 7 | ++---------------------+-------+------------------------------------+ + +SELECT ts, host, min(val) RANGE ('30 minute'::INTERVAL + '30 minute'::INTERVAL) FROM host ALIGN ('30 minute'::INTERVAL + '30 minute'::INTERVAL) ORDER BY host, ts; + ++---------------------+-------+-----------------------------------------------------------+ +| ts | host | min(host.val) RANGE Utf8("30 minute") + Utf8("30 minute") | ++---------------------+-------+-----------------------------------------------------------+ +| 1970-01-01T01:00:00 | host1 | 0 | +| 1970-01-01T02:00:00 | host1 | 1 | +| 1971-01-02T03:00:00 | host1 | 2 | +| 1971-01-02T04:00:00 | host1 | 3 | +| 1970-01-01T01:00:00 | host2 | 4 | +| 1970-01-01T02:00:00 | host2 | 5 | +| 1971-01-02T03:00:00 | host2 | 6 | +| 1971-01-02T04:00:00 | host2 | 7 | ++---------------------+-------+-----------------------------------------------------------+ + +--- Test nested cast, even though it is meaningless ---- +SELECT ts, host, min(val) RANGE ((INTERVAL '1' hour)::INTERVAL) FROM host ALIGN ('1 hour'::INTERVAL::INTERVAL) ORDER BY host, ts; + ++---------------------+-------+---------------------------------------------------------------------------------------------------------------------+ +| ts | host | min(host.val) RANGE IntervalMonthDayNano("IntervalMonthDayNano { months: 0, days: 0, nanoseconds: 3600000000000 }") | ++---------------------+-------+---------------------------------------------------------------------------------------------------------------------+ +| 1970-01-01T01:00:00 | host1 | 0 | +| 1970-01-01T02:00:00 | host1 | 1 | +| 1971-01-02T03:00:00 | host1 | 2 | +| 1971-01-02T04:00:00 | host1 | 3 | +| 1970-01-01T01:00:00 | host2 | 4 | +| 1970-01-01T02:00:00 | host2 | 5 | +| 1971-01-02T03:00:00 | host2 | 6 | +| 1971-01-02T04:00:00 | host2 | 7 | ++---------------------+-------+---------------------------------------------------------------------------------------------------------------------+ + DROP TABLE host; Affected Rows: 0 diff --git a/tests/cases/standalone/common/range/interval.sql b/tests/cases/standalone/common/range/interval.sql index 3c5bae3ed103..0acf3e48922e 100644 --- a/tests/cases/standalone/common/range/interval.sql +++ b/tests/cases/standalone/common/range/interval.sql @@ -18,4 +18,13 @@ SELECT ts, host, min(val) RANGE (INTERVAL '1 year') FROM host ALIGN (INTERVAL '1 SELECT ts, host, min(val) RANGE (INTERVAL '1' day) FROM host ALIGN (INTERVAL '1' day) ORDER BY host, ts; +SELECT ts, host, min(val) RANGE ('1 day'::INTERVAL) FROM host ALIGN ('1 day'::INTERVAL) ORDER BY host, ts; + +SELECT ts, host, min(val) RANGE ('1 hour'::INTERVAL) FROM host ALIGN ('1 hour'::INTERVAL) ORDER BY host, ts; + +SELECT ts, host, min(val) RANGE ('30 minute'::INTERVAL + '30 minute'::INTERVAL) FROM host ALIGN ('30 minute'::INTERVAL + '30 minute'::INTERVAL) ORDER BY host, ts; + +--- Test nested cast, even though it is meaningless ---- +SELECT ts, host, min(val) RANGE ((INTERVAL '1' hour)::INTERVAL) FROM host ALIGN ('1 hour'::INTERVAL::INTERVAL) ORDER BY host, ts; + DROP TABLE host;