Defining a Scalar UDTF
Use the@scalar_udtf decorator on a function that yields output rows. Geneva infers the output schema from the return type annotation.
Input parameters are bound to source columns by name — the parameter video_path binds to source column video_path, just like standard UDFs.
List return pattern
If you prefer to build the full list in memory rather than yielding, you can return alist instead of an Iterator:
Batched scalar UDTF
For vectorized processing, usebatch=True. The function receives Arrow arrays and returns a RecordBatch of expanded rows. Because the return type pa.RecordBatch cannot be inferred, you must supply output_schema explicitly:
Creating a Scalar UDTF View
Scalar UDTFs use thecreate_scalar_udtf_view API:
The query parameter controls which source columns are inherited. Columns listed in .select() are carried into every child row automatically.
Inherited Columns
Child rows automatically include the parent’s columns — no manual join required. The columns available in the child table are determined by the query’s.select():
videos table (source)
clips table (derived, 1:N)
The first three rows come from the
/v/a.mp4 source row, the last two from /v/b.mp4. Inherited columns (video_path, metadata) are carried over automatically; clip_start, clip_end, and clip_bytes are generated by the UDTF.
Adding Computed Columns After Creation
Since scalar UDTF views are materialized views, you can add UDF-computed columns to the child table and backfill them: This is a powerful pattern: expand source rows with a scalar UDTF, then enrich the expanded rows with standard UDFs.Incremental Refresh
Scalar UDTFs support incremental refresh, just like standard materialized views:- New source rows: The UDTF runs on new rows, inserting child rows.
- Deleted source rows: Child rows linked to the deleted parent are cascade-deleted.
- Updated source rows: Old children are deleted, UDTF re-runs, new children inserted.