Skip to content

RFC: end-to-end UDF support #2353

Closed
Closed

Description

@ryzhyk

The goal of this RFC is to expose UDFs, already supported by the compiler, to the user.

Design

  • The user declares UDFs in SQL; the SQL compiler generates Rust prototypes [this is already implemented].
  • These prototypes serve as guidelines for the user (in addition to documentation describing SQL-to-Rust type mappings), who is responsible for implementing the functions in Rust. Specifically, the user supplies a udf.rs file and a udf.toml file, which the compiler server uses to generate a separate crate for UDFs. The reason for a separate crate is to avoid dependency clashes with the main circuit crate. This crate gets imported from the main generated Rust program.

API

  • Pipeline-related endpoints (POST, PUT, PATCH) will take two additional (optional) fields: rust and toml code.
  • The GET endpoint will retrieve these fields, along with compiler-generated UDF prototypes.

WebConsole

  • Add two additional tabs for Rust and toml code.
  • We also need a way to expose compiler-generated prototypes in the UI. The simplest way is in yet another read-only tab.

Error handling

Compilation errors in UDFs should surface as Rust compilation errors. This should work already without any changes. Line numbers should match line numbers in the Rust code, since we will use the user-provided Rust file as is.

Security

In this design we trust user's Rust code. If we want a more secure version of UDFs, we can build support for wasm-based UDFs on top of this later. The pipeline manager can implement a security policy disallowing non-privileged users to submit Rust UDFs.

Discussion

The most unpleasant part of this is keeping compiler-generated prototypes and user rust code in sync. I can imagine the UI (or better yet the backend) automatically merging changes to UDFs declared in SQL into udf.rs, but that's not trivial. Better suggestions are welcome.

Activity

  1. added
    documentationImprovements or additions to documentation
    RFCRequest for Comments
    SQL compilerRelated to the SQL compiler
    Web ConsoleRelated to the browser based UI
    APIDistributed system APIs
    Pipeline managerPipeline manager (API, API server, runner, compiler server)
    python-sdkIssues related to the feldera python sdk
    on Aug 27, 2024
  2. lalithsuresh commented on Aug 28, 2024

    @lalithsuresh
    Contributor

    The most unpleasant part of this is keeping compiler-generated prototypes and user rust code in sync. I can imagine the UI (or better yet the backend) automatically merging changes to UDFs declared in SQL into udf.rs, but that's not trivial. Better suggestions are welcome.

    If I understand this correctly, this is probably where a Postgres style create function blah(integer) returning integer as "udfs::foo" syntax comes in handy. The compiler can generate the prototype function blah that calls udfs::foo with the correct arguments, and the user stays focused on writing udfs::foo(). If udfs:foo is missing or the types don't line up, you get a meaningful Rust error message. This also keeps the compiler-generated code/prototypes separate from udf.rs, without any merges /into/ udf.rs.

    So I imagine it'll be something like:

    • User writes create function blah(integer) returning integer as "udfs::foo"
    • Compiler looks at SQL and generates a udf-crate/prototypes.rs which generates a function call blah that delegates to udfs::foo().
    • We add udf.rs into udf-crate/udfs.rs
    • The rust compiler compiles udf-crate, and so prototypes.rs expects calls in udf.rs with the correct signature (name, types, etc.).
  3. ryzhyk commented on Aug 28, 2024

    @ryzhyk
    ContributorAuthor

    Unless you want Rust functions to have different names from the SQL declarations, there's no need for the as "udfs::foo" part. Likewise, prototypes.rs might be an unnecessary middleman, although it could actually be useful as a shim between SQL and Rust, e.g., to inject line numbers in error message or deal with NULLs automatically. Need to think about it.

    This doesn't solve the problem of the user having to guess (or rather derive from documentation) function signatures. For this, I was thinking of exposing compiler-generated signatures to the user through the API, so they can copy/paste the definitions, rather than making them up manually.

  4. lalithsuresh commented on Aug 28, 2024

    @lalithsuresh
    Contributor

    Without the middleman behind-the-scenes, you'll likely have trouble with synchronization (SQL <-> udf.rs and the circular nature of it). With the middleman, the synchronization is simple: the compiler always generates prototypes.rs mechanically from the SQL, that expects functions with a particular signature. When the rust compiler kicks in, the user should hopefully get back a meaningful error message from the Rust compiler that specifies the signatures missing.

  5. lalithsuresh commented on Aug 28, 2024

    @lalithsuresh
    Contributor

    This doesn't solve the problem of the user having to guess (or rather derive from documentation) function signatures. For this, I was thinking of exposing compiler-generated signatures to the user through the API, so they can copy/paste the definitions, rather than making them up manually.

    This is definitely good UX to tell the user the signature. That said, as a first cut, you can skip it if you use the middleman and just rely on the Rust compiler's error messages.

  6. Karakatiza666 commented on Aug 28, 2024

    @Karakatiza666
    Contributor

    Hopefully the first version of UDF-related API will have at least some way to get compiler-generated signatures. It can easily be displayed in UI as-is, and later that can be changed to a more convenient UX

  7. ryzhyk commented on Aug 28, 2024

    @ryzhyk
    ContributorAuthor

    This doesn't solve the problem of the user having to guess (or rather derive from documentation) function signatures. For this, I was thinking of exposing compiler-generated signatures to the user through the API, so they can copy/paste the definitions, rather than making them up manually.

    This is definitely good UX to tell the user the signature. That said, as a first cut, you can skip it if you use the middleman and just rely on the Rust compiler's error messages.

    Yes, this is what we're building.

  8. lalithsuresh commented on Oct 8, 2024

    @lalithsuresh
    Contributor

    Good to close?

  9. ryzhyk commented on Oct 8, 2024

    @ryzhyk
    ContributorAuthor

    Not quite, working on docs + UI improvements.

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

Metadata

Metadata

Labels

APIDistributed system APIsPipeline managerPipeline manager (API, API server, runner, compiler server)RFCRequest for CommentsSQL compilerRelated to the SQL compilerWeb ConsoleRelated to the browser based UIdocumentationImprovements or additions to documentationpython-sdkIssues related to the feldera python sdk

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions