functions/ that SQLBuild compiles and deploys to the warehouse alongside your models. They participate in the DAG - if a function definition changes, every model that uses it is rebuilt.
Scalar UDFs
Scalar UDFs return a single value per row. They can be written in SQL or Python.SQL UDFs
Place SQL function files underfunctions/sql/. Each file has a FUNCTION() header declaring arguments and return type, followed by a SQL expression body:
Python UDFs
Place Python function files underfunctions/python/. Each file has exactly one function decorated with @udf(...):
@udf decorator is used for static discovery only - SQLBuild parses the AST without importing your code.
Using scalar UDFs
Reference scalar UDFs in models with__udf("name"):
__udf() resolves to the adapter-native function call at compile time.
Table functions
Table functions return multiple rows and columns. They are written in SQL underfunctions/sql/ with a returns table(...) declaration:
Why table functions exist
Table functions are designed as an alternative to final-layer views for cases where views don’t push predicates efficiently. A view overfact_orders with a WHERE customer_id = ? filter may scan the entire table if the engine doesn’t push the predicate down. A table function accepts the filter as an argument and guarantees the predicate is applied at execution time.
Table functions are terminal
Table functions sit at the edge of the DAG, facing the consumer. They can reference models, seeds, sources, and other functions - but models cannot reference table functions. This is enforced at compile time. The semantic reason: table functions are parameterized queries meant to be called by applications or analysts, not intermediate pipeline steps. Interleaving them into the model DAG would break the clean separation between pipeline computation and consumer-facing access patterns.Using table functions
Table functions are called directly in SQL contexts that support table-valued functions:References inside functions
SQL functions can reference the same resources as models:| Reference | Syntax |
|---|---|
| Model | __ref("model_name") |
| Seed | __seed("seed_name") |
| Source | __source("source_name") |
| Scalar UDF | __udf("function_name") |
Change propagation
Functions participate in fingerprint-based change detection. If a function’s SQL body or Python source changes, SQLBuild redeploys the function and marks all dependent models as needing a rebuild.Project layout
Python UDF options
The@udf decorator accepts these keyword arguments:
| Argument | Required | Description |
|---|---|---|
arguments | Yes | Dict mapping argument names to SQL types |
returns | Yes | SQL return type string |
runtime_version | No | Python runtime version (e.g. "3.11") |
entry_point | No | Function name to use as the entry point (defaults to the decorated function name) |
packages | No | List of Python packages required at runtime |
Adapter support
All four supported adapters implement SQL UDFs, Python UDFs, and table functions:| Feature | DuckDB | Snowflake | BigQuery | Databricks |
|---|---|---|---|---|
| SQL UDFs | Yes | Yes | Yes | Yes |
| Python UDFs | Yes | Yes | Yes | Yes |
| Table functions | Yes | Yes | Yes | Yes |

