From 02c0c691b06124142a523b51d7abdd91b720493a Mon Sep 17 00:00:00 2001 From: ReguiguiMohamed Date: Tue, 18 Aug 2026 22:20:10 +0100 Subject: [PATCH] fix(model): keep quoting when normalizing column_descriptions keys _column_descriptions_validator built each key with part.this, which is the bare identifier string, so the quoted flag was gone before normalize_identifiers ran. A quoted key was then normalized as if it were unquoted, and on dialects where quoting makes a column case-sensitive the resulting name matched no column, so the description was dropped along with the rest of the table's comments. Normalize each part while it is still an identifier. Unquoted keys normalize exactly as before. Fixes #5943 Signed-off-by: ReguiguiMohamed --- sqlmesh/core/model/meta.py | 18 ++++++++++-------- tests/core/test_model.py | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/sqlmesh/core/model/meta.py b/sqlmesh/core/model/meta.py index 94956dff99..67ea32f9d9 100644 --- a/sqlmesh/core/model/meta.py +++ b/sqlmesh/core/model/meta.py @@ -321,17 +321,19 @@ def _column_descriptions_validator( if isinstance(vs, (exp.Tuple, exp.Array)): vs = vs.expressions - raw_col_descriptions = ( - vs + # Normalize each part while it is still an identifier, so that a quoted + # column keeps its case on dialects where quoting makes it significant. + col_descriptions = ( + {normalize_identifiers(k, dialect=dialect).name: v for k, v in vs.items()} if isinstance(vs, dict) - else {".".join([part.this for part in v.this.parts]): v.expression.name for v in vs} + else { + ".".join( + normalize_identifiers(part, dialect=dialect).name for part in v.this.parts + ): v.expression.name + for v in vs + } ) - col_descriptions = { - normalize_identifiers(k, dialect=dialect).name: v - for k, v in raw_col_descriptions.items() - } - columns_to_types = data.get("columns_to_types_") if columns_to_types: from sqlmesh.core.console import get_console diff --git a/tests/core/test_model.py b/tests/core/test_model.py index 1f3cde265b..3de4ff2ebc 100644 --- a/tests/core/test_model.py +++ b/tests/core/test_model.py @@ -1000,6 +1000,32 @@ def test_column_descriptions(sushi_context, assert_exp_eq): assert model.column_descriptions == {"id": "primary key", "foo": "bar"} +def test_column_descriptions_quoted_identifier(): + expressions = d.parse( + """ + MODEL ( + name db.table, + kind FULL, + dialect snowflake, + column_descriptions ( + "myColumn" = 'a case-sensitive column', + other_column = 'an unquoted column' + ) + ); + + SELECT 1 AS "myColumn", 2 AS other_column + """ + ) + model = load_sql_based_model(expressions, dialect="snowflake") + + # A quoted key keeps its case, an unquoted one is still normalized. + assert model.column_descriptions == { + "myColumn": "a case-sensitive column", + "OTHER_COLUMN": "an unquoted column", + } + assert set(model.column_descriptions) <= set(model.columns_to_types) + + def test_model_jinja_macro_reference_extraction(): @macro() def test_macro(**kwargs) -> None: