feat: replace regex+macro MV external target with config.get_rendered#645
feat: replace regex+macro MV external target with config.get_rendered#645dataders wants to merge 3 commits into
Conversation
- materialized_view: add `target_table` config key as ergonomic alternative to materialization_target_table() macro call. Config takes precedence; comment pattern retained for backward compat. Preferred path for dbt-fusion which resolves DAG deps via static analysis (issue ClickHouse#644). - incremental: accept 'delete+insert' as alias for 'delete_insert' strategy, matching the hyphen-style name used in dbt-fusion. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Removes the materialization_target_table() macro + SQL comment regex
approach for pointing a ClickHouse materialized view at an external
target table, replacing it with config.get_rendered('target_table').
Requires dbt-labs/dbt-core#12965.
Before:
{{ materialization_target_table(ref('my_target')) }}
SELECT ...
After:
{{ config(materialized='materialized_view', target_table=ref('my_target')) }}
SELECT ...
Changes:
- materialized_view.sql: top-level dispatcher uses config.get_rendered()
instead of config.get() + regex fallback. strip_identifier_quotes and
get_relation_from_string helpers removed; target_table_relation is now
a RelationProxy used directly in TO clause and INSERT.
- materialization_target_table() macro kept but emits a deprecation warning.
- Tests updated throughout to use the new config(target_table=ref(...))
syntax without | string (the dbt-core fix makes that unnecessary).
Closes ClickHouse#644
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
|
|
dataders
left a comment
There was a problem hiding this comment.
just sloppy stuff Claude. do better or I'm gonna make Codex take your job
| {% do clickhouse__incremental_legacy(existing_relation, intermediate_relation, column_changes, unique_key) %} | ||
| {% set need_swap = true %} | ||
| {% elif incremental_strategy == 'delete_insert' %} | ||
| {% elif incremental_strategy == 'delete+insert' or incremental_strategy == 'delete_insert' %} |
There was a problem hiding this comment.
Claude, did you add this just be you spelled the config wrong?
There was a problem hiding this comment.
Intentional — dbt-fusion spells this strategy (hyphen) rather than (underscore), so this alias lets models written for dbt-fusion run unmodified against the OSS adapter. Happy to split it into a separate PR if you'd prefer to keep the scope tighter.
| {{ materialization_target_table(ref('my_target_table')) }} | ||
| {{ config( | ||
| materialized='materialized_view', | ||
| target_table=ref('my_target') | string |
There was a problem hiding this comment.
no longer needed
| target_table=ref('my_target') | string | |
| target_table=ref('my_target') |
| {#- First check config, then try to extract from SQL comment -#} | ||
| {#- Extract target table from comment. Handles formats like: | ||
| `schema`.`table`, "schema"."table", schema.table -#} | ||
| {%- set target_match = modules.re.search('--\\s*materialization_target_table:\\s*(.+?)\\s*$', sql, modules.re.MULTILINE) -%} | ||
| {%- set materialization_target_table = target_match.group(1).strip() if target_match else none -%} |
There was a problem hiding this comment.
makes me day to put this to bed (one day)
|
|
||
| {#- | ||
| Deprecated: materialization_target_table() macro. | ||
| Use config(target_table=ref('my_target') | string) instead. |
There was a problem hiding this comment.
| Use config(target_table=ref('my_target') | string) instead. | |
| Use config(target_table=ref('my_target')) instead. |
| {% macro materialization_target_table(target_relation) %} | ||
| {%- do exceptions.warn( | ||
| "materialization_target_table() is deprecated. " | ||
| ~ "Use {{ config(target_table=ref('my_target') | string) }} instead. " |
There was a problem hiding this comment.
| ~ "Use {{ config(target_table=ref('my_target') | string) }} instead. " | |
| ~ "Use {{ config(target_table=ref('my_target')) }} instead. " |
| "materialization_target_table() is deprecated. " | ||
| ~ "Use {{ config(target_table=ref('my_target') | string) }} instead. " | ||
| ~ "See https://github.com/ClickHouse/dbt-clickhouse/issues/644" |
There was a problem hiding this comment.
maybe this string can be set and used in both the comment block as well as this exceptions.warn piece?
| {% endmacro %} | ||
|
|
||
| {% macro clickhouse__materialized_view_with_external_target(materialization_target_table, sql) %} | ||
| {% macro clickhouse__materialized_view_with_external_target(target_table_str, sql) %} |
There was a problem hiding this comment.
all instances of target_table_str should be replcaed with materialization_target_table to minimize the diff of this PR
| {% macro clickhouse__materialized_view_with_external_target(target_table_str, sql) %} | |
| {% macro clickhouse__materialized_view_with_external_target(materialization_target_table, sql) %} |
| {{ config(materialized='materialized_view', catchup=False) }} | ||
|
|
||
| {{ materialization_target_table(ref('mv_target_table')) }} | ||
| {{ config(target_table=ref('mv_target_table')) }} |
There was a problem hiding this comment.
these config blocks need to be combined to look like
{{ config(
materialized='materialized_view',
target_table=ref('mv_target_table').
catchup=False)
}}| {{ config(materialized='materialized_view') }} | ||
|
|
||
| {{ materialization_target_table(ref('hackers_target')) }} | ||
| {{ config(target_table=ref('hackers_target')) }} | ||
|
|
There was a problem hiding this comment.
same consolidate the config blocks! there's many instances in this file fwiw
…idation - Rename target_table_str → materialization_target_table (minimize diff) - Replace string-split comparison with .schema/.identifier (RelationProxy is now passed directly, so strip_identifier_quotes and split() are unneeded) - Remove | string from header docblock and deprecation text - Extract _deprecation_url variable in materialization_target_table() so comment and warn message share a single source of truth for the URL - Consolidate split config() blocks into single calls in test models Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Summary
Replaces the
materialization_target_table()macro + SQL comment regex approach for external-target materialized views withconfig.get_rendered('target_table'), a clean dbt-native config key.Closes #644.
Requires dbt-labs/dbt-core#12965 (bridging
config.get_renderedacross the compile→materialization boundary).Before
Users had to call a macro that injected a SQL comment, which the materialization then regex-parsed:
{{ config(materialized='materialized_view') }} {{ materialization_target_table(ref('my_target')) }} {# outputs: -- materialization_target_table: schema.table #} SELECT ...This was incompatible with dbt-fusion (which does static DAG analysis, not SQL comment parsing) and required the regex:
After
{{ config( materialized='materialized_view', target_table=ref('my_target') ) }} SELECT ...The
target_tablevalue is aRelationProxycaptured at render time viaconfig.get_rendered('target_table')and used directly in theTOclause — no comment injection, no regex, no| stringcoercion.Changes
macros/materializations/materialized_view.sqlconfig.get_rendered('target_table')replacesconfig.get()+ regex fallbackclickhouse__materialized_view_with_external_targetnow receives aRelationProxydirectly — uses.schema/.identifierfor target-change comparison, passes relation straight toTOclause andINSERT INTOstrip_identifier_quotesandget_relation_from_stringhelpers removed (no longer needed)materialization_target_table()macro kept but emits a deprecation warningTests
config(target_table=ref(...))— no| stringTest plan
TestBasicExternalTargetMV— create MV, verify catchup, verify live inserts flow throughTestExternalTargetMVDisabledCatchup— target starts emptyTestUpdateExternalTargetMVWithSchemaChange— full refresh with schema evolutionTestExternalTargetMVTargetChange— target-change validation error, full-refresh swapTestMultipleExternalTargetMV— multiple MVs writing to same targetTestUpdateMultipleExternalTargetMVFullRefresh— full refresh with multiple MVs🤖 Generated with Claude Code