Skip to main content
Functions are SQL or Python definitions under 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 under functions/sql/. Each file has a FUNCTION() header declaring arguments and return type, followed by a SQL expression body:

Python UDFs

Place Python function files under functions/python/. Each file has exactly one function decorated with @udf(...):
Python UDFs are deployed as warehouse-native functions (Snowflake Python UDFs, BigQuery remote functions, etc.). The @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"):
The function name is the file stem (filename without extension). __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 under functions/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 over fact_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.

Managed table function dependencies

Models call managed table functions with __table_fn("name")(arguments...):
The name must be double quoted and the second argument list is required, including for a zero-argument function. SQLBuild validates that the target exists, is a table function, and receives the declared number of arguments. The reference creates a typed dependency in the project DAG. Selecting a consuming model also selects the required function, function changes propagate to consumers, and cycles through models and functions are rejected. SQLBuild treats the call as an opaque relation for SQL analysis and uses the function’s declared returns table(...) columns for star expansion and terminal lineage. Incremental state still belongs to the consuming model. A table function does not own a cursor interval or retain execution state.

Using table functions

Applications and analysts can call the deployed warehouse function directly:

References inside functions

SQL functions can reference the same resources as models: These references are resolved at compile time and create DAG edges. If a referenced model changes, the function is redeployed. A SQL function uses macros, constants, and enums available from its file under functions/sql/. See How Visibility Works to limit declarations to one function folder or to that folder and its children.

Change propagation

Functions participate in fingerprint-based change detection. Their identity includes dependencies and declared return contracts in addition to the function body and runtime metadata. If a function changes, SQLBuild redeploys it and marks dependent models as changed.

Project layout

Python UDF options

The @udf decorator accepts these keyword arguments:

Adapter support

All four supported adapters implement SQL UDFs, Python UDFs, and table functions: Future adapters may not support all function types. SQLBuild raises a clear error if a function type is unsupported by the configured adapter.