Support UDTFs in information_schema.routines / SHOW FUNCTIONS#23438
Conversation
f297568 to
55cb62d
Compare
There was a problem hiding this comment.
Pull request overview
This PR extends DataFusion’s SQL discovery surfaces to include table functions (UDTFs) in information_schema.routines and SHOW FUNCTIONS, addressing the gap where only scalar/aggregate/window UDFs were listed.
Changes:
- Snapshot
SessionState.table_functionsintoInformationSchemaProviderso UDTFs can be exposed without introducing crate cycles. - Emit
information_schema.routinesandinformation_schema.parametersrows for UDTFs (syntheticOUTparameter row withdata_type = 'TABLE'). - Rewrite
SHOW FUNCTIONSSQL to start fromOUTparameter rows andLEFT JOININparameter rows so functions with no inputs can appear.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
datafusion/sql/src/statement.rs |
Rewrites SHOW FUNCTIONS to be compatible with functions that lack IN parameters (e.g., UDTFs). |
datafusion/core/src/execution/session_state.rs |
Passes the session’s registered table functions into the per-query InformationSchemaProvider. |
datafusion/catalog/src/information_schema.rs |
Stores table functions in InformationSchemaConfig and emits corresponding routines / parameters rows. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| o.specific_name function_name, | ||
| o.data_type return_type, | ||
| array_agg(i.parameter_name ORDER BY i.ordinal_position ASC) parameters, | ||
| array_agg(i.data_type ORDER BY i.ordinal_position ASC) parameter_types |
| // Table functions (UDTFs) don't have scalar signatures; their return | ||
| // type is always a table, so emit a single row per UDTF with | ||
| // routine_type = "TABLE" and data_type = "TABLE". |
| // Note: use LEFT JOIN from OUT rows to IN rows so functions without | ||
| // input parameters (notably UDTFs / table functions) still appear. | ||
| let query = format!( |
55cb62d to
3ede8a3
Compare
3ede8a3 to
129e008
Compare
- add table_functions field to InformationSchemaConfig + builder - emit UDTFs from make_routines (function_type=TABLE, data_type=TABLE) - emit synthetic OUT parameter for UDTFs so JOIN in SHOW FUNCTIONS resolves - rewrite SHOW FUNCTIONS SQL as OUT LEFT JOIN IN so args-less UDTFs surface - wire SessionState.table_functions snapshot into InformationSchemaProvider
129e008 to
3fb1e9a
Compare
Bumps DataFusion to 6363fbe (Support UDTFs in information_schema.routines / SHOW FUNCTIONS, apache/datafusion equivalent open as apache/datafusion#23438).
Bumps DataFusion to 6363fbe (Support UDTFs in information_schema.routines / SHOW FUNCTIONS, apache/datafusion equivalent open as apache/datafusion#23438).
Bumps DataFusion to 6363fbe (Support UDTFs in information_schema.routines / SHOW FUNCTIONS, apache/datafusion equivalent open as apache/datafusion#23438).
alamb
left a comment
There was a problem hiding this comment.
Makes sense to me -- thank you @zhuqi-lucas
| #[derive(Clone, Debug)] | ||
| struct InformationSchemaConfig { | ||
| catalog_list: Arc<dyn CatalogProviderList>, | ||
| table_functions: HashMap<String, Arc<TableFunction>>, |
There was a problem hiding this comment.
I don't understand why we have to treat TableFunctions specially -- the PR descriptio says it is to avoid a crate dependency cycle, but I don't understand why the same thing doesn't apply to Scalar and Aggregate functons
Mabe we just need to move a definition of TableFunction to datafusion-expr 🤔 But maybe that is not possible
There was a problem hiding this comment.
Thanks @alamb for good catch, refreshed the PR description with the concrete cycle explanation.
TableFunction lives in datafusion-catalog, which already depends on datafusion-session — putting table_functions() on the Session trait reverses that edge. Scalar/Agg live in datafusion-expr (below session), so no cycle there. Same root as #23348 — I'll open a follow-up PR that moves TableFunction down and deletes with_table_functions().
|
Thank you @alamb and @xudong963 for review, merged to main. |
Which issue does this PR close?
Rationale for this change
information_schema.routinesandSHOW FUNCTIONScurrently enumerate only scalar / aggregate / window UDFs. Table functions (UDTFs) registered viaSessionContext::register_udtfare omitted, making them undiscoverable through SQL. Discovery matters because downstream tooling (DataFusion CLI, Massive's atlas SQL surface, dbt-datafusion, etc.) uses these SQL surfaces to list available functions.What changes are included in this PR?
1. Snapshot table functions into the information_schema provider.
Session::table_functions()cannot exist on theSessiontrait today:TableFunctionlives indatafusion-catalog, which already depends ondatafusion-session(bothTableProvider::scanandTableFunctiontake&dyn Session). Adding a trait method returning&HashMap<String, Arc<TableFunction>>would reverse that edge and create a crate-dependency cycle.ScalarUDF/AggregateUDF/WindowUDFdon't hit this because they live indatafusion-expr, which sits belowdatafusion-session.This is the same underlying constraint as #23348. A follow-up PR will move
TableFunction(or hoist the relevant traits) so the builder can be deleted.For this PR,
SessionState.table_functionsis snapshotted intoInformationSchemaProviderviawith_table_functions()at construction. The provider is built per-query, so the snapshot stays fresh.2. Emit UDTF rows in
information_schema.routinesonly.make_routines: one row per UDTF withroutine_type = "FUNCTION",function_type = "TABLE",data_type = "TABLE".UDTFs deliberately do NOT appear in
information_schema.parameters. A same-named scalar UDF (e.g.generate_seriesexists as both a scalar UDF infunctions-nestedand a UDTF infunctions-table) would cross-join with a UDTF row keyed only by (name, rid) and produce spuriousTABLE-typed variants of every scalar signature inSHOW FUNCTIONS.3. Rewrite the
SHOW FUNCTIONSSQL to UNION UDTFs directly fromroutines.The old query joined
parameters p (INNER)requiring both IN and OUT rows before joiningroutines. UDTFs have no IN parameters, so they fell out. The new plan sources scalar / aggregate / window signatures fromparametersas before, and UDTFs from a separateUNIONbranch that readsroutinesdirectly (guarded byfunction_type = TABLE). This avoids the cross-join blowup mentioned above.Are these changes tested?
Verified locally by registering a UDTF and running
SHOW FUNCTIONS:Happy to add sqllogictests in
datafusion/sqllogictest/test_files/information_schema.sltif maintainers prefer.Are there any user-facing changes?
Yes:
SHOW FUNCTIONSoutput now includes table functions.information_schema.routinesgains one row per registered UDTF.information_schema.parametersis intentionally unchanged (see rationale in point 2 above). Existing rows are unchanged.