Skip to content

Commit 0b9ff52

Browse files
authored
Fix: Only allow to increase precision of VARCHAR for redshift (#4008)
1 parent ada21c4 commit 0b9ff52

4 files changed

Lines changed: 97 additions & 1 deletion

File tree

sqlmesh/core/engine_adapter/redshift.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class RedshiftEngineAdapter(
6060
exp.DataType.build("CHAR", dialect=DIALECT).this: 4096,
6161
exp.DataType.build("VARCHAR", dialect=DIALECT).this: 65535,
6262
},
63+
precision_increase_allowed_types={exp.DataType.build("VARCHAR", dialect=DIALECT).this},
6364
drop_cascade=True,
6465
)
6566
VARIABLE_LENGTH_DATA_TYPES = {

sqlmesh/core/schema_diff.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ class SchemaDiffer(PydanticModel):
341341
coerceable_types_: t.Dict[exp.DataType, t.Set[exp.DataType]] = Field(
342342
default_factory=dict, alias="coerceable_types"
343343
)
344+
precision_increase_allowed_types: t.Optional[t.Set[exp.DataType.Type]] = None
344345
support_coercing_compatible_types: bool = False
345346
drop_cascade: bool = False
346347
parameterized_type_defaults: t.Dict[
@@ -367,7 +368,10 @@ def coerceable_types(self) -> t.Dict[exp.DataType, t.Set[exp.DataType]]:
367368
def _is_compatible_type(self, current_type: exp.DataType, new_type: exp.DataType) -> bool:
368369
# types are identical or both types are parameterized and new has higher precision
369370
# - default parameter values are automatically provided if not present
370-
if current_type == new_type or self._is_precision_increase(current_type, new_type):
371+
if current_type == new_type or (
372+
self._is_precision_increase_allowed(current_type)
373+
and self._is_precision_increase(current_type, new_type)
374+
):
371375
return True
372376
# types are un-parameterized and compatible
373377
if current_type in self.compatible_types:
@@ -390,6 +394,12 @@ def _is_coerceable_type(self, current_type: exp.DataType, new_type: exp.DataType
390394
return is_coerceable
391395
return False
392396

397+
def _is_precision_increase_allowed(self, current_type: exp.DataType) -> bool:
398+
return (
399+
self.precision_increase_allowed_types is None
400+
or current_type.this in self.precision_increase_allowed_types
401+
)
402+
393403
def _is_precision_increase(self, current_type: exp.DataType, new_type: exp.DataType) -> bool:
394404
if current_type.this == new_type.this and not current_type.is_type(
395405
*exp.DataType.NESTED_TYPES

tests/core/engine_adapter/test_redshift.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,55 @@ def table_columns(table_name: str) -> t.Dict[str, exp.DataType]:
366366
]
367367

368368

369+
def test_alter_table_precision_increase_varchar(adapter: t.Callable):
370+
current_table_name = "test_table"
371+
target_table_name = "target_table"
372+
373+
def table_columns(table_name: str) -> t.Dict[str, exp.DataType]:
374+
if table_name == current_table_name:
375+
return {
376+
"id": exp.DataType.build("int"),
377+
"test_column": exp.DataType.build("VARCHAR(10)"),
378+
}
379+
else:
380+
return {
381+
"id": exp.DataType.build("int"),
382+
"test_column": exp.DataType.build("VARCHAR(20)"),
383+
}
384+
385+
adapter.columns = table_columns
386+
387+
adapter.alter_table(adapter.get_alter_expressions(current_table_name, target_table_name))
388+
assert to_sql_calls(adapter) == [
389+
'ALTER TABLE "test_table" ALTER COLUMN "test_column" TYPE VARCHAR(20)',
390+
]
391+
392+
393+
def test_alter_table_precision_increase_decimal(adapter: t.Callable):
394+
current_table_name = "test_table"
395+
target_table_name = "target_table"
396+
397+
def table_columns(table_name: str) -> t.Dict[str, exp.DataType]:
398+
if table_name == current_table_name:
399+
return {
400+
"id": exp.DataType.build("int"),
401+
"test_column": exp.DataType.build("DECIMAL(10, 10)"),
402+
}
403+
else:
404+
return {
405+
"id": exp.DataType.build("int"),
406+
"test_column": exp.DataType.build("DECIMAL(25, 10)"),
407+
}
408+
409+
adapter.columns = table_columns
410+
411+
adapter.alter_table(adapter.get_alter_expressions(current_table_name, target_table_name))
412+
assert to_sql_calls(adapter) == [
413+
'ALTER TABLE "test_table" DROP COLUMN "test_column" CASCADE',
414+
'ALTER TABLE "test_table" ADD COLUMN "test_column" DECIMAL(25, 10)',
415+
]
416+
417+
369418
def test_merge(make_mocked_engine_adapter: t.Callable, mocker: MockerFixture):
370419
adapter = make_mocked_engine_adapter(RedshiftEngineAdapter)
371420
mocker.patch(

tests/core/test_schema_diff.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,42 @@ def test_schema_diff_calculate_type_transitions():
884884
],
885885
{},
886886
),
887+
# Increase the precision of a type is ALTER when the type is supported
888+
(
889+
"STRUCT<id INT, address VARCHAR(120)>",
890+
"STRUCT<id INT, address VARCHAR(121)>",
891+
[
892+
TableAlterOperation.alter_type(
893+
TableAlterColumn.primitive("address"),
894+
"VARCHAR(121)",
895+
current_type="VARCHAR(120)",
896+
expected_table_struct="STRUCT<id INT, address VARCHAR(121)>",
897+
)
898+
],
899+
dict(
900+
precision_increase_allowed_types={exp.DataType.build("VARCHAR").this},
901+
),
902+
),
903+
# Increase the precision of a type is DROP/ADD when the type is not supported
904+
(
905+
"STRUCT<id INT, address VARCHAR(120)>",
906+
"STRUCT<id INT, address VARCHAR(121)>",
907+
[
908+
TableAlterOperation.drop(
909+
TableAlterColumn.primitive("address"),
910+
"STRUCT<id INT>",
911+
"VARCHAR(120)",
912+
),
913+
TableAlterOperation.add(
914+
TableAlterColumn.primitive("address"),
915+
"VARCHAR(121)",
916+
expected_table_struct="STRUCT<id INT, address VARCHAR(121)>",
917+
),
918+
],
919+
dict(
920+
precision_increase_allowed_types={exp.DataType.build("DECIMAL").this},
921+
),
922+
),
887923
# Decrease the precision of a type is DROP/ADD
888924
(
889925
"STRUCT<id INT, address VARCHAR(120)>",

0 commit comments

Comments
 (0)