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}