1use std::fmt::{self, Debug, Formatter};
33use std::hash::{Hash, Hasher};
34use std::sync::Arc;
35
36use crate::PhysicalExpr;
37use crate::expressions::{LambdaExpr, Literal};
38
39use arrow::array::{Array, RecordBatch};
40use arrow::datatypes::{DataType, FieldRef, Schema};
41use datafusion_common::config::{ConfigEntry, ConfigOptions};
42use datafusion_common::datatype::FieldExt;
43use datafusion_common::utils::remove_list_null_values;
44use datafusion_common::{
45 Result, ScalarValue, exec_err, internal_datafusion_err, internal_err,
46 plan_datafusion_err, plan_err,
47};
48use datafusion_expr::type_coercion::functions::value_fields_with_higher_order_udf;
49use datafusion_expr::{
50 ColumnarValue, HigherOrderFunctionArgs, HigherOrderReturnFieldArgs, HigherOrderUDF,
51 LambdaArgument, LambdaParametersProgress, ValueOrLambda, Volatility, expr_vec_fmt,
52};
53
54enum ArgSlot {
61 Value,
63 Lambda(Arc<LambdaExpr>),
67}
68
69pub struct HigherOrderFunctionExpr {
71 fun: Arc<HigherOrderUDF>,
73 name: String,
75 args: Vec<Arc<dyn PhysicalExpr>>,
91 slots: Vec<ArgSlot>,
95 return_field: FieldRef,
100 config_options: Arc<ConfigOptions>,
102}
103
104impl Debug for HigherOrderFunctionExpr {
105 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
106 let lambda_positions: Vec<_> = self
107 .slots
108 .iter()
109 .enumerate()
110 .filter_map(|(i, slot)| matches!(slot, ArgSlot::Lambda(_)).then_some(i))
111 .collect();
112 f.debug_struct("HigherOrderFunctionExpr")
113 .field("fun", &"<FUNC>")
114 .field("name", &self.name)
115 .field("args", &self.args)
116 .field("lambda_positions", &lambda_positions)
117 .field("return_field", &self.return_field)
118 .finish()
119 }
120}
121
122impl HigherOrderFunctionExpr {
123 pub fn try_new_with_schema(
128 fun: Arc<HigherOrderUDF>,
129 args: Vec<Arc<dyn PhysicalExpr>>,
130 schema: &Schema,
131 config_options: Arc<ConfigOptions>,
132 ) -> Result<Self> {
133 let name = fun.name().to_string();
134 let mut slots = Vec::with_capacity(args.len());
135 let arg_fields = args
136 .iter()
137 .map(|e| match e.downcast_ref::<LambdaExpr>() {
138 Some(lambda) => {
139 slots.push(ArgSlot::Lambda(Arc::new(lambda.clone())));
140 Ok(ValueOrLambda::Lambda(lambda.body().return_field(schema)?))
141 }
142 None => {
143 slots.push(ArgSlot::Value);
144 Ok(ValueOrLambda::Value(e.return_field(schema)?))
145 }
146 })
147 .collect::<Result<Vec<_>>>()?;
148
149 value_fields_with_higher_order_udf(&arg_fields, fun.as_ref())?;
151
152 let arguments = args
153 .iter()
154 .map(|e| e.downcast_ref::<Literal>().map(|literal| literal.value()))
155 .collect::<Vec<_>>();
156
157 let ret_args = HigherOrderReturnFieldArgs {
158 arg_fields: &arg_fields,
159 scalar_arguments: &arguments,
160 };
161
162 let return_field = fun.return_field_from_args(ret_args)?;
163
164 Ok(Self {
165 fun,
166 name,
167 args,
168 slots,
169 return_field,
170 config_options,
171 })
172 }
173
174 pub fn fun(&self) -> &HigherOrderUDF {
176 self.fun.as_ref()
177 }
178
179 pub fn name(&self) -> &str {
181 &self.name
182 }
183
184 pub fn args(&self) -> &[Arc<dyn PhysicalExpr>] {
186 &self.args
187 }
188
189 pub fn return_type(&self) -> &DataType {
191 self.return_field.data_type()
192 }
193
194 pub fn nullable(&self) -> bool {
195 self.return_field.is_nullable()
196 }
197
198 pub fn config_options(&self) -> &ConfigOptions {
199 &self.config_options
200 }
201
202 fn resolve_lambda_parameters(
206 &self,
207 fields: &[ValueOrLambda<FieldRef, Option<FieldRef>>],
208 ) -> Result<Vec<Vec<FieldRef>>> {
209 let num_lambdas = self
210 .slots
211 .iter()
212 .filter(|s| matches!(s, ArgSlot::Lambda(_)))
213 .count();
214 if num_lambdas == 0 {
215 return Ok(Vec::new());
216 }
217 match self.fun().lambda_parameters(0, fields)? {
218 LambdaParametersProgress::Partial(_) => plan_err!(
219 "{} lambda_parameters returned a partial result when the return type of all it's lambdas were provided",
220 self.name()
221 ),
222 LambdaParametersProgress::Complete(items) => {
223 if items.len() < num_lambdas {
228 return exec_err!(
229 "{} invocation defined {num_lambdas} but lambda_parameters returned only {}",
230 self.name(),
231 items.len()
232 );
233 }
234 Ok(items)
235 }
236 }
237 }
238}
239
240impl fmt::Display for HigherOrderFunctionExpr {
241 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
242 write!(f, "{}({})", self.name, expr_vec_fmt!(self.args))
243 }
244}
245
246impl PartialEq for HigherOrderFunctionExpr {
247 fn eq(&self, o: &Self) -> bool {
248 if std::ptr::eq(self, o) {
249 return true;
251 }
252 let Self {
255 fun,
256 name,
257 args,
258 slots: _,
259 return_field,
260 config_options,
261 } = self;
262 fun.eq(&o.fun)
263 && name.eq(&o.name)
264 && args.eq(&o.args)
265 && return_field.eq(&o.return_field)
266 && (Arc::ptr_eq(config_options, &o.config_options)
267 || sorted_config_entries(config_options)
268 == sorted_config_entries(&o.config_options))
269 }
270}
271impl Eq for HigherOrderFunctionExpr {}
272impl Hash for HigherOrderFunctionExpr {
273 fn hash<H: Hasher>(&self, state: &mut H) {
274 let Self {
275 fun,
276 name,
277 args,
278 slots: _,
279 return_field,
280 config_options: _, } = self;
282 fun.hash(state);
283 name.hash(state);
284 args.hash(state);
285 return_field.hash(state);
286 }
287}
288
289fn sorted_config_entries(config_options: &ConfigOptions) -> Vec<ConfigEntry> {
290 let mut entries = config_options.entries();
291 entries.sort_by(|l, r| l.key.cmp(&r.key));
292 entries
293}
294
295impl PhysicalExpr for HigherOrderFunctionExpr {
296 fn evaluate(&self, batch: &RecordBatch) -> Result<ColumnarValue> {
297 let mut arg_fields = Vec::with_capacity(self.args.len());
298 let mut fields = Vec::with_capacity(self.args.len());
299 for (arg, slot) in self.args.iter().zip(&self.slots) {
300 match slot {
301 ArgSlot::Lambda(lambda) => {
302 let field = lambda.body().return_field(batch.schema_ref())?;
303 arg_fields.push(ValueOrLambda::Lambda(Arc::clone(&field)));
304 fields.push(ValueOrLambda::Lambda(Some(field)));
305 }
306 ArgSlot::Value => {
307 let field = arg.return_field(batch.schema_ref())?;
308 arg_fields.push(ValueOrLambda::Value(Arc::clone(&field)));
309 fields.push(ValueOrLambda::Value(field));
310 }
311 }
312 }
313
314 let mut lambda_parameters = self.resolve_lambda_parameters(&fields)?.into_iter();
315
316 let args = self
317 .args
318 .iter()
319 .zip(&self.slots)
320 .map(|(arg, slot)| match slot {
321 ArgSlot::Lambda(lambda) => {
322 let lambda_params = lambda_parameters.next().ok_or_else(|| {
323 internal_datafusion_err!(
324 "params len should have been checked above"
325 )
326 })?;
327
328 if lambda.params().len() > lambda_params.len() {
329 return exec_err!(
330 "lambda defined {} params but higher-order function support only {}",
331 lambda.params().len(),
332 lambda_params.len()
333 );
334 }
335
336 let params = std::iter::zip(lambda.params(), lambda_params)
337 .map(|(name, param)| param.renamed(name.as_str()))
338 .collect();
339
340 let projection = lambda
342 .projection()
343 .iter()
344 .copied()
345 .filter(|i| *i < batch.num_columns())
346 .collect::<Vec<_>>();
347
348 Ok(ValueOrLambda::Lambda(LambdaArgument::new(
349 params,
350 Arc::clone(lambda.projected_body()),
351 if projection.is_empty() {
352 None
353 } else {
354 Some(batch.project(&projection)?)
355 },
356 lambda.used_param_indices(),
357 )))
358 }
359 ArgSlot::Value => {
360 let value = arg.evaluate(batch)?;
361
362 let value = if self.fun.clear_null_values()
363 && matches!(
364 value.data_type(),
365 DataType::List(_) | DataType::LargeList(_)
366 )
367 {
368 let arr = value.into_array(batch.num_rows())?;
369 if arr.null_count() == 0 {
370 ColumnarValue::Array(arr)
371 } else {
372 ColumnarValue::Array(remove_list_null_values(&arr)?)
373 }
374 } else {
375 value
376 };
377
378 Ok(ValueOrLambda::Value(value))
379 }
380 })
381 .collect::<Result<Vec<_>>>()?;
382
383 let input_empty = args.is_empty();
384 let input_all_scalar = args
385 .iter()
386 .all(|arg| matches!(arg, ValueOrLambda::Value(ColumnarValue::Scalar(_))));
387
388 let output = self.fun.invoke_with_args(HigherOrderFunctionArgs {
390 args,
391 arg_fields,
392 number_rows: batch.num_rows(),
393 return_field: Arc::clone(&self.return_field),
394 config_options: Arc::clone(&self.config_options),
395 })?;
396
397 if let ColumnarValue::Array(array) = &output
398 && array.len() != batch.num_rows()
399 {
400 let preserve_scalar = array.len() == 1 && !input_empty && input_all_scalar;
403 return if preserve_scalar {
404 ScalarValue::try_from_array(array, 0).map(ColumnarValue::Scalar)
405 } else {
406 internal_err!(
407 "higher-order function {} returned a different number of rows than expected. Expected: {}, Got: {}",
408 self.name,
409 batch.num_rows(),
410 array.len()
411 )
412 };
413 }
414 Ok(output)
415 }
416
417 fn return_field(&self, _input_schema: &Schema) -> Result<FieldRef> {
418 Ok(Arc::clone(&self.return_field))
419 }
420
421 fn children(&self) -> Vec<&Arc<dyn PhysicalExpr>> {
422 self.args.iter().collect()
423 }
424
425 fn with_new_children(
426 self: Arc<Self>,
427 children: Vec<Arc<dyn PhysicalExpr>>,
428 ) -> Result<Arc<dyn PhysicalExpr>> {
429 if children.len() != self.args.len() {
430 return internal_err!(
431 "HigherOrderFunctionExpr expects exactly {} child, got {}",
432 self.args.len(),
433 children.len()
434 );
435 }
436
437 let mut new_slots = Vec::with_capacity(children.len());
440 for (i, child) in children.iter().enumerate() {
441 match &self.slots[i] {
442 ArgSlot::Lambda(_) => {
443 let lambda = wrapped_lambda(child).ok_or_else(|| {
444 plan_datafusion_err!(
445 "{} unable to unwrap lambda from {} at position {i}",
446 &children[i],
447 self.name()
448 )
449 })?;
450 new_slots.push(ArgSlot::Lambda(Arc::new(lambda.clone())));
451 }
452 ArgSlot::Value => {
453 if child.is::<LambdaExpr>() {
454 return plan_err!(
455 "{} received a lambda via with_new_children at position {i} that wasn't a lambda before",
456 self.name()
457 );
458 }
459 new_slots.push(ArgSlot::Value);
460 }
461 }
462 }
463
464 Ok(Arc::new(HigherOrderFunctionExpr {
465 name: self.name.clone(),
466 fun: Arc::clone(&self.fun),
467 args: children,
468 slots: new_slots,
469 return_field: Arc::clone(&self.return_field),
470 config_options: Arc::clone(&self.config_options),
471 }))
472 }
473
474 fn fmt_sql(&self, f: &mut Formatter<'_>) -> fmt::Result {
475 write!(f, "{}(", self.name)?;
476 for (i, expr) in self.args.iter().enumerate() {
477 if i > 0 {
478 write!(f, ", ")?;
479 }
480 expr.fmt_sql(f)?;
481 }
482 write!(f, ")")
483 }
484
485 fn is_volatile_node(&self) -> bool {
486 self.fun.signature().volatility == Volatility::Volatile
487 }
488}
489
490fn wrapped_lambda(expr: &Arc<dyn PhysicalExpr>) -> Option<&LambdaExpr> {
491 let mut current = expr;
492
493 loop {
494 if let Some(lambda) = current.downcast_ref::<LambdaExpr>() {
495 return Some(lambda);
496 } else if current.is::<HigherOrderFunctionExpr>() {
497 return None;
498 }
499
500 match current.children().as_slice() {
501 [single_child] => current = *single_child,
502 _ => return None,
503 }
504 }
505}
506
507#[cfg(test)]
508mod tests {
509 use std::sync::Arc;
510
511 use super::*;
512 use crate::HigherOrderFunctionExpr;
513 use crate::create_physical_expr;
514 use crate::expressions::Column;
515 use crate::expressions::NoOp;
516 use crate::expressions::lambda;
517 use crate::expressions::not;
518 use arrow::array::RecordBatchOptions;
519 use arrow::array::{ArrayRef, Int32Array};
520 use arrow::datatypes::{DataType, Field, Schema};
521 use datafusion_common::Result;
522 use datafusion_common::assert_contains;
523 use datafusion_expr::execution_props::ExecutionProps;
524 use datafusion_expr::physical_planning_context::PhysicalPlanningContext;
525 use datafusion_expr::{
526 HigherOrderFunctionArgs, HigherOrderSignature, HigherOrderUDF, HigherOrderUDFImpl,
527 };
528 use datafusion_expr_common::columnar_value::ColumnarValue;
529 use datafusion_physical_expr_common::physical_expr::PhysicalExpr;
530 use datafusion_physical_expr_common::physical_expr::is_volatile;
531
532 #[derive(Debug, PartialEq, Eq, Hash)]
534 struct MockHigherOrderUDF {
535 signature: HigherOrderSignature,
536 }
537
538 impl HigherOrderUDFImpl for MockHigherOrderUDF {
539 fn name(&self) -> &str {
540 "mock_function"
541 }
542
543 fn signature(&self) -> &HigherOrderSignature {
544 &self.signature
545 }
546
547 fn lambda_parameters(
548 &self,
549 _step: usize,
550 _fields: &[ValueOrLambda<FieldRef, Option<FieldRef>>],
551 ) -> Result<LambdaParametersProgress> {
552 Ok(LambdaParametersProgress::Complete(vec![vec![
554 Arc::new(Field::new("", DataType::Int32, true)),
555 Arc::new(Field::new("", DataType::Int32, true)),
556 ]]))
557 }
558
559 fn return_field_from_args(
560 &self,
561 args: HigherOrderReturnFieldArgs,
562 ) -> Result<FieldRef> {
563 match &args.arg_fields[0] {
564 ValueOrLambda::Lambda(field) | ValueOrLambda::Value(field) => {
565 Ok(Arc::clone(field))
566 }
567 }
568 }
569
570 fn invoke_with_args(
571 &self,
572 args: HigherOrderFunctionArgs,
573 ) -> Result<ColumnarValue> {
574 match &args.args[0] {
575 ValueOrLambda::Lambda(lambda) => lambda.evaluate(
576 &[
577 &|| {
579 Ok(Arc::new(Int32Array::from(vec![-1000; args.number_rows]))
580 as ArrayRef)
581 },
582 &|| {
583 Ok(Arc::new(Int32Array::from_iter_values(
584 (0..args.number_rows as i32).map(|i| 10 * (i + 1)),
585 )) as ArrayRef)
586 },
587 ],
588 |arrays| Ok(arrays.to_vec()),
589 ),
590 ValueOrLambda::Value(value) => Ok(value.clone()),
591 }
592 }
593 }
594
595 #[test]
596 fn test_higher_order_function_volatile_node() {
597 let volatile_udf = Arc::new(HigherOrderUDF::new_from_impl(MockHigherOrderUDF {
599 signature: HigherOrderSignature::variadic_any(Volatility::Volatile),
600 }));
601
602 let stable_udf = Arc::new(HigherOrderUDF::new_from_impl(MockHigherOrderUDF {
604 signature: HigherOrderSignature::variadic_any(Volatility::Stable),
605 }));
606
607 let schema = Schema::new(vec![Field::new("a", DataType::Float32, false)]);
608 let args = vec![Arc::new(Column::new("a", 0)) as Arc<dyn PhysicalExpr>];
609 let config_options = Arc::new(ConfigOptions::new());
610
611 let volatile_expr = HigherOrderFunctionExpr::try_new_with_schema(
613 volatile_udf,
614 args.clone(),
615 &schema,
616 Arc::clone(&config_options),
617 )
618 .unwrap();
619
620 assert!(volatile_expr.is_volatile_node());
621 let volatile_arc: Arc<dyn PhysicalExpr> = Arc::new(volatile_expr);
622 assert!(is_volatile(&volatile_arc));
623
624 let stable_expr = HigherOrderFunctionExpr::try_new_with_schema(
626 stable_udf,
627 args,
628 &schema,
629 config_options,
630 )
631 .unwrap();
632
633 assert!(!stable_expr.is_volatile_node());
634 let stable_arc: Arc<dyn PhysicalExpr> = Arc::new(stable_expr);
635 assert!(!is_volatile(&stable_arc));
636 }
637
638 #[test]
639 fn test_higher_order_function_wrapped_lambda() {
640 let fun = Arc::new(HigherOrderUDF::new_from_impl(MockHigherOrderUDF {
641 signature: HigherOrderSignature::variadic_any(Volatility::Stable),
642 }));
643
644 let expected = ScalarValue::Int32(Some(42));
645
646 let hof = HigherOrderFunctionExpr::try_new_with_schema(
647 fun,
648 vec![lambda(["a"], Arc::new(Literal::new(expected.clone()))).unwrap()],
649 &Schema::empty(),
650 Arc::new(ConfigOptions::new()),
651 )
652 .unwrap();
653
654 let new_children = vec![not(Arc::clone(&hof.args[0])).unwrap()];
655 let wrapped = Arc::new(hof).with_new_children(new_children).unwrap();
656
657 let result = wrapped
658 .evaluate(
659 &RecordBatch::try_new_with_options(
660 Arc::new(Schema::empty()),
661 vec![],
662 &RecordBatchOptions::new().with_row_count(Some(0)),
663 )
664 .unwrap(),
665 )
666 .unwrap();
667
668 let ColumnarValue::Scalar(result) = result else {
669 unreachable!()
670 };
671
672 assert_eq!(result, expected);
673 }
674
675 #[test]
676 fn test_higher_order_function_badly_wrapped_lambda() {
677 let fun = Arc::new(HigherOrderUDF::new_from_impl(MockHigherOrderUDF {
678 signature: HigherOrderSignature::variadic_any(Volatility::Stable),
679 }));
680
681 let hof = HigherOrderFunctionExpr::try_new_with_schema(
682 fun,
683 vec![
684 not(
685 lambda(["a"], Arc::new(Literal::new(ScalarValue::Int32(Some(42)))))
686 .unwrap(),
687 )
688 .unwrap(),
689 ],
690 &Schema::empty(),
691 Arc::new(ConfigOptions::new()),
692 )
693 .unwrap();
694
695 let result = hof
696 .evaluate(
697 &RecordBatch::try_new_with_options(
698 Arc::new(Schema::empty()),
699 vec![],
700 &RecordBatchOptions::new().with_row_count(Some(0)),
701 )
702 .unwrap(),
703 )
704 .unwrap_err();
705
706 assert_contains!(
707 result.to_string(),
708 "LambdaExpr::evaluate() should not be called"
709 );
710 }
711
712 #[test]
713 fn test_higher_order_function_unexpected_lambda() {
714 let fun = Arc::new(HigherOrderUDF::new_from_impl(MockHigherOrderUDF {
715 signature: HigherOrderSignature::variadic_any(Volatility::Stable),
716 }));
717
718 let hof = HigherOrderFunctionExpr::try_new_with_schema(
719 fun,
720 vec![Arc::new(NoOp::new())],
721 &Schema::empty(),
722 Arc::new(ConfigOptions::new()),
723 )
724 .unwrap();
725
726 let result = Arc::new(hof)
727 .with_new_children(vec![lambda(["a"], Arc::new(NoOp::new())).unwrap()])
728 .unwrap_err();
729
730 assert_contains!(
731 result.to_string(),
732 "mock_function received a lambda via with_new_children at position 0 that wasn't a lambda before"
733 );
734 }
735
736 #[test]
739 fn test_higher_order_function_two_lambda_params_capture_and_unused_param() {
740 use datafusion_common::DFSchema;
741 use datafusion_expr::expr::{HigherOrderFunction, LambdaVariable};
742 use datafusion_expr::{Expr, col, lambda as logical_lambda};
743
744 let fun = Arc::new(HigherOrderUDF::new_from_impl(MockHigherOrderUDF {
745 signature: HigherOrderSignature::variadic_any(Volatility::Stable),
746 }));
747
748 let v = Expr::LambdaVariable(LambdaVariable::new(
750 "v".to_string(),
751 Some(Arc::new(Field::new("v", DataType::Int32, true))),
752 ));
753 let body = col("a") + v;
754 let lambda_expr = logical_lambda(["k", "v"], body);
755
756 let schema = DFSchema::from_unqualified_fields(
757 vec![Field::new("a", DataType::Int32, false)].into(),
758 std::collections::HashMap::new(),
759 )
760 .unwrap();
761
762 let physical_expr = create_physical_expr(
763 &Expr::HigherOrderFunction(HigherOrderFunction::new(fun, vec![lambda_expr])),
764 &schema,
765 &ExecutionProps::new(),
766 &PhysicalPlanningContext::default(),
767 )
768 .unwrap();
769
770 let batch = RecordBatch::try_new(
771 Arc::clone(schema.inner()),
772 vec![Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef],
773 )
774 .unwrap();
775
776 let result = physical_expr.evaluate(&batch).unwrap();
777 let ColumnarValue::Array(result) = result else {
778 unreachable!()
779 };
780
781 let expected = Int32Array::from(vec![11, 22, 33]);
783 assert_eq!(result.as_ref(), &expected);
784 }
785}