Skip to content

Commit cd33d9c

Browse files
committed
Fix!: respect quoting setting in dbt
1 parent 725ebcc commit cd33d9c

File tree

5 files changed

+104
-23
lines changed

5 files changed

+104
-23
lines changed

sqlmesh/dbt/manifest.py

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

393+
model_kwargs = node_config.copy()
394+
if not model_kwargs.get("quoting") and (
395+
quoting := getattr(self._manifest.metadata, "quoting", None)
396+
):
397+
model_kwargs["quoting"] = quoting.to_dict()
398+
393399
if node.resource_type in {"model", "snapshot"}:
394400
sql = node.raw_code if DBT_VERSION >= (1, 3, 0) else node.raw_sql # type: ignore
395401
dependencies = Dependencies(
@@ -408,22 +414,12 @@ def _load_models_and_seeds(self) -> None:
408414
self._flatten_dependencies_from_macros(dependencies.macros, node.package_name)
409415
)
410416

411-
self._models_per_package[node.package_name][node_name] = ModelConfig(
412-
**dict(
413-
node_config,
414-
sql=sql,
415-
dependencies=dependencies,
416-
tests=tests,
417-
)
418-
)
417+
model_kwargs.update({"sql": sql, "dependencies": dependencies, "tests": tests})
418+
self._models_per_package[node.package_name][node_name] = ModelConfig(**model_kwargs)
419419
else:
420-
self._seeds_per_package[node.package_name][node_name] = SeedConfig(
421-
**dict(
422-
node_config,
423-
dependencies=Dependencies(macros=macro_references),
424-
tests=tests,
425-
)
426-
)
420+
dependencies = Dependencies(macros=macro_references)
421+
model_kwargs.update({"dependencies": dependencies, "tests": tests})
422+
self._seeds_per_package[node.package_name][node_name] = SeedConfig(**model_kwargs)
427423

428424
def _load_on_run_start_end(self) -> None:
429425
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
@@ -477,14 +477,14 @@ def test_seed_config(sushi_test_project: Project, mocker: MockerFixture):
477477
assert actual_config == expected_config
478478

479479
context = sushi_test_project.context
480-
assert raw_items_seed.canonical_name(context) == "sushi.waiter_names"
481-
assert raw_items_seed.to_sqlmesh(context).name == "sushi.waiter_names"
480+
assert raw_items_seed.canonical_name(context) == '"sushi"."waiter_names"'
481+
assert raw_items_seed.to_sqlmesh(context).name == '"sushi"."waiter_names"'
482482

483483
raw_items_seed.dialect_ = "snowflake"
484-
assert raw_items_seed.to_sqlmesh(sushi_test_project.context).name == "sushi.waiter_names"
484+
assert raw_items_seed.to_sqlmesh(sushi_test_project.context).name == '"sushi"."waiter_names"'
485485
assert (
486486
raw_items_seed.to_sqlmesh(sushi_test_project.context).fqn
487-
== '"MEMORY"."SUSHI"."WAITER_NAMES"'
487+
== '"MEMORY"."sushi"."waiter_names"'
488488
)
489489

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

502-
assert waiter_revenue_semicolon_seed.canonical_name(context) == "sushi.waiter_revenue_semicolon"
503502
assert (
504-
waiter_revenue_semicolon_seed.to_sqlmesh(context).name == "sushi.waiter_revenue_semicolon"
503+
waiter_revenue_semicolon_seed.canonical_name(context)
504+
== '"sushi"."waiter_revenue_semicolon"'
505+
)
506+
assert (
507+
waiter_revenue_semicolon_seed.to_sqlmesh(context).name
508+
== '"sushi"."waiter_revenue_semicolon"'
505509
)
506510
assert waiter_revenue_semicolon_seed.delimiter == ";"
507511
assert set(waiter_revenue_semicolon_seed.columns.keys()) == {"waiter_id", "revenue", "quarter"}

tests/dbt/test_manifest.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,3 +367,83 @@ def test_macro_assignment_shadowing(create_empty_project):
367367
models = helper.models()
368368
assert "model_using_path_macro" in models
369369
assert "path" in models["model_using_path_macro"].dependencies.model_attrs.attrs
370+
371+
372+
def test_quoting_config(tmp_path: Path):
373+
if DBT_VERSION < (1, 10, 0):
374+
pytest.skip(
375+
"The 'quoting' setting in dbt_projects.yml is not respected for dbt-core < v1.10"
376+
)
377+
378+
# Create dbt_project.yml with quoting config
379+
(tmp_path / "dbt_project.yml").write_text("""
380+
name: 'test_project'
381+
version: '1.0.0'
382+
config-version: 2
383+
profile: 'test_project'
384+
385+
model-paths: ["models"]
386+
387+
models:
388+
test_project:
389+
+materialized: table
390+
391+
quoting:
392+
database: true
393+
schema: true
394+
identifier: false
395+
""")
396+
397+
# Create profiles.yml
398+
(tmp_path / "profiles.yml").write_text("""
399+
test_project:
400+
target: dev
401+
outputs:
402+
dev:
403+
type: duckdb
404+
path: ':memory:'
405+
""")
406+
407+
# Create a simple model without quoting override
408+
models_dir = tmp_path / "models"
409+
models_dir.mkdir()
410+
(models_dir / "test_model.sql").write_text("SELECT 1 as id")
411+
412+
# Create a model with inline quoting override
413+
(models_dir / "test_model_with_override.sql").write_text("""
414+
{{
415+
config(
416+
quoting={
417+
"database": false,
418+
"schema": false,
419+
"identifier": true
420+
}
421+
)
422+
}}
423+
SELECT 2 as id
424+
""")
425+
426+
profile = Profile.load(DbtContext(tmp_path))
427+
helper = ManifestHelper(
428+
tmp_path,
429+
tmp_path,
430+
"test_project",
431+
profile.target,
432+
model_defaults=ModelDefaultsConfig(start="2020-01-01"),
433+
)
434+
435+
models = helper.models()
436+
test_model = models["test_model"]
437+
438+
# Model should inherit quoting from dbt_project.yml
439+
assert test_model.quoting is not None
440+
assert test_model.quoting["database"] is True
441+
assert test_model.quoting["schema"] is True
442+
assert test_model.quoting["identifier"] is False
443+
444+
# Model with inline override should use its own quoting settings
445+
test_model_override = models["test_model_with_override"]
446+
assert test_model_override.quoting is not None
447+
assert test_model_override.quoting["database"] is False
448+
assert test_model_override.quoting["schema"] is False
449+
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
@@ -556,7 +556,7 @@ def test_load_deprecated_incremental_time_column(
556556
assert model.kind.auto_restatement_intervals is None
557557
assert model.kind.partition_by_time_column is True
558558
assert (
559-
"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'."
559+
"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\"'."
560560
in caplog.text
561561
)
562562

0 commit comments

Comments
 (0)