You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/04/11 05:13:53 UTC

(datafusion) 01/01: fix(sql): return planner error for malformed typed literals (#21454)

This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch gh-readonly-queue/main/pr-21454-540d8ecd5fc5ad07cb7ca5d77df9eb45f5da290e
in repository https://gitbox.apache.org/repos/asf/datafusion.git

commit 374806c3ed7ec6b60b8a9ae9d05009bb9d81cd89
Author: Asish Kumar <87...@users.noreply.github.com>
AuthorDate: Sat Apr 11 10:43:33 2026 +0530

    fix(sql): return planner error for malformed typed literals (#21454)
    
    ## Which issue does this PR close?
    
    - Closes #21431.
    
    ## Rationale for this change
    
    Planning a malformed typed literal such as `time 17542368000000000`
    currently panics in `datafusion-sql` because the planner assumes every
    `TypedString` payload is string-backed and calls
    `into_string().unwrap()`.
    
    Invalid SQL should surface as a normal planner error rather than
    aborting the planning path.
    
    ## What changes are included in this PR?
    
    - Replace the `unwrap()` in the `SQLExpr::TypedString` planning path
    with explicit validation.
    - Return a planner error when the typed literal payload is not
    string-backed.
    - Add a regression test covering the invalid `TIME` typed literal
    reported in the issue.
    
    ## Are these changes tested?
    
    - `cargo test -p datafusion-sql`
    
    ## Are there any user-facing changes?
    
    Users now receive a planner error for malformed typed literals instead
    of a panic.
    
    Signed-off-by: Asish Kumar <of...@gmail.com>
    Co-authored-by: Jonah Gao <jo...@msn.com>
---
 datafusion/sql/src/expr/mod.rs          | 17 +++++++++++++----
 datafusion/sql/tests/sql_integration.rs | 24 ++++++++++++++++++++++--
 2 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/datafusion/sql/src/expr/mod.rs b/datafusion/sql/src/expr/mod.rs
index fefbf134bd..d599373f0a 100644
--- a/datafusion/sql/src/expr/mod.rs
+++ b/datafusion/sql/src/expr/mod.rs
@@ -306,10 +306,19 @@ impl<S: ContextProvider> 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 2c2c7eac8b..574468f4b1 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";


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org