Skip to content

Commit

Permalink
feat: Implement float*interval and interval/float
Browse files Browse the repository at this point in the history
  • Loading branch information
mcheshkov committed Jul 22, 2024
1 parent aae150a commit bba28d6
Show file tree
Hide file tree
Showing 3 changed files with 399 additions and 2 deletions.
158 changes: 158 additions & 0 deletions datafusion/core/tests/sql/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,164 @@ async fn test_interval_expressions() -> Result<()> {
Ok(())
}

#[tokio::test]
async fn test_interval_mul_div_float() -> Result<()> {
macro_rules! test_mul {
($L:expr, $R:expr, $EXPECTED:expr) => {
test_expression!(format!("({}) * ({})", $L, $R), $EXPECTED);
test_expression!(format!("({}) * ({})", $R, $L), $EXPECTED);
};
}

test_mul!(
"0.5",
"interval '1 month'",
"0 years 0 mons 15 days 0 hours 0 mins 0.000000000 secs"
);
test_mul!(
"1.0",
"interval '1 month'",
"0 years 1 mons 0 days 0 hours 0 mins 0.000000000 secs"
);
test_mul!(
"1.5",
"interval '1 month'",
"0 years 1 mons 15 days 0 hours 0 mins 0.000000000 secs"
);
test_mul!(
"2.0",
"interval '1 month'",
"0 years 2 mons 0 days 0 hours 0 mins 0.000000000 secs"
);
test_mul!(
"-1.5",
"interval '1 month'",
"0 years -1 mons -15 days 0 hours 0 mins 0.000000000 secs"
);
test_expression!(
"1.5 * (interval '1 month') / 1.5",
"0 years 0 mons 30 days 0 hours 0 mins 0.000000000 secs"
);

test_mul!(
"0.5",
"interval '1 day'",
"0 years 0 mons 0 days 12 hours 0 mins 0.000000000 secs"
);
test_mul!(
"1.0",
"interval '1 day'",
"0 years 0 mons 1 days 0 hours 0 mins 0.000000000 secs"
);
test_mul!(
"1.5",
"interval '1 day'",
"0 years 0 mons 1 days 12 hours 0 mins 0.000000000 secs"
);
test_mul!(
"2.0",
"interval '1 day'",
"0 years 0 mons 2 days 0 hours 0 mins 0.000000000 secs"
);
test_mul!(
"-1.5",
"interval '1 day'",
"0 years 0 mons -1 days -12 hours 0 mins 0.000000000 secs"
);
test_expression!(
"1.5 * (interval '1 day') / 1.5",
"0 years 0 mons 0 days 24 hours 0 mins 0.000000000 secs"
);

test_mul!(
"0.5",
"interval '1 second'",
"0 years 0 mons 0 days 0 hours 0 mins 0.500000000 secs"
);
test_mul!(
"1.0",
"interval '1 second'",
"0 years 0 mons 0 days 0 hours 0 mins 1.000000000 secs"
);
test_mul!(
"1.5",
"interval '1 second'",
"0 years 0 mons 0 days 0 hours 0 mins 1.500000000 secs"
);
test_mul!(
"2.0",
"interval '1 second'",
"0 years 0 mons 0 days 0 hours 0 mins 2.000000000 secs"
);
// This test prints as -1.-500000000 secs, that looks like a bug in printing
// TODO fix it
// test_mul!(
// "-1.5",
// "interval '1 second'",
// "0 years 0 mons 0 days 0 hours 0 mins -1.500000000 secs"
// );
test_expression!(
"1.5 * (interval '1 second') / 1.5",
"0 years 0 mons 0 days 0 hours 0 mins 1.000000000 secs"
);

// Carry-over cases
test_mul!(
"1.5",
"interval '1 month 1 day'",
"0 years 1 mons 16 days 12 hours 0 mins 0.000000000 secs"
);
test_mul!(
"1.5",
"interval '1 month 1 second'",
"0 years 1 mons 15 days 0 hours 0 mins 1.500000000 secs"
);
test_mul!(
"1.5",
"interval '1 day 1 second'",
"0 years 0 mons 1 days 12 hours 0 mins 1.500000000 secs"
);

Ok(())
}

#[tokio::test]
async fn test_interval_mul_bad_float() -> Result<()> {
macro_rules! test_expr_error {
($SQL:expr, $EXPECTED:expr) => {
let ctx = SessionContext::new();
let sql = format!("SELECT {}", $SQL);
let actual_err = plan_and_collect(&ctx, sql.as_str()).await.unwrap_err();
assert_eq!(actual_err.to_string(), $EXPECTED);
};
}
macro_rules! test_mul_error {
($L:expr, $R:expr, $EXPECTED:expr) => {
test_expr_error!(format!("({}) * ({})", $L, $R), $EXPECTED);
test_expr_error!(format!("({}) * ({})", $R, $L), $EXPECTED);
};
}

// This behaviour was checked on PostgreSQL 15.7
test_mul_error!(
"cast('NaN' as double precision)",
"interval '1 month'",
"Execution error: interval out of range (float)"
);
test_mul_error!(
"cast('inf' as double precision)",
"interval '1 month'",
"Execution error: interval out of range (float)"
);
test_mul_error!(
"cast('-inf' as double precision)",
"interval '1 month'",
"Execution error: interval out of range (float)"
);

Ok(())
}

#[cfg(feature = "unicode_expressions")]
#[tokio::test]
async fn test_substring_expr() -> Result<()> {
Expand Down
14 changes: 14 additions & 0 deletions datafusion/expr/src/binary_rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,20 @@ pub fn distinct_coercion(
| (Interval(unit), UInt8)
| (Null, Interval(unit))
| (Interval(unit), Null) => Some(Interval(unit.clone())),
// float*interval result is always represented as MonthDayNano, to avoid precision loss
(Float64, Interval(_))
| (Interval(_), Float64)
| (Float32, Interval(_))
| (Interval(_), Float32)
| (Float16, Interval(_))
| (Interval(_), Float16) => Some(Interval(MonthDayNano)),
_ => None,
},
Operator::Divide => match (lhs_type, rhs_type) {
// interval/float result is represented as MonthDayNano, to avoid precision loss
(Interval(_), Float64) | (Interval(_), Float32) | (Interval(_), Float16) => {
Some(Interval(MonthDayNano))
}
_ => None,
},
_ => None,
Expand Down
Loading

0 comments on commit bba28d6

Please sign in to comment.