dbt中post_hook调用宏提前执行而非物化后,原因咨询
dbt中post_hook调用宏与直接写DDL的执行顺序差异原因
现象概述
- 在dbt里,post_hook直接配置DDL字符串时,会在模型物化完成后执行,符合预期
- 但将DDL逻辑封装成宏,在post_hook里调用该宏时,宏的逻辑会提前到模型物化前执行,导致依赖的表被提前删除,模型构建报错
问题复现代码
报错场景的模型配置
{{ config( materialized = 'view' ,pre_hook = testCreate() ,post_hook = testDrop() ) }} select * from testdb.sdl.xxx
对应的宏定义
{%- macro testCreate() -%} {% if execute %} {{ log("creating table...", info=true) }} {%- do run_query("create or replace table testdb.sdl.xxx as (select 1 x)") -%} {% endif %} {%- endmacro -%} {%- macro testDrop() -%} {% if execute %} {{ log("dropping table...", info=true) }} {%- do run_query("drop table if exists testdb.sdl.xxx") -%} {% endif %} {%- endmacro -%}
错误执行日志
08:35:42 1 of 1 START sql view model SDL.test ........................................... [RUN] 08:35:42 creating table... 08:35:44 dropping table... 08:35:44 1 of 1 ERROR creating sql view model SDL.test .................................. [ERROR in 2.01s] 08:35:45 08:35:45 Finished running 1 view model in 0 hours 0 minutes and 3.99 seconds (3.99s). 08:35:45 08:35:45 Completed with 1 error, 0 partial successes, and 0 warnings: 08:35:45 08:35:45 Database Error in model test (models/test.sql) 002003 (42S02): SQL compilation error: Object 'TESTDB.SDL.XXX' does not exist or not authorized. compiled code at /tmp/dbt/target/run/test/models/test.sql
正常执行的配置(直接写DDL)
{{ config( materialized = 'view' ,pre_hook = "create or replace table testdb.sdl.xxx as (select 1 x)" ,post_hook = "drop table if exists testdb.sdl.xxx" ) }} select * from testdb.sdl.xxx
正常执行日志
01:48:25.055508 [info ] [Thread-2 (]: 1 of 1 START sql view model SDL.test ........................................... [RUN] 01:48:27.129852 [info ] [Thread-2 (]: 1 of 1 OK created sql view model SDL.test ...................................... [SUCCESS 1 in 2.07s]
核心原因
dbt的配置处理分编译阶段和运行阶段,两者的行为差异导致了这个问题:
直接写DDL字符串的情况:
- 编译阶段仅将DDL字符串作为钩子内容保存,不会执行任何数据库操作
- 运行阶段严格按照
pre_hook → 模型物化 → post_hook的顺序,依次执行这些DDL语句
直接调用宏(带括号
testDrop())的情况:- 宏在编译阶段就会被立即解析执行,而宏里的
run_query函数会直接触发数据库操作(因为execute在非dry run的编译阶段为True) - 这就跳过了post_hook的既定执行顺序,宏里的删表逻辑提前在模型编译时完成,等模型开始物化时,依赖的表已经不存在了
- 宏在编译阶段就会被立即解析执行,而宏里的
关键细节
dbt的execute变量在正常编译(非语法检查的dry run)时为True,所以宏里的{% if execute %}条件会成立,run_query直接在编译阶段执行SQL,而非被当作钩子语句延迟到运行阶段。
如果要让宏配合post_hook正常工作,应该让宏返回SQL字符串,而非直接在宏里执行run_query:
修正后的宏定义
{%- macro testCreate() -%} create or replace table testdb.sdl.xxx as (select 1 x) {%- endmacro -%} {%- macro testDrop() -%} drop table if exists testdb.sdl.xxx {%- endmacro -%}
这样配置post_hook = testDrop()时,宏会返回DDL字符串,dbt会将其作为钩子语句,在模型物化完成后执行,就能恢复正常顺序。
内容的提问来源于stack exchange,提问作者whetstone
相关产品推荐
相关产品推荐

