diff --git a/sqlmesh/core/dialect.py b/sqlmesh/core/dialect.py index 1aa7ca839a..e110907e80 100644 --- a/sqlmesh/core/dialect.py +++ b/sqlmesh/core/dialect.py @@ -783,7 +783,10 @@ def format_model_expressions( A string representing the formatted model. """ if len(expressions) == 1 and is_meta_expression(expressions[0]): - return expressions[0].sql(pretty=True, dialect=dialect) + # 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(pretty=True, dialect=None) if rewrite_casts: @@ -815,7 +818,14 @@ def cast_to_colon(node: exp.Expr) -> exp.Expr: expressions = new_expressions return ";\n\n".join( - expression.sql(pretty=True, dialect=dialect, **kwargs) for expression in expressions + # Meta expressions (MODEL/AUDIT/METRIC) are SQLMesh DDL and must stay + # dialect-agnostic; only the actual query/statement expressions transpile. + expression.sql( + pretty=True, + dialect=None if is_meta_expression(expression) else dialect, + **kwargs, + ) + for expression in expressions ).strip() diff --git a/tests/core/test_dialect.py b/tests/core/test_dialect.py index 8b5adf82c0..f74d23b8f4 100644 --- a/tests/core/test_dialect.py +++ b/tests/core/test_dialect.py @@ -230,6 +230,60 @@ def test_format_model_expressions(): 1::Int32 AS bar""" ) + x = format_model_expressions( + parse( + """ + MODEL(name a.b, kind FULL, dialect tsql, allow_partials true); + SELECT TRUE AS col, CAST(x AS INT) AS y FROM t + """ + ), + dialect="tsql", + ) + # The MODEL header is SQLMesh DDL and must not be transpiled: a boolean property + # such as `allow_partials true` must stay `TRUE`, not become tsql's `(1 = 1)`. + # The query body must still transpile to the target dialect. + assert ( + x + == """MODEL ( + name a.b, + kind FULL, + dialect tsql, + allow_partials TRUE +); + +SELECT + 1 AS col, + x::INTEGER AS y +FROM t""" + ) + + x = format_model_expressions( + parse( + """ + AUDIT(name my_audit, dialect tsql, blocking false); + SELECT TRUE AS col, CAST(x AS INT) AS y FROM t WHERE x > 0 + """ + ), + dialect="tsql", + ) + # AUDIT headers are SQLMesh DDL too: a `false` boolean property must stay + # `FALSE`, not become tsql's `(1 = 0)`, while the query body still transpiles. + assert ( + x + == """AUDIT ( + name my_audit, + dialect tsql, + blocking FALSE +); + +SELECT + 1 AS col, + x::INTEGER AS y +FROM t +WHERE + x > 0""" + ) + x = format_model_expressions( parse( """