Skip to content

Commit 404ea84

Browse files
committed
feat: add ignore destructive support
1 parent e4ea4c8 commit 404ea84

File tree

32 files changed

+3072
-243
lines changed

32 files changed

+3072
-243
lines changed

docs/concepts/models/overview.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -507,11 +507,15 @@ Some properties are only available in specific model kinds - see the [model conf
507507
: Set this to true to indicate that all changes to this model should be [forward-only](../plans.md#forward-only-plans).
508508

509509
### on_destructive_change
510-
: What should happen when a change to a [forward-only model](../../guides/incremental_time.md#forward-only-models) or incremental model in a [forward-only plan](../plans.md#forward-only-plans) causes a destructive modification to the table schema (i.e., requires dropping an existing column).
510+
: What should happen when a change to a [forward-only model](../../guides/incremental_time.md#forward-only-models) or incremental model in a [forward-only plan](../plans.md#forward-only-plans) causes a destructive modification to the table schema (i.e., requires dropping an existing column or modifying column constraints in ways that could cause data loss).
511511

512512
SQLMesh checks for destructive changes at plan time based on the model definition and run time based on the model's underlying physical tables.
513513

514-
Must be one of the following values: `allow`, `warn`, or `error` (default).
514+
Must be one of the following values: `allow`, `warn`, `error` (default), or `ignore`.
515+
516+
!!! warning "Ignore is Dangerous"
517+
518+
`ignore` is dangerous since it can result in error or data loss. It likely should never be used but could be useful as an "escape-hatch" or a way to workaround unexpected behavior.
515519

516520
### disable_restatement
517521
: Set this to true to indicate that [data restatement](../plans.md#restatement-plans) is disabled for this model.

docs/guides/incremental_time.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,12 @@ The check is performed at plan time based on the model definition. SQLMesh may n
171171

172172
A model's `on_destructive_change` [configuration setting](../reference/model_configuration.md#incremental-models) determines what happens when SQLMesh detects a destructive change.
173173

174-
By default, SQLMesh will error so no data is lost. You can set `on_destructive_change` to `warn` or `allow` in the model's `MODEL` block to allow destructive changes.
174+
By default, SQLMesh will error so no data is lost. You can set `on_destructive_change` to `warn` or `allow` in the model's `MODEL` block to allow destructive changes.
175+
`ignore` can be used to not perform the schema change and allow the table's definition to diverge from the model definition.
176+
177+
!!! warning "Ignore is Dangerous"
178+
179+
`ignore` is dangerous since it can result in error or data loss. It likely should never be used but could be useful as an "escape-hatch" or a way to workaround unexpected behavior.
175180

176181
This example configures a model to silently `allow` destructive changes:
177182

examples/custom_materializations/custom_materializations/custom_kind.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ def insert(
2424
query_or_df: QueryOrDF,
2525
model: Model,
2626
is_first_insert: bool,
27+
render_kwargs: t.Dict[str, t.Any],
2728
**kwargs: t.Any,
2829
) -> None:
2930
assert type(model.kind).__name__ == "ExtendedCustomKind"
3031

31-
self._replace_query_for_model(model, table_name, query_or_df)
32+
self._replace_query_for_model(model, table_name, query_or_df, render_kwargs)

examples/custom_materializations/custom_materializations/full.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ def insert(
1717
query_or_df: QueryOrDF,
1818
model: Model,
1919
is_first_insert: bool,
20+
render_kwargs: t.Dict[str, t.Any],
2021
**kwargs: t.Any,
2122
) -> None:
22-
self._replace_query_for_model(model, table_name, query_or_df)
23+
self._replace_query_for_model(model, table_name, query_or_df, render_kwargs)

sqlmesh/core/dialect.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from sqlglot.tokens import Token
2424

2525
from sqlmesh.core.constants import MAX_MODEL_DEFINITION_SIZE
26+
from sqlmesh.utils import get_source_columns_to_types
2627
from sqlmesh.utils.errors import SQLMeshError, ConfigError
2728
from sqlmesh.utils.pandas import columns_to_types_from_df
2829

@@ -1134,31 +1135,43 @@ def select_from_values_for_batch_range(
11341135
batch_start: int,
11351136
batch_end: int,
11361137
alias: str = "t",
1138+
source_columns: t.Optional[t.List[str]] = None,
11371139
) -> exp.Select:
1138-
casted_columns = [
1139-
exp.alias_(exp.cast(exp.column(column), to=kind), column, copy=False)
1140-
for column, kind in columns_to_types.items()
1141-
]
1140+
source_columns = source_columns or list(columns_to_types)
1141+
source_columns_to_types = get_source_columns_to_types(columns_to_types, source_columns)
11421142

11431143
if not values:
11441144
# Ensures we don't generate an empty VALUES clause & forces a zero-row output
11451145
where = exp.false()
1146-
expressions = [tuple(exp.cast(exp.null(), to=kind) for kind in columns_to_types.values())]
1146+
expressions = [
1147+
tuple(exp.cast(exp.null(), to=kind) for kind in source_columns_to_types.values())
1148+
]
11471149
else:
11481150
where = None
11491151
expressions = [
1150-
tuple(transform_values(v, columns_to_types)) for v in values[batch_start:batch_end]
1152+
tuple(transform_values(v, source_columns_to_types))
1153+
for v in values[batch_start:batch_end]
11511154
]
11521155

1153-
values_exp = exp.values(expressions, alias=alias, columns=columns_to_types)
1156+
values_exp = exp.values(expressions, alias=alias, columns=source_columns_to_types)
11541157
if values:
11551158
# BigQuery crashes on `SELECT CAST(x AS TIMESTAMP) FROM UNNEST([NULL]) AS x`, but not
11561159
# on `SELECT CAST(x AS TIMESTAMP) FROM UNNEST([CAST(NULL AS TIMESTAMP)]) AS x`. This
11571160
# ensures nulls under the `Values` expression are cast to avoid similar issues.
1158-
for value, kind in zip(values_exp.expressions[0].expressions, columns_to_types.values()):
1161+
for value, kind in zip(
1162+
values_exp.expressions[0].expressions, source_columns_to_types.values()
1163+
):
11591164
if isinstance(value, exp.Null):
11601165
value.replace(exp.cast(value, to=kind))
11611166

1167+
casted_columns = [
1168+
exp.alias_(
1169+
exp.cast(exp.column(column) if column in source_columns else exp.Null(), to=kind),
1170+
column,
1171+
copy=False,
1172+
)
1173+
for column, kind in columns_to_types.items()
1174+
]
11621175
return exp.select(*casted_columns).from_(values_exp, copy=False).where(where, copy=False)
11631176

11641177

sqlmesh/core/engine_adapter/_typing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
snowpark = optional_import("snowflake.snowpark")
1515

16-
Query = t.Union[exp.Query, exp.DerivedTable]
16+
Query = exp.Query
1717
PySparkSession = t.Union[pyspark.sql.SparkSession, pyspark.sql.connect.dataframe.SparkSession]
1818
PySparkDataFrame = t.Union[pyspark.sql.DataFrame, pyspark.sql.connect.dataframe.DataFrame]
1919

sqlmesh/core/engine_adapter/athena.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ def replace_query(
434434
columns_to_types: t.Optional[t.Dict[str, exp.DataType]] = None,
435435
table_description: t.Optional[str] = None,
436436
column_descriptions: t.Optional[t.Dict[str, str]] = None,
437+
source_columns: t.Optional[t.List[str]] = None,
437438
**kwargs: t.Any,
438439
) -> None:
439440
table = exp.to_table(table_name)
@@ -447,6 +448,7 @@ def replace_query(
447448
columns_to_types=columns_to_types,
448449
table_description=table_description,
449450
column_descriptions=column_descriptions,
451+
source_columns=source_columns,
450452
**kwargs,
451453
)
452454

0 commit comments

Comments
 (0)