From 1952387a0e23ad54e2ddc66b4e71598aad517ffc Mon Sep 17 00:00:00 2001 From: Simen Strand Date: Thu, 23 Jul 2026 12:13:16 +0200 Subject: [PATCH 1/2] Fix Redshift parenthesized single-column SORTKEY Signed-off-by: Simen Strand --- sqlmesh/core/engine_adapter/redshift.py | 7 ++++++- tests/core/engine_adapter/test_redshift.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/sqlmesh/core/engine_adapter/redshift.py b/sqlmesh/core/engine_adapter/redshift.py index 39453f0cd2..22dc2f9dbc 100644 --- a/sqlmesh/core/engine_adapter/redshift.py +++ b/sqlmesh/core/engine_adapter/redshift.py @@ -295,7 +295,12 @@ def _to_identifier_if_string(expression: exp.Expr) -> exp.Expr: sortkey = table_properties.get("SORTKEY") if sortkey: - sortkey_expressions = sortkey.expressions if sortkey.expressions else [sortkey] + if isinstance(sortkey, (exp.Tuple, exp.Array)): + sortkey_expressions = sortkey.expressions + elif isinstance(sortkey, exp.Paren): + sortkey_expressions = [sortkey.unnest()] + else: + sortkey_expressions = [sortkey] properties.append( exp.SortKeyProperty( this=[ diff --git a/tests/core/engine_adapter/test_redshift.py b/tests/core/engine_adapter/test_redshift.py index ddd2c7c2c8..490fd6102a 100644 --- a/tests/core/engine_adapter/test_redshift.py +++ b/tests/core/engine_adapter/test_redshift.py @@ -167,7 +167,7 @@ def test_create_table_physical_properties_from_model_definition( physical_properties ( diststyle = key, distkey = "id_file", - sortkey = "batch_time" + sortkey = ("batch_time") ) ); SELECT id_file::INT, batch_time::TIMESTAMP; From 663a0a2e34f4783297a1456694d66f76d01f0e83 Mon Sep 17 00:00:00 2001 From: Simen Strand Date: Tue, 29 Sep 2026 12:46:28 +0200 Subject: [PATCH 2/2] Test all Redshift sortkey forms in model physical_properties Parametrize the model-definition test over bare, quoted, parenthesized single-column, tuple, and array sortkey values, restoring coverage for the bare single-column form. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Simen Strand --- tests/core/engine_adapter/test_redshift.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/core/engine_adapter/test_redshift.py b/tests/core/engine_adapter/test_redshift.py index 490fd6102a..9a88900779 100644 --- a/tests/core/engine_adapter/test_redshift.py +++ b/tests/core/engine_adapter/test_redshift.py @@ -152,22 +152,33 @@ def test_create_table_physical_properties_with_string_columns( ] +@pytest.mark.parametrize( + "sortkey, expected_sortkey", + [ + ('"batch_time"', 'SORTKEY("batch_time")'), + ("batch_time", 'SORTKEY("batch_time")'), + ('("batch_time")', 'SORTKEY("batch_time")'), + ("(batch_time)", 'SORTKEY("batch_time")'), + ('("batch_time", "id_file")', 'SORTKEY("batch_time", "id_file")'), + ('["batch_time", "id_file"]', 'SORTKEY("batch_time", "id_file")'), + ], +) def test_create_table_physical_properties_from_model_definition( - make_mocked_engine_adapter: t.Callable, + make_mocked_engine_adapter: t.Callable, sortkey: str, expected_sortkey: str ): adapter = make_mocked_engine_adapter(RedshiftEngineAdapter) model: SqlModel = t.cast( SqlModel, load_sql_based_model( d.parse( - """ + f""" MODEL ( name test_schema.test_table, kind full, physical_properties ( diststyle = key, distkey = "id_file", - sortkey = ("batch_time") + sortkey = {sortkey} ) ); SELECT id_file::INT, batch_time::TIMESTAMP; @@ -183,7 +194,7 @@ def test_create_table_physical_properties_from_model_definition( ) assert to_sql_calls(adapter) == [ - 'CREATE TABLE IF NOT EXISTS "test_schema"."test_table" ("id_file" INTEGER, "batch_time" TIMESTAMP) DISTSTYLE KEY DISTKEY("id_file") SORTKEY("batch_time")', + f'CREATE TABLE IF NOT EXISTS "test_schema"."test_table" ("id_file" INTEGER, "batch_time" TIMESTAMP) DISTSTYLE KEY DISTKEY("id_file") {expected_sortkey}', ]