Skip to content

Commit b8d4223

Browse files
author
B Vadlamani
committed
fix_overflow_condition
1 parent a75281c commit b8d4223

2 files changed

Lines changed: 43 additions & 11 deletions

File tree

datafusion/spark/src/function/conversion/cast.rs

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,26 +42,36 @@ fn secs_to_micros(secs: i64) -> i64 {
4242
}
4343

4444
/// Convert float seconds to microseconds
45-
/// Returns None for NaN/Infinity/Overflow in non-ANSI mode, error in ANSI mode
45+
/// Returns None for NaN/Infinity in non-ANSI mode, error in ANSI mode
46+
/// Saturates to i64::MAX/MIN for overflow
4647
#[inline]
4748
fn float_secs_to_micros(val: f64, enable_ansi_mode: bool) -> Result<Option<i64>> {
4849
if val.is_nan() || val.is_infinite() {
4950
if enable_ansi_mode {
50-
return exec_err!(
51-
"Cannot cast {} to TIMESTAMP",
52-
if val.is_nan() { "NaN" } else { "Infinity" }
53-
);
51+
let display_val = if val.is_nan() {
52+
"NaN"
53+
} else if val.is_sign_positive() {
54+
"Infinity"
55+
} else {
56+
"-Infinity"
57+
};
58+
return exec_err!("Cannot cast {} to TIMESTAMP", display_val);
5459
}
5560
return Ok(None);
5661
}
5762
let micros = val * MICROS_PER_SECOND as f64;
58-
if micros >= i64::MIN as f64 && micros <= i64::MAX as f64 {
63+
if micros.floor() <= i64::MAX as f64 && micros.ceil() >= i64::MIN as f64 {
5964
Ok(Some(micros as i64))
6065
} else {
6166
if enable_ansi_mode {
6267
return exec_err!("Overflow casting {} to TIMESTAMP", val);
6368
}
64-
Ok(None)
69+
// Saturate to i64::MAX or i64::MIN like Spark does for overflow
70+
if micros.is_sign_negative() {
71+
Ok(Some(i64::MIN))
72+
} else {
73+
Ok(Some(i64::MAX))
74+
}
6575
}
6676
}
6777

@@ -75,10 +85,11 @@ fn float_secs_to_micros(val: f64, enable_ansi_mode: bool) -> Result<Option<i64>>
7585
/// ```
7686
///
7787
/// # Currently supported conversions
78-
/// - Int8/Int16/Int32/Int64 -> Timestamp (target_type = 'timestamp')
88+
/// - Int8/Int16/Int32/Int64/Float32/Float64 -> Timestamp (target_type = 'timestamp')
7989
///
8090
/// The integer value is interpreted as seconds since the Unix epoch (1970-01-01 00:00:00 UTC)
81-
/// and converted to a timestamp with microsecond precision (matches spark's spec)
91+
/// and converted to a timestamp with microsecond precision (matches spark's spec). Same is the case
92+
/// with Float but with higher precision to support micro / nanoseconds.
8293
///
8394
/// # Overflow behavior
8495
/// Uses saturating multiplication to handle overflow - values that would overflow
@@ -905,7 +916,7 @@ mod tests {
905916

906917
#[test]
907918
fn test_cast_float_overflow_non_ansi_mode() {
908-
// Value too large to fit in i64 microseconds - should return NULL in non-ANSI mode
919+
// Value too large to fit in i64 microseconds - should saturate to i64::MAX like Spark
909920
let cast = SparkCast::new();
910921
let large_value = 1e19; // Way too large for i64 microseconds
911922
let args = make_args_with_ansi_mode(
@@ -914,7 +925,23 @@ mod tests {
914925
false,
915926
);
916927
let result = cast.invoke_with_args(args).unwrap();
917-
assert_scalar_null(result);
928+
// Spark saturates overflow to i64::MAX
929+
assert_scalar_timestamp(result, i64::MAX);
930+
}
931+
932+
#[test]
933+
fn test_cast_float_negative_overflow_non_ansi_mode() {
934+
// Large negative value - should saturate to i64::MIN like Spark
935+
let cast = SparkCast::new();
936+
let large_value = -1e19; // Way too large negative for i64 microseconds
937+
let args = make_args_with_ansi_mode(
938+
ColumnarValue::Scalar(ScalarValue::Float64(Some(large_value))),
939+
"timestamp",
940+
false,
941+
);
942+
let result = cast.invoke_with_args(args).unwrap();
943+
// Spark saturates negative overflow to i64::MIN
944+
assert_scalar_timestamp(result, i64::MIN);
918945
}
919946

920947
#[test]

datafusion/sqllogictest/test_files/spark/conversion/cast_float_to_timestamp.slt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ DROP TABLE float_test;
188188
statement ok
189189
DROP TABLE float_special;
190190

191+
# Note: Overflow saturation tests (1e19, -1e19) are not included here because
192+
# DataFusion's timestamp formatter cannot display i64::MAX/MIN microsecond values.
193+
# The saturation behavior (matching Spark) is verified in unit tests:
194+
# test_cast_float_overflow_non_ansi_mode and test_cast_float_negative_overflow_non_ansi_mode
195+
191196
#############################
192197
# ANSI Mode Tests
193198
#############################

0 commit comments

Comments
 (0)