diff --git a/datafusion/sql/src/expr/mod.rs b/datafusion/sql/src/expr/mod.rs index fefbf134bd11c..d599373f0a313 100644 --- a/datafusion/sql/src/expr/mod.rs +++ b/datafusion/sql/src/expr/mod.rs @@ -306,10 +306,19 @@ impl SqlToRel<'_, S> { data_type, value, uses_odbc_syntax: _, - }) => Ok(Expr::Cast(Cast::new_from_field( - Box::new(lit(value.into_string().unwrap())), - self.convert_data_type_to_field(&data_type)?, - ))), + }) => { + let value = match value.into_string() { + Some(value) => value, + None => { + return plan_err!("Typed literal requires a string payload"); + } + }; + + Ok(Expr::Cast(Cast::new_from_field( + Box::new(lit(value)), + self.convert_data_type_to_field(&data_type)?, + ))) + } SQLExpr::IsNull(expr) => Ok(Expr::IsNull(Box::new( self.sql_expr_to_logical_expr(*expr, schema, planner_context)?, diff --git a/datafusion/sql/tests/sql_integration.rs b/datafusion/sql/tests/sql_integration.rs index 2c2c7eac8bfc4..574468f4b111f 100644 --- a/datafusion/sql/tests/sql_integration.rs +++ b/datafusion/sql/tests/sql_integration.rs @@ -26,7 +26,7 @@ use std::vec; use arrow::datatypes::{TimeUnit::Nanosecond, *}; use common::MockContextProvider; -use datafusion_common::{DataFusionError, Result, assert_contains}; +use datafusion_common::{DFSchema, DataFusionError, Result, assert_contains}; use datafusion_expr::{ ColumnarValue, CreateIndex, DdlStatement, ScalarFunctionArgs, ScalarUDF, ScalarUDFImpl, Signature, Volatility, col, logical_plan::LogicalPlan, @@ -35,7 +35,7 @@ use datafusion_expr::{ use datafusion_functions::{string, unicode}; use datafusion_sql::{ parser::DFParser, - planner::{NullOrdering, ParserOptions, SqlToRel}, + planner::{NullOrdering, ParserOptions, PlannerContext, SqlToRel}, }; use crate::common::{CustomExprPlanner, CustomTypePlanner, MockSessionState}; @@ -52,6 +52,7 @@ use datafusion_functions_window::{rank::rank_udwf, row_number::row_number_udwf}; use insta::{allow_duplicates, assert_snapshot}; use rstest::rstest; use sqlparser::dialect::{Dialect, GenericDialect, HiveDialect, MySqlDialect}; +use sqlparser::parser::Parser; mod cases; mod common; @@ -254,6 +255,25 @@ fn within_group_rejected_for_non_ordered_set_udaf() { ); } +#[test] +fn typed_literal_without_string_payload_returns_error() { + let sql_expr = Parser::new(&GenericDialect {}) + .try_with_sql("time 17542368000000000") + .unwrap() + .parse_expr() + .unwrap(); + let context = MockContextProvider { + state: MockSessionState::default(), + }; + let sql_to_rel = SqlToRel::new(&context); + + let err = sql_to_rel + .sql_to_expr(sql_expr, &DFSchema::empty(), &mut PlannerContext::new()) + .expect_err("planning invalid typed literals should return an error"); + + assert_contains!(err.to_string(), "Typed literal requires a string payload"); +} + #[test] fn parse_ident_normalization_5() { let sql = "SELECT AGE FROM PERSON";