datafusion_expr/higher_order_function.rs
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! [`HigherOrderUDF`]: User Defined Higher Order Functions
19
20use crate::expr::{
21 HigherOrderFunction, display_comma_separated,
22 schema_name_from_exprs_comma_separated_without_space,
23};
24use crate::type_coercion::functions::value_fields_with_higher_order_udf;
25use crate::udf_eq::UdfEq;
26use crate::{ColumnarValue, Documentation, Expr, ExprSchemable};
27use arrow::array::{ArrayRef, RecordBatch, RecordBatchOptions};
28use arrow::datatypes::{DataType, FieldRef, Schema};
29use arrow_schema::SchemaRef;
30use datafusion_common::config::ConfigOptions;
31use datafusion_common::datatype::FieldExt;
32use datafusion_common::hash_map::EntryRef;
33use datafusion_common::tree_node::{
34 Transformed, TreeNode, TreeNodeContainer, TreeNodeRecursion,
35};
36use datafusion_common::{
37 DFSchema, HashMap, HashSet, Result, ScalarValue, exec_err, internal_datafusion_err,
38 internal_err, not_impl_err, plan_datafusion_err, plan_err,
39};
40use datafusion_expr_common::dyn_eq::{DynEq, DynHash};
41use datafusion_expr_common::signature::Volatility;
42use datafusion_physical_expr_common::physical_expr::PhysicalExpr;
43use std::any::Any;
44use std::cmp::Ordering;
45use std::fmt::Debug;
46use std::hash::{Hash, Hasher};
47use std::mem;
48use std::sync::Arc;
49
50/// The types of arguments for which a function has implementations.
51///
52/// [`HigherOrderTypeSignature`] **DOES NOT** define the types that a user query could call the
53/// function with. DataFusion will automatically coerce (cast) argument types to
54/// one of the supported function signatures, if possible.
55///
56/// # Overview
57/// Functions typically provide implementations for a small number of different
58/// argument [`DataType`]s, rather than all possible combinations. If a user
59/// calls a function with arguments that do not match any of the declared types,
60/// DataFusion will attempt to automatically coerce (add casts to) function
61/// arguments so they match the [`HigherOrderTypeSignature`]. See the [`type_coercion`] module
62/// for more details
63///
64/// [`type_coercion`]: crate::type_coercion
65#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
66pub enum HigherOrderTypeSignature {
67 /// The acceptable signature and coercions rules are special for this
68 /// function.
69 ///
70 /// If this signature is specified,
71 /// DataFusion will call [`HigherOrderUDFImpl::coerce_value_types`] to prepare argument types.
72 UserDefined,
73 /// One or more lambdas or arguments with arbitrary types
74 VariadicAny,
75 /// The specified number of lambdas or arguments with arbitrary types.
76 Any(usize),
77 /// Exactly the specified arguments in the given order, with arbitrary types.
78 /// DataFusion will call [`HigherOrderUDFImpl::coerce_value_types`] to prepare the value
79 /// argument types.
80 Exact(Vec<ValueOrLambda<(), ()>>),
81}
82
83/// Provides information necessary for calling a higher order function.
84///
85/// - [`HigherOrderTypeSignature`] defines the argument types that a function has implementations
86/// for.
87///
88/// - [`Volatility`] defines how the output of the function changes with the input.
89#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
90pub struct HigherOrderSignature {
91 /// The data types that the function accepts. See [HigherOrderTypeSignature] for more information.
92 pub type_signature: HigherOrderTypeSignature,
93 /// The volatility of the function. See [Volatility] for more information.
94 pub volatility: Volatility,
95 /// The max number of times to call [HigherOrderUDFImpl::lambda_parameters] before raising an error.
96 /// Used to guard against implementations that causes an infinite loop by endlessly returning
97 /// [LambdaParametersProgress::Partial]. Defaults to 256
98 pub lambda_parameters_max_iterations: usize,
99}
100
101const LAMBDA_PARAMETERS_MAX_ITERATIONS: usize = 256;
102
103impl HigherOrderSignature {
104 /// Creates a new `HigherOrderSignature` from a given type signature and volatility.
105 pub fn new(type_signature: HigherOrderTypeSignature, volatility: Volatility) -> Self {
106 HigherOrderSignature {
107 type_signature,
108 volatility,
109 lambda_parameters_max_iterations: LAMBDA_PARAMETERS_MAX_ITERATIONS,
110 }
111 }
112
113 /// User-defined coercion rules for the function.
114 pub fn user_defined(volatility: Volatility) -> Self {
115 Self {
116 type_signature: HigherOrderTypeSignature::UserDefined,
117 volatility,
118 lambda_parameters_max_iterations: LAMBDA_PARAMETERS_MAX_ITERATIONS,
119 }
120 }
121
122 /// An arbitrary number of lambdas or arguments of any type.
123 pub fn variadic_any(volatility: Volatility) -> Self {
124 Self {
125 type_signature: HigherOrderTypeSignature::VariadicAny,
126 volatility,
127 lambda_parameters_max_iterations: LAMBDA_PARAMETERS_MAX_ITERATIONS,
128 }
129 }
130
131 /// A specified number of arguments of any type
132 pub fn any(arg_count: usize, volatility: Volatility) -> Self {
133 Self {
134 type_signature: HigherOrderTypeSignature::Any(arg_count),
135 volatility,
136 lambda_parameters_max_iterations: LAMBDA_PARAMETERS_MAX_ITERATIONS,
137 }
138 }
139
140 /// Exactly the specified arguments in the given order, with arbitrary types.
141 /// DataFusion will call [`HigherOrderUDFImpl::coerce_value_types`] to prepare the value
142 /// argument types.
143 ///
144 /// # Example
145 /// A function that takes one value argument followed by one lambda:
146 /// ```
147 /// # use datafusion_expr::{HigherOrderSignature, ValueOrLambda, Volatility};
148 /// let sig = HigherOrderSignature::exact(
149 /// vec![ValueOrLambda::Value(()), ValueOrLambda::Lambda(())],
150 /// Volatility::Immutable,
151 /// );
152 /// ```
153 pub fn exact(args: Vec<ValueOrLambda<(), ()>>, volatility: Volatility) -> Self {
154 Self {
155 type_signature: HigherOrderTypeSignature::Exact(args),
156 volatility,
157 lambda_parameters_max_iterations: LAMBDA_PARAMETERS_MAX_ITERATIONS,
158 }
159 }
160}
161
162impl PartialEq for dyn HigherOrderUDFImpl {
163 fn eq(&self, other: &Self) -> bool {
164 self.dyn_eq(other as _)
165 }
166}
167
168impl PartialOrd for dyn HigherOrderUDFImpl {
169 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
170 let mut cmp = self.name().cmp(other.name());
171 if cmp == Ordering::Equal {
172 cmp = self.signature().partial_cmp(other.signature())?;
173 }
174 if cmp == Ordering::Equal {
175 cmp = self.aliases().partial_cmp(other.aliases())?;
176 }
177 // Contract for PartialOrd and PartialEq consistency requires that
178 // a == b if and only if partial_cmp(a, b) == Some(Equal).
179 if cmp == Ordering::Equal && self != other {
180 // Functions may have other properties besides name and signature
181 // that differentiate two instances (e.g. type, or arbitrary parameters).
182 // We cannot return Some(Equal) in such case.
183 return None;
184 }
185 debug_assert!(
186 cmp == Ordering::Equal || self != other,
187 "Detected incorrect implementation of PartialEq when comparing functions: '{}' and '{}'. \
188 The functions compare as equal, but they are not equal based on general properties that \
189 the PartialOrd implementation observes,",
190 self.name(),
191 other.name()
192 );
193 Some(cmp)
194 }
195}
196
197impl Eq for dyn HigherOrderUDFImpl {}
198
199impl Hash for dyn HigherOrderUDFImpl {
200 fn hash<H: Hasher>(&self, state: &mut H) {
201 self.dyn_hash(state)
202 }
203}
204
205/// Arguments passed to [`HigherOrderUDFImpl::invoke_with_args`] when invoking a
206/// higher order function.
207#[derive(Debug, Clone)]
208pub struct HigherOrderFunctionArgs {
209 /// The evaluated arguments and lambdas to the function
210 pub args: Vec<ValueOrLambda<ColumnarValue, LambdaArgument>>,
211 /// Field associated with each arg, if it exists
212 /// For lambdas, it will be the field of the result of
213 /// the lambda if evaluated with the parameters
214 /// returned from [`HigherOrderUDFImpl::lambda_parameters`]
215 pub arg_fields: Vec<ValueOrLambda<FieldRef, FieldRef>>,
216 /// The number of rows in record batch being evaluated
217 pub number_rows: usize,
218 /// The return field of the higher order function returned
219 /// (from `return_field_from_args`) when creating the
220 /// physical expression from the logical expression
221 pub return_field: FieldRef,
222 /// The config options at execution time
223 pub config_options: Arc<ConfigOptions>,
224}
225
226impl HigherOrderFunctionArgs {
227 /// The return type of the function. See [`Self::return_field`] for more
228 /// details.
229 pub fn return_type(&self) -> &DataType {
230 self.return_field.data_type()
231 }
232}
233
234/// A lambda argument to a HigherOrderFunction
235#[derive(Clone, Debug)]
236pub struct LambdaArgument {
237 /// The parameters defined in this lambda
238 ///
239 /// For example, for `array_transform([2], v -> -v)`,
240 /// this will be `vec![Field::new("v", DataType::Int32, true)]`
241 params: Vec<FieldRef>,
242 /// Indices into [`Self::params`] of the parameters that are actually
243 /// referenced by [`Self::body`] (taking nested-lambda shadowing into
244 /// account), in the original declaration order of `params`.
245 ///
246 /// [`Self::evaluate`] only evaluates and pushes the closures whose
247 /// corresponding parameter index appears here, so unused declared
248 /// parameters leave no slot in the merged batch and the body's compressed
249 /// column indices line up directly with what the evaluator built.
250 ///
251 /// Callers who already have a `LambdaExpr` should pass
252 /// `LambdaExpr::used_param_indices()` directly to [`Self::new`] — both
253 /// are indices into the same positionally-aligned `params` list.
254 ///
255 /// Every index here must be `< params.len()`; see the precondition on
256 /// [`Self::new`].
257 ///
258 /// Relies on captures sorting before this lambda's own params in the
259 /// planner's (un-projected) index space, which is what makes
260 /// `captures ++ used_params` below line up with the projected body.
261 used_param_indices: Vec<usize>,
262 /// The body of the lambda
263 ///
264 /// For example, for `array_transform([2], v -> -v)`,
265 /// this will be the physical expression of `-v`
266 body: Arc<dyn PhysicalExpr>,
267 /// Cached schema built from `params`. Reused across every `evaluate` call
268 /// (and across every nested-list iteration when the lambda is called once
269 /// per outer sublist), avoiding the per-call `Schema::new` build that
270 /// includes constructing the internal name -> index map.
271 schema: SchemaRef,
272 /// A RecordBatch containing the captured columns inside this lambda body, if any
273 ///
274 /// For example, for `array_transform([2], v -> v + a + b)`,
275 /// this will be a `RecordBatch` with two columns, `a` and `b`
276 captures: Option<RecordBatch>,
277}
278
279impl LambdaArgument {
280 /// # Preconditions
281 ///
282 /// Every index in `used_param_indices` must be `< params.len()`;
283 /// violating this panics on out-of-bounds indexing below. Callers should
284 /// pass `LambdaExpr::used_param_indices()`, which always indexes into the
285 /// same `params` list, rather than constructing indices by hand.
286 pub fn new(
287 params: Vec<FieldRef>,
288 body: Arc<dyn PhysicalExpr>,
289 captures: Option<RecordBatch>,
290 used_param_indices: &[usize],
291 ) -> Self {
292 debug_assert!(
293 used_param_indices.iter().all(|i| *i < params.len()),
294 "used_param_indices contains an index out of bounds for params \
295 (len {}): {:?}",
296 params.len(),
297 used_param_indices
298 );
299
300 let used_param_indices = used_param_indices.to_vec();
301 let effective_params = used_param_indices.iter().map(|i| Arc::clone(¶ms[*i]));
302
303 let fields: Vec<FieldRef> = match &captures {
304 Some(batch) => batch
305 .schema_ref()
306 .fields()
307 .iter()
308 .cloned()
309 .chain(effective_params)
310 .collect(),
311 None => effective_params.collect(),
312 };
313
314 let schema = Arc::new(Schema::new(fields));
315
316 Self {
317 params,
318 used_param_indices,
319 body,
320 schema,
321 captures,
322 }
323 }
324
325 /// Evaluate this lambda
326 /// `args` should evaluate to the value of each parameter
327 /// of the correspondent lambda returned in [HigherOrderUDFImpl::lambda_parameters].
328 ///
329 /// Only the closures in `args` for parameters the lambda body actually
330 /// references are called; closures for declared-but-unused parameters
331 /// are skipped entirely. Callers should not rely on every closure in
332 /// `args` being invoked.
333 ///
334 /// `spread_captures` is responsible for transforming the captured column arrays
335 /// so they align with the evaluation batch. Captures are snapshotted from the
336 /// outer batch at construction time, giving one value per outer row, but the
337 /// function may evaluate the lambda body over a batch with a different number
338 /// of rows. It is the function's responsibility to provide the appropriate
339 /// `spread_captures` closure to expand (or otherwise reshape) the captures
340 /// to match.
341 ///
342 /// Taking as an example the following table:
343 ///
344 /// ```sql
345 /// CREATE TABLE t (arr INT[], a INT) AS VALUES
346 /// ([1, 2, 3], 10),
347 /// ([], 20),
348 /// ([4], 30);
349 /// ```
350 ///
351 /// `SELECT array_transform(arr, v -> v + a) from t` would execute over three outer rows:
352 ///
353 /// ```text
354 /// arr (ListArray): [[1, 2, 3], [], [4]] -- 3 outer rows, 4 total elements
355 /// a (captured): [10, 20, 30] -- one value per outer row
356 /// ```
357 ///
358 /// `array_transform` flattens the list elements into a single batch of 4 rows,
359 /// so `spread_captures` must repeat/drop captured values to match:
360 ///
361 /// ```text
362 /// v (flattened args): [1, 2, 3, 4]
363 /// a (spread): [10, 10, 10, 30] -- 10 repeated for 3 elements in row 0,
364 /// -- 20 dropped for the empty sublist in row 1,
365 /// -- 30 once for the single element in row 2
366 /// ```
367 ///
368 /// The lambda body `v + a` then evaluates element-wise over these 4-row arrays,
369 /// producing `[11, 12, 13, 34]`, which `array_transform` reassembles into `[[11, 12, 13], [], [34]]`.
370 ///
371 /// If the lambda has no captures, `spread_captures` is never called.
372 pub fn evaluate(
373 &self,
374 args: &[&dyn Fn() -> Result<ArrayRef>],
375 spread_captures: impl FnOnce(&[ArrayRef]) -> Result<Vec<ArrayRef>>,
376 ) -> Result<ColumnarValue> {
377 let spread_captures = self
378 .captures
379 .as_ref()
380 .map(|captures| {
381 let spread_columns = spread_captures(captures.columns())?;
382
383 RecordBatch::try_new(captures.schema(), spread_columns)
384 })
385 .transpose()?;
386
387 let merged = merge_captures_with_variables(
388 spread_captures.as_ref(),
389 Arc::clone(&self.schema),
390 &self.params,
391 &self.used_param_indices,
392 args,
393 )?;
394
395 self.body.evaluate(&merged)
396 }
397}
398
399fn merge_captures_with_variables(
400 captures: Option<&RecordBatch>,
401 schema: SchemaRef,
402 params: &[FieldRef],
403 used_param_indices: &[usize],
404 variables: &[&dyn Fn() -> Result<ArrayRef>],
405) -> Result<RecordBatch> {
406 if variables.len() < params.len() {
407 return exec_err!(
408 "expected at least {} lambda arguments to merge with captures, got {}",
409 params.len(),
410 variables.len()
411 );
412 }
413
414 let push_param_arrays = |columns: &mut Vec<ArrayRef>| -> Result<()> {
415 for &i in used_param_indices {
416 columns.push(variables[i]()?);
417 }
418 Ok(())
419 };
420
421 let columns = match captures {
422 Some(captures) => {
423 let mut columns = captures.columns().to_vec();
424 push_param_arrays(&mut columns)?;
425 columns
426 }
427 None => {
428 let mut columns = Vec::with_capacity(used_param_indices.len());
429 push_param_arrays(&mut columns)?;
430 columns
431 }
432 };
433
434 if columns.is_empty() {
435 // No columns to derive a row count from, so borrow one variable's
436 // array length instead (all variables have the same length).
437 let row_count = variables.first().ok_or_else(|| {
438 internal_datafusion_err!(
439 "merge_captures_with_variables: no variables to derive a row count from"
440 )
441 })?()?
442 .len();
443 return Ok(RecordBatch::try_new_with_options(
444 schema,
445 vec![],
446 &RecordBatchOptions::new().with_row_count(Some(row_count)),
447 )?);
448 }
449
450 Ok(RecordBatch::try_new(schema, columns)?)
451}
452
453/// Information about arguments passed to the function
454///
455/// This structure contains metadata about how the function was called
456/// such as the type of the arguments, any scalar arguments and if the
457/// arguments can (ever) be null
458///
459/// See [`HigherOrderUDFImpl::return_field_from_args`] for more information
460#[derive(Clone, Debug)]
461pub struct HigherOrderReturnFieldArgs<'a> {
462 /// The data types of the arguments to the function
463 ///
464 /// If argument `i` to the function is a lambda, it will be the field of the result of the
465 /// lambda if evaluated with the parameters returned from [`HigherOrderUDFImpl::lambda_parameters`]
466 ///
467 /// For example, with `array_transform([1], v -> v == 5)`
468 /// this field will be
469 /// ```ignore
470 /// [
471 /// ValueOrLambda::Value(Field::new("", DataType::new_list(DataType::Int32, true), true)),
472 /// ValueOrLambda::Lambda(Field::new("", DataType::Boolean, true))
473 /// ]
474 /// ```
475 pub arg_fields: &'a [ValueOrLambda<FieldRef, FieldRef>],
476 /// Is argument `i` to the function a scalar (constant)?
477 ///
478 /// If the argument `i` is not a scalar, it will be None
479 ///
480 /// For example, if a function is called like `array_transform([1], v -> v == 5)`
481 /// this field will be `[Some(ScalarValue::List(...), None]`
482 pub scalar_arguments: &'a [Option<&'a ScalarValue>],
483}
484
485/// An argument to a higher order function
486#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Hash)]
487pub enum ValueOrLambda<V, L> {
488 /// A value with associated data
489 Value(V),
490 /// A lambda with associated data
491 Lambda(L),
492}
493
494/// Represents a step during the resolution of the parameters of all lambdas of a given
495/// higher-order function via [HigherOrderUDFImpl::lambda_parameters]. It's valid that the
496/// fields of a given lambda changes between steps, and is up to the implementation to
497/// provide during the function evaluation the parameters that matches the fields returned
498/// at the [LambdaParametersProgress::Complete] step. See [HigherOrderUDFImpl::lambda_parameters]
499/// docs for more details
500pub enum LambdaParametersProgress {
501 /// The parameters of some lambdas are unknown due to a dependency on another lambda output field
502 /// or are placeholders due to a dependency on it's own output field. It's perfectly valid to
503 /// contain only `Some`'s and not a single `None`, representing lambdas that depends only on itself
504 /// and not on others. [HigherOrderUDFImpl::lambda_parameters] will be called again with the output
505 /// field of all lambdas with known parameters.
506 Partial(Vec<Option<Vec<FieldRef>>>),
507 /// There are no unmet dependencies and all parameters are known, [HigherOrderUDFImpl::lambda_parameters]
508 /// will not be called again
509 Complete(Vec<Vec<FieldRef>>),
510}
511
512/// Trait for implementing user defined higher order functions.
513///
514/// This trait exposes the full API for implementing user defined functions and
515/// can be used to implement any function.
516///
517/// New higher order functions typically implement this trait and are then
518/// wrapped in a [`HigherOrderUDF`] for registration with DataFusion.
519///
520/// See [`array_transform.rs`] for a commented complete implementation
521///
522/// [`array_transform.rs`]: https://github.com/apache/datafusion/blob/main/datafusion/functions-nested/src/array_transform.rs
523pub trait HigherOrderUDFImpl: Debug + DynEq + DynHash + Send + Sync + Any {
524 /// Returns this function's name
525 fn name(&self) -> &str;
526
527 /// Returns any aliases (alternate names) for this function.
528 ///
529 /// Aliases can be used to invoke the same function using different names.
530 /// For example in some databases `now()` and `current_timestamp()` are
531 /// aliases for the same function. This behavior can be obtained by
532 /// returning `current_timestamp` as an alias for the `now` function.
533 ///
534 /// Note: `aliases` should only include names other than [`Self::name`].
535 /// Defaults to `[]` (no aliases)
536 fn aliases(&self) -> &[String] {
537 &[]
538 }
539
540 /// Returns the name of the column this expression would create
541 ///
542 /// See [`Expr::schema_name`] for details
543 fn schema_name(&self, args: &[Expr]) -> Result<String> {
544 Ok(format!(
545 "{}({})",
546 self.name(),
547 schema_name_from_exprs_comma_separated_without_space(args)?
548 ))
549 }
550
551 /// Returns a [`HigherOrderSignature`] describing the argument types for which this
552 /// function has an implementation, and the function's [`Volatility`].
553 ///
554 /// See [`HigherOrderSignature`] for more details on argument type handling
555 /// and [`Self::return_field_from_args`] for computing the return type.
556 ///
557 /// [`Volatility`]: datafusion_expr_common::signature::Volatility
558 fn signature(&self) -> &HigherOrderSignature;
559
560 /// Return the field of all the parameters supported by the lambdas in `fields`.
561 /// If a lambda support multiple parameters, all should be returned, regardless of
562 /// whether they are used or not on a particular invocation
563 ///
564 /// Tip: If you have a [`HigherOrderFunction`] invocation, you can call the helper
565 /// [`HigherOrderFunction::lambda_parameters`] instead of this method directly
566 ///
567 /// The name of the returned fields are ignored.
568 ///
569 /// This function is repeatedelly called until [LambdaParametersProgress::Complete] is returned, with
570 /// `step` increased by one at each invocation, starting at 0.
571 ///
572 /// For functions which all lambda parameters depend only on the field of it's value arguments,
573 /// this can return [LambdaParametersProgress::Complete] at step 0. Taking as an example a strict
574 /// array_reduce with the signature `(arr: [V], initial_value: I, (I, V) -> I, (I) -> O) -> O`, which
575 /// requires it's initial value to be the exact same type of it's merge output, which is also the
576 /// parameter of it's finish lambda, the expression
577 ///
578 /// `array_reduce([1.2, 2.1], 0.0, (acc, v) -> acc + v + 1.5, v -> v > 5.1)`
579 ///
580 /// would result in this function being called as the following:
581 ///
582 /// ```ignore
583 /// let lambda_parameters = array_reduce.lambda_parameters(
584 /// 0,
585 /// &[
586 /// // the Field of the literal `[1.2, 2.1]`, the array being reduced
587 /// ValueOrLambda::Value(Arc::new(Field::new("", DataType::new_list(DataType::Float32, true), true))),
588 /// // the Field of the literal `0.0`, the initial value
589 /// ValueOrLambda::Value(Arc::new(Field::new("", DataType::Float32, true))),
590 /// // the Field of the output of the merge lambda, which is unknown at this point because it depends
591 /// // on the return of this call
592 /// ValueOrLambda::Lambda(None),
593 /// // the Field of the output of the finish lambda, unknown for the same reason as above
594 /// ValueOrLambda::Lambda(None),
595 /// ])?;
596 ///
597 /// assert_eq!(
598 /// lambda_parameters,
599 /// LambdaParametersProgress::Complete(vec![
600 /// // the finish lambda supported parameters, regardless of how many are actually used
601 /// vec![
602 /// // the accumulator which is the field of the initial value
603 /// Arc::new(Field::new("ignored_name", DataType::Float32, true)),
604 /// // the array values being reduced
605 /// Arc::new(Field::new("", DataType::Float32, true)),
606 /// ],
607 /// // the merge lambda supported parameters
608 /// vec![
609 /// // the reduced value which is the field of the initial value
610 /// Arc::new(Field::new("ignored_name", DataType::Float32, true)),
611 /// ],
612 /// ])
613 /// );
614 /// ```
615 ///
616 /// For functions which lambda parameters depends on the output of other lambdas, or on their own lambda,
617 /// this can return [LambdaParametersProgress::Partial] until all dependencies are met. Note that for
618 /// lambda with cyclic dependencies, you likely want to use [HigherOrderUDFImpl::coerce_values_for_lambdas] too.
619 /// Take as an example a flexible array_reduce with the signature `(arr: [V], initial_value: I, (ACC, V) -> ACC, (ACC) -> O) -> O`.
620 /// It has a cyclic dependency in the merge lambda, and a dependency of the finish lambda in the merge lambda,
621 /// and only requires the initial value to be *coercible* to the output of the merge lambda, which is defined by
622 /// it's [HigherOrderUDFImpl::coerce_values_for_lambdas] implementation. The expression
623 ///
624 /// `array_reduce([1.2, 2.1], 0, (acc, v) -> acc + v + 1.5, v -> v > 5.1)`
625 ///
626 /// would result in this function being called as the following:
627 ///
628 /// ```ignore
629 /// let lambda_parameters = array_reduce.lambda_parameters(
630 /// 0,
631 /// &[
632 /// // the Field of the literal `[1.2, 2.1]`, the array being reduced
633 /// ValueOrLambda::Value(Arc::new(Field::new("", DataType::new_list(DataType::Float32, true), true))),
634 /// // the Field of the literal `0`, the initial value
635 /// ValueOrLambda::Value(Arc::new(Field::new("", DataType::Int32, true))),
636 /// // the Field of the output of the merge lambda, which is unknown at this point because it depends on
637 /// // the return this call
638 /// ValueOrLambda::Lambda(None),
639 /// // the Field of the output of the finish lambda, unknown for the same reason as above
640 /// ValueOrLambda::Lambda(None),
641 /// ])?;
642 ///
643 /// assert_eq!(
644 /// lambda_parameters,
645 /// LambdaParametersProgress::Partial(vec![
646 /// // the finish lambda supported parameters, regardless of how many are actually used
647 /// Some(vec![
648 /// // at step 0, use the field of the initial value
649 /// Arc::new(Field::new("ignored_name", DataType::Int32, true)),
650 /// // the array values being reduced
651 /// Arc::new(Field::new("", DataType::Float32, true)),
652 /// ]),
653 /// // the merge lambda supported parameters, unknown at this point due to dependency on the merge output
654 /// None,
655 /// ])
656 /// );
657 ///
658 /// let lambda_parameters = array_reduce.lambda_parameters(
659 /// 1,
660 /// &[
661 /// // the Field of the literal `[1.2, 2.1]`, the array being reduced
662 /// ValueOrLambda::Value(Arc::new(Field::new("", DataType::new_list(DataType::Float32, true), true))),
663 /// // the Field of the literal `0`, the initial value
664 /// ValueOrLambda::Value(Arc::new(Field::new("", DataType::Int32, true))),
665 /// // the Field of the output of the merge lambda, which could be inferred to be a Float32 based on the
666 /// // returned values of the previous step
667 /// ValueOrLambda::Value(Arc::new(Field::new("", DataType::Float32, true))),
668 /// // the Field of the output of the finish lambda, which is unknown at this point because it depends
669 /// // on the return of this call
670 /// ValueOrLambda::Lambda(None),
671 /// ])?;
672 ///
673 /// assert_eq!(
674 /// lambda_parameters,
675 /// LambdaParametersProgress::Complete(vec![
676 /// // the finish lambda supported parameters, regardless of how many are actually used
677 /// vec![
678 /// // the finish lambda own output now used as it's accumulator
679 /// Arc::new(Field::new("ignored_name", DataType::Float32, true)),
680 /// // the array values being reduced
681 /// Arc::new(Field::new("", DataType::Float32, true)),
682 /// ],
683 /// // the merge lambda supported parameters, which is the output of the merge lambda,
684 /// vec![
685 /// // the output of the merge lambda
686 /// Arc::new(Field::new("", DataType::Float32, true)),
687 /// ],
688 /// ])
689 /// );
690 ///
691 /// let coerce_to = array_reduce.coerce_values_for_lambdas(&[
692 /// // the literal `[1.2, 2.1]` data type, the array being reduced
693 /// ValueOrLambda::Value(DataType::new_list(DataType::Float32, true)),
694 /// // the literal `0` data type, the initial value
695 /// ValueOrLambda::Value(DataType::Int32),
696 /// // the output data type of the merge lambda
697 /// ValueOrLambda::Lambda(DataType::Float32),
698 /// // the output data type of the finish lambda
699 /// ValueOrLambda::Lambda(DataType::Boolean),
700 /// ])?;
701 ///
702 /// assert_eq!(
703 /// coerce_to,
704 /// Some(vec![
705 /// // return the same type for the array being reduced
706 /// DataType::new_list(DataType::Float32, true),
707 /// // coerce the initial value to the output of the merge lambda
708 /// DataType::Float32,
709 /// ])
710 /// );
711 ///
712 /// ```
713 ///
714 /// Note this may also be called at step 0 with all lambda outputs already set, and in that case,
715 /// [LambdaParametersProgress::Complete] must be returned
716 ///
717 /// The implementation can assume that some other part of the code has coerced
718 /// the actual argument types to match [`Self::signature`], except the coercion defined by
719 /// [Self::coerce_values_for_lambdas].
720 ///
721 /// [`HigherOrderFunction`]: crate::expr::HigherOrderFunction
722 /// [`HigherOrderFunction::lambda_parameters`]: crate::expr::HigherOrderFunction::lambda_parameters
723 fn lambda_parameters(
724 &self,
725 step: usize,
726 fields: &[ValueOrLambda<FieldRef, Option<FieldRef>>],
727 ) -> Result<LambdaParametersProgress>;
728
729 /// Coerce value arguments of a function call to types that the function can evaluate also taking into
730 /// account the *output type of it's lambdas*. This differs from [HigherOrderUDFImpl::coerce_value_types]
731 /// that only has access to the type of it's value arguments because it's called before the output type
732 /// of lambdas are known.
733 ///
734 /// See the [type coercion module](crate::type_coercion)
735 /// documentation for more details on type coercion
736 ///
737 /// # Parameters
738 /// * `fields`: The argument types of the value arguments of this function, or the output type of lambdas
739 ///
740 /// # Return value
741 /// If `Some`, contains a Vec with the same number of [ValueOrLambda::Value] in `fields`.
742 /// DataFusion will `CAST` the function call arguments to these specific types. If `None`, no
743 /// coercion will be applied beyond the one defined by the function signature.
744 ///
745 /// For example, a flexible array_reduce implementation (see [Self::lambda_parameters] docs), when working
746 /// with the expression below, may want to coerce it's initial value argument, the *integer* `0`,
747 /// to match the output of it's merge function, which is a *float*:
748 ///
749 /// `array_reduce([1.2, 2.1], 0, (acc, v) -> acc + v + 1.5, v -> v > 2.0)`
750 fn coerce_values_for_lambdas(
751 &self,
752 _fields: &[ValueOrLambda<DataType, DataType>],
753 ) -> Result<Option<Vec<DataType>>> {
754 Ok(None)
755 }
756
757 /// What type will be returned by this function, given the arguments?
758 ///
759 /// The implementation can assume that some other part of the code has coerced
760 /// the actual argument types to match [`Self::signature`], including the coercion
761 /// defined by [Self::coerce_values_for_lambdas].
762 ///
763 /// # Example creating `Field`
764 ///
765 /// Note the name of the `Field` is ignored, except for structured types such as
766 /// `DataType::Struct`.
767 ///
768 /// ```rust
769 /// # use std::sync::Arc;
770 /// # use arrow::datatypes::{DataType, Field, FieldRef};
771 /// # use datafusion_common::Result;
772 /// # use datafusion_expr::HigherOrderReturnFieldArgs;
773 /// # struct Example{}
774 /// # impl Example {
775 /// fn return_field_from_args(&self, args: HigherOrderReturnFieldArgs) -> Result<FieldRef> {
776 /// let field = Arc::new(Field::new("ignored_name", DataType::Int32, true));
777 /// Ok(field)
778 /// }
779 /// # }
780 /// ```
781 fn return_field_from_args(
782 &self,
783 args: HigherOrderReturnFieldArgs,
784 ) -> Result<FieldRef>;
785
786 /// Whether List or LargeList arguments should have it's non-empty null
787 /// sublists cleaned with [remove_list_null_values] before invoking this function
788 ///
789 /// The default implementation always returns true and should only be implemented
790 /// if you want to handle non-empty null sublists yourself
791 ///
792 /// [remove_list_null_values]: datafusion_common::utils::remove_list_null_values
793 // todo: extend this to listview and maps when remove_list_null_values supports it
794 fn clear_null_values(&self) -> bool {
795 true
796 }
797
798 /// Invoke the function returning the appropriate result.
799 ///
800 /// # Performance
801 ///
802 /// For the best performance, the implementations should handle the common case
803 /// when one or more of their arguments are constant values (aka
804 /// [`ColumnarValue::Scalar`]).
805 ///
806 /// [`ColumnarValue::values_to_arrays`] can be used to convert the arguments
807 /// to arrays, which will likely be simpler code, but be slower.
808 fn invoke_with_args(&self, args: HigherOrderFunctionArgs) -> Result<ColumnarValue>;
809
810 /// Returns true if some of this `exprs` subexpressions may not be evaluated
811 /// and thus any side effects (like divide by zero) may not be encountered.
812 ///
813 /// Setting this to true prevents certain optimizations such as common
814 /// subexpression elimination
815 ///
816 /// When overriding this function to return `true`, [HigherOrderUDFImpl::conditional_arguments] can also be
817 /// overridden to report more accurately which arguments are eagerly evaluated and which ones
818 /// lazily.
819 fn short_circuits(&self) -> bool {
820 false
821 }
822
823 /// Determines which of the arguments passed to *this higher-order function*
824 /// are evaluated eagerly and which may be evaluated lazily. Note that this
825 /// does *not* applies to the arguments that *lambda functions* pass to it's
826 /// body expression
827 ///
828 /// If this function returns `None`, all arguments are eagerly evaluated.
829 /// Returning `None` is a micro optimization that saves a needless `Vec`
830 /// allocation.
831 ///
832 /// If the function returns `Some`, returns (`eager`, `lazy`) where `eager`
833 /// are the arguments that are always evaluated, and `lazy` are the
834 /// arguments that may be evaluated lazily (i.e. may not be evaluated at all
835 /// in some cases).
836 ///
837 /// Implementations must ensure that the two returned `Vec`s are disjunct,
838 /// and that each argument from `args` is present in one the two `Vec`s.
839 ///
840 /// When overriding this function, [HigherOrderUDFImpl::short_circuits] must
841 /// be overridden to return `true`.
842 fn conditional_arguments<'a>(
843 &self,
844 args: &'a [Expr],
845 ) -> Option<(Vec<&'a Expr>, Vec<&'a Expr>)> {
846 if self.short_circuits() {
847 Some((vec![], args.iter().collect()))
848 } else {
849 None
850 }
851 }
852
853 /// Coerce value arguments of a function call to types that the function can evaluate.
854 /// Note that if you need to coerce values based on the output type of lambdas, you
855 /// must use [HigherOrderUDFImpl::coerce_values_for_lambdas], as this function is used before
856 /// the output type of lambdas are known
857 ///
858 /// See the [type coercion module](crate::type_coercion)
859 /// documentation for more details on type coercion
860 ///
861 /// For example, if your function requires a contiguous list argument, but the user calls
862 /// it like `my_func(c, v -> v+2)` (i.e. with `c` as a ListView), coerce_types can return `[DataType::List(..)]`
863 /// to ensure the argument is converted to a List
864 ///
865 /// # Parameters
866 /// * `arg_types`: The argument types of the value arguments of this function, excluding lambdas
867 ///
868 /// # Return value
869 /// A Vec the same length as `arg_types`. DataFusion will `CAST` the function call
870 /// arguments to these specific types.
871 fn coerce_value_types(&self, _arg_types: &[DataType]) -> Result<Vec<DataType>> {
872 not_impl_err!(
873 "Function {} does not implement coerce_value_types",
874 self.name()
875 )
876 }
877
878 /// Returns the documentation for this function.
879 ///
880 /// Documentation can be accessed programmatically as well as generating
881 /// publicly facing documentation.
882 fn documentation(&self) -> Option<&Documentation> {
883 None
884 }
885}
886
887/// Logical representation of a Higher Order User Defined Function.
888///
889/// A higher order function takes one or more lambda arguments in addition to
890/// regular value arguments. This struct contains the information DataFusion
891/// needs to plan and invoke functions you supply such as name, type signature,
892/// return type, and actual implementation.
893#[derive(Debug, Clone)]
894pub struct HigherOrderUDF {
895 inner: Arc<dyn HigherOrderUDFImpl>,
896}
897
898impl PartialEq for HigherOrderUDF {
899 fn eq(&self, other: &Self) -> bool {
900 self.inner.as_ref().dyn_eq(other.inner.as_ref())
901 }
902}
903
904impl PartialOrd for HigherOrderUDF {
905 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
906 let mut cmp = self.name().cmp(other.name());
907 if cmp == Ordering::Equal {
908 cmp = self.signature().partial_cmp(other.signature())?;
909 }
910 if cmp == Ordering::Equal {
911 cmp = self.aliases().partial_cmp(other.aliases())?;
912 }
913 // Contract for PartialOrd and PartialEq consistency requires that
914 // a == b if and only if partial_cmp(a, b) == Some(Equal).
915 if cmp == Ordering::Equal && self != other {
916 // Functions may have other properties besides name and signature
917 // that differentiate two instances (e.g. type, or arbitrary parameters).
918 // We cannot return Some(Equal) in such case.
919 return None;
920 }
921 debug_assert!(
922 cmp == Ordering::Equal || self != other,
923 "Detected incorrect implementation of PartialEq when comparing functions: '{}' and '{}'. \
924 The functions compare as equal, but they are not equal based on general properties that \
925 the PartialOrd implementation observes,",
926 self.name(),
927 other.name()
928 );
929 Some(cmp)
930 }
931}
932
933impl Eq for HigherOrderUDF {}
934
935impl Hash for HigherOrderUDF {
936 fn hash<H: Hasher>(&self, state: &mut H) {
937 self.inner.dyn_hash(state)
938 }
939}
940
941impl HigherOrderUDF {
942 /// Create a new `HigherOrderUDF` from a [`HigherOrderUDFImpl`] trait object.
943 ///
944 /// Note this is the same as using the `From` impl (`HigherOrderUDF::from`).
945 pub fn new_from_impl<F>(fun: F) -> HigherOrderUDF
946 where
947 F: HigherOrderUDFImpl + 'static,
948 {
949 Self::new_from_shared_impl(Arc::new(fun))
950 }
951
952 /// Create a new `HigherOrderUDF` from a shared [`HigherOrderUDFImpl`] trait object.
953 pub fn new_from_shared_impl(fun: Arc<dyn HigherOrderUDFImpl>) -> HigherOrderUDF {
954 Self { inner: fun }
955 }
956
957 /// Return the underlying [`HigherOrderUDFImpl`] trait object for this function.
958 pub fn inner(&self) -> &Arc<dyn HigherOrderUDFImpl> {
959 &self.inner
960 }
961
962 /// Adds additional names that can be used to invoke this function, in
963 /// addition to `name`.
964 ///
965 /// If you implement [`HigherOrderUDFImpl`] directly you should return aliases
966 /// directly.
967 pub fn with_aliases(self, aliases: impl IntoIterator<Item = &'static str>) -> Self {
968 Self::new_from_impl(AliasedHigherOrderUDFImpl::new(
969 Arc::clone(&self.inner),
970 aliases,
971 ))
972 }
973
974 /// Returns this function's name.
975 ///
976 /// See [`HigherOrderUDFImpl::name`] for more details.
977 pub fn name(&self) -> &str {
978 self.inner.name()
979 }
980
981 /// Returns the aliases for this function.
982 ///
983 /// See [`HigherOrderUDF::with_aliases`] for more details.
984 pub fn aliases(&self) -> &[String] {
985 self.inner.aliases()
986 }
987
988 /// Returns this function's schema_name.
989 ///
990 /// See [`HigherOrderUDFImpl::schema_name`] for more details.
991 pub fn schema_name(&self, args: &[Expr]) -> Result<String> {
992 self.inner.schema_name(args)
993 }
994
995 /// Returns this function's [`HigherOrderSignature`].
996 pub fn signature(&self) -> &HigherOrderSignature {
997 self.inner.signature()
998 }
999
1000 /// Returns the parameters of all lambdas of this function for the current step.
1001 ///
1002 /// See [`HigherOrderUDFImpl::lambda_parameters`] for more details.
1003 pub fn lambda_parameters(
1004 &self,
1005 step: usize,
1006 fields: &[ValueOrLambda<FieldRef, Option<FieldRef>>],
1007 ) -> Result<LambdaParametersProgress> {
1008 self.inner.lambda_parameters(step, fields)
1009 }
1010
1011 /// Coerce value arguments based on lambda output types.
1012 ///
1013 /// See [`HigherOrderUDFImpl::coerce_values_for_lambdas`] for more details.
1014 pub fn coerce_values_for_lambdas(
1015 &self,
1016 fields: &[ValueOrLambda<DataType, DataType>],
1017 ) -> Result<Option<Vec<DataType>>> {
1018 self.inner.coerce_values_for_lambdas(fields)
1019 }
1020
1021 /// Returns the return field of the function given its arguments.
1022 ///
1023 /// See [`HigherOrderUDFImpl::return_field_from_args`] for more details.
1024 pub fn return_field_from_args(
1025 &self,
1026 args: HigherOrderReturnFieldArgs,
1027 ) -> Result<FieldRef> {
1028 self.inner.return_field_from_args(args)
1029 }
1030
1031 /// Whether List or LargeList arguments should have non-empty null sublists
1032 /// cleaned before invoking this function.
1033 pub fn clear_null_values(&self) -> bool {
1034 self.inner.clear_null_values()
1035 }
1036
1037 /// Invoke the function returning the appropriate result.
1038 ///
1039 /// See [`HigherOrderUDFImpl::invoke_with_args`] for more details.
1040 pub fn invoke_with_args(
1041 &self,
1042 args: HigherOrderFunctionArgs,
1043 ) -> Result<ColumnarValue> {
1044 self.inner.invoke_with_args(args)
1045 }
1046
1047 /// Returns true if some of this function's subexpressions may not be evaluated.
1048 ///
1049 /// See [`HigherOrderUDFImpl::short_circuits`] for more details.
1050 pub fn short_circuits(&self) -> bool {
1051 self.inner.short_circuits()
1052 }
1053
1054 /// Returns which arguments are evaluated eagerly vs lazily.
1055 ///
1056 /// See [`HigherOrderUDFImpl::conditional_arguments`] for more details.
1057 pub fn conditional_arguments<'a>(
1058 &self,
1059 args: &'a [Expr],
1060 ) -> Option<(Vec<&'a Expr>, Vec<&'a Expr>)> {
1061 self.inner.conditional_arguments(args)
1062 }
1063
1064 /// Coerce value arguments of a function call to types that the function can evaluate.
1065 ///
1066 /// See [`HigherOrderUDFImpl::coerce_value_types`] for more details.
1067 pub fn coerce_value_types(&self, arg_types: &[DataType]) -> Result<Vec<DataType>> {
1068 self.inner.coerce_value_types(arg_types)
1069 }
1070
1071 /// Returns the documentation for this function, if any.
1072 pub fn documentation(&self) -> Option<&Documentation> {
1073 self.inner.documentation()
1074 }
1075}
1076
1077impl<F> From<F> for HigherOrderUDF
1078where
1079 F: HigherOrderUDFImpl + 'static,
1080{
1081 fn from(fun: F) -> Self {
1082 Self::new_from_impl(fun)
1083 }
1084}
1085
1086/// `HigherOrderUDFImpl` that adds aliases to the underlying function. It is
1087/// better to implement [`HigherOrderUDFImpl`], which supports aliases, directly
1088/// if possible.
1089#[derive(Debug, PartialEq, Eq, Hash)]
1090struct AliasedHigherOrderUDFImpl {
1091 inner: UdfEq<Arc<dyn HigherOrderUDFImpl>>,
1092 aliases: Vec<String>,
1093}
1094
1095impl AliasedHigherOrderUDFImpl {
1096 fn new(
1097 inner: Arc<dyn HigherOrderUDFImpl>,
1098 new_aliases: impl IntoIterator<Item = &'static str>,
1099 ) -> Self {
1100 let mut aliases = inner.aliases().to_vec();
1101 aliases.extend(new_aliases.into_iter().map(|s| s.to_string()));
1102 Self {
1103 inner: inner.into(),
1104 aliases,
1105 }
1106 }
1107}
1108
1109#[warn(clippy::missing_trait_methods)] // Delegates, so it should implement every single trait method
1110impl HigherOrderUDFImpl for AliasedHigherOrderUDFImpl {
1111 fn name(&self) -> &str {
1112 self.inner.name()
1113 }
1114
1115 fn aliases(&self) -> &[String] {
1116 &self.aliases
1117 }
1118
1119 fn schema_name(&self, args: &[Expr]) -> Result<String> {
1120 self.inner.schema_name(args)
1121 }
1122
1123 fn signature(&self) -> &HigherOrderSignature {
1124 self.inner.signature()
1125 }
1126
1127 fn lambda_parameters(
1128 &self,
1129 step: usize,
1130 fields: &[ValueOrLambda<FieldRef, Option<FieldRef>>],
1131 ) -> Result<LambdaParametersProgress> {
1132 self.inner.lambda_parameters(step, fields)
1133 }
1134
1135 fn coerce_values_for_lambdas(
1136 &self,
1137 fields: &[ValueOrLambda<DataType, DataType>],
1138 ) -> Result<Option<Vec<DataType>>> {
1139 self.inner.coerce_values_for_lambdas(fields)
1140 }
1141
1142 fn return_field_from_args(
1143 &self,
1144 args: HigherOrderReturnFieldArgs,
1145 ) -> Result<FieldRef> {
1146 self.inner.return_field_from_args(args)
1147 }
1148
1149 fn clear_null_values(&self) -> bool {
1150 self.inner.clear_null_values()
1151 }
1152
1153 fn invoke_with_args(&self, args: HigherOrderFunctionArgs) -> Result<ColumnarValue> {
1154 self.inner.invoke_with_args(args)
1155 }
1156
1157 fn short_circuits(&self) -> bool {
1158 self.inner.short_circuits()
1159 }
1160
1161 fn conditional_arguments<'a>(
1162 &self,
1163 args: &'a [Expr],
1164 ) -> Option<(Vec<&'a Expr>, Vec<&'a Expr>)> {
1165 self.inner.conditional_arguments(args)
1166 }
1167
1168 fn coerce_value_types(&self, arg_types: &[DataType]) -> Result<Vec<DataType>> {
1169 self.inner.coerce_value_types(arg_types)
1170 }
1171
1172 fn documentation(&self) -> Option<&Documentation> {
1173 self.inner.documentation()
1174 }
1175}
1176
1177pub(crate) fn resolve_lambda_variables(
1178 expr: Expr,
1179 schema: &DFSchema,
1180 // a map of lambda variable name => a never empty stack of fields [ [..shadowed], in_scope ]
1181 vars: &mut HashMap<String, Vec<FieldRef>>,
1182) -> Result<Transformed<Expr>> {
1183 expr.transform_down(|expr| match expr {
1184 Expr::HigherOrderFunction(HigherOrderFunction { func, args }) => {
1185 // not inlined to reduce nesting
1186 resolve_higher_order_function(func, args, schema, vars)
1187 }
1188 Expr::LambdaVariable(mut var) => {
1189 let field_stack = vars.get(&var.name).ok_or_else(|| {
1190 plan_datafusion_err!(
1191 "missing field of lambda variable {} while resolving",
1192 var.name
1193 )
1194 })?;
1195
1196 let field = field_stack.last().ok_or_else(|| {
1197 internal_datafusion_err!("every entry should have at least one field")
1198 })?;
1199
1200 let field = Arc::clone(field).renamed(&var.name);
1201
1202 let transformed = var.field.as_ref().is_none_or(|old| old != &field);
1203
1204 var.field = Some(field);
1205
1206 Ok(Transformed::new_transformed(
1207 Expr::LambdaVariable(var),
1208 transformed,
1209 ))
1210 }
1211 _ => Ok(Transformed::no(expr)),
1212 })
1213}
1214
1215fn resolve_higher_order_function(
1216 func: Arc<HigherOrderUDF>,
1217 args: Vec<Expr>,
1218 schema: &DFSchema,
1219 // a map of lambda variable name => a never empty stack of fields [ [..shadowed], in_scope ]
1220 vars: &mut HashMap<String, Vec<FieldRef>>,
1221) -> Result<Transformed<Expr>> {
1222 let args = if !vars.is_empty() {
1223 /* if this is a nested lambda, we must resolve non-lambda args before invoking
1224 lambda_parameters because it will invoke ExprSchemable::to_field for every
1225 non-lambda parameter, and if one them contains a lambda variable, it will fail
1226 due to it being unresolved. Example query:
1227
1228 array_transform([[1, 2]], a -> array_transform(a, b -> b+1))
1229
1230 the nested array_transform's lambda_parameters will call Lambdavariable::to_field
1231 on it's first argument, the variable `a`, which must be resolved
1232 */
1233 args.map_elements(|arg| match arg {
1234 Expr::Lambda(_) => Ok(Transformed::no(arg)),
1235 _ => resolve_lambda_variables(arg, schema, vars),
1236 })?
1237 } else {
1238 Transformed::no(args)
1239 };
1240
1241 let transformed = args.transformed;
1242 let mut args = args.data;
1243
1244 let current_fields = args
1245 .iter()
1246 .map(|e| match e {
1247 Expr::Lambda(_lambda_function) => Ok(ValueOrLambda::Lambda(None)),
1248 _ => Ok(ValueOrLambda::Value(e.to_field(schema)?.1)),
1249 })
1250 .collect::<Result<Vec<_>>>()?;
1251
1252 // coerce fields because coercion may alter the lambda parameters
1253 let mut fields = value_fields_with_higher_order_udf(¤t_fields, func.as_ref())?;
1254
1255 let num_lambdas = args.iter().filter(|a| matches!(a, Expr::Lambda(_))).count();
1256
1257 let mut step = 0;
1258
1259 let lambda_params = loop {
1260 match func.lambda_parameters(step, &fields)? {
1261 LambdaParametersProgress::Partial(params) => {
1262 let mut params = params.into_iter();
1263
1264 if params.len() != num_lambdas {
1265 return plan_err!(
1266 "{} lambda_parameters returned {} lambdas but {num_lambdas} expected",
1267 func.name(),
1268 params.len()
1269 );
1270 }
1271
1272 for (arg, field) in std::iter::zip(&mut args, &mut fields) {
1273 match (arg, field) {
1274 (Expr::Lambda(lambda), ValueOrLambda::Lambda(field)) => {
1275 let params = params.next().ok_or_else(|| {
1276 internal_datafusion_err!(
1277 "params len should have been checked above"
1278 )
1279 })?;
1280
1281 if let Some(params) = params {
1282 for (name, field) in
1283 std::iter::zip(&lambda.params, params)
1284 {
1285 vars.entry_ref(name)
1286 .or_default()
1287 .push(field.renamed(name.as_str()));
1288 }
1289
1290 let body_with_vars = resolve_lambda_variables(
1291 mem::take(lambda.body.as_mut()),
1292 schema,
1293 vars,
1294 )?;
1295
1296 remove_scope(vars, &lambda.params)?;
1297
1298 *field = Some(body_with_vars.data.to_field(schema)?.1);
1299 *lambda.body = body_with_vars.data;
1300 }
1301 }
1302 (_, ValueOrLambda::Lambda(_)) => {
1303 return internal_err!(
1304 "value_fields_with_higher_order_udf returned a value for a lambda argument"
1305 );
1306 }
1307 (Expr::Lambda(_), ValueOrLambda::Value(_)) => {
1308 return internal_err!(
1309 "value_fields_with_higher_order_udf returned a lambda for a value argument"
1310 );
1311 }
1312 (_, ValueOrLambda::Value(_)) => {} // nothing to do
1313 }
1314 }
1315 }
1316 LambdaParametersProgress::Complete(params) => break params,
1317 }
1318
1319 let limit = func.signature().lambda_parameters_max_iterations;
1320
1321 step += 1;
1322
1323 if step > limit {
1324 return plan_err!(
1325 "{} lambda_parameters called {limit} times without completion",
1326 func.name()
1327 );
1328 }
1329 };
1330
1331 let mut lambda_params = lambda_params.into_iter();
1332
1333 if num_lambdas != lambda_params.len() {
1334 return plan_err!(
1335 "{} lambda_parameters returned {} values for {num_lambdas} lambdas",
1336 func.name(),
1337 lambda_params.len()
1338 );
1339 }
1340
1341 let args = args.map_elements(|arg| match arg {
1342 Expr::Lambda(mut lambda) => {
1343 let lambda_params = lambda_params.next().ok_or_else(|| {
1344 internal_datafusion_err!(
1345 "lambda_params len should have been checked above"
1346 )
1347 })?;
1348
1349 if lambda.params.len() > lambda_params.len() {
1350 return plan_err!(
1351 "{} lambda defined {} params ({}), but only {} supported",
1352 func.name(),
1353 lambda.params.len(),
1354 display_comma_separated(&lambda.params),
1355 lambda_params.len()
1356 );
1357 }
1358
1359 if !all_unique(&lambda.params) {
1360 return plan_err!(
1361 "lambda params must be unique, got ({})",
1362 lambda.params.join(", ")
1363 );
1364 }
1365
1366 for (param, field) in std::iter::zip(&lambda.params, lambda_params) {
1367 vars.entry_ref(param)
1368 .or_default()
1369 .push(field.renamed(param.as_str()));
1370 }
1371
1372 let transformed =
1373 resolve_lambda_variables(mem::take(lambda.body.as_mut()), schema, vars)?;
1374
1375 *lambda.body = transformed.data;
1376
1377 remove_scope(vars, &lambda.params)?;
1378
1379 Ok(Transformed::new(
1380 Expr::Lambda(lambda),
1381 transformed.transformed,
1382 TreeNodeRecursion::Jump,
1383 ))
1384 }
1385 arg => Ok(Transformed::no(arg)), // resolved at the start of the function
1386 })?;
1387
1388 Ok(Transformed::new(
1389 Expr::HigherOrderFunction(HigherOrderFunction::new(func, args.data)),
1390 transformed || args.transformed,
1391 TreeNodeRecursion::Jump,
1392 ))
1393}
1394
1395fn remove_scope(
1396 vars: &mut HashMap<String, Vec<FieldRef>>,
1397 scope: &[String],
1398) -> Result<()> {
1399 for param in scope {
1400 match vars.entry_ref(param) {
1401 EntryRef::Occupied(mut v) => {
1402 if v.get().len() == 1 {
1403 v.remove();
1404 } else {
1405 v.get_mut().pop().ok_or_else(|| {
1406 internal_datafusion_err!(
1407 "every entry should have at least one field"
1408 )
1409 })?;
1410 }
1411 }
1412 EntryRef::Vacant(_v) => {
1413 return internal_err!("no empty value should be in the map");
1414 }
1415 }
1416 }
1417
1418 Ok(())
1419}
1420
1421fn all_unique(params: &[String]) -> bool {
1422 match params.len() {
1423 0 | 1 => true,
1424 2 => params[0] != params[1],
1425 _ => {
1426 let mut set = HashSet::with_capacity(params.len());
1427
1428 params.iter().all(|p| set.insert(p.as_str()))
1429 }
1430 }
1431}
1432
1433#[cfg(test)]
1434mod tests {
1435 use super::*;
1436 use std::hash::DefaultHasher;
1437 use std::sync::Arc;
1438
1439 use arrow_schema::{DataType, Field, FieldRef, Schema};
1440 use datafusion_common::{DFSchema, Result};
1441 use datafusion_expr_common::columnar_value::ColumnarValue;
1442 use datafusion_expr_common::signature::Volatility;
1443
1444 use crate::{
1445 Expr, HigherOrderSignature, HigherOrderUDF, HigherOrderUDFImpl,
1446 LambdaParametersProgress, ValueOrLambda, col,
1447 expr::{HigherOrderFunction, LambdaVariable},
1448 lambda, lambda_var, lit,
1449 };
1450
1451 #[derive(Debug, PartialEq, Eq, Hash)]
1452 struct TestHigherOrderUDF {
1453 name: &'static str,
1454 field: &'static str,
1455 signature: HigherOrderSignature,
1456 }
1457 impl HigherOrderUDFImpl for TestHigherOrderUDF {
1458 fn name(&self) -> &str {
1459 self.name
1460 }
1461
1462 fn signature(&self) -> &HigherOrderSignature {
1463 &self.signature
1464 }
1465
1466 fn lambda_parameters(
1467 &self,
1468 _step: usize,
1469 _fields: &[ValueOrLambda<FieldRef, Option<FieldRef>>],
1470 ) -> Result<LambdaParametersProgress> {
1471 unimplemented!()
1472 }
1473
1474 fn return_field_from_args(
1475 &self,
1476 _args: HigherOrderReturnFieldArgs,
1477 ) -> Result<FieldRef> {
1478 unimplemented!()
1479 }
1480
1481 fn invoke_with_args(
1482 &self,
1483 _args: HigherOrderFunctionArgs,
1484 ) -> Result<ColumnarValue> {
1485 unimplemented!()
1486 }
1487 }
1488
1489 // PartialEq and Hash must be consistent, and also PartialEq and PartialOrd
1490 // must be consistent, so they are tested together.
1491 #[test]
1492 fn test_partial_eq_hash_and_partial_ord() {
1493 // A parameterized function
1494 let f = test_func("foo", "a");
1495
1496 // Same like `f`, different instance
1497 let f2 = test_func("foo", "a");
1498 assert_eq!(&f, &f2);
1499 assert_eq!(hash(&f), hash(&f2));
1500 assert_eq!(f.partial_cmp(&f2), Some(Ordering::Equal));
1501
1502 // Different parameter
1503 let b = test_func("foo", "b");
1504 assert_ne!(&f, &b);
1505 assert_ne!(hash(&f), hash(&b)); // hash can collide for different values but does not collide in this test
1506 assert_eq!(f.partial_cmp(&b), None);
1507
1508 // Different name
1509 let o = test_func("other", "a");
1510 assert_ne!(&f, &o);
1511 assert_ne!(hash(&f), hash(&o)); // hash can collide for different values but does not collide in this test
1512 assert_eq!(f.partial_cmp(&o), Some(Ordering::Less));
1513
1514 // Different name and parameter
1515 assert_ne!(&b, &o);
1516 assert_ne!(hash(&b), hash(&o)); // hash can collide for different values but does not collide in this test
1517 assert_eq!(b.partial_cmp(&o), Some(Ordering::Less));
1518 }
1519
1520 fn test_func(name: &'static str, parameter: &'static str) -> Arc<HigherOrderUDF> {
1521 Arc::new(HigherOrderUDF::new_from_impl(TestHigherOrderUDF {
1522 name,
1523 field: parameter,
1524 signature: HigherOrderSignature::variadic_any(Volatility::Immutable),
1525 }))
1526 }
1527
1528 fn hash<T: Hash>(value: &T) -> u64 {
1529 let hasher = &mut DefaultHasher::new();
1530 value.hash(hasher);
1531 hasher.finish()
1532 }
1533
1534 #[derive(Debug, PartialEq, Eq, Hash)]
1535 struct MockArrayReduce {
1536 signature: HigherOrderSignature,
1537 }
1538
1539 impl HigherOrderUDFImpl for MockArrayReduce {
1540 fn name(&self) -> &str {
1541 "array_reduce"
1542 }
1543
1544 fn aliases(&self) -> &[String] {
1545 &[]
1546 }
1547
1548 fn signature(&self) -> &HigherOrderSignature {
1549 &self.signature
1550 }
1551
1552 fn lambda_parameters(
1553 &self,
1554 step: usize,
1555 fields: &[ValueOrLambda<FieldRef, Option<FieldRef>>],
1556 ) -> Result<LambdaParametersProgress> {
1557 // optional finish not supported for simplicity
1558 let [
1559 ValueOrLambda::Value(list),
1560 ValueOrLambda::Value(initial_value),
1561 ValueOrLambda::Lambda(merge),
1562 ValueOrLambda::Lambda(_finish),
1563 ] = fields
1564 else {
1565 unreachable!()
1566 };
1567
1568 let list_field = match list.data_type() {
1569 DataType::List(field) => field,
1570 _ => unreachable!(),
1571 };
1572
1573 Ok(match (step, merge) {
1574 (0, None) => {
1575 // at the first step, we use the initial_value as merge accumulator,
1576 // and return None for finish since we don't know the output of merge
1577 LambdaParametersProgress::Partial(vec![
1578 // merge
1579 Some(vec![Arc::clone(initial_value), Arc::clone(list_field)]),
1580 // finish
1581 None,
1582 ])
1583 }
1584 (1, Some(accumulator)) | (0, Some(accumulator)) => {
1585 // now we can use the merge output as it's accumulator and
1586 // as the finish parameter
1587 LambdaParametersProgress::Complete(vec![
1588 // merge
1589 vec![Arc::clone(accumulator), Arc::clone(list_field)],
1590 // finish
1591 vec![Arc::clone(accumulator)],
1592 ])
1593 }
1594 (1, None) => {
1595 unreachable!()
1596 }
1597 _ => unreachable!(),
1598 })
1599 }
1600
1601 fn return_field_from_args(
1602 &self,
1603 args: HigherOrderReturnFieldArgs,
1604 ) -> Result<FieldRef> {
1605 // optional finish not supported for simplicity
1606 let [
1607 ValueOrLambda::Value(_list),
1608 ValueOrLambda::Value(_initial_value),
1609 ValueOrLambda::Lambda(_merge),
1610 ValueOrLambda::Lambda(finish),
1611 ] = args.arg_fields
1612 else {
1613 unreachable!()
1614 };
1615
1616 Ok(Arc::clone(finish))
1617 }
1618
1619 fn invoke_with_args(
1620 &self,
1621 _args: HigherOrderFunctionArgs,
1622 ) -> Result<ColumnarValue> {
1623 unreachable!()
1624 }
1625 }
1626
1627 #[test]
1628 fn test_resolve_lambda_variables() {
1629 let schema = DFSchema::try_from(Schema::new(vec![Field::new(
1630 "c",
1631 DataType::new_list(DataType::new_list(DataType::Int32, true), true),
1632 true,
1633 )]))
1634 .unwrap();
1635
1636 let func = Arc::new(HigherOrderUDF::new_from_impl(MockArrayReduce {
1637 signature: HigherOrderSignature::variadic_any(Volatility::Immutable),
1638 }));
1639
1640 /*
1641 array_reduce(
1642 c,
1643 0,
1644 (acc1, v) -> acc + array_reduce(
1645 v,
1646 0,
1647 (acc2, v) -> acc2 + acc1 + v,
1648 reduced -> reduced * 2.0
1649 ),
1650 reduced -> reduced * 2
1651 )
1652 */
1653 let expr = Expr::HigherOrderFunction(HigherOrderFunction::new(
1654 Arc::clone(&func),
1655 vec![
1656 col("c"),
1657 lit(0),
1658 lambda(
1659 ["acc1", "v"],
1660 lambda_var("acc1")
1661 + Expr::HigherOrderFunction(HigherOrderFunction::new(
1662 Arc::clone(&func),
1663 vec![
1664 lambda_var("v"),
1665 lit(0),
1666 lambda(
1667 ["acc2", "v"],
1668 lambda_var("acc2")
1669 + lambda_var("acc1")
1670 + lambda_var("v"),
1671 ),
1672 lambda(["reduced"], lambda_var("reduced") * lit(2.0)),
1673 ],
1674 )),
1675 ),
1676 lambda(["reduced"], lambda_var("reduced") * lit(2)),
1677 ],
1678 ));
1679
1680 let resolved_expr = expr.resolve_lambda_variables(&schema).unwrap().data;
1681
1682 /*
1683 array_reduce(
1684 c@[[Int32]],
1685 0@Int64,
1686 (acc1@Float64, v@[Int32]) -> acc@Float64 + array_reduce(
1687 v@[Int32],
1688 0@Int64,
1689 (acc2@Float64, v@Int32) -> acc2@Float64 + acc1@Float64 + v@Int32,
1690 reducedFloat64 -> reduced@Float64 * 2.0@Float64
1691 ),
1692 reduced@Float64 -> reduced@Float64 * 2@Int64
1693 )
1694 */
1695 let expected = Expr::HigherOrderFunction(HigherOrderFunction::new(
1696 Arc::clone(&func),
1697 vec![
1698 col("c"),
1699 lit(0),
1700 lambda(
1701 ["acc1", "v"],
1702 resolved_lambda_var("acc1", DataType::Float64, true)
1703 + Expr::HigherOrderFunction(HigherOrderFunction::new(
1704 Arc::clone(&func),
1705 vec![
1706 resolved_lambda_var(
1707 "v",
1708 DataType::new_list(DataType::Int32, true),
1709 true,
1710 ),
1711 lit(0),
1712 lambda(
1713 ["acc2", "v"],
1714 resolved_lambda_var("acc2", DataType::Float64, true)
1715 + resolved_lambda_var(
1716 "acc1",
1717 DataType::Float64,
1718 true,
1719 )
1720 + resolved_lambda_var("v", DataType::Int32, true),
1721 ),
1722 lambda(
1723 ["reduced"],
1724 resolved_lambda_var(
1725 "reduced",
1726 DataType::Float64,
1727 true,
1728 ) * lit(2.0),
1729 ),
1730 ],
1731 )),
1732 ),
1733 lambda(
1734 ["reduced"],
1735 resolved_lambda_var("reduced", DataType::Float64, true) * lit(2),
1736 ),
1737 ],
1738 ));
1739
1740 assert_eq!(resolved_expr, expected);
1741 }
1742
1743 fn resolved_lambda_var(name: &str, dt: DataType, nullable: bool) -> Expr {
1744 Expr::LambdaVariable(LambdaVariable::new(
1745 name.into(),
1746 Some(Arc::new(Field::new(name, dt, nullable))),
1747 ))
1748 }
1749
1750 /// A physical expression that reads the column at a fixed index of the
1751 /// batch it is evaluated against, for exercising [`LambdaArgument`]
1752 /// directly without depending on `datafusion-physical-expr`.
1753 #[derive(Debug, Eq, PartialEq, Hash)]
1754 struct ColumnAt(usize);
1755
1756 impl std::fmt::Display for ColumnAt {
1757 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1758 write!(f, "column_at({})", self.0)
1759 }
1760 }
1761
1762 impl PhysicalExpr for ColumnAt {
1763 fn evaluate(&self, batch: &RecordBatch) -> Result<ColumnarValue> {
1764 Ok(ColumnarValue::Array(Arc::clone(batch.column(self.0))))
1765 }
1766
1767 fn children(&self) -> Vec<&Arc<dyn PhysicalExpr>> {
1768 vec![]
1769 }
1770
1771 fn with_new_children(
1772 self: Arc<Self>,
1773 _children: Vec<Arc<dyn PhysicalExpr>>,
1774 ) -> Result<Arc<dyn PhysicalExpr>> {
1775 Ok(self)
1776 }
1777
1778 fn fmt_sql(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1779 write!(f, "{self}")
1780 }
1781 }
1782
1783 /// `(k, v) -> v` with only `v` used must push `v`'s array, not `k`'s.
1784 #[test]
1785 fn test_lambda_argument_evaluate_pushes_only_used_param() {
1786 use arrow::array::Int32Array;
1787
1788 let k_field = Arc::new(Field::new("k", DataType::Int32, true));
1789 let v_field = Arc::new(Field::new("v", DataType::Int32, true));
1790
1791 let body = Arc::new(ColumnAt(0)) as Arc<dyn PhysicalExpr>;
1792 let lambda_arg = LambdaArgument::new(vec![k_field, v_field], body, None, &[1]);
1793
1794 let k_values: ArrayRef = Arc::new(Int32Array::from(vec![100, 200, 300]));
1795 let v_values: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3]));
1796 let k_closure = || -> Result<ArrayRef> { Ok(Arc::clone(&k_values)) };
1797 let v_closure = || -> Result<ArrayRef> { Ok(Arc::clone(&v_values)) };
1798 let args: Vec<&dyn Fn() -> Result<ArrayRef>> = vec![&k_closure, &v_closure];
1799
1800 let result = lambda_arg
1801 .evaluate(&args, |arrays| Ok(arrays.to_vec()))
1802 .unwrap();
1803 let ColumnarValue::Array(result) = result else {
1804 unreachable!()
1805 };
1806
1807 assert_eq!(
1808 result.as_any().downcast_ref::<Int32Array>().unwrap(),
1809 &Int32Array::from(vec![1, 2, 3]),
1810 "body should read v's values, not k's"
1811 );
1812 }
1813
1814 /// Same as above, but with a capture occupying the leading slot.
1815 #[test]
1816 fn test_lambda_argument_evaluate_pushes_only_used_param_with_captures() {
1817 use arrow::array::Int32Array;
1818
1819 let cap_field = Arc::new(Field::new("cap", DataType::Int32, true));
1820 let k_field = Arc::new(Field::new("k", DataType::Int32, true));
1821 let v_field = Arc::new(Field::new("v", DataType::Int32, true));
1822
1823 let body = Arc::new(ColumnAt(1)) as Arc<dyn PhysicalExpr>;
1824
1825 let cap_values: ArrayRef = Arc::new(Int32Array::from(vec![9, 9, 9]));
1826 let captures = RecordBatch::try_new(
1827 Arc::new(Schema::new(vec![cap_field])),
1828 vec![cap_values],
1829 )
1830 .unwrap();
1831
1832 let lambda_arg =
1833 LambdaArgument::new(vec![k_field, v_field], body, Some(captures), &[1]);
1834
1835 let k_values: ArrayRef = Arc::new(Int32Array::from(vec![100, 200, 300]));
1836 let v_values: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3]));
1837 let k_closure = || -> Result<ArrayRef> { Ok(Arc::clone(&k_values)) };
1838 let v_closure = || -> Result<ArrayRef> { Ok(Arc::clone(&v_values)) };
1839 let args: Vec<&dyn Fn() -> Result<ArrayRef>> = vec![&k_closure, &v_closure];
1840
1841 let result = lambda_arg
1842 .evaluate(&args, |arrays| Ok(arrays.to_vec()))
1843 .unwrap();
1844 let ColumnarValue::Array(result) = result else {
1845 unreachable!()
1846 };
1847
1848 assert_eq!(
1849 result.as_any().downcast_ref::<Int32Array>().unwrap(),
1850 &Int32Array::from(vec![1, 2, 3]),
1851 "body should read v's values, not k's or the capture's"
1852 );
1853 }
1854}