Skip to content

Construction of user-defined table functions (UDTFs) should be async to allow for async schemas #10889

Open

Description

@matthewgapp

Is your feature request related to a problem or challenge?

Accessing a TableProviders schema is a sync function call (see here). This means that the TableProvider must know its schema before construction.

DataFusion recently introduced TableFunctionImpl, which allows users to define a function to create a TableProvider. Unfortunately, this call method is sync, meaning that the user-defined table function must know its schema upfront in a non-blocking way. This isn't possible when implementing TableProviders, which might infer their schema async, like an HTTP connector that can connect to arbitrary sources with payloads only known once the response is streaming in.

Describe the solution you'd like

I propose we make the call method async to allow for async schemas and thus async table provider construction.

current code

use super::TableProvider;

use datafusion_common::Result;
use datafusion_expr::Expr;

use std::sync::Arc;

/// A trait for table function implementations
pub trait TableFunctionImpl: Sync + Send {
    /// Create a table provider
    fn call(&self, args: &[Expr]) -> Result<Arc<dyn TableProvider>>;
}

/// A table that uses a function to generate data
pub struct TableFunction {
    /// Name of the table function
    name: String,
    /// Function implementation
    fun: Arc<dyn TableFunctionImpl>,
}

impl TableFunction {
    /// Create a new table function
    pub fn new(name: String, fun: Arc<dyn TableFunctionImpl>) -> Self {
        Self { name, fun }
    }

    /// Get the name of the table function
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Get the function implementation and generate a table
    pub fn create_table_provider(&self, args: &[Expr]) -> Result<Arc<dyn TableProvider>> {
        self.fun.call(args)
    }
}

where the new trait would looks something like

/// A trait for table function implementations
pub trait TableFunctionImpl: Sync + Send {
    /// Create a table provider
    async fn call(&self, args: &[Expr]) -> Result<Arc<dyn TableProvider>>;
}

Describe alternatives you've considered

I've worked around this by creating the table outside of data fusion, but I would prefer to use the table functions to achieve the same thing.

Additional context

No response

Activity

  1. alamb commented on Jun 14, 2024

    @alamb
    Contributor

    I think this proposal sounds like a great idea to me, FWIW

    Thank you for the suggestion @matthewgapp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions