Skip to content

Commit c8bee08

Browse files
Fix: Only keep refs and sources that exist to match dbt load time behaviour (#5509)
1 parent b866d33 commit c8bee08

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

sqlmesh/dbt/basemodel.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,10 @@ def sqlmesh_model_kwargs(
317317
dependencies = dependencies.union(custom_mat.dependencies)
318318

319319
model_dialect = self.dialect(context)
320+
321+
# Only keep refs and sources that exist in the context to match dbt behavior
322+
dependencies.refs.intersection_update(context.refs)
323+
dependencies.sources.intersection_update(context.sources)
320324
model_context = context.context_for_dependencies(
321325
dependencies.union(self.tests_ref_source_dependencies)
322326
)

tests/dbt/test_model.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from sqlmesh.dbt.test import TestConfig
1919
from sqlmesh.utils.yaml import YAML
2020
from sqlmesh.utils.date import to_ds
21+
import typing as t
2122

2223
pytestmark = pytest.mark.dbt
2324

@@ -1028,3 +1029,58 @@ def test_ephemeral_model_ignores_grants() -> None:
10281029

10291030
assert sqlmesh_model.kind.is_embedded
10301031
assert sqlmesh_model.grants is None # grants config is skipped for ephemeral / embedded models
1032+
1033+
1034+
def test_conditional_ref_in_unexecuted_branch(copy_to_temp_path: t.Callable):
1035+
path = copy_to_temp_path("tests/fixtures/dbt/sushi_test")
1036+
temp_project = path[0]
1037+
1038+
models_dir = temp_project / "models"
1039+
models_dir.mkdir(parents=True, exist_ok=True)
1040+
1041+
test_model_content = """
1042+
{{ config(
1043+
materialized='table',
1044+
) }}
1045+
1046+
{% if true %}
1047+
WITH source AS (
1048+
SELECT *
1049+
FROM {{ ref('simple_model_a') }}
1050+
)
1051+
{% else %}
1052+
WITH source AS (
1053+
SELECT *
1054+
FROM {{ ref('nonexistent_model') }} -- this doesn't exist but is in unexecuted branch
1055+
)
1056+
{% endif %}
1057+
1058+
SELECT * FROM source
1059+
""".strip()
1060+
1061+
(models_dir / "conditional_ref_model.sql").write_text(test_model_content)
1062+
sushi_context = Context(paths=[str(temp_project)])
1063+
1064+
# the model should load successfully without raising MissingModelError
1065+
model = sushi_context.get_model("sushi.conditional_ref_model")
1066+
assert model is not None
1067+
1068+
# Verify only the executed ref is in the dependencies
1069+
assert len(model.depends_on) == 1
1070+
assert '"memory"."sushi"."simple_model_a"' in model.depends_on
1071+
1072+
# Also the model can be rendered successfully with the executed ref
1073+
rendered = model.render_query()
1074+
assert rendered is not None
1075+
assert (
1076+
rendered.sql()
1077+
== 'WITH "source" AS (SELECT "simple_model_a"."a" AS "a" FROM "memory"."sushi"."simple_model_a" AS "simple_model_a") SELECT "source"."a" AS "a" FROM "source" AS "source"'
1078+
)
1079+
1080+
# And run plan with this conditional model for good measure
1081+
plan = sushi_context.plan(select_models=["sushi.conditional_ref_model", "sushi.simple_model_a"])
1082+
sushi_context.apply(plan)
1083+
upstream_ref = sushi_context.engine_adapter.fetchone("SELECT * FROM sushi.simple_model_a")
1084+
assert upstream_ref == (1,)
1085+
result = sushi_context.engine_adapter.fetchone("SELECT * FROM sushi.conditional_ref_model")
1086+
assert result == (1,)

0 commit comments

Comments
 (0)