dbt-risingwave supports the upstream dbt function resource for a constrained first version of RisingWave UDF support.
This first version supports:
- SQL scalar functions
- JavaScript scalar functions through
config.language: javascript - external Python scalar functions through
config.language: python- with
config.link: http://host:port - optional
config.remote_name - optional
config.always_retry_on_network_error
- with
- JavaScript async options through adapter config:
asyncbatchalways_retry_on_network_error
- function creation from dbt
functions/resources - function references from models through
{{ function('name') }}(...) - function volatility config:
deterministic->IMMUTABLEstable->STABLEnon-deterministic->VOLATILE
Project layout:
functions/
price_for_xlarge.sql
price_for_xlarge.yml
models/
udf_example.sql
Function SQL:
select price * 2Function YAML:
functions:
- name: price_for_xlarge
description: Double the price
arguments:
- name: price
data_type: float
returns:
data_type: floatModel usage:
{{ config(materialized='view') }}
select {{ function('price_for_xlarge') }}(100::float8) as xlarge_priceProject layout:
functions/
price_for_xlarge_js.sql
price_for_xlarge_js.yml
models/
js_udf_example.sql
Function file:
export function price_for_xlarge_js(price) {
return price * 2;
}Function YAML:
functions:
- name: price_for_xlarge_js
config:
language: javascript
arguments:
- name: price
data_type: float8
returns:
data_type: float8Model usage:
{{ config(materialized='view') }}
select {{ function('price_for_xlarge_js') }}(100::float8) as xlarge_priceProject layout:
functions/
price_for_xlarge_py.sql
price_for_xlarge_py.yml
models/
py_udf_example.sql
Function file:
-- The adapter ignores the body for external Python UDFs.
-- The real implementation lives in the external UDF server.Function YAML:
functions:
- name: price_for_xlarge_py
config:
language: python
link: http://127.0.0.1:8815
arguments:
- name: price
data_type: float8
returns:
data_type: float8Model usage:
{{ config(materialized='view') }}
select {{ function('price_for_xlarge_py') }}(100::float8) as xlarge_priceThis adapter currently materializes SQL scalar functions with:
CREATE FUNCTION IF NOT EXISTS ...That contract has two important consequences:
- dbt can create and reference the function.
- dbt does not replace or update an existing RisingWave function body.
If the function definition changes, drop the function first or deploy it under a new name.
For JavaScript, the function body is emitted as:
CREATE FUNCTION IF NOT EXISTS ... LANGUAGE JAVASCRIPT AS $$ ... $$;The exported JavaScript function should match the dbt function name.
This uses an adapter-level workaround for current dbt-core limits. Upstream dbt function parsing currently only accepts .sql and .py files, so JavaScript UDFs are authored in functions/*.sql and switched to JavaScript with config.language: javascript.
For external Python, dbt-core's native python function contract still expects fields such as runtime_version and entry_point, but RisingWave external Python UDFs use AS 'remote_name' USING LINK 'http://host:port'. So this adapter authors external Python UDFs in functions/*.sql with config.language: python and adapter-specific config.
Current Python-specific contract:
- set
config.language: python - set
config.link - optional
config.remote_name; defaults to the dbt function name - optional
config.always_retry_on_network_error - you do not need to set
runtime_versionorentry_point; the adapter fills placeholders for dbt-core validation
Python UDFs are materialized as:
CREATE FUNCTION IF NOT EXISTS ... AS 'remote_name' USING LINK 'http://host:port';For embedded JavaScript scalar UDFs, the adapter also maps these function configs into RisingWave WITH (...) options:
functions:
- name: http_get_todo_name_js
config:
language: javascript
async: true
batch: false
always_retry_on_network_error: falseCurrent mapping:
config.async: true->WITH (async = true)config.batch: true->WITH (batch = true)config.always_retry_on_network_error: true->WITH (always_retry_on_network_error = true)
This is enough to support real async fetch(...) use cases such as HTTP GET and POST.
This first version does not support:
CREATE OR REPLACE FUNCTION- updating an existing function body through dbt
- overload-family management
- aggregate functions
- table functions
- default arguments
- native
.jsfunction resources in dbt-core - native
.pyfunction resources for RisingWave external Python UDFs
JavaScript UDF creation depends on RisingWave having embedded JavaScript UDF support enabled. If the cluster disables enable_embedded_javascript_udf, dbt function creation will fail at execution time.
External Python UDF creation requires the configured UDF server link to be reachable from RisingWave. If the UDF server is down or unreachable, function creation or execution will fail at runtime.
The overload limitation is especially important. The adapter only treats a function name as a manageable dbt relation when that name maps to a single signature inside the schema.
The live example and singular tests for this first version live in the companion project:
dbt_rw_nexmark
See its function example for a runnable RisingWave validation flow.