1use std::sync::Arc;
19
20use crate::scalar_subquery::ScalarSubqueryExpr;
21use crate::{HigherOrderFunctionExpr, ScalarFunctionExpr};
22use crate::{
23 PhysicalExpr,
24 expressions::{self, Column, Literal, binary, like, similar_to},
25};
26
27use arrow::datatypes::Schema;
28use datafusion_common::config::ConfigOptions;
29use datafusion_common::datatype::FieldExt;
30use datafusion_common::metadata::{FieldMetadata, format_type_and_metadata};
31use datafusion_common::{
32 DFSchema, Result, ScalarValue, TableReference, ToDFSchema, exec_err,
33 internal_datafusion_err, not_impl_err, plan_datafusion_err, plan_err,
34};
35use datafusion_expr::execution_props::ExecutionProps;
36use datafusion_expr::expr::{
37 Alias, Cast, HigherOrderFunction, InList, Lambda, LambdaVariable, Placeholder,
38 ScalarFunction,
39};
40use datafusion_expr::physical_planning_context::PhysicalPlanningContext;
41use datafusion_expr::var_provider::VarType;
42use datafusion_expr::var_provider::is_system_variables;
43use datafusion_expr::{
44 Between, BinaryExpr, Expr, ExprSchemable, Like, Operator, TryCast, binary_expr, lit,
45};
46
47#[cfg_attr(feature = "recursive_protection", recursive::recursive)]
133pub fn create_physical_expr(
134 e: &Expr,
135 input_dfschema: &DFSchema,
136 execution_props: &ExecutionProps,
137 planning_ctx: &PhysicalPlanningContext,
138) -> Result<Arc<dyn PhysicalExpr>> {
139 let input_schema = input_dfschema.as_arrow();
140
141 match e {
142 Expr::Alias(Alias { expr, metadata, .. }) => {
143 if let Expr::Literal(v, prior_metadata) = expr.as_ref() {
144 let new_metadata = FieldMetadata::merge_options(
145 prior_metadata.as_ref(),
146 metadata.as_ref(),
147 );
148 Ok(Arc::new(Literal::new_with_metadata(
149 v.clone(),
150 new_metadata,
151 )))
152 } else {
153 Ok(create_physical_expr(
154 expr,
155 input_dfschema,
156 execution_props,
157 planning_ctx,
158 )?)
159 }
160 }
161 Expr::Column(c) => {
162 let idx = input_dfschema.index_of_column(c)?;
163 Ok(Arc::new(Column::new(&c.name, idx)))
164 }
165 Expr::Literal(value, metadata) => Ok(Arc::new(Literal::new_with_metadata(
166 value.clone(),
167 metadata.clone(),
168 ))),
169 Expr::ScalarVariable(_, variable_names) => {
170 if is_system_variables(variable_names) {
171 match execution_props.get_var_provider(VarType::System) {
172 Some(provider) => {
173 let scalar_value = provider.get_value(variable_names.clone())?;
174 Ok(Arc::new(Literal::new(scalar_value)))
175 }
176 _ => plan_err!("No system variable provider found"),
177 }
178 } else {
179 match execution_props.get_var_provider(VarType::UserDefined) {
180 Some(provider) => {
181 let scalar_value = provider.get_value(variable_names.clone())?;
182 Ok(Arc::new(Literal::new(scalar_value)))
183 }
184 _ => plan_err!("No user defined variable provider found"),
185 }
186 }
187 }
188 Expr::IsTrue(expr) => {
189 let binary_op = binary_expr(
190 expr.as_ref().clone(),
191 Operator::IsNotDistinctFrom,
192 lit(true),
193 );
194 create_physical_expr(
195 &binary_op,
196 input_dfschema,
197 execution_props,
198 planning_ctx,
199 )
200 }
201 Expr::IsNotTrue(expr) => {
202 let binary_op =
203 binary_expr(expr.as_ref().clone(), Operator::IsDistinctFrom, lit(true));
204 create_physical_expr(
205 &binary_op,
206 input_dfschema,
207 execution_props,
208 planning_ctx,
209 )
210 }
211 Expr::IsFalse(expr) => {
212 let binary_op = binary_expr(
213 expr.as_ref().clone(),
214 Operator::IsNotDistinctFrom,
215 lit(false),
216 );
217 create_physical_expr(
218 &binary_op,
219 input_dfschema,
220 execution_props,
221 planning_ctx,
222 )
223 }
224 Expr::IsNotFalse(expr) => {
225 let binary_op =
226 binary_expr(expr.as_ref().clone(), Operator::IsDistinctFrom, lit(false));
227 create_physical_expr(
228 &binary_op,
229 input_dfschema,
230 execution_props,
231 planning_ctx,
232 )
233 }
234 Expr::IsUnknown(expr) => {
235 let binary_op = binary_expr(
236 expr.as_ref().clone(),
237 Operator::IsNotDistinctFrom,
238 Expr::Literal(ScalarValue::Boolean(None), None),
239 );
240 create_physical_expr(
241 &binary_op,
242 input_dfschema,
243 execution_props,
244 planning_ctx,
245 )
246 }
247 Expr::IsNotUnknown(expr) => {
248 let binary_op = binary_expr(
249 expr.as_ref().clone(),
250 Operator::IsDistinctFrom,
251 Expr::Literal(ScalarValue::Boolean(None), None),
252 );
253 create_physical_expr(
254 &binary_op,
255 input_dfschema,
256 execution_props,
257 planning_ctx,
258 )
259 }
260 Expr::BinaryExpr(BinaryExpr { left, op, right }) => {
261 let lhs = create_physical_expr(
263 left,
264 input_dfschema,
265 execution_props,
266 planning_ctx,
267 )?;
268 let rhs = create_physical_expr(
269 right,
270 input_dfschema,
271 execution_props,
272 planning_ctx,
273 )?;
274 binary(lhs, *op, rhs, input_schema)
282 }
283 Expr::Like(Like {
284 negated,
285 expr,
286 pattern,
287 escape_char,
288 case_insensitive,
289 }) => {
290 if escape_char.unwrap_or('\\') != '\\' {
292 return exec_err!(
293 "LIKE does not support escape_char other than the backslash (\\)"
294 );
295 }
296 let physical_expr = create_physical_expr(
297 expr,
298 input_dfschema,
299 execution_props,
300 planning_ctx,
301 )?;
302 let physical_pattern = create_physical_expr(
303 pattern,
304 input_dfschema,
305 execution_props,
306 planning_ctx,
307 )?;
308 like(
309 *negated,
310 *case_insensitive,
311 physical_expr,
312 physical_pattern,
313 input_schema,
314 )
315 }
316 Expr::SimilarTo(Like {
317 negated,
318 expr,
319 pattern,
320 escape_char,
321 case_insensitive,
322 }) => {
323 if escape_char.is_some() {
324 return exec_err!("SIMILAR TO does not support escape_char yet");
325 }
326 let physical_expr = create_physical_expr(
327 expr,
328 input_dfschema,
329 execution_props,
330 planning_ctx,
331 )?;
332 let physical_pattern = create_physical_expr(
333 pattern,
334 input_dfschema,
335 execution_props,
336 planning_ctx,
337 )?;
338 similar_to(*negated, *case_insensitive, physical_expr, physical_pattern)
339 }
340 Expr::Case(case) => {
341 let expr: Option<Arc<dyn PhysicalExpr>> = if let Some(e) = &case.expr {
342 Some(create_physical_expr(
343 e.as_ref(),
344 input_dfschema,
345 execution_props,
346 planning_ctx,
347 )?)
348 } else {
349 None
350 };
351 let (when_expr, then_expr): (Vec<&Expr>, Vec<&Expr>) = case
352 .when_then_expr
353 .iter()
354 .map(|(w, t)| (w.as_ref(), t.as_ref()))
355 .unzip();
356 let when_expr = create_physical_exprs(
357 when_expr,
358 input_dfschema,
359 execution_props,
360 planning_ctx,
361 )?;
362 let then_expr = create_physical_exprs(
363 then_expr,
364 input_dfschema,
365 execution_props,
366 planning_ctx,
367 )?;
368 let when_then_expr: Vec<(Arc<dyn PhysicalExpr>, Arc<dyn PhysicalExpr>)> =
369 when_expr
370 .iter()
371 .zip(then_expr.iter())
372 .map(|(w, t)| (Arc::clone(w), Arc::clone(t)))
373 .collect();
374 let else_expr: Option<Arc<dyn PhysicalExpr>> =
375 if let Some(e) = &case.else_expr {
376 Some(create_physical_expr(
377 e.as_ref(),
378 input_dfschema,
379 execution_props,
380 planning_ctx,
381 )?)
382 } else {
383 None
384 };
385 Ok(expressions::case(expr, when_then_expr, else_expr)?)
386 }
387 Expr::Cast(Cast { expr, field }) => expressions::cast_with_target_field(
388 create_physical_expr(expr, input_dfschema, execution_props, planning_ctx)?,
389 input_schema,
390 Arc::clone(field),
391 None,
392 ),
393 Expr::TryCast(TryCast { expr, field }) => {
394 if !field.metadata().is_empty() {
395 let (_, src_field) = expr.to_field(input_dfschema)?;
396 return plan_err!(
397 "TryCast from {} to {} is not supported",
398 format_type_and_metadata(
399 src_field.data_type(),
400 Some(src_field.metadata()),
401 ),
402 format_type_and_metadata(field.data_type(), Some(field.metadata()))
403 );
404 }
405
406 expressions::try_cast(
407 create_physical_expr(
408 expr,
409 input_dfschema,
410 execution_props,
411 planning_ctx,
412 )?,
413 input_schema,
414 field.data_type().clone(),
415 )
416 }
417 Expr::Not(expr) => expressions::not(create_physical_expr(
418 expr,
419 input_dfschema,
420 execution_props,
421 planning_ctx,
422 )?),
423 Expr::Negative(expr) => expressions::negative(
424 create_physical_expr(expr, input_dfschema, execution_props, planning_ctx)?,
425 input_schema,
426 ),
427 Expr::IsNull(expr) => expressions::is_null(create_physical_expr(
428 expr,
429 input_dfschema,
430 execution_props,
431 planning_ctx,
432 )?),
433 Expr::IsNotNull(expr) => expressions::is_not_null(create_physical_expr(
434 expr,
435 input_dfschema,
436 execution_props,
437 planning_ctx,
438 )?),
439 Expr::ScalarFunction(ScalarFunction { func, args }) => {
440 let physical_args = create_physical_exprs(
441 args,
442 input_dfschema,
443 execution_props,
444 planning_ctx,
445 )?;
446 let config_options = match execution_props.config_options.as_ref() {
447 Some(config_options) => Arc::clone(config_options),
448 None => Arc::new(ConfigOptions::default()),
449 };
450
451 Ok(Arc::new(ScalarFunctionExpr::try_new(
452 Arc::clone(func),
453 physical_args,
454 input_schema,
455 config_options,
456 )?))
457 }
458 Expr::Between(Between {
459 expr,
460 negated,
461 low,
462 high,
463 }) => {
464 let value_expr = create_physical_expr(
465 expr,
466 input_dfschema,
467 execution_props,
468 planning_ctx,
469 )?;
470 let low_expr =
471 create_physical_expr(low, input_dfschema, execution_props, planning_ctx)?;
472 let high_expr = create_physical_expr(
473 high,
474 input_dfschema,
475 execution_props,
476 planning_ctx,
477 )?;
478
479 let binary_expr = binary(
481 binary(
482 Arc::clone(&value_expr),
483 Operator::GtEq,
484 low_expr,
485 input_schema,
486 )?,
487 Operator::And,
488 binary(
489 Arc::clone(&value_expr),
490 Operator::LtEq,
491 high_expr,
492 input_schema,
493 )?,
494 input_schema,
495 );
496
497 if *negated {
498 expressions::not(binary_expr?)
499 } else {
500 binary_expr
501 }
502 }
503 Expr::InList(InList {
504 expr,
505 list,
506 negated,
507 }) => match expr.as_ref() {
508 Expr::Literal(ScalarValue::Utf8(None), _) => {
509 Ok(expressions::lit(ScalarValue::Boolean(None)))
510 }
511 _ => {
512 let value_expr = create_physical_expr(
513 expr,
514 input_dfschema,
515 execution_props,
516 planning_ctx,
517 )?;
518
519 let list_exprs = create_physical_exprs(
520 list,
521 input_dfschema,
522 execution_props,
523 planning_ctx,
524 )?;
525 expressions::in_list(value_expr, list_exprs, negated, input_schema)
526 }
527 },
528 Expr::ScalarSubquery(sq) => {
529 match planning_ctx.index_of(sq) {
530 Some(index) => {
531 let schema = sq.subquery.schema();
532 if schema.fields().len() != 1 {
533 return plan_err!(
534 "Scalar subquery must return exactly one column, got {}",
535 schema.fields().len()
536 );
537 }
538 let dt = schema.field(0).data_type().clone();
539 let nullable = schema.field(0).is_nullable();
540 Ok(Arc::new(ScalarSubqueryExpr::new(
541 dt,
542 nullable,
543 index,
544 planning_ctx.results().clone(),
545 )))
546 }
547 None => {
548 not_impl_err!(
552 "Physical plan does not support logical expression {e:?}"
553 )
554 }
555 }
556 }
557 Expr::Placeholder(Placeholder { id, .. }) => {
558 exec_err!("Placeholder '{id}' was not provided a value for execution.")
559 }
560 Expr::HigherOrderFunction(invocation @ HigherOrderFunction { func, args }) => {
561 let num_lambdas = args
562 .iter()
563 .filter(|arg| matches!(arg, Expr::Lambda(_)))
564 .count();
565
566 let mut lambda_parameters =
567 invocation.lambda_parameters(input_dfschema)?.into_iter();
568
569 if num_lambdas > lambda_parameters.len() {
570 return plan_err!(
571 "{} lambda_parameters returned only {} values for {num_lambdas} lambdas",
572 func.name(),
573 lambda_parameters.len()
574 );
575 }
576
577 let lambda_qualifier = 1 + input_dfschema
578 .iter()
579 .filter_map(|(qualifier, _field)| {
580 qualifier.and_then(|tbl| {
581 tbl.table().strip_prefix("lambda_")?.parse::<usize>().ok()
582 })
583 })
584 .max()
585 .unwrap_or_default();
586
587 let qualifier = TableReference::bare(format!("lambda_{lambda_qualifier}"));
588
589 let physical_args = args
590 .iter()
591 .map(|arg| match arg {
592 Expr::Lambda(lambda) => {
593 let lambda_parameters = lambda_parameters
594 .next()
595 .ok_or_else(|| {
596 internal_datafusion_err!(
597 "lambda_parameters len should have been checked above"
598 )
599 })?
600 .into_iter()
601 .zip(&lambda.params)
602 .map(|(field, name)| {
603 (Some(qualifier.clone()), field.renamed(name.as_str()))
604 });
605
606 let new_fields = input_dfschema
607 .iter()
608 .map(|(tbl, field)| (tbl.cloned(), Arc::clone(field)))
609 .chain(lambda_parameters)
610 .collect();
611
612 let lambda_schema = DFSchema::new_with_metadata(
613 new_fields,
614 input_dfschema.metadata().clone(),
615 )?;
616
617 let planning_ctx = planning_ctx
618 .clone()
619 .with_qualified_lambda_variables(&qualifier, &lambda.params);
620
621 create_physical_expr(
622 arg,
623 &lambda_schema,
624 execution_props,
625 &planning_ctx,
626 )
627 }
628 _ => create_physical_expr(
629 arg,
630 input_dfschema,
631 execution_props,
632 planning_ctx,
633 ),
634 })
635 .collect::<Result<_>>()?;
636
637 let config_options = match execution_props.config_options.as_ref() {
638 Some(config_options) => Arc::clone(config_options),
639 None => Arc::new(ConfigOptions::default()),
640 };
641
642 Ok(Arc::new(HigherOrderFunctionExpr::try_new_with_schema(
643 Arc::clone(func),
644 physical_args,
645 input_schema,
646 config_options,
647 )?))
648 }
649 Expr::Lambda(Lambda { params, body }) => expressions::lambda(
650 params,
651 create_physical_expr(body, input_dfschema, execution_props, planning_ctx)?,
652 ),
653 Expr::LambdaVariable(LambdaVariable {
654 name,
655 field,
656 spans: _,
657 }) => {
658 let field = field.as_ref().ok_or_else(|| {
659 plan_datafusion_err!("unresolved LambdaVariable {name}")
660 })?;
661
662 let qualifier =
663 planning_ctx
664 .lambda_variable_qualifier(name)
665 .ok_or_else(|| {
666 plan_datafusion_err!(
667 "qualifier for lambda variable {name} not found"
668 )
669 })?;
670
671 let index = input_dfschema
672 .index_of_column_by_name(Some(qualifier), name)
673 .ok_or_else(|| {
674 plan_datafusion_err!(
675 "lambda variable {qualifier}.{name} not found in planning schema"
676 )
677 })?;
678
679 let schema_field = input_dfschema.field(index);
680
681 let renamed_field = Arc::clone(field).renamed(name);
689
690 if &renamed_field != schema_field {
691 return plan_err!(
692 "LambdaVariable field and schema field mismatch {} != {}",
693 renamed_field,
694 schema_field
695 );
696 }
697
698 Ok(Arc::new(expressions::LambdaVariable::new(
699 index,
700 Arc::clone(schema_field),
701 )))
702 }
703 other => {
704 not_impl_err!("Physical plan does not support logical expression {other:?}")
705 }
706 }
707}
708
709pub fn create_physical_exprs<'a, I>(
713 exprs: I,
714 input_dfschema: &DFSchema,
715 execution_props: &ExecutionProps,
716 planning_ctx: &PhysicalPlanningContext,
717) -> Result<Vec<Arc<dyn PhysicalExpr>>>
718where
719 I: IntoIterator<Item = &'a Expr>,
720{
721 exprs
722 .into_iter()
723 .map(|expr| {
724 create_physical_expr(expr, input_dfschema, execution_props, planning_ctx)
725 })
726 .collect()
727}
728
729pub fn logical2physical(expr: &Expr, schema: &Schema) -> Arc<dyn PhysicalExpr> {
731 let df_schema = schema.clone().to_dfschema().unwrap();
733 let execution_props = ExecutionProps::new();
734 create_physical_expr(
735 expr,
736 &df_schema,
737 &execution_props,
738 &PhysicalPlanningContext::default(),
739 )
740 .unwrap()
741}
742
743#[cfg(test)]
744mod tests {
745 use arrow::array::{ArrayRef, BooleanArray, RecordBatch, StringArray};
746 use arrow::datatypes::{DataType, Field};
747 use datafusion_expr::col;
748
749 use super::*;
750
751 fn test_cast_schema() -> Schema {
752 Schema::new(vec![Field::new("a", DataType::Int32, false)])
753 }
754
755 fn lower_cast_expr(expr: &Expr, schema: &Schema) -> Result<Arc<dyn PhysicalExpr>> {
756 let df_schema = DFSchema::try_from(schema.clone())?;
757 create_physical_expr(
758 expr,
759 &df_schema,
760 &ExecutionProps::new(),
761 &PhysicalPlanningContext::default(),
762 )
763 }
764
765 fn as_planner_cast(physical: &Arc<dyn PhysicalExpr>) -> &expressions::CastExpr {
766 physical
767 .downcast_ref::<expressions::CastExpr>()
768 .expect("planner should lower logical CAST to CastExpr")
769 }
770
771 #[test]
772 fn test_create_physical_expr_scalar_input_output() -> Result<()> {
773 let expr = col("letter").eq(lit("A"));
774
775 let schema = Schema::new(vec![Field::new("letter", DataType::Utf8, false)]);
776 let df_schema = DFSchema::try_from_qualified_schema("data", &schema)?;
777 let p = create_physical_expr(
778 &expr,
779 &df_schema,
780 &ExecutionProps::new(),
781 &PhysicalPlanningContext::default(),
782 )?;
783
784 let batch = RecordBatch::try_new(
785 Arc::new(schema),
786 vec![Arc::new(StringArray::from_iter_values(vec![
787 "A", "B", "C", "D",
788 ]))],
789 )?;
790 let result = p.evaluate(&batch)?;
791 let result = result.into_array(4).expect("Failed to convert to array");
792
793 assert_eq!(
794 &result,
795 &(Arc::new(BooleanArray::from(vec![true, false, false, false,])) as ArrayRef)
796 );
797
798 Ok(())
799 }
800
801 #[test]
802 fn test_cast_lowering_preserves_target_field_metadata() -> Result<()> {
803 let schema = test_cast_schema();
804 let target_field = Arc::new(
805 Field::new("cast_target", DataType::Int64, true)
806 .with_metadata([("target_meta".to_string(), "1".to_string())].into()),
807 );
808 let cast_expr = Expr::Cast(Cast::new_from_field(
809 Box::new(col("a")),
810 Arc::clone(&target_field),
811 ));
812
813 let physical = lower_cast_expr(&cast_expr, &schema)?;
814 let cast = as_planner_cast(&physical);
815
816 assert_eq!(cast.target_field(), &target_field);
817 assert_eq!(physical.return_field(&schema)?, target_field);
818 assert!(physical.nullable(&schema)?);
819
820 Ok(())
821 }
822
823 #[test]
824 fn test_cast_lowering_preserves_standard_cast_semantics() -> Result<()> {
825 let schema = test_cast_schema();
826 let cast_expr = Expr::Cast(Cast::new(Box::new(col("a")), DataType::Int64));
827
828 let physical = lower_cast_expr(&cast_expr, &schema)?;
829 let cast = as_planner_cast(&physical);
830 let returned_field = physical.return_field(&schema)?;
831
832 assert_eq!(cast.cast_type(), &DataType::Int64);
833 assert_eq!(returned_field.name(), "a");
834 assert_eq!(returned_field.data_type(), &DataType::Int64);
835 assert!(!physical.nullable(&schema)?);
836
837 Ok(())
838 }
839
840 #[test]
841 fn test_cast_lowering_preserves_same_type_field_semantics() -> Result<()> {
842 let schema = test_cast_schema();
843 let target_field = Arc::new(
844 Field::new("same_type_cast", DataType::Int32, true).with_metadata(
845 [("target_meta".to_string(), "same-type".to_string())].into(),
846 ),
847 );
848 let cast_expr = Expr::Cast(Cast::new_from_field(
849 Box::new(col("a")),
850 Arc::clone(&target_field),
851 ));
852
853 let physical = lower_cast_expr(&cast_expr, &schema)?;
854 let cast = as_planner_cast(&physical);
855
856 assert_eq!(cast.target_field(), &target_field);
857 assert_eq!(physical.return_field(&schema)?, target_field);
858 assert!(physical.nullable(&schema)?);
859
860 Ok(())
861 }
862
863 #[test]
868 #[cfg_attr(not(feature = "recursive_protection"), ignore)]
869 fn test_deeply_nested_binary_expr() -> Result<()> {
870 let depth = 1000;
873
874 let mut expr = col("a");
875 for _ in 0..depth {
876 expr = Expr::BinaryExpr(BinaryExpr {
877 left: Box::new(expr),
878 op: Operator::Plus,
879 right: Box::new(col("a")),
880 });
881 }
882
883 let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]);
884 let df_schema = DFSchema::try_from(schema)?;
885
886 let _physical_expr = create_physical_expr(
888 &expr,
889 &df_schema,
890 &ExecutionProps::new(),
891 &PhysicalPlanningContext::default(),
892 )?;
893
894 Ok(())
895 }
896}