diff --git a/datafusion/expr/src/logical_plan/plan.rs b/datafusion/expr/src/logical_plan/plan.rs index 68a3869d6531e..c4c604c462a17 100644 --- a/datafusion/expr/src/logical_plan/plan.rs +++ b/datafusion/expr/src/logical_plan/plan.rs @@ -2054,12 +2054,13 @@ impl LogicalPlan { let (value, metadata) = param_values .get_placeholders_with_values(&id)? .into_inner(); - if field.as_ref().is_some_and(|field| { + let object_field = field.as_ref().filter(|field| { field .metadata() .get("embucket.snowflake_logical_type") .is_some_and(|logical_type| logical_type == "OBJECT") - }) && !value.is_null() + }); + if object_field.is_some() && !value.is_null() && !metadata.as_ref().is_some_and(|metadata| { metadata .inner() @@ -2076,6 +2077,15 @@ impl LogicalPlan { value.data_type() ); } + let value = if value.is_null() { + if let Some(field) = object_field { + ScalarValue::try_new_null(field.data_type())? + } else { + value + } + } else { + value + }; Ok(Transformed::yes(Expr::Literal(value, metadata))) } else { Ok(Transformed::no(e)) diff --git a/datafusion/sql/tests/sql_integration.rs b/datafusion/sql/tests/sql_integration.rs index f7796a14a5d42..9264de5e8413e 100644 --- a/datafusion/sql/tests/sql_integration.rs +++ b/datafusion/sql/tests/sql_integration.rs @@ -1002,8 +1002,18 @@ fn plan_insert_values_validates_bound_object_source() -> Result<()> { .expect_err("bound scalar needs an explicit OBJECT conversion"); assert_contains!(err.to_string(), "to OBJECT; use TO_OBJECT"); } - plan.clone().with_param_values(vec![ScalarValue::Null])?; - plan.with_param_values(vec![ScalarValue::Utf8(None)])?; + for value in [ScalarValue::Null, ScalarValue::Utf8(None)] { + let bound = plan.clone().with_param_values(vec![value])?; + let mut input = &bound; + while !matches!(input, LogicalPlan::Values(_)) { + input = input + .inputs() + .into_iter() + .next() + .expect("INSERT VALUES input"); + } + assert_eq!(input.schema().field(0).data_type(), &DataType::Utf8); + } Ok(()) }