diff --git a/datafusion/sql/src/expr/value.rs b/datafusion/sql/src/expr/value.rs index e1a6965fca590..25c912eddd4fc 100644 --- a/datafusion/sql/src/expr/value.rs +++ b/datafusion/sql/src/expr/value.rs @@ -108,17 +108,21 @@ impl SqlToRel<'_, S> { } if self.options.parse_float_as_decimal { - parse_decimal( + let decimal = parse_decimal( unsigned_number, negative, self.options.trim_decimal_literal_trailing_zeros, - ) + ); + match decimal { + Err(DataFusionError::NotImplemented(_)) + if unsigned_number.contains(['.', 'e', 'E']) => + { + parse_float(&signed_number) + } + result => result, + } } else { - signed_number.parse::().map(lit).map_err(|_| { - DataFusionError::from(ParserError(format!( - "Cannot parse {signed_number} as f64" - ))) - }) + parse_float(&signed_number) } } @@ -379,6 +383,12 @@ fn bigint_to_i256(v: &BigInt) -> Option { } } +fn parse_float(signed_number: &str) -> Result { + signed_number.parse::().map(lit).map_err(|_| { + DataFusionError::from(ParserError(format!("Cannot parse {signed_number} as f64"))) + }) +} + fn parse_decimal( unsigned_number: &str, negative: bool, diff --git a/datafusion/sql/tests/sql_integration.rs b/datafusion/sql/tests/sql_integration.rs index 390e8cc017921..a83d070355e0b 100644 --- a/datafusion/sql/tests/sql_integration.rs +++ b/datafusion/sql/tests/sql_integration.rs @@ -26,7 +26,9 @@ use std::vec; use arrow::datatypes::{TimeUnit::Nanosecond, *}; use common::MockContextProvider; -use datafusion_common::{DFSchema, DataFusionError, Result, assert_contains}; +use datafusion_common::{ + DFSchema, DataFusionError, Result, ScalarValue, assert_contains, +}; use datafusion_expr::{ ColumnarValue, CreateIndex, DdlStatement, Expr, HigherOrderFunctionArgs, HigherOrderReturnFieldArgs, HigherOrderSignature, HigherOrderUDF, HigherOrderUDFImpl, @@ -194,6 +196,36 @@ fn parse_decimals_9() { ); } +#[test] +fn parse_decimal_out_of_range_float_falls_back_to_float64() { + let sql = "SELECT 1.7976931348623157e+308, 2.2250738585072014e-308"; + let options = parse_decimals_parser_options(); + let plan = logical_plan_with_options(sql, options).unwrap(); + let LogicalPlan::Projection(projection) = plan else { + panic!("expected a projection") + }; + let values = projection + .expr + .iter() + .map(|expr| match expr { + Expr::Literal(ScalarValue::Float64(Some(value)), _) => value.to_bits(), + _ => panic!("expected Float64 literals, got {expr}"), + }) + .collect::>(); + assert_eq!(values, [f64::MAX.to_bits(), f64::MIN_POSITIVE.to_bits()]); +} + +#[test] +fn parse_decimal_out_of_range_integer_remains_an_error() { + let sql = format!("SELECT {}", "1".repeat(77)); + let options = parse_decimals_parser_options(); + let error = logical_plan_with_options(&sql, options).unwrap_err(); + assert_contains!( + error.to_string(), + "Decimal precision 77 exceeds the maximum supported precision: 76" + ); +} + #[test] fn parse_decimals_trim_insignificant_trailing_zeros() { let sql = "SELECT 10.00, 10.10, 0.00100, 100.0001, 1.2300e2";