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
37 changes: 32 additions & 5 deletions sqlmesh/core/dialect.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,8 @@ def parse(self: Parser) -> t.Optional[exp.Expr]:
"METRIC": _create_parser(Metric, ["name"]),
}

_SQLMESH_COLUMNS_DIALECT = "sqlmesh_columns_dialect"


def _props_sql(self: Generator, expressions: t.List[exp.Expr]) -> str:
props = []
Expand All @@ -712,7 +714,25 @@ def _props_sql(self: Generator, expressions: t.List[exp.Expr]) -> str:
if isinstance(prop, MacroFunc):
sql = self.indent(self.sql(prop, comment=False))
else:
sql = self.indent(f"{prop.name} {self.sql(prop, 'value')}")
value = prop.args.get("value")
parent = prop.parent
columns_dialect = parent.meta.get(_SQLMESH_COLUMNS_DIALECT) if parent else None
if prop.name.lower() == "columns" and columns_dialect and isinstance(value, exp.Expr):
value_sql = value.sql(
dialect=columns_dialect,
pretty=self.pretty,
identify=self.identify,
normalize=self.normalize,
pad=self.pad,
indent=self._indent,
normalize_functions=self.normalize_functions,
leading_comma=self.leading_comma,
max_text_width=self.max_text_width,
comments=self.comments,
)
else:
value_sql = self.sql(prop, "value")
sql = self.indent(f"{prop.name} {value_sql}")

if i < size - 1:
sql += ","
Expand Down Expand Up @@ -819,11 +839,18 @@ def format_model_expressions(
Returns:
A string representing the formatted model.
"""

def expression_with_columns_dialect(expression: exp.Expr) -> exp.Expr:
if isinstance(expression, Model) and dialect:
expression = expression.copy()
expression.meta[_SQLMESH_COLUMNS_DIALECT] = dialect
return expression

if len(expressions) == 1 and is_meta_expression(expressions[0]):
# Meta expressions (MODEL/AUDIT/METRIC) are SQLMesh DDL, not standard SQL,
# so they must never be transpiled to the target dialect (e.g. tsql would
# rewrite a boolean property like `allow_partials TRUE` to `(1 = 1)`).
return expressions[0].sql(
return expression_with_columns_dialect(expressions[0]).sql(
pretty=True, dialect=None, normalize_functions=normalize_functions
)

Expand Down Expand Up @@ -857,9 +884,9 @@ def cast_to_colon(node: exp.Expr) -> exp.Expr:
expressions = new_expressions

return ";\n\n".join(
# Meta expressions (MODEL/AUDIT/METRIC) are SQLMesh DDL and must stay
# dialect-agnostic; only the actual query/statement expressions transpile.
expression.sql(
# Meta expressions (MODEL/AUDIT/METRIC) stay dialect-agnostic, except for
# MODEL column types. Actual query/statement expressions are transpiled.
expression_with_columns_dialect(expression).sql(
pretty=True,
dialect=None if is_meta_expression(expression) else dialect,
normalize_functions=normalize_functions,
Expand Down
38 changes: 38 additions & 0 deletions tests/core/test_dialect.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,44 @@ def test_format_model_expressions():
)


@pytest.mark.parametrize("dialect", ["tsql", "fabric"])
def test_format_model_columns_uses_model_dialect(dialect: str):
formatted = format_model_expressions(
parse(
f"""
MODEL (
name test_model,
dialect {dialect},
description 'my description',
formatting false,
columns (
_dwh_load_datetime_utc DATETIME2(6)
)
);

SELECT 1 AS id
"""
),
dialect=dialect,
)

assert (
formatted
== f"""MODEL (
name test_model,
dialect {dialect},
description 'my description',
formatting FALSE,
columns (
_dwh_load_datetime_utc DATETIME2(6)
)
);

SELECT
1 AS id"""
)


def test_format_model_expressions_normalize_functions():
"""Regression: formatter function-name casing behavior.

Expand Down