1use super::{Between, Expr, Like, predicate_bounds};
19use crate::ValueOrLambda;
20use crate::expr::{
21 AggregateFunction, AggregateFunctionParams, Alias, BinaryExpr, Cast, InList,
22 InSubquery, Lambda, Placeholder, ScalarFunction, TryCast, Unnest, WindowFunction,
23 WindowFunctionParams,
24};
25use crate::expr::{FieldMetadata, LambdaVariable};
26use crate::higher_order_function::HigherOrderReturnFieldArgs;
27use crate::type_coercion::functions::value_fields_with_higher_order_udf_and_lambdas;
28use crate::type_coercion::functions::{UDFCoercionExt, fields_with_udf};
29use crate::udf::ReturnFieldArgs;
30use crate::{LogicalPlan, Projection, Subquery, WindowFunctionDefinition, utils};
31use arrow::compute::can_cast_types;
32use arrow::datatypes::FieldRef;
33use arrow::datatypes::{DataType, Field};
34use arrow_schema::extension::{EXTENSION_TYPE_METADATA_KEY, EXTENSION_TYPE_NAME_KEY};
35use datafusion_common::datatype::FieldExt;
36use datafusion_common::{
37 Column, DataFusionError, ExprSchema, Result, ScalarValue, Spans, TableReference,
38 not_impl_err, plan_datafusion_err, plan_err,
39};
40use datafusion_expr_common::type_coercion::binary::BinaryTypeCoercer;
41use datafusion_functions_window_common::field::WindowUDFFieldArgs;
42use std::sync::Arc;
43
44pub trait ExprSchemable {
46 fn get_type(&self, schema: &dyn ExprSchema) -> Result<DataType>;
48
49 fn nullable(&self, input_schema: &dyn ExprSchema) -> Result<bool>;
51
52 fn metadata(&self, schema: &dyn ExprSchema) -> Result<FieldMetadata>;
54
55 fn to_field(
57 &self,
58 input_schema: &dyn ExprSchema,
59 ) -> Result<(Option<TableReference>, Arc<Field>)>;
60
61 fn cast_to(self, cast_to_type: &DataType, schema: &dyn ExprSchema) -> Result<Expr>;
63
64 #[deprecated(
66 since = "51.0.0",
67 note = "Use `to_field().1.is_nullable` and `to_field().1.data_type()` directly instead"
68 )]
69 fn data_type_and_nullable(&self, schema: &dyn ExprSchema)
70 -> Result<(DataType, bool)>;
71}
72
73fn cast_output_field(
88 source_field: &FieldRef,
89 target_field: &FieldRef,
90 force_nullable: bool,
91) -> Arc<Field> {
92 let is_type_only = target_field.name().is_empty()
94 && target_field.is_nullable()
95 && target_field.metadata().is_empty();
96
97 let metadata = if is_type_only {
98 let mut meta = source_field.metadata().clone();
100 meta.remove(EXTENSION_TYPE_NAME_KEY);
101 meta.remove(EXTENSION_TYPE_METADATA_KEY);
102 meta
103 } else {
104 target_field.metadata().clone()
106 };
107
108 let mut f = source_field
109 .as_ref()
110 .clone()
111 .with_data_type(target_field.data_type().clone())
112 .with_metadata(metadata);
113 if force_nullable {
114 f = f.with_nullable(true);
115 }
116 Arc::new(f)
117}
118
119impl ExprSchemable for Expr {
120 #[cfg_attr(feature = "recursive_protection", recursive::recursive)]
161 fn get_type(&self, schema: &dyn ExprSchema) -> Result<DataType> {
162 match self {
163 Expr::Alias(Alias { expr, name, .. }) => match &**expr {
164 Expr::Placeholder(Placeholder { field, .. }) => match &field {
165 None => schema.data_type(&Column::from_name(name)).cloned(),
166 Some(field) => Ok(field.data_type().clone()),
167 },
168 _ => expr.get_type(schema),
169 },
170 Expr::Negative(expr) => expr.get_type(schema),
171 Expr::Column(c) => Ok(schema.data_type(c)?.clone()),
172 Expr::OuterReferenceColumn(field, _) => Ok(field.data_type().clone()),
173 Expr::ScalarVariable(field, _) => Ok(field.data_type().clone()),
174 Expr::Literal(l, _) => Ok(l.data_type()),
175 Expr::Case(case) => {
176 for (_, then_expr) in &case.when_then_expr {
177 let then_type = then_expr.get_type(schema)?;
178 if !then_type.is_null() {
179 return Ok(then_type);
180 }
181 }
182 case.else_expr
183 .as_ref()
184 .map_or(Ok(DataType::Null), |e| e.get_type(schema))
185 }
186 Expr::Cast(Cast { field, .. }) | Expr::TryCast(TryCast { field, .. }) => {
187 Ok(field.data_type().clone())
188 }
189 Expr::Unnest(Unnest { expr, .. }) => {
190 let arg_data_type = expr.get_type(schema)?;
191 match arg_data_type {
193 DataType::List(field)
194 | DataType::LargeList(field)
195 | DataType::FixedSizeList(field, _)
196 | DataType::ListView(field)
197 | DataType::LargeListView(field) => Ok(field.data_type().clone()),
198 DataType::Struct(_) => Ok(arg_data_type),
199 DataType::Null => {
200 not_impl_err!("unnest() does not support null yet")
201 }
202 _ => {
203 plan_err!(
204 "unnest() can only be applied to array, struct and null"
205 )
206 }
207 }
208 }
209 Expr::ScalarFunction(_)
210 | Expr::WindowFunction(_)
211 | Expr::AggregateFunction(_) => {
212 Ok(self.to_field(schema)?.1.data_type().clone())
213 }
214 Expr::Not(_)
215 | Expr::IsNull(_)
216 | Expr::Exists { .. }
217 | Expr::InSubquery(_)
218 | Expr::SetComparison(_)
219 | Expr::Between { .. }
220 | Expr::InList { .. }
221 | Expr::IsNotNull(_)
222 | Expr::IsTrue(_)
223 | Expr::IsFalse(_)
224 | Expr::IsUnknown(_)
225 | Expr::IsNotTrue(_)
226 | Expr::IsNotFalse(_)
227 | Expr::IsNotUnknown(_) => Ok(DataType::Boolean),
228 Expr::ScalarSubquery(subquery) => {
229 Ok(subquery.subquery.schema().field(0).data_type().clone())
230 }
231 Expr::BinaryExpr(BinaryExpr { left, right, op }) => BinaryTypeCoercer::new(
232 &left.get_type(schema)?,
233 op,
234 &right.get_type(schema)?,
235 )
236 .get_result_type(),
237 Expr::Like { .. } | Expr::SimilarTo { .. } => Ok(DataType::Boolean),
238 Expr::Placeholder(Placeholder { field, .. }) => {
239 if let Some(field) = field {
240 Ok(field.data_type().clone())
241 } else {
242 Ok(DataType::Null)
245 }
246 }
247 #[expect(deprecated)]
248 Expr::Wildcard { .. } => Ok(DataType::Null),
249 Expr::GroupingSet(_) => {
250 Ok(DataType::Null)
252 }
253 Expr::HigherOrderFunction(_func) => {
254 Ok(self.to_field(schema)?.1.data_type().clone())
255 }
256 Expr::Lambda(_lambda) => Ok(DataType::Null),
257 Expr::LambdaVariable(LambdaVariable { field, .. }) => match field {
258 Some(f) => Ok(f.data_type().clone()),
259 None => Ok(DataType::Null),
262 },
263 }
264 }
265
266 fn nullable(&self, input_schema: &dyn ExprSchema) -> Result<bool> {
278 match self {
279 Expr::Alias(Alias { expr, .. }) | Expr::Not(expr) | Expr::Negative(expr) => {
280 expr.nullable(input_schema)
281 }
282
283 Expr::InList(InList { expr, list, .. }) => {
284 const MAX_INSPECT_LIMIT: usize = 6;
286 let has_nullable = std::iter::once(expr.as_ref())
288 .chain(list)
289 .take(MAX_INSPECT_LIMIT)
290 .find_map(|e| {
291 e.nullable(input_schema)
292 .map(|nullable| if nullable { Some(()) } else { None })
293 .transpose()
294 })
295 .transpose()?;
296 Ok(match has_nullable {
297 Some(_) => true,
299 None if list.len() + 1 > MAX_INSPECT_LIMIT => true,
301 _ => false,
303 })
304 }
305
306 Expr::Between(Between {
307 expr, low, high, ..
308 }) => Ok(expr.nullable(input_schema)?
309 || low.nullable(input_schema)?
310 || high.nullable(input_schema)?),
311
312 Expr::Column(c) => input_schema.nullable(c),
313 Expr::OuterReferenceColumn(field, _) => Ok(field.is_nullable()),
314 Expr::Literal(value, _) => Ok(value.is_null()),
315 Expr::Case(case) => {
316 let nullable_then = case
317 .when_then_expr
318 .iter()
319 .filter_map(|(w, t)| {
320 let is_nullable = match t.nullable(input_schema) {
321 Err(e) => return Some(Err(e)),
322 Ok(n) => n,
323 };
324
325 if !is_nullable {
328 return None;
329 }
330
331 if case.expr.is_some() {
333 return Some(Ok(()));
334 }
335
336 let bounds = match predicate_bounds::evaluate_bounds(
340 w,
341 Some(unwrap_certainly_null_expr(t)),
342 input_schema,
343 ) {
344 Err(e) => return Some(Err(e)),
345 Ok(b) => b,
346 };
347
348 let can_be_true = match bounds
349 .contains_value(ScalarValue::Boolean(Some(true)))
350 {
351 Err(e) => return Some(Err(e)),
352 Ok(b) => b,
353 };
354
355 if !can_be_true {
356 None
360 } else {
361 Some(Ok(()))
363 }
364 })
365 .next();
366
367 if let Some(nullable_then) = nullable_then {
368 nullable_then.map(|_| true)
372 } else if let Some(e) = &case.else_expr {
373 e.nullable(input_schema)
376 } else {
377 Ok(true)
380 }
381 }
382 Expr::Cast(Cast { expr, .. }) => expr.nullable(input_schema),
383 Expr::ScalarFunction(_)
384 | Expr::AggregateFunction(_)
385 | Expr::WindowFunction(_) => Ok(self.to_field(input_schema)?.1.is_nullable()),
386 Expr::ScalarVariable(field, _) => Ok(field.is_nullable()),
387 Expr::TryCast { .. } | Expr::Unnest(_) | Expr::Placeholder(_) => Ok(true),
388 Expr::IsNull(_)
389 | Expr::IsNotNull(_)
390 | Expr::IsTrue(_)
391 | Expr::IsFalse(_)
392 | Expr::IsUnknown(_)
393 | Expr::IsNotTrue(_)
394 | Expr::IsNotFalse(_)
395 | Expr::IsNotUnknown(_)
396 | Expr::Exists { .. } => Ok(false),
397 Expr::SetComparison(_) => Ok(true),
398 Expr::InSubquery(InSubquery { expr, subquery, .. }) => {
399 let expr_nullable = expr.nullable(input_schema)?;
400 let subquery_nullable = subquery.subquery.schema().fields().first().ok_or_else(|| {
401 plan_datafusion_err!("subquery must return exactly one column of data to compare against")
402 })?.is_nullable();
403
404 Ok(expr_nullable | subquery_nullable)
405 }
406 Expr::ScalarSubquery(subquery) => {
407 Ok(subquery.subquery.schema().field(0).is_nullable())
408 }
409 Expr::BinaryExpr(BinaryExpr { left, right, .. }) => {
410 Ok(left.nullable(input_schema)? || right.nullable(input_schema)?)
411 }
412 Expr::Like(Like { expr, pattern, .. })
413 | Expr::SimilarTo(Like { expr, pattern, .. }) => {
414 Ok(expr.nullable(input_schema)? || pattern.nullable(input_schema)?)
415 }
416 #[expect(deprecated)]
417 Expr::Wildcard { .. } => Ok(false),
418 Expr::GroupingSet(_) => {
419 Ok(true)
422 }
423 Expr::HigherOrderFunction(_func) => {
424 Ok(self.to_field(input_schema)?.1.is_nullable())
425 }
426 Expr::Lambda(_lambda) => Ok(true),
427 Expr::LambdaVariable(LambdaVariable { field, .. }) => match field {
428 Some(f) => Ok(f.is_nullable()),
429 None => Ok(true),
432 },
433 }
434 }
435
436 fn metadata(&self, schema: &dyn ExprSchema) -> Result<FieldMetadata> {
437 self.to_field(schema)
438 .map(|(_, field)| FieldMetadata::from(field.metadata()))
439 }
440
441 fn data_type_and_nullable(
452 &self,
453 schema: &dyn ExprSchema,
454 ) -> Result<(DataType, bool)> {
455 let field = self.to_field(schema)?.1;
456
457 Ok((field.data_type().clone(), field.is_nullable()))
458 }
459
460 fn to_field(
512 &self,
513 schema: &dyn ExprSchema,
514 ) -> Result<(Option<TableReference>, Arc<Field>)> {
515 let (relation, schema_name) = self.qualified_name();
516 #[expect(deprecated)]
517 let field = match self {
518 Expr::Alias(Alias {
519 expr,
520 name: _,
521 metadata,
522 ..
523 }) => {
524 let mut combined_metadata = expr.metadata(schema)?;
525 if let Some(metadata) = metadata {
526 combined_metadata.extend(metadata.clone());
527 }
528
529 Ok(expr
530 .to_field(schema)
531 .map(|(_, f)| f)?
532 .with_field_metadata(&combined_metadata))
533 }
534 Expr::Negative(expr) => expr.to_field(schema).map(|(_, f)| f),
535 Expr::Column(c) => schema.field_from_column(c).map(Arc::clone),
536 Expr::OuterReferenceColumn(field, _) => {
537 Ok(Arc::clone(field).renamed(&schema_name))
538 }
539 Expr::ScalarVariable(field, _) => Ok(Arc::clone(field).renamed(&schema_name)),
540 Expr::Literal(l, metadata) => Ok(Arc::new(
541 Field::new(&schema_name, l.data_type(), l.is_null())
542 .with_field_metadata_opt(metadata.as_ref()),
543 )),
544 Expr::IsNull(_)
545 | Expr::IsNotNull(_)
546 | Expr::IsTrue(_)
547 | Expr::IsFalse(_)
548 | Expr::IsUnknown(_)
549 | Expr::IsNotTrue(_)
550 | Expr::IsNotFalse(_)
551 | Expr::IsNotUnknown(_)
552 | Expr::Exists { .. } => {
553 Ok(Arc::new(Field::new(&schema_name, DataType::Boolean, false)))
554 }
555 Expr::ScalarSubquery(subquery) => {
556 Ok(Arc::clone(&subquery.subquery.schema().fields()[0]))
557 }
558 Expr::BinaryExpr(BinaryExpr { left, right, op }) => {
559 let (left_field, right_field) =
560 (left.to_field(schema)?.1, right.to_field(schema)?.1);
561
562 let (lhs_type, lhs_nullable) =
563 (left_field.data_type(), left_field.is_nullable());
564 let (rhs_type, rhs_nullable) =
565 (right_field.data_type(), right_field.is_nullable());
566 let mut coercer = BinaryTypeCoercer::new(lhs_type, op, rhs_type);
567 coercer.set_lhs_spans(left.spans().cloned().unwrap_or_default());
568 coercer.set_rhs_spans(right.spans().cloned().unwrap_or_default());
569 Ok(Arc::new(Field::new(
570 &schema_name,
571 coercer.get_result_type()?,
572 lhs_nullable || rhs_nullable,
573 )))
574 }
575 Expr::WindowFunction(window_function) => {
576 let WindowFunction {
577 fun,
578 params: WindowFunctionParams { args, .. },
579 ..
580 } = window_function.as_ref();
581
582 let fields = args
583 .iter()
584 .map(|e| e.to_field(schema).map(|(_, f)| f))
585 .collect::<Result<Vec<_>>>()?;
586 match fun {
587 WindowFunctionDefinition::AggregateUDF(udaf) => {
588 let new_fields =
589 verify_function_arguments(udaf.as_ref(), &fields)?;
590 let return_field = udaf.return_field(&new_fields)?;
591 Ok(return_field)
592 }
593 WindowFunctionDefinition::WindowUDF(udwf) => {
594 let new_fields =
595 verify_function_arguments(udwf.as_ref(), &fields)?;
596 let return_field = udwf
597 .field(WindowUDFFieldArgs::new(&new_fields, &schema_name))?;
598 Ok(return_field)
599 }
600 }
601 }
602 Expr::AggregateFunction(AggregateFunction {
603 func,
604 params: AggregateFunctionParams { args, .. },
605 }) => {
606 let fields = args
607 .iter()
608 .map(|e| e.to_field(schema).map(|(_, f)| f))
609 .collect::<Result<Vec<_>>>()?;
610 let new_fields = verify_function_arguments(func.as_ref(), &fields)?;
611 func.return_field(&new_fields)
612 }
613 Expr::ScalarFunction(ScalarFunction { func, args }) => {
614 let fields = args
615 .iter()
616 .map(|e| e.to_field(schema).map(|(_, f)| f))
617 .collect::<Result<Vec<_>>>()?;
618 let new_fields = verify_function_arguments(func.as_ref(), &fields)?;
619
620 let arguments = args
621 .iter()
622 .map(|e| match e {
623 Expr::Literal(sv, _) => Some(sv),
624 _ => None,
625 })
626 .collect::<Vec<_>>();
627 let args = ReturnFieldArgs {
628 arg_fields: &new_fields,
629 scalar_arguments: &arguments,
630 };
631
632 func.return_field_from_args(args)
633 }
634 Expr::Cast(Cast { expr, field }) => expr
636 .to_field(schema)
637 .map(|(_table_ref, src)| cast_output_field(&src, field, false)),
638 Expr::Placeholder(Placeholder {
639 id: _,
640 field: Some(field),
641 }) => Ok(Arc::clone(field).renamed(&schema_name)),
642 Expr::TryCast(TryCast { expr, field }) => expr
643 .to_field(schema)
644 .map(|(_table_ref, src)| cast_output_field(&src, field, true)),
645 Expr::LambdaVariable(LambdaVariable {
646 field: Some(field), ..
647 }) => Ok(Arc::clone(field).renamed(&schema_name)),
648 Expr::Like(_)
649 | Expr::SimilarTo(_)
650 | Expr::Not(_)
651 | Expr::Between(_)
652 | Expr::Case(_)
653 | Expr::InList(_)
654 | Expr::InSubquery(_)
655 | Expr::SetComparison(_)
656 | Expr::Wildcard { .. }
657 | Expr::GroupingSet(_)
658 | Expr::Placeholder(_)
659 | Expr::Unnest(_)
660 | Expr::Lambda(_)
661 | Expr::LambdaVariable(_) => Ok(Arc::new(Field::new(
662 &schema_name,
663 self.get_type(schema)?,
664 self.nullable(schema)?,
665 ))),
666 Expr::HigherOrderFunction(func) => {
667 let arg_fields = func
668 .args
669 .iter()
670 .map(|arg| match arg {
671 Expr::Lambda(Lambda { params: _, body }) => {
672 Ok(ValueOrLambda::Lambda(Arc::new(Field::new(
674 arg.qualified_name().1,
675 body.get_type(schema)?,
676 body.nullable(schema)?,
677 ))))
678 }
679 _ => Ok(ValueOrLambda::Value(arg.to_field(schema)?.1)),
680 })
681 .collect::<Result<Vec<_>>>()?;
682
683 let new_fields = value_fields_with_higher_order_udf_and_lambdas(
684 &arg_fields,
685 func.func.as_ref(),
686 )?;
687
688 let arguments = func
689 .args
690 .iter()
691 .map(|e| match e {
692 Expr::Literal(sv, _) => Some(sv),
693 _ => None,
694 })
695 .collect::<Vec<_>>();
696
697 let args = HigherOrderReturnFieldArgs {
698 arg_fields: &new_fields,
699 scalar_arguments: &arguments,
700 };
701
702 func.func.return_field_from_args(args)
703 }
704 }?;
705
706 Ok((
707 relation,
708 field.renamed(&schema_name),
710 ))
711 }
712
713 fn cast_to(self, cast_to_type: &DataType, schema: &dyn ExprSchema) -> Result<Expr> {
720 let this_type = self.get_type(schema)?;
721 if this_type == *cast_to_type {
722 return Ok(self);
723 }
724
725 let can_cast = match (&this_type, cast_to_type) {
731 (DataType::Struct(_), DataType::Struct(_)) => {
732 true
734 }
735 _ => can_cast_types(&this_type, cast_to_type),
736 };
737
738 if can_cast {
739 match self {
740 Expr::ScalarSubquery(subquery) => {
741 Ok(Expr::ScalarSubquery(cast_subquery(subquery, cast_to_type)?))
742 }
743 _ => Ok(Expr::Cast(Cast::new(Box::new(self), cast_to_type.clone()))),
744 }
745 } else {
746 plan_err!("Cannot automatically convert {this_type} to {cast_to_type}")
747 }
748 }
749}
750
751fn verify_function_arguments<F: UDFCoercionExt>(
754 function: &F,
755 input_fields: &[FieldRef],
756) -> Result<Vec<FieldRef>> {
757 fields_with_udf(input_fields, function).map_err(|err| {
758 let data_types = input_fields
759 .iter()
760 .map(|f| f.data_type())
761 .cloned()
762 .collect::<Vec<_>>();
763 plan_datafusion_err!(
764 "{}. {}",
765 match err {
766 DataFusionError::Plan(msg) => msg,
767 err => err.to_string(),
768 },
769 utils::generate_signature_error_message(
770 function.name(),
771 function.signature(),
772 &data_types
773 )
774 )
775 })
776}
777
778fn unwrap_certainly_null_expr(expr: &Expr) -> &Expr {
780 match expr {
781 Expr::Not(e) => unwrap_certainly_null_expr(e),
782 Expr::Negative(e) => unwrap_certainly_null_expr(e),
783 Expr::Cast(e) => unwrap_certainly_null_expr(e.expr.as_ref()),
784 _ => expr,
785 }
786}
787
788pub fn cast_subquery(subquery: Subquery, cast_to_type: &DataType) -> Result<Subquery> {
796 if subquery.subquery.schema().field(0).data_type() == cast_to_type {
797 return Ok(subquery);
798 }
799
800 let plan = subquery.subquery.as_ref();
801 let new_plan = match plan {
802 LogicalPlan::Projection(projection) => {
803 let cast_expr = projection.expr[0]
804 .clone()
805 .cast_to(cast_to_type, projection.input.schema())?;
806 LogicalPlan::Projection(Projection::try_new(
807 vec![cast_expr],
808 Arc::clone(&projection.input),
809 )?)
810 }
811 _ => {
812 let cast_expr = Expr::Column(Column::from(plan.schema().qualified_field(0)))
813 .cast_to(cast_to_type, subquery.subquery.schema())?;
814 LogicalPlan::Projection(Projection::try_new(
815 vec![cast_expr],
816 subquery.subquery,
817 )?)
818 }
819 };
820 Ok(Subquery {
821 subquery: Arc::new(new_plan),
822 outer_ref_columns: subquery.outer_ref_columns,
823 spans: Spans::new(),
824 })
825}
826
827#[cfg(test)]
828mod tests {
829 use std::collections::HashMap;
830
831 use super::*;
832 use crate::logical_plan::builder::LogicalTableSource;
833 use crate::{
834 LogicalPlanBuilder, and, col, in_subquery, lit, not, or,
835 out_ref_col_with_metadata, when,
836 };
837
838 use arrow::datatypes::Schema;
839 use datafusion_common::{DFSchema, assert_or_internal_err};
840
841 macro_rules! test_is_expr_nullable {
842 ($EXPR_TYPE:ident) => {{
843 let expr = lit(ScalarValue::Null).$EXPR_TYPE();
844 assert!(!expr.nullable(&MockExprSchema::new()).unwrap());
845 }};
846 }
847
848 #[test]
849 fn expr_schema_nullability() {
850 let expr = col("foo").eq(lit(1));
851 assert!(!expr.nullable(&MockExprSchema::new()).unwrap());
852 assert!(
853 expr.nullable(&MockExprSchema::new().with_nullable(true))
854 .unwrap()
855 );
856
857 test_is_expr_nullable!(is_null);
858 test_is_expr_nullable!(is_not_null);
859 test_is_expr_nullable!(is_true);
860 test_is_expr_nullable!(is_not_true);
861 test_is_expr_nullable!(is_false);
862 test_is_expr_nullable!(is_not_false);
863 test_is_expr_nullable!(is_unknown);
864 test_is_expr_nullable!(is_not_unknown);
865 }
866
867 #[test]
868 fn test_between_nullability() {
869 let get_schema = |nullable| {
870 MockExprSchema::new()
871 .with_data_type(DataType::Int32)
872 .with_nullable(nullable)
873 };
874
875 let expr = col("foo").between(lit(1), lit(2));
876 assert!(!expr.nullable(&get_schema(false)).unwrap());
877 assert!(expr.nullable(&get_schema(true)).unwrap());
878
879 let null = lit(ScalarValue::Int32(None));
880
881 let expr = col("foo").between(null.clone(), lit(2));
882 assert!(expr.nullable(&get_schema(false)).unwrap());
883
884 let expr = col("foo").between(lit(1), null.clone());
885 assert!(expr.nullable(&get_schema(false)).unwrap());
886
887 let expr = col("foo").between(null.clone(), null);
888 assert!(expr.nullable(&get_schema(false)).unwrap());
889 }
890
891 fn assert_nullability(expr: &Expr, schema: &dyn ExprSchema, expected: bool) {
892 assert_eq!(
893 expr.nullable(schema).unwrap(),
894 expected,
895 "Nullability of '{expr}' should be {expected}"
896 );
897 }
898
899 fn assert_not_nullable(expr: &Expr, schema: &dyn ExprSchema) {
900 assert_nullability(expr, schema, false);
901 }
902
903 fn assert_nullable(expr: &Expr, schema: &dyn ExprSchema) {
904 assert_nullability(expr, schema, true);
905 }
906
907 #[test]
908 fn test_case_expression_nullability() -> Result<()> {
909 let nullable_schema = MockExprSchema::new()
910 .with_data_type(DataType::Int32)
911 .with_nullable(true);
912
913 let not_nullable_schema = MockExprSchema::new()
914 .with_data_type(DataType::Int32)
915 .with_nullable(false);
916
917 let e = when(col("x").is_not_null(), col("x")).otherwise(lit(0))?;
919 assert_not_nullable(&e, &nullable_schema);
920 assert_not_nullable(&e, ¬_nullable_schema);
921
922 let e = when(not(col("x").is_null()), col("x")).otherwise(lit(0))?;
924 assert_not_nullable(&e, &nullable_schema);
925 assert_not_nullable(&e, ¬_nullable_schema);
926
927 let e = when(col("x").eq(lit(5)), col("x")).otherwise(lit(0))?;
929 assert_not_nullable(&e, &nullable_schema);
930 assert_not_nullable(&e, ¬_nullable_schema);
931
932 let e = when(and(col("x").is_not_null(), col("x").eq(lit(5))), col("x"))
934 .otherwise(lit(0))?;
935 assert_not_nullable(&e, &nullable_schema);
936 assert_not_nullable(&e, ¬_nullable_schema);
937
938 let e = when(and(col("x").eq(lit(5)), col("x").is_not_null()), col("x"))
940 .otherwise(lit(0))?;
941 assert_not_nullable(&e, &nullable_schema);
942 assert_not_nullable(&e, ¬_nullable_schema);
943
944 let e = when(or(col("x").is_not_null(), col("x").eq(lit(5))), col("x"))
946 .otherwise(lit(0))?;
947 assert_not_nullable(&e, &nullable_schema);
948 assert_not_nullable(&e, ¬_nullable_schema);
949
950 let e = when(or(col("x").eq(lit(5)), col("x").is_not_null()), col("x"))
952 .otherwise(lit(0))?;
953 assert_not_nullable(&e, &nullable_schema);
954 assert_not_nullable(&e, ¬_nullable_schema);
955
956 let e = when(
958 or(
959 and(col("x").eq(lit(5)), col("x").is_not_null()),
960 and(col("x").eq(col("bar")), col("x").is_not_null()),
961 ),
962 col("x"),
963 )
964 .otherwise(lit(0))?;
965 assert_not_nullable(&e, &nullable_schema);
966 assert_not_nullable(&e, ¬_nullable_schema);
967
968 let e = when(or(col("x").eq(lit(5)), col("x").is_null()), col("x"))
970 .otherwise(lit(0))?;
971 assert_nullable(&e, &nullable_schema);
972 assert_not_nullable(&e, ¬_nullable_schema);
973
974 let e = when(col("x").is_true(), col("x")).otherwise(lit(0))?;
976 assert_not_nullable(&e, &nullable_schema);
977 assert_not_nullable(&e, ¬_nullable_schema);
978
979 let e = when(col("x").is_not_true(), col("x")).otherwise(lit(0))?;
981 assert_nullable(&e, &nullable_schema);
982 assert_not_nullable(&e, ¬_nullable_schema);
983
984 let e = when(col("x").is_false(), col("x")).otherwise(lit(0))?;
986 assert_not_nullable(&e, &nullable_schema);
987 assert_not_nullable(&e, ¬_nullable_schema);
988
989 let e = when(col("x").is_not_false(), col("x")).otherwise(lit(0))?;
991 assert_nullable(&e, &nullable_schema);
992 assert_not_nullable(&e, ¬_nullable_schema);
993
994 let e = when(col("x").is_unknown(), col("x")).otherwise(lit(0))?;
996 assert_nullable(&e, &nullable_schema);
997 assert_not_nullable(&e, ¬_nullable_schema);
998
999 let e = when(col("x").is_not_unknown(), col("x")).otherwise(lit(0))?;
1001 assert_not_nullable(&e, &nullable_schema);
1002 assert_not_nullable(&e, ¬_nullable_schema);
1003
1004 let e = when(col("x").like(lit("x")), col("x")).otherwise(lit(0))?;
1006 assert_not_nullable(&e, &nullable_schema);
1007 assert_not_nullable(&e, ¬_nullable_schema);
1008
1009 let e = when(lit(0), col("x")).otherwise(lit(0))?;
1011 assert_not_nullable(&e, &nullable_schema);
1012 assert_not_nullable(&e, ¬_nullable_schema);
1013
1014 let e = when(lit(1), col("x")).otherwise(lit(0))?;
1016 assert_nullable(&e, &nullable_schema);
1017 assert_not_nullable(&e, ¬_nullable_schema);
1018
1019 Ok(())
1020 }
1021
1022 #[test]
1023 fn test_inlist_nullability() {
1024 let get_schema = |nullable| {
1025 MockExprSchema::new()
1026 .with_data_type(DataType::Int32)
1027 .with_nullable(nullable)
1028 };
1029
1030 let expr = col("foo").in_list(vec![lit(1); 5], false);
1031 assert!(!expr.nullable(&get_schema(false)).unwrap());
1032 assert!(expr.nullable(&get_schema(true)).unwrap());
1033 assert!(
1035 expr.nullable(&get_schema(false).with_error_on_nullable(true))
1036 .is_err()
1037 );
1038
1039 let null = lit(ScalarValue::Int32(None));
1040 let expr = col("foo").in_list(vec![null, lit(1)], false);
1041 assert!(expr.nullable(&get_schema(false)).unwrap());
1042
1043 let expr = col("foo").in_list(vec![lit(1); 6], false);
1045 assert!(expr.nullable(&get_schema(false)).unwrap());
1046 }
1047
1048 #[test]
1049 fn test_like_nullability() {
1050 let get_schema = |nullable| {
1051 MockExprSchema::new()
1052 .with_data_type(DataType::Utf8)
1053 .with_nullable(nullable)
1054 };
1055
1056 let expr = col("foo").like(lit("bar"));
1057 assert!(!expr.nullable(&get_schema(false)).unwrap());
1058 assert!(expr.nullable(&get_schema(true)).unwrap());
1059
1060 let expr = col("foo").like(lit(ScalarValue::Utf8(None)));
1061 assert!(expr.nullable(&get_schema(false)).unwrap());
1062 }
1063
1064 #[test]
1065 fn expr_schema_data_type() {
1066 let expr = col("foo");
1067 assert_eq!(
1068 DataType::Utf8,
1069 expr.get_type(&MockExprSchema::new().with_data_type(DataType::Utf8))
1070 .unwrap()
1071 );
1072 }
1073
1074 #[test]
1075 fn test_expr_metadata() {
1076 let mut meta = HashMap::new();
1077 meta.insert("bar".to_string(), "buzz".to_string());
1078 let meta = FieldMetadata::from(meta);
1079 let expr = col("foo");
1080 let schema = MockExprSchema::new()
1081 .with_data_type(DataType::Int32)
1082 .with_metadata(meta.clone());
1083
1084 assert_eq!(meta, expr.metadata(&schema).unwrap());
1086 assert_eq!(meta, expr.clone().alias("bar").metadata(&schema).unwrap());
1087 assert_eq!(
1088 meta,
1089 expr.clone()
1090 .cast_to(&DataType::Int64, &schema)
1091 .unwrap()
1092 .metadata(&schema)
1093 .unwrap()
1094 );
1095
1096 let schema = DFSchema::from_unqualified_fields(
1097 vec![meta.add_to_field(Field::new("foo", DataType::Int32, true))].into(),
1098 HashMap::new(),
1099 )
1100 .unwrap();
1101
1102 assert_eq!(meta, expr.metadata(&schema).unwrap());
1104
1105 let outer_ref = out_ref_col_with_metadata(
1107 DataType::Int32,
1108 meta.to_hashmap(),
1109 Column::from_name("foo"),
1110 );
1111 assert_eq!(meta, outer_ref.metadata(&schema).unwrap());
1112 }
1113
1114 #[test]
1115 fn test_alias_metadata_is_preserved_in_field_metadata() {
1116 let schema = MockExprSchema::new().with_data_type(DataType::Int32);
1117 let alias_metadata = FieldMetadata::from(HashMap::from([(
1118 "some_key".to_string(),
1119 "some_value".to_string(),
1120 )]));
1121
1122 let Expr::Alias(alias) = col("foo").alias("alias") else {
1123 unreachable!();
1124 };
1125 let expr = Expr::Alias(alias.with_metadata(Some(alias_metadata.clone())));
1126
1127 let field = expr.to_field(&schema).unwrap().1;
1128 assert_eq!(
1129 field.metadata().get("some_key"),
1130 Some(&"some_value".to_string())
1131 );
1132 assert_eq!(expr.metadata(&schema).unwrap(), alias_metadata);
1133 }
1134
1135 #[test]
1136 fn test_expr_placeholder() {
1137 let schema = MockExprSchema::new();
1138
1139 let mut placeholder_meta = HashMap::new();
1140 placeholder_meta.insert("bar".to_string(), "buzz".to_string());
1141 let placeholder_meta = FieldMetadata::from(placeholder_meta);
1142
1143 let expr = Expr::Placeholder(Placeholder::new_with_field(
1144 "".to_string(),
1145 Some(
1146 Field::new("", DataType::Utf8, true)
1147 .with_metadata(placeholder_meta.to_hashmap())
1148 .into(),
1149 ),
1150 ));
1151
1152 let field = expr.to_field(&schema).unwrap().1;
1153 assert_eq!(
1154 (field.data_type(), field.is_nullable()),
1155 (&DataType::Utf8, true)
1156 );
1157 assert_eq!(placeholder_meta, expr.metadata(&schema).unwrap());
1158
1159 let expr_alias = expr.alias("a placeholder by any other name");
1160 let expr_alias_field = expr_alias.to_field(&schema).unwrap().1;
1161 assert_eq!(
1162 (expr_alias_field.data_type(), expr_alias_field.is_nullable()),
1163 (&DataType::Utf8, true)
1164 );
1165 assert_eq!(placeholder_meta, expr_alias.metadata(&schema).unwrap());
1166
1167 let expr = Expr::Placeholder(Placeholder::new_with_field(
1169 "".to_string(),
1170 Some(Field::new("", DataType::Utf8, false).into()),
1171 ));
1172 let expr_field = expr.to_field(&schema).unwrap().1;
1173 assert_eq!(
1174 (expr_field.data_type(), expr_field.is_nullable()),
1175 (&DataType::Utf8, false)
1176 );
1177
1178 let expr_alias = expr.alias("a placeholder by any other name");
1179 let expr_alias_field = expr_alias.to_field(&schema).unwrap().1;
1180 assert_eq!(
1181 (expr_alias_field.data_type(), expr_alias_field.is_nullable()),
1182 (&DataType::Utf8, false)
1183 );
1184 }
1185
1186 #[derive(Debug)]
1187 struct MockExprSchema {
1188 field: FieldRef,
1189 error_on_nullable: bool,
1190 }
1191
1192 impl MockExprSchema {
1193 fn new() -> Self {
1194 Self {
1195 field: Arc::new(Field::new("mock_field", DataType::Null, false)),
1196 error_on_nullable: false,
1197 }
1198 }
1199
1200 fn with_nullable(mut self, nullable: bool) -> Self {
1201 Arc::make_mut(&mut self.field).set_nullable(nullable);
1202 self
1203 }
1204
1205 fn with_data_type(mut self, data_type: DataType) -> Self {
1206 Arc::make_mut(&mut self.field).set_data_type(data_type);
1207 self
1208 }
1209
1210 fn with_error_on_nullable(mut self, error_on_nullable: bool) -> Self {
1211 self.error_on_nullable = error_on_nullable;
1212 self
1213 }
1214
1215 fn with_metadata(mut self, metadata: FieldMetadata) -> Self {
1216 self.field =
1217 Arc::new(metadata.add_to_field(Arc::unwrap_or_clone(self.field)));
1218 self
1219 }
1220 }
1221
1222 impl ExprSchema for MockExprSchema {
1223 fn nullable(&self, _col: &Column) -> Result<bool> {
1224 assert_or_internal_err!(!self.error_on_nullable, "nullable error");
1225 Ok(self.field.is_nullable())
1226 }
1227
1228 fn field_from_column(&self, _col: &Column) -> Result<&FieldRef> {
1229 Ok(&self.field)
1230 }
1231 }
1232
1233 fn scan_t(a_nullable: bool) -> LogicalPlanBuilder {
1235 let schema = Schema::new(vec![Field::new("a", DataType::Int32, a_nullable)]);
1236 let source = Arc::new(LogicalTableSource::new(Arc::new(schema)));
1237 LogicalPlanBuilder::scan("t", source, None).unwrap()
1238 }
1239
1240 #[test]
1241 fn in_subquery_nullability() {
1242 let cases = [
1246 (false, false, false),
1247 (false, true, true),
1248 (true, false, true),
1249 (true, true, true),
1250 ];
1251
1252 for (x_nullable, a_nullable, expected) in cases {
1253 let subquery = scan_t(a_nullable)
1254 .project(vec![col("a")])
1255 .unwrap()
1256 .build()
1257 .unwrap();
1258 let expr = in_subquery(col("x"), Arc::new(subquery));
1259 let schema = MockExprSchema::new().with_nullable(x_nullable);
1260
1261 assert_eq!(expr.nullable(&schema).unwrap(), expected);
1262 }
1263 }
1264
1265 #[test]
1266 fn in_subquery_nullability_uses_subquery_output_schema() {
1267 let subquery = scan_t(true)
1270 .project(vec![col("a")])
1271 .unwrap()
1272 .distinct()
1273 .unwrap()
1274 .build()
1275 .unwrap();
1276 let expr = in_subquery(col("x"), Arc::new(subquery));
1277 assert!(expr.nullable(&MockExprSchema::new()).unwrap());
1278
1279 let subquery = scan_t(false)
1283 .project(vec![col("a") + lit(1)])
1284 .unwrap()
1285 .build()
1286 .unwrap();
1287 let expr = in_subquery(col("x"), Arc::new(subquery));
1288 assert!(!expr.nullable(&MockExprSchema::new()).unwrap());
1289 }
1290
1291 #[test]
1292 fn in_subquery_nullability_errors_for_no_subquery_columns() {
1293 let subquery = LogicalPlanBuilder::empty(false).build().unwrap();
1294 let expr = in_subquery(col("x"), Arc::new(subquery));
1295
1296 let err = expr.nullable(&MockExprSchema::new()).unwrap_err();
1297 assert_eq!(
1298 err.strip_backtrace(),
1299 "Error during planning: subquery must return exactly one column of data to compare against"
1300 );
1301 }
1302
1303 #[test]
1304 fn test_scalar_variable() {
1305 let mut meta = HashMap::new();
1306 meta.insert("bar".to_string(), "buzz".to_string());
1307 let meta = FieldMetadata::from(meta);
1308
1309 let field = Field::new("foo", DataType::Int32, true);
1310 let field = meta.add_to_field(field);
1311 let field = Arc::new(field);
1312
1313 let expr = Expr::ScalarVariable(field, vec!["foo".to_string()]);
1314
1315 let schema = MockExprSchema::new();
1316
1317 assert_eq!(meta, expr.metadata(&schema).unwrap());
1318 }
1319
1320 #[test]
1321 fn test_cast_and_try_cast_extension_type_metadata() {
1322 use crate::expr::{Cast, TryCast};
1323 use arrow_schema::extension::{
1324 EXTENSION_TYPE_METADATA_KEY, EXTENSION_TYPE_NAME_KEY,
1325 };
1326
1327 fn make_cast_expr(
1329 expr: Expr,
1330 target_field: FieldRef,
1331 use_try_cast: bool,
1332 ) -> Expr {
1333 if use_try_cast {
1334 Expr::TryCast(TryCast {
1335 expr: Box::new(expr),
1336 field: target_field,
1337 })
1338 } else {
1339 Expr::Cast(Cast {
1340 expr: Box::new(expr),
1341 field: target_field,
1342 })
1343 }
1344 }
1345
1346 for use_try_cast in [false, true] {
1348 let cast_name = if use_try_cast { "TryCast" } else { "Cast" };
1349
1350 let mut source_meta = HashMap::new();
1352 source_meta.insert(
1353 EXTENSION_TYPE_NAME_KEY.to_string(),
1354 "arrow.uuid".to_string(),
1355 );
1356 source_meta.insert("custom_key".to_string(), "custom_value".to_string());
1357
1358 let source_field = Field::new("foo", DataType::FixedSizeBinary(16), false)
1359 .with_metadata(source_meta);
1360
1361 let schema = MockExprSchema::new()
1362 .with_data_type(DataType::FixedSizeBinary(16))
1363 .with_metadata(FieldMetadata::from(source_field.metadata().clone()));
1364
1365 let cast_expr = make_cast_expr(
1368 col("foo"),
1369 Arc::new(Field::new("", DataType::Utf8, true)),
1370 use_try_cast,
1371 );
1372
1373 let (_, result_field) = cast_expr.to_field(&schema).unwrap();
1374 assert!(
1375 result_field
1376 .metadata()
1377 .get(EXTENSION_TYPE_NAME_KEY)
1378 .is_none(),
1379 "{cast_name}: Extension type name should be stripped when target has no extension metadata"
1380 );
1381 assert_eq!(
1382 result_field.metadata().get("custom_key"),
1383 Some(&"custom_value".to_string()),
1384 "{cast_name}: Non-extension metadata should be preserved"
1385 );
1386 if use_try_cast {
1387 assert!(
1388 result_field.is_nullable(),
1389 "TryCast result should be nullable"
1390 );
1391 }
1392
1393 let mut target_meta = HashMap::new();
1395 target_meta.insert(
1396 EXTENSION_TYPE_NAME_KEY.to_string(),
1397 "arrow.json".to_string(),
1398 );
1399 target_meta.insert(EXTENSION_TYPE_METADATA_KEY.to_string(), "{}".to_string());
1400
1401 let target_field =
1402 Field::new("", DataType::Utf8, true).with_metadata(target_meta);
1403
1404 let cast_expr =
1405 make_cast_expr(col("foo"), Arc::new(target_field), use_try_cast);
1406
1407 let (_, result_field) = cast_expr.to_field(&schema).unwrap();
1408 assert_eq!(
1409 result_field.metadata().get(EXTENSION_TYPE_NAME_KEY),
1410 Some(&"arrow.json".to_string()),
1411 "{cast_name}: Extension type name should come from target field"
1412 );
1413 assert_eq!(
1414 result_field.metadata().get(EXTENSION_TYPE_METADATA_KEY),
1415 Some(&"{}".to_string()),
1416 "{cast_name}: Extension type metadata should come from target field"
1417 );
1418 assert!(
1419 result_field.metadata().get("custom_key").is_none(),
1420 "{cast_name}: Source metadata should NOT propagate when target has explicit metadata"
1421 );
1422 if use_try_cast {
1423 assert!(
1424 result_field.is_nullable(),
1425 "TryCast result should be nullable"
1426 );
1427 }
1428 }
1429 }
1430}