Skip to main content

vortex_array/scalar_fn/
erased.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Type-erased scalar function ([`ScalarFnRef`]).
5
6use std::any::type_name;
7use std::fmt::Debug;
8use std::fmt::Display;
9use std::fmt::Formatter;
10use std::hash::Hash;
11use std::hash::Hasher;
12use std::sync::Arc;
13
14use vortex_error::VortexExpect;
15use vortex_error::VortexResult;
16use vortex_error::vortex_err;
17use vortex_utils::debug_with::DebugWith;
18
19use crate::ArrayRef;
20use crate::ExecutionCtx;
21use crate::dtype::DType;
22use crate::expr::BoundExpression;
23use crate::expr::display::ExprDisplay;
24use crate::scalar_fn::ArrayReduceNode;
25use crate::scalar_fn::ExecutionArgs;
26use crate::scalar_fn::ExpressionReduceNode;
27use crate::scalar_fn::ReduceNodeValidity;
28use crate::scalar_fn::ScalarFnId;
29use crate::scalar_fn::ScalarFnVTable;
30use crate::scalar_fn::options::ScalarFnOptions;
31use crate::scalar_fn::signature::ScalarFnSignature;
32use crate::scalar_fn::typed::DynScalarFn;
33use crate::scalar_fn::typed::TypedScalarFnInstance;
34
35/// A type-erased scalar function, pairing a vtable with bound options behind a trait object.
36///
37/// This stores a [`ScalarFnVTable`] and its options behind an `Arc<dyn DynScalarFn>`, allowing
38/// heterogeneous storage inside [`crate::arrays::ScalarFnArray`].
39///
40/// Use [`super::TypedScalarFnInstance::new()`] to construct, and [`super::TypedScalarFnInstance::erased()`] to
41/// obtain a [`ScalarFnRef`].
42#[derive(Clone)]
43pub struct ScalarFnRef(pub(super) Arc<dyn DynScalarFn>);
44
45impl ScalarFnRef {
46    /// Returns the ID of this scalar function.
47    pub fn id(&self) -> ScalarFnId {
48        self.0.id()
49    }
50
51    /// Returns whether the scalar function is of the given vtable type.
52    pub fn is<V: ScalarFnVTable>(&self) -> bool {
53        self.0.as_any().is::<TypedScalarFnInstance<V>>()
54    }
55
56    /// Returns the typed options for this scalar function if it matches the given vtable type.
57    pub fn as_opt<V: ScalarFnVTable>(&self) -> Option<&V::Options> {
58        self.0
59            .as_any()
60            .downcast_ref::<TypedScalarFnInstance<V>>()
61            .map(|sf| sf.options())
62    }
63
64    /// Returns the typed options for this scalar function if it matches the given vtable type.
65    ///
66    /// # Panics
67    ///
68    /// Panics if the vtable type does not match.
69    pub fn as_<V: ScalarFnVTable>(&self) -> &V::Options {
70        self.as_opt::<V>()
71            .vortex_expect("Expression options type mismatch")
72    }
73
74    /// Downcast to the concrete [`TypedScalarFnInstance`].
75    ///
76    /// Returns `Err(self)` if the downcast fails.
77    pub fn try_downcast<V: ScalarFnVTable>(
78        self,
79    ) -> Result<Arc<TypedScalarFnInstance<V>>, ScalarFnRef> {
80        if self.0.as_any().is::<TypedScalarFnInstance<V>>() {
81            let ptr = Arc::into_raw(self.0) as *const TypedScalarFnInstance<V>;
82            Ok(unsafe { Arc::from_raw(ptr) })
83        } else {
84            Err(self)
85        }
86    }
87
88    /// Downcast to the concrete [`TypedScalarFnInstance`].
89    ///
90    /// # Panics
91    ///
92    /// Panics if the downcast fails.
93    pub fn downcast<V: ScalarFnVTable>(self) -> Arc<TypedScalarFnInstance<V>> {
94        self.try_downcast::<V>()
95            .map_err(|this| {
96                vortex_err!(
97                    "Failed to downcast ScalarFnRef {} to {}",
98                    this.0.id(),
99                    type_name::<V>(),
100                )
101            })
102            .vortex_expect("Failed to downcast ScalarFnRef")
103    }
104
105    /// Try to downcast into a typed [`TypedScalarFnInstance`].
106    pub fn downcast_ref<V: ScalarFnVTable>(&self) -> Option<&TypedScalarFnInstance<V>> {
107        self.0.as_any().downcast_ref::<TypedScalarFnInstance<V>>()
108    }
109
110    /// The type-erased options for this scalar function.
111    pub fn options(&self) -> ScalarFnOptions<'_> {
112        ScalarFnOptions { inner: &*self.0 }
113    }
114
115    /// Signature information for this scalar function.
116    pub fn signature(&self) -> ScalarFnSignature<'_> {
117        ScalarFnSignature { inner: &*self.0 }
118    }
119
120    /// Compute the return [`DType`] of this expression given the input argument types.
121    pub fn return_dtype(&self, arg_types: &[DType]) -> VortexResult<DType> {
122        self.0.return_dtype(arg_types)
123    }
124
125    /// Symbolic validity of this node in an expression tree
126    pub(crate) fn validity_expression<'a>(
127        &self,
128        node: &ExpressionReduceNode<'a>,
129    ) -> VortexResult<ReduceNodeValidity<ExpressionReduceNode<'a>>> {
130        self.0.validity_expression(node)
131    }
132
133    /// Symbolic validity of this in an array tree
134    pub(crate) fn validity_array<'a>(
135        &self,
136        node: &ArrayReduceNode<'a>,
137    ) -> VortexResult<ReduceNodeValidity<ArrayReduceNode<'a>>> {
138        self.0.validity_array(node)
139    }
140
141    /// Execute the expression given the input arguments.
142    pub fn execute(
143        &self,
144        args: &dyn ExecutionArgs,
145        ctx: &mut ExecutionCtx,
146    ) -> VortexResult<ArrayRef> {
147        self.0.execute(args, ctx)
148    }
149
150    /// Perform abstract reduction on this scalar function node in an expression tree.
151    pub fn reduce_expression<'a>(
152        &self,
153        node: &ExpressionReduceNode<'a>,
154    ) -> VortexResult<Option<ExpressionReduceNode<'a>>> {
155        self.0.reduce_expression(node)
156    }
157
158    /// Perform abstract reduction on this scalar function node in an array tree.
159    pub fn reduce_array<'a>(
160        &self,
161        node: &ArrayReduceNode<'a>,
162    ) -> VortexResult<Option<ArrayReduceNode<'a>>> {
163        self.0.reduce_array(node)
164    }
165
166    // ------------------------------------------------------------------
167    // Expression-taking methods — used by expr/ module via pub(crate)
168    // ------------------------------------------------------------------
169
170    /// Format an expression tree in SQL-style format.
171    pub(crate) fn fmt_sql(
172        &self,
173        expr: &dyn ExprDisplay,
174        f: &mut Formatter<'_>,
175    ) -> std::fmt::Result {
176        self.0.fmt_sql(expr, f)
177    }
178
179    /// Simplify a bound expression using the dtypes carried by its nodes.
180    pub(crate) fn simplify(&self, expr: &BoundExpression) -> VortexResult<Option<BoundExpression>> {
181        self.0.simplify(expr)
182    }
183}
184
185impl Debug for ScalarFnRef {
186    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
187        f.debug_struct("ScalarFnRef")
188            .field("vtable", &self.0.id())
189            .field("options", &DebugWith(|fmt| self.0.options_debug(fmt)))
190            .finish()
191    }
192}
193
194impl Display for ScalarFnRef {
195    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
196        write!(f, "{}(", self.0.id())?;
197        self.0.options_display(f)?;
198        write!(f, ")")
199    }
200}
201
202impl PartialEq for ScalarFnRef {
203    fn eq(&self, other: &Self) -> bool {
204        self.0.id() == other.0.id() && self.0.options_eq(other.0.options_any())
205    }
206}
207impl Eq for ScalarFnRef {}
208
209impl Hash for ScalarFnRef {
210    fn hash<H: Hasher>(&self, state: &mut H) {
211        self.0.id().hash(state);
212        self.0.options_hash(state);
213    }
214}