Skip to content

Commit 84e8e59

Browse files
committed
Fix!: respect quoting setting in dbt
1 parent 51772bd commit 84e8e59

File tree

5 files changed

+105
-23
lines changed

5 files changed

+105
-23
lines changed

sqlmesh/dbt/manifest.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,12 @@ def _load_models_and_seeds(self) -> None:
387387
if node_version:
388388
node_name = f"{node_name}_v{node_version}"
389389

390+
model_kwargs = node_config.copy()
391+
if not model_kwargs.get("quoting") and (
392+
quoting := getattr(self._manifest.metadata, "quoting", None)
393+
):
394+
model_kwargs["quoting"] = quoting.to_dict()
395+
390396
if node.resource_type in {"model", "snapshot"}:
391397
sql = node.raw_code if DBT_VERSION >= (1, 3, 0) else node.raw_sql # type: ignore
392398
dependencies = Dependencies(
@@ -405,22 +411,12 @@ def _load_models_and_seeds(self) -> None:
405411
self._flatten_dependencies_from_macros(dependencies.macros, node.package_name)
406412
)
407413

408-
self._models_per_package[node.package_name][node_name] = ModelConfig(
409-
**dict(
410-
node_config,
411-
sql=sql,
412-
dependencies=dependencies,
413-
tests=tests,
414-
)
415-
)
414+
model_kwargs.update({"sql": sql, "dependencies": dependencies, "tests": tests})
415+
self._models_per_package[node.package_name][node_name] = ModelConfig(**model_kwargs)
416416
else:
417-
self._seeds_per_package[node.package_name][node_name] = SeedConfig(
418-
**dict(
419-
node_config,
420-
dependencies=Dependencies(macros=macro_references),
421-
tests=tests,
422-
)
423-
)
417+
dependencies = Dependencies(macros=macro_references)
418+
model_kwargs.update({"dependencies": dependencies, "tests": tests})
419+
self._seeds_per_package[node.package_name][node_name] = SeedConfig(**model_kwargs)
424420

425421
def _load_on_run_start_end(self) -> None:
426422
for node in self._manifest.nodes.values():

tests/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
sqlmesh_pyproject.toml
1+
sqlmesh_pyproject.toml
2+
.user.yml

tests/dbt/test_config.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -475,14 +475,14 @@ def test_seed_config(sushi_test_project: Project, mocker: MockerFixture):
475475
assert actual_config == expected_config
476476

477477
context = sushi_test_project.context
478-
assert raw_items_seed.canonical_name(context) == "sushi.waiter_names"
479-
assert raw_items_seed.to_sqlmesh(context).name == "sushi.waiter_names"
478+
assert raw_items_seed.canonical_name(context) == '"sushi"."waiter_names"'
479+
assert raw_items_seed.to_sqlmesh(context).name == '"sushi"."waiter_names"'
480480

481481
raw_items_seed.dialect_ = "snowflake"
482-
assert raw_items_seed.to_sqlmesh(sushi_test_project.context).name == "sushi.waiter_names"
482+
assert raw_items_seed.to_sqlmesh(sushi_test_project.context).name == '"sushi"."waiter_names"'
483483
assert (
484484
raw_items_seed.to_sqlmesh(sushi_test_project.context).fqn
485-
== '"MEMORY"."SUSHI"."WAITER_NAMES"'
485+
== '"MEMORY"."sushi"."waiter_names"'
486486
)
487487

488488
waiter_revenue_semicolon_seed = seed_configs["waiter_revenue_semicolon"]
@@ -497,9 +497,13 @@ def test_seed_config(sushi_test_project: Project, mocker: MockerFixture):
497497
}
498498
assert actual_config_semicolon == expected_config_semicolon
499499

500-
assert waiter_revenue_semicolon_seed.canonical_name(context) == "sushi.waiter_revenue_semicolon"
501500
assert (
502-
waiter_revenue_semicolon_seed.to_sqlmesh(context).name == "sushi.waiter_revenue_semicolon"
501+
waiter_revenue_semicolon_seed.canonical_name(context)
502+
== '"sushi"."waiter_revenue_semicolon"'
503+
)
504+
assert (
505+
waiter_revenue_semicolon_seed.to_sqlmesh(context).name
506+
== '"sushi"."waiter_revenue_semicolon"'
503507
)
504508
assert waiter_revenue_semicolon_seed.delimiter == ";"
505509
assert set(waiter_revenue_semicolon_seed.columns.keys()) == {"waiter_id", "revenue", "quarter"}

tests/dbt/test_manifest.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,3 +324,84 @@ def test_macro_depenency_none_str():
324324

325325
# "None" macro shouldn't raise a KeyError
326326
_macro_references(helper._manifest, node)
327+
328+
329+
@pytest.mark.xdist_group("dbt_manifest")
330+
def test_quoting_config(tmp_path: Path):
331+
if DBT_VERSION < (1, 10, 0):
332+
pytest.skip(
333+
"The 'quoting' setting in dbt_projects.yml is not respected for dbt-core < v1.10"
334+
)
335+
336+
# Create dbt_project.yml with quoting config
337+
(tmp_path / "dbt_project.yml").write_text("""
338+
name: 'test_project'
339+
version: '1.0.0'
340+
config-version: 2
341+
profile: 'test_project'
342+
343+
model-paths: ["models"]
344+
345+
models:
346+
test_project:
347+
+materialized: table
348+
349+
quoting:
350+
database: true
351+
schema: true
352+
identifier: false
353+
""")
354+
355+
# Create profiles.yml
356+
(tmp_path / "profiles.yml").write_text("""
357+
test_project:
358+
target: dev
359+
outputs:
360+
dev:
361+
type: duckdb
362+
path: ':memory:'
363+
""")
364+
365+
# Create a simple model without quoting override
366+
models_dir = tmp_path / "models"
367+
models_dir.mkdir()
368+
(models_dir / "test_model.sql").write_text("SELECT 1 as id")
369+
370+
# Create a model with inline quoting override
371+
(models_dir / "test_model_with_override.sql").write_text("""
372+
{{
373+
config(
374+
quoting={
375+
"database": false,
376+
"schema": false,
377+
"identifier": true
378+
}
379+
)
380+
}}
381+
SELECT 2 as id
382+
""")
383+
384+
profile = Profile.load(DbtContext(tmp_path))
385+
helper = ManifestHelper(
386+
tmp_path,
387+
tmp_path,
388+
"test_project",
389+
profile.target,
390+
model_defaults=ModelDefaultsConfig(start="2020-01-01"),
391+
)
392+
393+
models = helper.models()
394+
test_model = models["test_model"]
395+
396+
# Model should inherit quoting from dbt_project.yml
397+
assert test_model.quoting is not None
398+
assert test_model.quoting["database"] is True
399+
assert test_model.quoting["schema"] is True
400+
assert test_model.quoting["identifier"] is False
401+
402+
# Model with inline override should use its own quoting settings
403+
test_model_override = models["test_model_with_override"]
404+
assert test_model_override.quoting is not None
405+
assert test_model_override.quoting["database"] is False
406+
assert test_model_override.quoting["schema"] is False
407+
assert test_model_override.quoting["identifier"] is True

tests/dbt/test_model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ def test_load_deprecated_incremental_time_column(
553553
assert model.kind.auto_restatement_intervals is None
554554
assert model.kind.partition_by_time_column is True
555555
assert (
556-
"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'."
556+
"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\"'."
557557
in caplog.text
558558
)
559559

0 commit comments

Comments
 (0)