Skip to content

Feat(dbt): Add support for dbt custom materializations#5435

Merged
themisvaltinos merged 7 commits intomainfrom
themis/dbt_custom_materialisations
Sep 30, 2025
Merged

Feat(dbt): Add support for dbt custom materializations#5435
themisvaltinos merged 7 commits intomainfrom
themis/dbt_custom_materialisations

Conversation

@themisvaltinos
Copy link
Contributor

This update adds support for dbt custom materializations which allows users to define their own strategies via jinja macros.

Docs: https://docs.getdbt.com/guides/create-new-materializations?step=2

class DbtCustomKind(_ModelKind):
name: t.Literal[ModelKindName.DBT_CUSTOM] = ModelKindName.DBT_CUSTOM
materialization: str
adapter: str = "default"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this adapter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's to allow users to define different materialisation tied to different adapters and for dispatch then to appropriately match the correct one used

{% materialization my_materialization_name, default %}
 -- cross-adapter materialization... assume Redshift is not supported
{% endmaterialization %}

{% materialization my_materialization_name, adapter='redshift' %}
-- override the materialization for Redshift
{% endmaterialization %}

test for this example: test_adapter_specific_materialization_override

@property
def metadata_hash_values(self) -> t.List[t.Optional[str]]:
return [
*super().metadata_hash_values,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the point of this override?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you good catch. I had originally the definition there and then realised it should be in the data hash and moved it but forgot to remove the metadata_hash_values entirely since it's not needed will do

adapter.session(snapshot.model.render_session_properties(**render_statements_kwargs)),
):
adapter.execute(model.render_pre_statements(**render_statements_kwargs))
if not snapshot.is_dbt_custom:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the custom materialization strategy calls the pre-hooks?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes internally using run_hooks so it's up to the user when they should call them or if the specific materialisation doesn't call them at all: https://docs.getdbt.com/guides/create-new-materializations?step=2#run-pre-hooks

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, can we instead extend the materialization strategy with:

    def run_pre_statements(...)
    
    def run_post_statements(...)

and in the default implementation we do

adapter.execute(model.render_pre_statements(**render_statements_kwargs))

while for DbtCustomMaterializationStrategy it's a noop

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes that seems better, revised it with run methods instead in the materialisation strategy

) -> None:
from sqlmesh.dbt.builtin import create_builtin_globals

jinja_macros = getattr(model, "jinja_macros", JinjaMacroRegistry())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to use getattr instead of accessing the attribute directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes good point I forgot all our model classes have jinja_macros

"execution_dt": kwargs.get("execution_time"),
}

context = create_builtin_globals(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we manually construct the context instead of relying on JinjaMacros to do it for us?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so that I didn't use its private method, but I revised it to use jinjamacros instead if this is what you meant. if you meant to do it through build_environment that got quite complicated and it updates internally the macros that were needed which lead to failures. if you mean why this entire logic is needed in the first place it is because we need to inject runtime values specific to the materialization, like the actual sql of the query being materialized, the pre/post hooks, the physical table name parts if it’s a vde etc.

@themisvaltinos themisvaltinos force-pushed the themis/dbt_custom_materialisations branch from 7d2829b to 6cf1de3 Compare September 25, 2025 13:05
raise SQLMeshError(f"Custom materialization '{name}' not present in the Python environment")


class DbtCustomMaterialization(MaterializableStrategy):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's follow the existing naming convention and add Strategy at the end of the name

@themisvaltinos themisvaltinos force-pushed the themis/dbt_custom_materialisations branch 4 times, most recently from 75a4b8e to 2aab482 Compare September 29, 2025 20:27
@themisvaltinos themisvaltinos force-pushed the themis/dbt_custom_materialisations branch from 2aab482 to 7de8247 Compare September 30, 2025 15:20
@themisvaltinos themisvaltinos merged commit a255e17 into main Sep 30, 2025
36 checks passed
@themisvaltinos themisvaltinos deleted the themis/dbt_custom_materialisations branch September 30, 2025 16:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants