dbt test block 简单说明二
以前简单说明过dbt test block,属于一个自定义的扩展,同时也简单说明了一些使用,实际上dbt 的test 也是一个物化处理,支持物化的类型为test
参考manifest
- test 类型的定义
因为存在此定义,按照dbt 的处理会基于物化的规则来进行sql 生成处理
test 物化参考定义
- 参考macro 定义
老版本的global,新版本的adapter 中
- test 处理
{%- materialization test, default -%}
{% set relations = [] %}
# 可以看到会有一个是否保存结果的配置,这个官方文档也有说明
{% if should_store_failures() %}
{% set identifier = model['alias'] %}
{% set old_relation = adapter.get_relation(database=database, schema=schema, identifier=identifier) %}
{% set target_relation = api.Relation.create(
identifier=identifier, schema=schema, database=database, type='table') -%} %}
{% if old_relation %}
{% do adapter.drop_relation(old_relation) %}
{% endif %}
{% call statement(auto_begin=True) %}
{{ create_table_as(False, target_relation, sql) }}
{% endcall %}
{% do relations.append(target_relation) %}
{% set main_sql %}
select *
from {{ target_relation }}
{% endset %}
{{ adapter.commit() }}
{% else %}
{% set main_sql = sql %}
{% endif %}
# 以下几个是通用的配置,实际官方文档也有介绍
{% set limit = config.get('limit') %}
{% set fail_calc = config.get('fail_calc') %}
{% set warn_if = config.get('warn_if') %}
{% set error_if = config.get('error_if') %}
{% call statement('main', fetch_result=True) -%}
# get_test_sql 获取生成test sql 的macro
{{ get_test_sql(main_sql, fail_calc, warn_if, error_if, limit)}}
{%- endcall %}
{{ return({'relations': relations}) }}
{%- endmaterialization -%}
- get_test_sql 处理
会结合传递的fail_calc,warn_if,error_if,limit
{% macro get_test_sql(main_sql, fail_calc, warn_if, error_if, limit) -%}
{{ adapter.dispatch('get_test_sql', 'dbt')(main_sql, fail_calc, warn_if, error_if, limit) }}
{%- endmacro %}
{% macro default__get_test_sql(main_sql, fail_calc, warn_if, error_if, limit) -%}
select
{{ fail_calc }} as failures,
{{ fail_calc }} {{ warn_if }} as should_warn,
{{ fail_calc }} {{ error_if }} as should_error
from (
{{ main_sql }}
{{ "limit " ~ limit if limit != none }}
) dbt_internal_test
{%- endmacro %}
- get_where_subquery macro 处理
如果阅读官方文档了,可以看到会有关于where 的处理,同时也可以自定义,当然目前此部分与其他基于dispatch 处理的模式不太一样,是一个代码层处理的,参考处理
# 此处是上下文的
def _build_test_namespace(self):
depends_on_macros = []
# all generic tests use a macro named 'get_where_subquery' to wrap 'model' arg
# see generic_test_builders.build_model_str
get_where_subquery = self.macro_resolver.macros_by_name.get("get_where_subquery")
if get_where_subquery:
depends_on_macros.append(get_where_subquery.unique_id)
if self.model.depends_on and self.model.depends_on.macros:
depends_on_macros.extend(self.model.depends_on.macros)
lookup_macros = depends_on_macros.copy()
for macro_unique_id in lookup_macros:
lookup_macro = self.macro_resolver.macros.get(macro_unique_id)
if lookup_macro:
depends_on_macros.extend(lookup_macro.depends_on.macros)
macro_namespace = TestMacroNamespace(
self.macro_resolver, self._ctx, self.model, self.thread_ctx, depends_on_macros
)
self.namespace = macro_namespace
解析部分
generic_test_builders 会进行test 模型macro 的处理
def build_model_str(self):
targ = self.target
if isinstance(self.target, UnparsedModelUpdate):
if self.version:
target_str = f"ref('{targ.name}', version='{self.version}')"
else:
target_str = f"ref('{targ.name}')"
elif isinstance(self.target, UnparsedNodeUpdate):
target_str = f"ref('{targ.name}')"
elif isinstance(self.target, UnpatchedSourceDefinition):
target_str = f"source('{targ.source.name}', '{targ.table.name}')"
return f"{{{{ get_where_subquery({target_str}) }}}}"
生成元数据信息
说明
dbt 实际上几个比较核心的功能基本都是基于物化配置处理的,只是部分内部实现上稍有一些不同,同时注意dbt test 命令应该在dbt run 执行之后,否则
数据结果可能会不符合实际(这个流程也比较符合实际数据处理的流程)
参考资料
core/dbt/context/providers.py
core/dbt/parser/generic_test.py
https://docs.getdbt.com/reference/data-test-configs
https://docs.getdbt.com/reference/resource-configs/where