jinja2 默认是没有return macro 的,dbt 在实现的时候比较有意思,通过一个exception 触发的,以下是简单说明
参考使用
- 一个包含return 的macro
{% macro demoapp(name,version) %}
{% if version =='v1' %}
{{return("appdemo") }}
{% else %}
{{return("dalongdemo")}}
{% endif %}
{{name }} -- {{version}}
{% endmacro %}
- 简单说明
以上如果version 为v1 会返回appdemo ,否则为dalongdemo,后边部分是不会执行的,一个使用生成效果
{{ config(materialized='table') }}
select * from {{demoapp('dalong','v1')}}
编译sql
dbt 内部实现简单说明
实际上return 为dbt 通过context 传递为jinja2 模版引擎的,return 实现是一个exception,这样执行就异常了,但是dbt 自己处理了异常
- context 变量定义
@contextmember("return")
@staticmethod
def _return(data: Any) -> NoReturn:
"""The `return` function can be used in macros to return data to the
caller. The type of the data (`dict`, `list`, `int`, etc) will be
preserved through the return call.
:param data: The data to return to the caller
> macros/example.sql:
{% macro get_data() %}
{{ return([1,2,3]) }}
{% endmacro %}
> models/my_model.sql:
select
-- getdata() returns a list!
{% for i in getdata() %}
{{ i }}
{% if not loop.last %},{% endif %}
{% endfor %}
"""
raise MacroReturn(data)
- MacroReturn 实现
class MacroReturn(DbtBaseException):
"""This is how we return a value from a macro, not an exception.
Hack of all hacks
"""
def __init__(self, value) -> None:
self.value = value
- 调用部分
dbt_common/clients/jinja.py BaseMacroGenerator 类
def call_macro(self, *args, **kwargs):
# called from __call__ methods
if self.context is None:
raise DbtInternalError("Context is still None in call_macro!")
assert self.context is not None
macro = self.get_macro()
with self.exception_handler():
try:
return macro(*args, **kwargs)
except MacroReturn as e:
return e.value
说明
dbt 对于renturn macro 的实现比较有意思,实际上是通过raise exception 实现的,这样jinja2的模版就具有了特定条件返回执行的能力了
参考资料
dbt_common/clients/jinja.py
dbt_common/exceptions/macros.py
core/dbt/context/base.py