Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 11 additions & 15 deletions sqlmesh/dbt/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,12 @@ def _load_models_and_seeds(self) -> None:
if node_version:
node_name = f"{node_name}_v{node_version}"

model_kwargs = node_config.copy()
if not model_kwargs.get("quoting") and (
quoting := getattr(self._manifest.metadata, "quoting", None)
):
model_kwargs["quoting"] = quoting.to_dict()

if node.resource_type in {"model", "snapshot"}:
sql = node.raw_code if DBT_VERSION >= (1, 3, 0) else node.raw_sql # type: ignore
dependencies = Dependencies(
Expand All @@ -408,22 +414,12 @@ def _load_models_and_seeds(self) -> None:
self._flatten_dependencies_from_macros(dependencies.macros, node.package_name)
)

self._models_per_package[node.package_name][node_name] = ModelConfig(
**dict(
node_config,
sql=sql,
dependencies=dependencies,
tests=tests,
)
)
model_kwargs.update({"sql": sql, "dependencies": dependencies, "tests": tests})
self._models_per_package[node.package_name][node_name] = ModelConfig(**model_kwargs)
else:
self._seeds_per_package[node.package_name][node_name] = SeedConfig(
**dict(
node_config,
dependencies=Dependencies(macros=macro_references),
tests=tests,
)
)
dependencies = Dependencies(macros=macro_references)
model_kwargs.update({"dependencies": dependencies, "tests": tests})
self._seeds_per_package[node.package_name][node_name] = SeedConfig(**model_kwargs)

def _load_on_run_start_end(self) -> None:
for node in self._manifest.nodes.values():
Expand Down
3 changes: 2 additions & 1 deletion tests/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
sqlmesh_pyproject.toml
sqlmesh_pyproject.toml
.user.yml
16 changes: 10 additions & 6 deletions tests/dbt/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,14 +477,14 @@ def test_seed_config(sushi_test_project: Project, mocker: MockerFixture):
assert actual_config == expected_config

context = sushi_test_project.context
assert raw_items_seed.canonical_name(context) == "sushi.waiter_names"
assert raw_items_seed.to_sqlmesh(context).name == "sushi.waiter_names"
assert raw_items_seed.canonical_name(context) == '"sushi"."waiter_names"'
assert raw_items_seed.to_sqlmesh(context).name == '"sushi"."waiter_names"'

raw_items_seed.dialect_ = "snowflake"
assert raw_items_seed.to_sqlmesh(sushi_test_project.context).name == "sushi.waiter_names"
assert raw_items_seed.to_sqlmesh(sushi_test_project.context).name == '"sushi"."waiter_names"'
assert (
raw_items_seed.to_sqlmesh(sushi_test_project.context).fqn
== '"MEMORY"."SUSHI"."WAITER_NAMES"'
== '"MEMORY"."sushi"."waiter_names"'
)

waiter_revenue_semicolon_seed = seed_configs["waiter_revenue_semicolon"]
Expand All @@ -499,9 +499,13 @@ def test_seed_config(sushi_test_project: Project, mocker: MockerFixture):
}
assert actual_config_semicolon == expected_config_semicolon

assert waiter_revenue_semicolon_seed.canonical_name(context) == "sushi.waiter_revenue_semicolon"
assert (
waiter_revenue_semicolon_seed.to_sqlmesh(context).name == "sushi.waiter_revenue_semicolon"
waiter_revenue_semicolon_seed.canonical_name(context)
== '"sushi"."waiter_revenue_semicolon"'
)
assert (
waiter_revenue_semicolon_seed.to_sqlmesh(context).name
== '"sushi"."waiter_revenue_semicolon"'
)
assert waiter_revenue_semicolon_seed.delimiter == ";"
assert set(waiter_revenue_semicolon_seed.columns.keys()) == {"waiter_id", "revenue", "quarter"}
Expand Down
80 changes: 80 additions & 0 deletions tests/dbt/test_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,83 @@ def test_macro_assignment_shadowing(create_empty_project):
models = helper.models()
assert "model_using_path_macro" in models
assert "path" in models["model_using_path_macro"].dependencies.model_attrs.attrs


def test_quoting_config(tmp_path: Path):
if DBT_VERSION < (1, 10, 0):
pytest.skip(
"The 'quoting' setting in dbt_projects.yml is not respected for dbt-core < v1.10"
)

# Create dbt_project.yml with quoting config
(tmp_path / "dbt_project.yml").write_text("""
name: 'test_project'
version: '1.0.0'
config-version: 2
profile: 'test_project'

model-paths: ["models"]

models:
test_project:
+materialized: table

quoting:
database: true
schema: true
identifier: false
""")

# Create profiles.yml
(tmp_path / "profiles.yml").write_text("""
test_project:
target: dev
outputs:
dev:
type: duckdb
path: ':memory:'
""")

# Create a simple model without quoting override
models_dir = tmp_path / "models"
models_dir.mkdir()
(models_dir / "test_model.sql").write_text("SELECT 1 as id")

# Create a model with inline quoting override
(models_dir / "test_model_with_override.sql").write_text("""
{{
config(
quoting={
"database": false,
"schema": false,
"identifier": true
}
)
}}
SELECT 2 as id
""")

profile = Profile.load(DbtContext(tmp_path))
helper = ManifestHelper(
tmp_path,
tmp_path,
"test_project",
profile.target,
model_defaults=ModelDefaultsConfig(start="2020-01-01"),
)

models = helper.models()
test_model = models["test_model"]

# Model should inherit quoting from dbt_project.yml
assert test_model.quoting is not None
assert test_model.quoting["database"] is True
assert test_model.quoting["schema"] is True
assert test_model.quoting["identifier"] is False

# Model with inline override should use its own quoting settings
test_model_override = models["test_model_with_override"]
assert test_model_override.quoting is not None
assert test_model_override.quoting["database"] is False
assert test_model_override.quoting["schema"] is False
assert test_model_override.quoting["identifier"] is True
2 changes: 1 addition & 1 deletion tests/dbt/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ def test_load_deprecated_incremental_time_column(
assert model.kind.auto_restatement_intervals is None
assert model.kind.partition_by_time_column is True
assert (
"Using `time_column` on a model with incremental_strategy 'delete+insert' has been deprecated. Please use `incremental_by_time_range` instead in model 'main.incremental_time_range'."
"Using `time_column` on a model with incremental_strategy 'delete+insert' has been deprecated. Please use `incremental_by_time_range` instead in model '\"main\".\"incremental_time_range\"'."
in caplog.text
)

Expand Down
5 changes: 3 additions & 2 deletions tests/dbt/test_transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ def test_dbt_custom_materialization_with_time_filter_and_macro():
today = datetime.now()

# select both custom materialiasation models with the wildcard
selector = ["sushi.custom_incremental*"]
selector = ['"sushi"."custom_incremental*']
plan_builder = sushi_context.plan_builder(select_models=selector, execution_time=today)
plan = plan_builder.build()

Expand All @@ -191,6 +191,7 @@ def test_dbt_custom_materialization_with_time_filter_and_macro():

# - run ONE DAY LATER
a_day_later = today + timedelta(days=1)
selector = ['"sushi"."custom_incremental*']
sushi_context.run(select_models=selector, execution_time=a_day_later)
result_after_run = sushi_context.engine_adapter.fetchdf(select_daily)

Expand Down Expand Up @@ -2712,7 +2713,7 @@ def test_selected_resources_with_selectors():
assert any("customers" in model for model in plan.selected_models)

# Test wildcard selection
plan_builder = sushi_context.plan_builder(select_models=["sushi.waiter_*"])
plan_builder = sushi_context.plan_builder(select_models=['"sushi"."waiter_*'])
plan = plan_builder.build()
assert plan.selected_models is not None
assert len(plan.selected_models) >= 4
Expand Down