Skip to content

Commit 6d7db78

Browse files
fix(dbt): preserve source identifier quoting when it contains dots or spaces
1 parent d15203b commit 6d7db78

2 files changed

Lines changed: 69 additions & 1 deletion

File tree

sqlmesh/dbt/source.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,13 @@ def canonical_name(self, context: DbtContext) -> str:
8282
f"'source' macro failed for '{self.config_name}' with exception '{e}'."
8383
)
8484

85+
needs_identifier_quoting = bool(
86+
self.table_name and ("." in self.table_name or " " in self.table_name)
87+
)
8588
relation = relation.quote(
8689
database=False,
8790
schema=False,
88-
identifier=False,
91+
identifier=needs_identifier_quoting,
8992
)
9093
if relation.database == context.target.database:
9194
relation = relation.include(database=False)

tests/dbt/test_config.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,71 @@ def test_quoting():
521521
assert str(BaseRelation.create(**source.relation_info)) == 'foo."bar"'
522522

523523

524+
def test_source_canonical_name_with_dots_and_spaces(mocker):
525+
from dbt.adapters.base import BaseRelation
526+
527+
mock_context = mocker.Mock()
528+
mock_context.target.database = "target_db"
529+
530+
def mock_source_macro(source_name, table_name):
531+
if table_name == "my_table_dot":
532+
identifier = "FILENAME.CSV"
533+
elif table_name == "my_table_space":
534+
identifier = "my table space"
535+
else:
536+
identifier = "my_table_std"
537+
return BaseRelation.create(
538+
database="RAW_DEV",
539+
schema="raw_schema",
540+
identifier=identifier,
541+
)
542+
543+
mock_context.get_callable_macro.return_value = mock_source_macro
544+
545+
# 1. Identifier with a dot
546+
source_dot = SourceConfig(
547+
name="my_table_dot",
548+
source_name="my_source",
549+
identifier="FILENAME.CSV",
550+
)
551+
assert source_dot.canonical_name(mock_context) == 'RAW_DEV.raw_schema."FILENAME.CSV"'
552+
553+
# 2. Identifier with a space
554+
source_space = SourceConfig(
555+
name="my_table_space",
556+
source_name="my_source",
557+
identifier="my table space",
558+
)
559+
assert source_space.canonical_name(mock_context) == 'RAW_DEV.raw_schema."my table space"'
560+
561+
# 3. Standard identifier (without dots or spaces) should not be quoted
562+
source_std = SourceConfig(
563+
name="my_table_std",
564+
source_name="my_source",
565+
identifier="my_table_std",
566+
)
567+
assert source_std.canonical_name(mock_context) == "RAW_DEV.raw_schema.my_table_std"
568+
569+
# 4. Standard identifier, but with database matching target database (to test database omission)
570+
mock_context_target_db = mocker.Mock()
571+
mock_context_target_db.target.database = "RAW_DEV"
572+
mock_context_target_db.get_callable_macro.return_value = mock_source_macro
573+
574+
source_dot_target = SourceConfig(
575+
name="my_table_dot",
576+
source_name="my_source",
577+
identifier="FILENAME.CSV",
578+
)
579+
source_std_target = SourceConfig(
580+
name="my_table_std",
581+
source_name="my_source",
582+
identifier="my_table_std",
583+
)
584+
585+
assert source_dot_target.canonical_name(mock_context_target_db) == 'raw_schema."FILENAME.CSV"'
586+
assert source_std_target.canonical_name(mock_context_target_db) == "raw_schema.my_table_std"
587+
588+
524589
def _test_warehouse_config(
525590
config_yaml: str, target_class: t.Type[TargetConfig], *params_path: str
526591
) -> TargetConfig:

0 commit comments

Comments
 (0)