上一篇介紹了 BigQuery UDF 以及為何我們團隊想要在 dbt 上實作,這篇就來介紹是如何透過 macro 來實際實作。
#macros/ddl_generator.sql
{% macro ddl_generator(function_name, type, params, reture_type) %}
{%- set params_string -%}
{%- for param in params -%}
{{ param }}
{%- if not loop.last -%}, {%- endif -%}
{%- endfor -%}
{%- endset -%}
{% set return_string = '' %}
{% if type == 'FUNCTION' %}
{% set return_string = 'RETURNS' + return_type %}
{% endif %}
CREATE OR REPLACE {{ type }} `joshua-1000.udf.{{ function_name }}` ({{ params_string }}) {{ return_string }} AS
{% endmacro %}
#macros/udf/parse_datetime.sql
{% macro parse_datetime(env) %}
{{
ddl_generator(
function_name = 'parse_datetime',
type = 'FUNCTION',
params = [
'timestamp_str STRING'
],
return_type = 'DATETIME'
)
}}
(
PARSE_DATETIME('%Y-%m-%d', timestamp_str)
)
{% endmacro %}
{% if env == 'ci' %}
{% set dataset = 'udf_ci' %}
{% else %}
{% set dataset = 'udf' %}
# compile 的結果
CREATE OR REPLACE FUNCTION `joshua-1000.{{ dataset }}.parse_datetime` (timestamp_str STRING) RETURNS DATETIME(
PARSE_DATETIME('%Y-%m-%d', timestamp_str)
)
#macros/create_udfs.sql
{% macro create_udfs(function_list=[], table_function_list=[], env=none) %}
{% for function in function_list %}
{% set macro_func = context.get(function)%}
# 在 BQ 執行 create function 語法
{% do run_query(macro_func(env)) %};
{% endfor %}
# 因為 table function 有可能用到 function,所以先建立 function
{% for table_function in table_function_list %}
{% set macro_func = context.get(table_function)%}
{% do run_query(macro_func(env)) %};
{% endfor %}
{% endmacro %}
# CI
dbt run-operation create_udfs --args '{function_list = ['parse_datetime'], env='ci'}'
# CD
dbt run-operation create_udfs --args '{function_list = ['parse_datetime']}'
透過以上 4 個步驟就能透過 macro 來達到在 dbt 開發 UDF ,但其實這個方法隱含了很多缺點,在下篇文章中會說明 macro 管理 UDF 會有哪些缺點,以及我們團隊後續發現的解法。