datafusion_expr/udwf.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//! [`WindowUDF`]: User Defined Window Functions
19
20use arrow::compute::SortOptions;
21use std::cmp::Ordering;
22use std::hash::{Hash, Hasher};
23use std::{
24 any::Any,
25 fmt::{self, Debug, Display, Formatter},
26 sync::Arc,
27};
28
29use arrow::datatypes::{DataType, FieldRef};
30
31use crate::expr::WindowFunction;
32use crate::udf_eq::UdfEq;
33use crate::{
34 Expr, PartitionEvaluator, Signature, function::WindowFunctionSimplification,
35};
36use datafusion_common::{Result, not_impl_err};
37use datafusion_doc::Documentation;
38use datafusion_expr_common::dyn_eq::{DynEq, DynHash};
39use datafusion_functions_window_common::expr::ExpressionArgs;
40use datafusion_functions_window_common::field::WindowUDFFieldArgs;
41use datafusion_functions_window_common::partition::PartitionEvaluatorArgs;
42use datafusion_physical_expr_common::physical_expr::PhysicalExpr;
43
44/// Logical representation of a user-defined window function (UDWF).
45///
46/// A Window Function is called via the SQL `OVER` clause:
47///
48/// ```sql
49/// SELECT first_value(col) OVER (PARTITION BY a, b ORDER BY c) FROM foo;
50/// ```
51///
52/// A UDWF is different from a user defined function (UDF) in that it is
53/// stateful across batches.
54///
55/// See the documentation on [`PartitionEvaluator`] for more details
56///
57/// 1. For simple use cases, use [`create_udwf`] (examples in
58/// [`simple_udwf.rs`]).
59///
60/// 2. For advanced use cases, use [`WindowUDFImpl`] which provides full API
61/// access (examples in [`advanced_udwf.rs`]).
62///
63/// # API Note
64/// This is a separate struct from `WindowUDFImpl` to maintain backwards
65/// compatibility with the older API.
66///
67/// [`PartitionEvaluator`]: crate::PartitionEvaluator
68/// [`create_udwf`]: crate::expr_fn::create_udwf
69/// [`simple_udwf.rs`]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/simple_udwf.rs
70/// [`advanced_udwf.rs`]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/advanced_udwf.rs
71#[derive(Debug, Clone, PartialOrd)]
72pub struct WindowUDF {
73 inner: Arc<dyn WindowUDFImpl>,
74}
75
76/// Defines how the WindowUDF is shown to users
77impl Display for WindowUDF {
78 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
79 write!(f, "{}", self.name())
80 }
81}
82
83impl PartialEq for WindowUDF {
84 fn eq(&self, other: &Self) -> bool {
85 self.inner.dyn_eq(other.inner.as_ref() as &dyn Any)
86 }
87}
88
89impl Eq for WindowUDF {}
90
91impl Hash for WindowUDF {
92 fn hash<H: Hasher>(&self, state: &mut H) {
93 self.inner.dyn_hash(state)
94 }
95}
96
97impl WindowUDF {
98 /// Create a new `WindowUDF` from a `[WindowUDFImpl]` trait object
99 ///
100 /// Note this is the same as using the `From` impl (`WindowUDF::from`)
101 pub fn new_from_impl<F>(fun: F) -> WindowUDF
102 where
103 F: WindowUDFImpl + 'static,
104 {
105 Self::new_from_shared_impl(Arc::new(fun))
106 }
107
108 /// Create a new `WindowUDF` from a `[WindowUDFImpl]` trait object
109 pub fn new_from_shared_impl(fun: Arc<dyn WindowUDFImpl>) -> WindowUDF {
110 Self { inner: fun }
111 }
112
113 /// Return the underlying [`WindowUDFImpl`] trait object for this function
114 pub fn inner(&self) -> &Arc<dyn WindowUDFImpl> {
115 &self.inner
116 }
117
118 /// Adds additional names that can be used to invoke this function, in
119 /// addition to `name`
120 ///
121 /// If you implement [`WindowUDFImpl`] directly you should return aliases directly.
122 pub fn with_aliases(self, aliases: impl IntoIterator<Item = &'static str>) -> Self {
123 Self::new_from_impl(AliasedWindowUDFImpl::new(Arc::clone(&self.inner), aliases))
124 }
125
126 /// creates a [`Expr`] that calls the window function with default
127 /// values for `order_by`, `partition_by`, `window_frame`.
128 ///
129 /// See [`ExprFunctionExt`] for details on setting these values.
130 ///
131 /// This utility allows using a user defined window function without
132 /// requiring access to the registry, such as with the DataFrame API.
133 ///
134 /// [`ExprFunctionExt`]: crate::expr_fn::ExprFunctionExt
135 pub fn call(&self, args: Vec<Expr>) -> Expr {
136 let fun = crate::WindowFunctionDefinition::WindowUDF(Arc::new(self.clone()));
137
138 Expr::from(WindowFunction::new(fun, args))
139 }
140
141 /// Returns this function's name
142 ///
143 /// See [`WindowUDFImpl::name`] for more details.
144 pub fn name(&self) -> &str {
145 self.inner.name()
146 }
147
148 /// Returns the aliases for this function.
149 pub fn aliases(&self) -> &[String] {
150 self.inner.aliases()
151 }
152
153 /// Returns this function's signature (what input types are accepted)
154 ///
155 /// See [`WindowUDFImpl::signature`] for more details.
156 pub fn signature(&self) -> &Signature {
157 self.inner.signature()
158 }
159
160 /// Returns this window function's simplification hook, if any.
161 ///
162 /// See [`WindowUDFImpl::simplify`] for more details.
163 pub fn simplify(&self) -> Option<WindowFunctionSimplification> {
164 self.inner.simplify()
165 }
166
167 /// Expressions that are passed to the [`PartitionEvaluator`].
168 ///
169 /// See [`WindowUDFImpl::expressions`] for more details.
170 pub fn expressions(&self, expr_args: ExpressionArgs) -> Vec<Arc<dyn PhysicalExpr>> {
171 self.inner.expressions(expr_args)
172 }
173 /// Return a `PartitionEvaluator` for evaluating this window function
174 pub fn partition_evaluator_factory(
175 &self,
176 partition_evaluator_args: PartitionEvaluatorArgs,
177 ) -> Result<Box<dyn PartitionEvaluator>> {
178 self.inner.partition_evaluator(partition_evaluator_args)
179 }
180
181 /// Returns the field of the final result of evaluating this window function.
182 ///
183 /// See [`WindowUDFImpl::field`] for more details.
184 pub fn field(&self, field_args: WindowUDFFieldArgs) -> Result<FieldRef> {
185 self.inner.field(field_args)
186 }
187
188 /// Returns custom result ordering introduced by this window function
189 /// which is used to update ordering equivalences.
190 ///
191 /// See [`WindowUDFImpl::sort_options`] for more details.
192 pub fn sort_options(&self) -> Option<SortOptions> {
193 self.inner.sort_options()
194 }
195
196 /// See [`WindowUDFImpl::coerce_types`] for more details.
197 pub fn coerce_types(&self, arg_types: &[DataType]) -> Result<Vec<DataType>> {
198 self.inner.coerce_types(arg_types)
199 }
200
201 /// Returns the reversed user-defined window function when the
202 /// order of evaluation is reversed.
203 ///
204 /// See [`WindowUDFImpl::reverse_expr`] for more details.
205 pub fn reverse_expr(&self) -> ReversedUDWF {
206 self.inner.reverse_expr()
207 }
208
209 /// Returns the documentation for this Window UDF.
210 ///
211 /// Documentation can be accessed programmatically as well as
212 /// generating publicly facing documentation.
213 pub fn documentation(&self) -> Option<&Documentation> {
214 self.inner.documentation()
215 }
216}
217
218impl<F> From<F> for WindowUDF
219where
220 F: WindowUDFImpl + Send + Sync + 'static,
221{
222 fn from(fun: F) -> Self {
223 Self::new_from_impl(fun)
224 }
225}
226
227/// Trait for implementing [`WindowUDF`].
228///
229/// This trait exposes the full API for implementing user defined window functions and
230/// can be used to implement any function.
231///
232/// While the trait depends on [`DynEq`] and [`DynHash`] traits, these should not be
233/// implemented directly. Instead, implement [`Eq`] and [`Hash`] and leverage the
234/// blanket implementations of [`DynEq`] and [`DynHash`].
235///
236/// See [`advanced_udwf.rs`] for a full example with complete implementation and
237/// [`WindowUDF`] for other available options.
238///
239///
240/// [`advanced_udwf.rs`]: https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/advanced_udwf.rs
241/// # Basic Example
242/// ```
243/// # use std::sync::LazyLock;
244/// # use arrow::datatypes::{DataType, Field, FieldRef};
245/// # use datafusion_common::{DataFusionError, plan_err, Result};
246/// # use datafusion_expr::{col, Signature, Volatility, PartitionEvaluator, WindowFrame, ExprFunctionExt, Documentation, LimitEffect};
247/// # use datafusion_expr::{WindowUDFImpl, WindowUDF};
248/// # use datafusion_functions_window_common::field::WindowUDFFieldArgs;
249/// # use datafusion_functions_window_common::partition::PartitionEvaluatorArgs;
250/// # use datafusion_expr::window_doc_sections::DOC_SECTION_ANALYTICAL;
251/// # use datafusion_physical_expr_common::physical_expr;
252/// # use std::sync::Arc;
253///
254/// #[derive(Debug, Clone, PartialEq, Eq, Hash)]
255/// struct SmoothIt {
256/// signature: Signature,
257/// }
258///
259/// impl SmoothIt {
260/// fn new() -> Self {
261/// Self {
262/// signature: Signature::uniform(1, vec![DataType::Int32], Volatility::Immutable),
263/// }
264/// }
265/// }
266///
267/// static DOCUMENTATION: LazyLock<Documentation> = LazyLock::new(|| {
268/// Documentation::builder(DOC_SECTION_ANALYTICAL, "smooths the windows", "smooth_it(2)")
269/// .with_argument("arg1", "The int32 number to smooth by")
270/// .build()
271/// });
272///
273/// fn get_doc() -> &'static Documentation {
274/// &DOCUMENTATION
275/// }
276///
277/// /// Implement the WindowUDFImpl trait for SmoothIt
278/// impl WindowUDFImpl for SmoothIt {
279/// fn name(&self) -> &str { "smooth_it" }
280/// fn signature(&self) -> &Signature { &self.signature }
281/// // The actual implementation would smooth the window
282/// fn partition_evaluator(
283/// &self,
284/// _partition_evaluator_args: PartitionEvaluatorArgs,
285/// ) -> Result<Box<dyn PartitionEvaluator>> {
286/// unimplemented!()
287/// }
288/// fn field(&self, field_args: WindowUDFFieldArgs) -> Result<FieldRef> {
289/// if let Some(DataType::Int32) = field_args.get_input_field(0).map(|f| f.data_type().clone()) {
290/// Ok(Field::new(field_args.name(), DataType::Int32, false).into())
291/// } else {
292/// plan_err!("smooth_it only accepts Int32 arguments")
293/// }
294/// }
295/// fn documentation(&self) -> Option<&Documentation> {
296/// Some(get_doc())
297/// }
298/// fn limit_effect(&self, _args: &[Arc<dyn physical_expr::PhysicalExpr>]) -> LimitEffect {
299/// LimitEffect::Unknown
300/// }
301/// }
302///
303/// // Create a new WindowUDF from the implementation
304/// let smooth_it = WindowUDF::from(SmoothIt::new());
305///
306/// // Call the function `add_one(col)`
307/// // smooth_it(speed) OVER (PARTITION BY car ORDER BY time ASC)
308/// let expr = smooth_it.call(vec![col("speed")])
309/// .partition_by(vec![col("car")])
310/// .order_by(vec![col("time").sort(true, true)])
311/// .window_frame(WindowFrame::new(None))
312/// .build()
313/// .unwrap();
314/// ```
315pub trait WindowUDFImpl: Debug + DynEq + DynHash + Send + Sync + Any {
316 /// Returns this function's name
317 fn name(&self) -> &str;
318
319 /// Returns any aliases (alternate names) for this function.
320 ///
321 /// Note: `aliases` should only include names other than [`Self::name`].
322 /// Defaults to `[]` (no aliases)
323 fn aliases(&self) -> &[String] {
324 &[]
325 }
326
327 /// Returns the function's [`Signature`] for information about what input
328 /// types are accepted and the function's Volatility.
329 fn signature(&self) -> &Signature;
330
331 /// Returns the expressions that are passed to the [`PartitionEvaluator`].
332 fn expressions(&self, expr_args: ExpressionArgs) -> Vec<Arc<dyn PhysicalExpr>> {
333 expr_args.input_exprs().into()
334 }
335
336 /// Invoke the function, returning the [`PartitionEvaluator`] instance
337 fn partition_evaluator(
338 &self,
339 partition_evaluator_args: PartitionEvaluatorArgs,
340 ) -> Result<Box<dyn PartitionEvaluator>>;
341
342 /// Returns an optional hook for simplifying this user-defined window
343 /// function.
344 ///
345 /// Use this hook to apply function-specific rewrites during optimization.
346 /// The default implementation returns `None`.
347 ///
348 /// DataFusion already simplifies arguments and performs constant folding
349 /// (for example, `my_add(1, 2) -> 3`), so there is usually no need to
350 /// implement those optimizations manually for specific UDFs.
351 ///
352 /// Example:
353 /// `advanced_udwf.rs`: <https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/udf/advanced_udwf.rs>
354 ///
355 /// # Returns
356 /// `None` if simplify is not defined.
357 ///
358 /// Or, a closure ([`WindowFunctionSimplification`]) invoked with:
359 /// * `window_function`: [WindowFunction] with already simplified
360 /// arguments
361 /// * `info`: [crate::simplify::SimplifyContext]
362 ///
363 /// The closure returns a simplified [Expr] or an error.
364 ///
365 /// # Notes
366 /// The returned expression must have the same schema as the original
367 /// expression, including both the data type and nullability. For example,
368 /// if the original expression is nullable, the returned expression must
369 /// also be nullable, otherwise it may lead to schema verification errors
370 /// later in query planning.
371 fn simplify(&self) -> Option<WindowFunctionSimplification> {
372 None
373 }
374
375 /// The [`FieldRef`] of the final result of evaluating this window function.
376 ///
377 /// Call `field_args.name()` to get the fully qualified name for defining
378 /// the [`FieldRef`]. For a complete example see the implementation in the
379 /// [Basic Example](WindowUDFImpl#basic-example) section.
380 fn field(&self, field_args: WindowUDFFieldArgs) -> Result<FieldRef>;
381
382 /// Allows the window UDF to define a custom result ordering.
383 ///
384 /// By default, a window UDF doesn't introduce an ordering.
385 /// But when specified by a window UDF this is used to update
386 /// ordering equivalences.
387 fn sort_options(&self) -> Option<SortOptions> {
388 None
389 }
390
391 /// Coerce arguments of a function call to types that the function can evaluate.
392 ///
393 /// This function is only called if [`WindowUDFImpl::signature`] returns [`crate::TypeSignature::UserDefined`]. Most
394 /// UDWFs should return one of the other variants of `TypeSignature` which handle common
395 /// cases
396 ///
397 /// See the [type coercion module](crate::type_coercion)
398 /// documentation for more details on type coercion
399 ///
400 /// For example, if your function requires a floating point arguments, but the user calls
401 /// it like `my_func(1::int)` (aka with `1` as an integer), coerce_types could return `[DataType::Float64]`
402 /// to ensure the argument was cast to `1::double`
403 ///
404 /// # Parameters
405 /// * `arg_types`: The argument types of the arguments this function with
406 ///
407 /// # Return value
408 /// A Vec the same length as `arg_types`. DataFusion will `CAST` the function call
409 /// arguments to these specific types.
410 fn coerce_types(&self, _arg_types: &[DataType]) -> Result<Vec<DataType>> {
411 not_impl_err!("Function {} does not implement coerce_types", self.name())
412 }
413
414 /// Allows customizing the behavior of the user-defined window
415 /// function when it is evaluated in reverse order.
416 fn reverse_expr(&self) -> ReversedUDWF {
417 ReversedUDWF::NotSupported
418 }
419
420 /// Returns the documentation for this Window UDF.
421 ///
422 /// Documentation can be accessed programmatically as well as
423 /// generating publicly facing documentation.
424 fn documentation(&self) -> Option<&Documentation> {
425 None
426 }
427
428 /// If not causal, returns the effect this function will have on the window
429 fn limit_effect(&self, _args: &[Arc<dyn PhysicalExpr>]) -> LimitEffect {
430 LimitEffect::Unknown
431 }
432}
433
434impl dyn WindowUDFImpl {
435 /// Returns `true` if the implementation is of type `T`.
436 ///
437 /// Works correctly when called on `Arc<dyn WindowUDFImpl>` via auto-deref.
438 pub fn is<T: WindowUDFImpl>(&self) -> bool {
439 (self as &dyn Any).is::<T>()
440 }
441
442 /// Attempts to downcast to a concrete type `T`, returning `None` if the
443 /// implementation is not of that type.
444 ///
445 /// Works correctly when called on `Arc<dyn WindowUDFImpl>` via auto-deref,
446 /// unlike `(&arc as &dyn Any).downcast_ref::<T>()` which would attempt to
447 /// downcast the `Arc` itself.
448 pub fn downcast_ref<T: WindowUDFImpl>(&self) -> Option<&T> {
449 (self as &dyn Any).downcast_ref()
450 }
451}
452
453/// the effect this function will have on the limit pushdown
454pub enum LimitEffect {
455 /// Does not affect the limit (i.e. this is causal)
456 None,
457 /// Either undeclared, or dynamic (only evaluatable at run time)
458 Unknown,
459 /// Grow the limit by N rows
460 Relative(usize),
461 /// Limit needs to be at least N rows
462 Absolute(usize),
463}
464
465pub enum ReversedUDWF {
466 /// The result of evaluating the user-defined window function
467 /// remains identical when reversed.
468 Identical,
469 /// A window function which does not support evaluating the result
470 /// in reverse order.
471 NotSupported,
472 /// Customize the user-defined window function for evaluating the
473 /// result in reverse order.
474 Reversed(Arc<WindowUDF>),
475}
476
477impl PartialEq for dyn WindowUDFImpl {
478 fn eq(&self, other: &Self) -> bool {
479 self.dyn_eq(other as &dyn Any)
480 }
481}
482
483impl PartialOrd for dyn WindowUDFImpl {
484 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
485 match self.name().partial_cmp(other.name()) {
486 Some(Ordering::Equal) => self.signature().partial_cmp(other.signature()),
487 cmp => cmp,
488 }
489 // TODO (https://github.com/apache/datafusion/issues/17477) avoid recomparing all fields
490 .filter(|cmp| *cmp != Ordering::Equal || self == other)
491 }
492}
493
494/// WindowUDF that adds an alias to the underlying function. It is better to
495/// implement [`WindowUDFImpl`], which supports aliases, directly if possible.
496#[derive(Debug, PartialEq, Eq, Hash)]
497struct AliasedWindowUDFImpl {
498 inner: UdfEq<Arc<dyn WindowUDFImpl>>,
499 aliases: Vec<String>,
500}
501
502impl AliasedWindowUDFImpl {
503 pub fn new(
504 inner: Arc<dyn WindowUDFImpl>,
505 new_aliases: impl IntoIterator<Item = &'static str>,
506 ) -> Self {
507 let mut aliases = inner.aliases().to_vec();
508 aliases.extend(new_aliases.into_iter().map(|s| s.to_string()));
509
510 Self {
511 inner: inner.into(),
512 aliases,
513 }
514 }
515}
516
517#[warn(clippy::missing_trait_methods)] // Delegates, so it should implement every single trait method
518impl WindowUDFImpl for AliasedWindowUDFImpl {
519 fn name(&self) -> &str {
520 self.inner.name()
521 }
522
523 fn signature(&self) -> &Signature {
524 self.inner.signature()
525 }
526
527 fn expressions(&self, expr_args: ExpressionArgs) -> Vec<Arc<dyn PhysicalExpr>> {
528 expr_args
529 .input_exprs()
530 .first()
531 .map_or(vec![], |expr| vec![Arc::clone(expr)])
532 }
533
534 fn partition_evaluator(
535 &self,
536 partition_evaluator_args: PartitionEvaluatorArgs,
537 ) -> Result<Box<dyn PartitionEvaluator>> {
538 self.inner.partition_evaluator(partition_evaluator_args)
539 }
540
541 fn aliases(&self) -> &[String] {
542 &self.aliases
543 }
544
545 fn simplify(&self) -> Option<WindowFunctionSimplification> {
546 self.inner.simplify()
547 }
548
549 fn field(&self, field_args: WindowUDFFieldArgs) -> Result<FieldRef> {
550 self.inner.field(field_args)
551 }
552
553 fn sort_options(&self) -> Option<SortOptions> {
554 self.inner.sort_options()
555 }
556
557 fn coerce_types(&self, arg_types: &[DataType]) -> Result<Vec<DataType>> {
558 self.inner.coerce_types(arg_types)
559 }
560
561 fn reverse_expr(&self) -> ReversedUDWF {
562 self.inner.reverse_expr()
563 }
564
565 fn documentation(&self) -> Option<&Documentation> {
566 self.inner.documentation()
567 }
568
569 fn limit_effect(&self, args: &[Arc<dyn PhysicalExpr>]) -> LimitEffect {
570 self.inner.limit_effect(args)
571 }
572}
573
574#[cfg(test)]
575mod test {
576 use crate::{LimitEffect, PartitionEvaluator, WindowUDF, WindowUDFImpl};
577 use arrow::datatypes::{DataType, FieldRef};
578 use datafusion_common::Result;
579 use datafusion_expr_common::signature::{Signature, Volatility};
580 use datafusion_functions_window_common::field::WindowUDFFieldArgs;
581 use datafusion_functions_window_common::partition::PartitionEvaluatorArgs;
582 use datafusion_physical_expr_common::physical_expr::PhysicalExpr;
583 use std::cmp::Ordering;
584 use std::hash::{DefaultHasher, Hash, Hasher};
585 use std::sync::Arc;
586
587 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
588 struct AWindowUDF {
589 signature: Signature,
590 }
591
592 impl AWindowUDF {
593 fn new() -> Self {
594 Self {
595 signature: Signature::uniform(
596 1,
597 vec![DataType::Int32],
598 Volatility::Immutable,
599 ),
600 }
601 }
602 }
603
604 /// Implement the WindowUDFImpl trait for AddOne
605 impl WindowUDFImpl for AWindowUDF {
606 fn name(&self) -> &str {
607 "a"
608 }
609 fn signature(&self) -> &Signature {
610 &self.signature
611 }
612 fn partition_evaluator(
613 &self,
614 _partition_evaluator_args: PartitionEvaluatorArgs,
615 ) -> Result<Box<dyn PartitionEvaluator>> {
616 unimplemented!()
617 }
618 fn field(&self, _field_args: WindowUDFFieldArgs) -> Result<FieldRef> {
619 unimplemented!()
620 }
621
622 fn limit_effect(&self, _args: &[Arc<dyn PhysicalExpr>]) -> LimitEffect {
623 LimitEffect::Unknown
624 }
625 }
626
627 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
628 struct BWindowUDF {
629 signature: Signature,
630 }
631
632 impl BWindowUDF {
633 fn new() -> Self {
634 Self {
635 signature: Signature::uniform(
636 1,
637 vec![DataType::Int32],
638 Volatility::Immutable,
639 ),
640 }
641 }
642 }
643
644 /// Implement the WindowUDFImpl trait for AddOne
645 impl WindowUDFImpl for BWindowUDF {
646 fn name(&self) -> &str {
647 "b"
648 }
649 fn signature(&self) -> &Signature {
650 &self.signature
651 }
652 fn partition_evaluator(
653 &self,
654 _partition_evaluator_args: PartitionEvaluatorArgs,
655 ) -> Result<Box<dyn PartitionEvaluator>> {
656 unimplemented!()
657 }
658 fn field(&self, _field_args: WindowUDFFieldArgs) -> Result<FieldRef> {
659 unimplemented!()
660 }
661
662 fn limit_effect(&self, _args: &[Arc<dyn PhysicalExpr>]) -> LimitEffect {
663 LimitEffect::Unknown
664 }
665 }
666
667 #[test]
668 fn test_partial_eq() {
669 let a1 = WindowUDF::from(AWindowUDF::new());
670 let a2 = WindowUDF::from(AWindowUDF::new());
671 let eq = a1 == a2;
672 assert!(eq);
673 assert_eq!(a1, a2);
674 assert_eq!(hash(a1), hash(a2));
675 }
676
677 #[test]
678 fn test_partial_ord() {
679 let a1 = WindowUDF::from(AWindowUDF::new());
680 let a2 = WindowUDF::from(AWindowUDF::new());
681 assert_eq!(a1.partial_cmp(&a2), Some(Ordering::Equal));
682
683 let b1 = WindowUDF::from(BWindowUDF::new());
684 assert!(a1 < b1);
685 assert!(!(a1 == b1));
686 }
687
688 fn hash<T: Hash>(value: T) -> u64 {
689 let hasher = &mut DefaultHasher::new();
690 value.hash(hasher);
691 hasher.finish()
692 }
693}