Skip to main content

datafusion_session/
table.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
18use std::any::Any;
19use std::borrow::Cow;
20use std::fmt::Debug;
21use std::sync::Arc;
22
23use crate::session::Session;
24use arrow_schema::SchemaRef;
25use async_trait::async_trait;
26use datafusion_common::{Constraints, Statistics, not_impl_err};
27use datafusion_common::{DFSchemaRef, Result, internal_err};
28use datafusion_expr::Expr;
29use datafusion_expr::statistics::StatisticsRequest;
30
31use datafusion_expr::dml::{InsertOp, MergeIntoClause};
32use datafusion_expr::{
33    CreateExternalTable, LogicalPlan, TableProviderFilterPushDown, TableType,
34};
35use datafusion_physical_plan::ExecutionPlan;
36
37/// A table which can be queried and modified.
38///
39/// Please see [`CatalogProvider`] for details of implementing a custom catalog.
40///
41/// [`TableProvider`] represents a source of data which can provide data as
42/// Apache Arrow [`RecordBatch`]es. Implementations of this trait provide
43/// important information for planning such as:
44///
45/// 1. [`Self::schema`]: The schema (columns and their types) of the table
46/// 2. [`Self::supports_filters_pushdown`]: Should filters be pushed into this scan
47/// 2. [`Self::scan`]: An [`ExecutionPlan`] that can read data
48///
49/// [`RecordBatch`]: https://docs.rs/arrow/latest/arrow/record_batch/struct.RecordBatch.html
50/// [`CatalogProvider`]: super::CatalogProvider
51#[async_trait]
52pub trait TableProvider: Any + Debug + Sync + Send {
53    /// Get a reference to the schema for this table
54    fn schema(&self) -> SchemaRef;
55
56    /// Get a reference to the constraints of the table.
57    /// Returns:
58    /// - `None` for tables that do not support constraints.
59    /// - `Some(&Constraints)` for tables supporting constraints.
60    /// Therefore, a `Some(&Constraints::empty())` return value indicates that
61    /// this table supports constraints, but there are no constraints.
62    fn constraints(&self) -> Option<&Constraints> {
63        None
64    }
65
66    /// Get the type of this table for metadata/catalog purposes.
67    fn table_type(&self) -> TableType;
68
69    /// Get the create statement used to create this table, if available.
70    fn get_table_definition(&self) -> Option<&str> {
71        None
72    }
73
74    /// Get the [`LogicalPlan`] of this table, if available.
75    fn get_logical_plan(&'_ self) -> Option<Cow<'_, LogicalPlan>> {
76        None
77    }
78
79    /// Get the default value for a column, if available.
80    fn get_column_default(&self, _column: &str) -> Option<&Expr> {
81        None
82    }
83
84    /// Create an [`ExecutionPlan`] for scanning the table with optional
85    /// `projection`, `filter`, and `limit`, described below.
86    ///
87    /// The returned `ExecutionPlan` is responsible for scanning the datasource's
88    /// partitions in a streaming, parallelized fashion.
89    ///
90    /// # Projection
91    ///
92    /// If specified, only a subset of columns should be returned, in the order
93    /// specified. The projection is a set of indexes of the fields in
94    /// [`Self::schema`].
95    ///
96    /// DataFusion provides the projection so the scan reads only the columns
97    /// actually used in the query, an optimization called "Projection
98    /// Pushdown". Some datasources, such as Parquet, can use this information
99    /// to go significantly faster when only a subset of columns is required.
100    ///
101    /// # Filters
102    ///
103    /// A list of boolean filter [`Expr`]s to evaluate *during* the scan, in the
104    /// manner specified by [`Self::supports_filters_pushdown`]. Only rows for
105    /// which *all* of the `Expr`s evaluate to `true` must be returned (that is,
106    /// the expressions are `AND`ed together).
107    ///
108    /// To enable filter pushdown, override
109    /// [`Self::supports_filters_pushdown`]. The default implementation does not
110    /// push down filters, and `filters` will be empty.
111    ///
112    /// DataFusion pushes filters into scans whenever possible ("Filter
113    /// Pushdown"). Depending on the data format and implementation, evaluating
114    /// predicates during the scan can significantly improve performance.
115    ///
116    /// ## Note: Some columns may appear *only* in Filters
117    ///
118    /// In some cases, a query may use a column only in a filter and the
119    /// projection will not contain all columns referenced by the filter
120    /// expressions.
121    ///
122    /// For example, given the query `SELECT t.a FROM t WHERE t.b > 5`,
123    ///
124    /// ```text
125    /// ┌────────────────────┐
126    /// │  Projection(t.a)   │
127    /// └────────────────────┘
128    ///            ▲
129    ///            │
130    ///            │
131    /// ┌────────────────────┐     Filter     ┌────────────────────┐   Projection    ┌────────────────────┐
132    /// │  Filter(t.b > 5)   │────Pushdown──▶ │  Projection(t.a)   │ ───Pushdown───▶ │  Projection(t.a)   │
133    /// └────────────────────┘                └────────────────────┘                 └────────────────────┘
134    ///            ▲                                     ▲                                      ▲
135    ///            │                                     │                                      │
136    ///            │                                     │                           ┌────────────────────┐
137    /// ┌────────────────────┐                ┌────────────────────┐                 │        Scan        │
138    /// │        Scan        │                │        Scan        │                 │  filter=(t.b > 5)  │
139    /// └────────────────────┘                │  filter=(t.b > 5)  │                 │  projection=(t.a)  │
140    ///                                       └────────────────────┘                 └────────────────────┘
141    ///
142    /// Initial Plan                  If `TableProviderFilterPushDown`           Projection pushdown notes that
143    ///                               returns true, filter pushdown              the scan only needs t.a
144    ///                               pushes the filter into the scan
145    ///                                                                          BUT internally evaluating the
146    ///                                                                          predicate still requires t.b
147    /// ```
148    ///
149    /// # Limit
150    ///
151    /// If `limit` is specified, the scan must produce *at least* this many
152    /// rows, though it may return more. Like Projection Pushdown and Filter
153    /// Pushdown, DataFusion pushes `LIMIT`s as far down in the plan as
154    /// possible. This is called "Limit Pushdown", and some sources can use the
155    /// information to improve performance.
156    ///
157    /// Note: If any pushed-down filters are `Inexact`, the `LIMIT` cannot be
158    /// pushed down. Inexact filters do not guarantee that every filtered row is
159    /// removed, so applying the limit could leave too few rows to return in the
160    /// final result.
161    ///
162    /// # Evaluation Order
163    ///
164    /// The logical evaluation order is `filters`, then `limit`, then
165    /// `projection`.
166    ///
167    /// Note that `limit` applies to the filtered result, not to the unfiltered
168    /// input, and `projection` affects only which columns are returned, not
169    /// which rows qualify.
170    ///
171    /// For example, if a scan receives:
172    ///
173    /// - `projection = [a]`
174    /// - `filters = [b > 5]`
175    /// - `limit = Some(3)`
176    ///
177    /// It must logically produce results equivalent to:
178    ///
179    /// ```text
180    /// PROJECTION a (LIMIT 3 (SCAN WHERE b > 5))
181    /// ```
182    ///
183    /// As noted above, columns referenced only by pushed-down filters may be
184    /// absent from `projection`.
185    async fn scan(
186        &self,
187        state: &dyn Session,
188        projection: Option<&Vec<usize>>,
189        filters: &[Expr],
190        limit: Option<usize>,
191    ) -> Result<Arc<dyn ExecutionPlan>>;
192
193    /// Create an [`ExecutionPlan`] for scanning the table using structured arguments.
194    ///
195    /// This method uses [`ScanArgs`] to pass scan parameters in a structured way
196    /// and returns a [`ScanResult`] containing the execution plan.
197    ///
198    /// Table providers can override this method to take advantage of additional
199    /// parameters like the upcoming `preferred_ordering` that may not be available through
200    /// other scan methods.
201    ///
202    /// # Arguments
203    /// * `state` - The session state containing configuration and context
204    /// * `args` - Structured scan arguments including projection, filters, limit, and ordering preferences
205    ///
206    /// # Returns
207    /// A [`ScanResult`] containing the [`ExecutionPlan`] for scanning the table
208    ///
209    /// See [`Self::scan`] for detailed documentation about projection, filters, and limits.
210    async fn scan_with_args<'a>(
211        &self,
212        state: &dyn Session,
213        args: ScanArgs<'a>,
214    ) -> Result<ScanResult> {
215        let filters = args.filters().unwrap_or(&[]);
216        let projection = args.projection().map(|p| p.to_vec());
217        let limit = args.limit();
218        let plan = self
219            .scan(state, projection.as_ref(), filters, limit)
220            .await?;
221        Ok(plan.into())
222    }
223
224    /// Specify if DataFusion should provide filter expressions to the
225    /// TableProvider to apply *during* the scan.
226    ///
227    /// Some TableProviders can evaluate filters more efficiently than the
228    /// `Filter` operator in DataFusion, for example by using an index.
229    ///
230    /// # Parameters and Return Value
231    ///
232    /// The return `Vec` must have one element for each element of the `filters`
233    /// argument. The value of each element indicates if the TableProvider can
234    /// apply the corresponding filter during the scan. The position in the return
235    /// value corresponds to the expression in the `filters` parameter.
236    ///
237    /// If the length of the resulting `Vec` does not match the `filters` input
238    /// an error will be thrown.
239    ///
240    /// Each element in the resulting `Vec` is one of the following:
241    /// * [`Exact`] or [`Inexact`]: The TableProvider can apply the filter
242    /// during scan
243    /// * [`Unsupported`]: The TableProvider cannot apply the filter during scan
244    ///
245    /// By default, this function returns [`Unsupported`] for all filters,
246    /// meaning no filters will be provided to [`Self::scan`].
247    ///
248    /// [`Unsupported`]: TableProviderFilterPushDown::Unsupported
249    /// [`Exact`]: TableProviderFilterPushDown::Exact
250    /// [`Inexact`]: TableProviderFilterPushDown::Inexact
251    /// # Example
252    ///
253    /// ```rust
254    /// # use std::any::Any;
255    /// # use std::sync::Arc;
256    /// # use arrow_schema::SchemaRef;
257    /// # use async_trait::async_trait;
258    /// # use datafusion_session::{TableProvider, Session};
259    /// # use datafusion_common::Result;
260    /// # use datafusion_expr::{Expr, TableProviderFilterPushDown, TableType};
261    /// # use datafusion_physical_plan::ExecutionPlan;
262    /// // Define a struct that implements the TableProvider trait
263    /// #[derive(Debug)]
264    /// struct TestDataSource {}
265    ///
266    /// #[async_trait]
267    /// impl TableProvider for TestDataSource {
268    /// # fn schema(&self) -> SchemaRef { todo!() }
269    /// # fn table_type(&self) -> TableType { todo!() }
270    /// # async fn scan(&self, s: &dyn Session, p: Option<&Vec<usize>>, f: &[Expr], l: Option<usize>) -> Result<Arc<dyn ExecutionPlan>> {
271    ///         todo!()
272    /// # }
273    ///     // Override the supports_filters_pushdown to evaluate which expressions
274    ///     // to accept as pushdown predicates.
275    ///     fn supports_filters_pushdown(&self, filters: &[&Expr]) -> Result<Vec<TableProviderFilterPushDown>> {
276    ///         // Process each filter
277    ///         let support: Vec<_> = filters.iter().map(|expr| {
278    ///           match expr {
279    ///             // This example only supports a between expr with a single column named "c1".
280    ///             Expr::Between(between_expr) => {
281    ///                 between_expr.expr
282    ///                 .try_as_col()
283    ///                 .map(|column| {
284    ///                     if column.name == "c1" {
285    ///                         TableProviderFilterPushDown::Exact
286    ///                     } else {
287    ///                         TableProviderFilterPushDown::Unsupported
288    ///                     }
289    ///                 })
290    ///                 // If there is no column in the expr set the filter to unsupported.
291    ///                 .unwrap_or(TableProviderFilterPushDown::Unsupported)
292    ///             }
293    ///             _ => {
294    ///                 // For all other cases return Unsupported.
295    ///                 TableProviderFilterPushDown::Unsupported
296    ///             }
297    ///         }
298    ///     }).collect();
299    ///     Ok(support)
300    ///     }
301    /// }
302    /// ```
303    fn supports_filters_pushdown(
304        &self,
305        filters: &[&Expr],
306    ) -> Result<Vec<TableProviderFilterPushDown>> {
307        Ok(vec![
308            TableProviderFilterPushDown::Unsupported;
309            filters.len()
310        ])
311    }
312
313    /// Get statistics for this table, if available
314    /// Although not presently used in mainline DataFusion, this allows implementation specific
315    /// behavior for downstream repositories, in conjunction with specialized optimizer rules to
316    /// perform operations such as re-ordering of joins.
317    fn statistics(&self) -> Option<Statistics> {
318        None
319    }
320
321    /// Return an [`ExecutionPlan`] to insert data into this table, if
322    /// supported.
323    ///
324    /// The returned plan should return a single row in a UInt64
325    /// column called "count" such as the following
326    ///
327    /// ```text
328    /// +-------+,
329    /// | count |,
330    /// +-------+,
331    /// | 6     |,
332    /// +-------+,
333    /// ```
334    ///
335    /// # See Also
336    ///
337    /// See [`DataSinkExec`] for the common pattern of inserting a
338    /// streams of `RecordBatch`es as files to an ObjectStore.
339    ///
340    /// [`DataSinkExec`]: https://docs.rs/datafusion-datasource/latest/datafusion_datasource/sink/struct.DataSinkExec.html
341    async fn insert_into(
342        &self,
343        _state: &dyn Session,
344        _input: Arc<dyn ExecutionPlan>,
345        _insert_op: InsertOp,
346    ) -> Result<Arc<dyn ExecutionPlan>> {
347        not_impl_err!("Insert into not implemented for this table")
348    }
349
350    /// Delete rows matching the filter predicates.
351    ///
352    /// Returns an [`ExecutionPlan`] producing a single row with `count` (UInt64).
353    /// Empty `filters` deletes all rows.
354    async fn delete_from(
355        &self,
356        _state: &dyn Session,
357        _filters: Vec<Expr>,
358    ) -> Result<Arc<dyn ExecutionPlan>> {
359        not_impl_err!("DELETE not supported for {} table", self.table_type())
360    }
361
362    /// Update rows matching the filter predicates.
363    ///
364    /// Returns an [`ExecutionPlan`] producing a single row with `count` (UInt64).
365    /// Empty `filters` updates all rows.
366    async fn update(
367        &self,
368        _state: &dyn Session,
369        _assignments: Vec<(String, Expr)>,
370        _filters: Vec<Expr>,
371    ) -> Result<Arc<dyn ExecutionPlan>> {
372        not_impl_err!("UPDATE not supported for {} table", self.table_type())
373    }
374
375    /// Remove all rows from the table.
376    ///
377    /// Should return an [ExecutionPlan] producing a single row with count (UInt64),
378    /// representing the number of rows removed.
379    async fn truncate(&self, _state: &dyn Session) -> Result<Arc<dyn ExecutionPlan>> {
380        not_impl_err!("TRUNCATE not supported for {} table", self.table_type())
381    }
382
383    /// Merge rows from a source into this table.
384    ///
385    /// The `source` is an [`ExecutionPlan`] representing the USING clause.
386    /// The `merge_schema` contains the target columns followed by the source
387    /// columns, preserving their logical qualifiers. Providers can use this
388    /// schema to resolve the logical expressions against the combined rows
389    /// they construct while executing the merge.
390    /// The `on` condition is the join predicate from the ON clause.
391    /// The `clauses` describe the WHEN MATCHED / WHEN NOT MATCHED actions.
392    ///
393    /// Returns an [`ExecutionPlan`] producing a single row with `count` (UInt64).
394    async fn merge_into(
395        &self,
396        _state: &dyn Session,
397        _source: Arc<dyn ExecutionPlan>,
398        _merge_schema: DFSchemaRef,
399        _on: Expr,
400        _clauses: Vec<MergeIntoClause>,
401    ) -> Result<Arc<dyn ExecutionPlan>> {
402        not_impl_err!("MERGE INTO not supported for {} table", self.table_type())
403    }
404}
405
406impl dyn TableProvider {
407    /// Returns `true` if the table provider is of type `T`.
408    ///
409    /// Prefer this over `downcast_ref::<T>().is_some()`. Works correctly when
410    /// called on `Arc<dyn TableProvider>` via auto-deref.
411    pub fn is<T: TableProvider>(&self) -> bool {
412        (self as &dyn Any).is::<T>()
413    }
414
415    /// Attempts to downcast this table provider to a concrete type `T`,
416    /// returning `None` if the provider is not of that type.
417    ///
418    /// Works correctly when called on `Arc<dyn TableProvider>` via auto-deref,
419    /// unlike `(&arc as &dyn Any).downcast_ref::<T>()` which would attempt to
420    /// downcast the `Arc` itself.
421    pub fn downcast_ref<T: TableProvider>(&self) -> Option<&T> {
422        (self as &dyn Any).downcast_ref()
423    }
424}
425
426/// Arguments for scanning a table with [`TableProvider::scan_with_args`].
427#[derive(Debug, Clone, Default)]
428pub struct ScanArgs<'a> {
429    filters: Option<&'a [Expr]>,
430    projection: Option<&'a [usize]>,
431    limit: Option<usize>,
432    statistics_requests: &'a [StatisticsRequest],
433}
434
435impl<'a> ScanArgs<'a> {
436    /// Set the column projection for the scan.
437    ///
438    /// The projection is a list of column indices from [`TableProvider::schema`]
439    /// that should be included in the scan results. If `None`, all columns are included.
440    ///
441    /// # Arguments
442    /// * `projection` - Optional slice of column indices to project
443    pub fn with_projection(mut self, projection: Option<&'a [usize]>) -> Self {
444        self.projection = projection;
445        self
446    }
447
448    /// Get the column projection for the scan.
449    ///
450    /// Returns a reference to the projection column indices, or `None` if
451    /// no projection was specified (meaning all columns should be included).
452    pub fn projection(&self) -> Option<&'a [usize]> {
453        self.projection
454    }
455
456    /// Set the filter expressions for the scan.
457    ///
458    /// Filters are boolean expressions that should be evaluated during the scan
459    /// to reduce the number of rows returned. All expressions are combined with AND logic.
460    /// Whether filters are actually pushed down depends on [`TableProvider::supports_filters_pushdown`].
461    ///
462    /// # Arguments
463    /// * `filters` - Optional slice of filter expressions
464    pub fn with_filters(mut self, filters: Option<&'a [Expr]>) -> Self {
465        self.filters = filters;
466        self
467    }
468
469    /// Get the filter expressions for the scan.
470    ///
471    /// Returns a reference to the filter expressions, or `None` if no filters were specified.
472    pub fn filters(&self) -> Option<&'a [Expr]> {
473        self.filters
474    }
475
476    /// Set the maximum number of rows to return from the scan.
477    ///
478    /// If specified, the scan should return at most this many rows. This is typically
479    /// used to optimize queries with `LIMIT` clauses.
480    ///
481    /// # Arguments
482    /// * `limit` - Optional maximum number of rows to return
483    pub fn with_limit(mut self, limit: Option<usize>) -> Self {
484        self.limit = limit;
485        self
486    }
487
488    /// Get the maximum number of rows to return from the scan.
489    ///
490    /// Returns the row limit, or `None` if no limit was specified.
491    pub fn limit(&self) -> Option<usize> {
492        self.limit
493    }
494
495    /// Specifies the statistics the caller may use when optimizing the query.
496    ///
497    /// This is intended to allow the `TableProvider` to cheaply provide
498    /// statistics that may help, such as those it has in an in-memory catalog
499    /// or from some other metadata source.
500    ///
501    /// `TableProvider`s read these via [`Self::statistics_requests()`]; anything
502    /// a `TableProvider` cannot answer cheaply it simply ignores. DataFusion's
503    /// own `TableProvider`s ignore this field — it exists so a request can be
504    /// threaded from a custom optimizer rule (which annotates
505    /// `TableScan::statistics_requests`) through to a custom `TableProvider`.
506    pub fn with_statistics_requests(
507        mut self,
508        statistics_requests: &'a [StatisticsRequest],
509    ) -> Self {
510        self.statistics_requests = statistics_requests;
511        self
512    }
513
514    /// Get the statistics requests for the scan. Empty if none were set.
515    ///
516    /// See [`Self::with_statistics_requests`] for more details
517    pub fn statistics_requests(&self) -> &'a [StatisticsRequest] {
518        self.statistics_requests
519    }
520}
521
522/// Result of a table scan operation from [`TableProvider::scan_with_args`].
523#[derive(Debug, Clone)]
524pub struct ScanResult {
525    /// The ExecutionPlan to run.
526    plan: Arc<dyn ExecutionPlan>,
527}
528
529impl ScanResult {
530    /// Create a new `ScanResult` with the given execution plan.
531    ///
532    /// # Arguments
533    /// * `plan` - The execution plan that will perform the table scan
534    pub fn new(plan: Arc<dyn ExecutionPlan>) -> Self {
535        Self { plan }
536    }
537
538    /// Get a reference to the execution plan for this scan result.
539    ///
540    /// Returns a reference to the [`ExecutionPlan`] that will perform
541    /// the actual table scanning and data retrieval.
542    pub fn plan(&self) -> &Arc<dyn ExecutionPlan> {
543        &self.plan
544    }
545
546    /// Consume this ScanResult and return the execution plan.
547    ///
548    /// Returns the owned [`ExecutionPlan`] that will perform
549    /// the actual table scanning and data retrieval.
550    pub fn into_inner(self) -> Arc<dyn ExecutionPlan> {
551        self.plan
552    }
553}
554
555impl From<Arc<dyn ExecutionPlan>> for ScanResult {
556    fn from(plan: Arc<dyn ExecutionPlan>) -> Self {
557        Self::new(plan)
558    }
559}
560
561/// A factory which creates [`TableProvider`]s at runtime given a URL.
562///
563/// For example, this can be used to create a table "on the fly"
564/// from a directory of files only when that name is referenced.
565#[async_trait]
566pub trait TableProviderFactory: Debug + Sync + Send {
567    /// Create a TableProvider with the given url
568    async fn create(
569        &self,
570        state: &dyn Session,
571        cmd: &CreateExternalTable,
572    ) -> Result<Arc<dyn TableProvider>>;
573}
574
575/// Describes arguments provided to the table function call.
576pub struct TableFunctionArgs<'e, 's> {
577    /// Call arguments.
578    exprs: &'e [Expr],
579    /// Session within which the function is called.
580    session: &'s dyn Session,
581}
582
583impl<'e, 's> TableFunctionArgs<'e, 's> {
584    /// Make a new [`TableFunctionArgs`].
585    pub fn new(exprs: &'e [Expr], session: &'s dyn Session) -> Self {
586        Self { exprs, session }
587    }
588
589    /// Get expressions passed as the called function arguments.
590    pub fn exprs(&self) -> &'e [Expr] {
591        self.exprs
592    }
593
594    /// Get a session where the table function is called.
595    pub fn session(&self) -> &'s dyn Session {
596        self.session
597    }
598}
599
600/// A trait for table function implementations
601pub trait TableFunctionImpl: Debug + Sync + Send + Any {
602    /// Create a table provider
603    #[deprecated(
604        since = "53.0.0",
605        note = "Implement `TableFunctionImpl::call_with_args` instead"
606    )]
607    fn call(&self, _exprs: &[Expr]) -> Result<Arc<dyn TableProvider>> {
608        internal_err!(
609            "TableFunctionImpl::call is not implemented. Implement TableFunctionImpl::call_with_args instead."
610        )
611    }
612
613    /// Create a table provider
614    fn call_with_args(&self, args: TableFunctionArgs) -> Result<Arc<dyn TableProvider>> {
615        #[expect(deprecated)]
616        self.call(args.exprs)
617    }
618}
619
620/// A table that uses a function to generate data
621#[derive(Clone, Debug)]
622pub struct TableFunction {
623    /// Name of the table function
624    name: String,
625    /// Function implementation
626    fun: Arc<dyn TableFunctionImpl>,
627}
628
629impl TableFunction {
630    /// Create a new table function
631    pub fn new(name: String, fun: Arc<dyn TableFunctionImpl>) -> Self {
632        Self { name, fun }
633    }
634
635    /// Get the name of the table function
636    pub fn name(&self) -> &str {
637        &self.name
638    }
639
640    /// Get the implementation of the table function
641    pub fn function(&self) -> &Arc<dyn TableFunctionImpl> {
642        &self.fun
643    }
644
645    /// Get the function implementation and generate a table
646    #[deprecated(
647        since = "53.0.0",
648        note = "Use `TableFunction::create_table_provider_with_args` instead"
649    )]
650    pub fn create_table_provider(&self, args: &[Expr]) -> Result<Arc<dyn TableProvider>> {
651        #[expect(deprecated)]
652        self.fun.call(args)
653    }
654
655    /// Get the function implementation and generate a table
656    pub fn create_table_provider_with_args(
657        &self,
658        args: TableFunctionArgs,
659    ) -> Result<Arc<dyn TableProvider>> {
660        self.fun.call_with_args(args)
661    }
662}