Skip to content

Commit 42040a8

Browse files
revise to only prompt once; move logic to console
1 parent 6be88d0 commit 42040a8

3 files changed

Lines changed: 107 additions & 90 deletions

File tree

sqlmesh/core/console.py

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,19 @@ class DestroyConsole(abc.ABC):
186186
"""Console for describing a destroy operation"""
187187

188188
@abc.abstractmethod
189-
def start_destroy(self) -> bool:
189+
def start_destroy(
190+
self,
191+
schemas_to_delete: t.Optional[t.Set[str]] = None,
192+
views_to_delete: t.Optional[t.Set[str]] = None,
193+
tables_to_delete: t.Optional[t.Set[str]] = None,
194+
) -> bool:
190195
"""Start a destroy operation.
191196
197+
Args:
198+
schemas_to_delete: Set of schemas that will be deleted
199+
views_to_delete: Set of views that will be deleted
200+
tables_to_delete: Set of tables that will be deleted
201+
192202
Returns:
193203
Whether or not the destroy operation should proceed
194204
"""
@@ -830,7 +840,12 @@ def print_connection_config(
830840
) -> None:
831841
pass
832842

833-
def start_destroy(self) -> bool:
843+
def start_destroy(
844+
self,
845+
schemas_to_delete: t.Optional[t.Set[str]] = None,
846+
views_to_delete: t.Optional[t.Set[str]] = None,
847+
tables_to_delete: t.Optional[t.Set[str]] = None,
848+
) -> bool:
834849
return True
835850

836851
def stop_destroy(self, success: bool = True) -> None:
@@ -1282,22 +1297,57 @@ def stop_cleanup(self, success: bool = False) -> None:
12821297
else:
12831298
self.log_error("Cleanup failed!")
12841299

1285-
def start_destroy(self) -> bool:
1300+
def start_destroy(
1301+
self,
1302+
schemas_to_delete: t.Optional[t.Set[str]] = None,
1303+
views_to_delete: t.Optional[t.Set[str]] = None,
1304+
tables_to_delete: t.Optional[t.Set[str]] = None,
1305+
) -> bool:
12861306
self.log_error(
12871307
(
12881308
"!!! EXTREME CAUTION: DESTRUCTIVE OPERATION !!!\n\n"
1289-
"The 'destroy' command will PERMANENTLY DELETE:\n"
1309+
"The 'destroy' command will DELETE:\n"
12901310
" • ALL state tables and metadata\n"
12911311
" • ALL SQLMesh cache and build artifacts\n"
12921312
" • ALL tables and views in the project's schemas/datasets\n"
12931313
" • ALL schemas/datasets managed by SQLMesh in this project\n\n"
12941314
"!!! WARNING: This includes external tables created or managed by other tools !!!\n\n"
1295-
"The operation is irreversible and may disrupt any currently running or scheduled plans.\n"
1315+
"The operation may disrupt any currently running or scheduled plans.\n"
12961316
"Only use this command when you intend to COMPLETELY DESTROY the project.\n"
12971317
)
12981318
)
1299-
if not self._confirm("Do you understand the risks and want to see what will be deleted?"):
1300-
self.log_error("Destroy aborted!")
1319+
1320+
# Display what will be deleted
1321+
if schemas_to_delete or views_to_delete or tables_to_delete:
1322+
self.log_error("\n" + "=" * 50 + "\n")
1323+
if schemas_to_delete:
1324+
self.log_error("Schemas to be deleted:")
1325+
for schema in sorted(schemas_to_delete):
1326+
self.log_error(f" • {schema}")
1327+
1328+
if views_to_delete:
1329+
self.log_error("\nEnvironment views to be deleted:")
1330+
for view in sorted(views_to_delete):
1331+
self.log_error(f" • {view}")
1332+
1333+
if tables_to_delete:
1334+
self.log_error("\nSnapshot tables to be deleted:")
1335+
for table in sorted(tables_to_delete):
1336+
self.log_error(f" • {table}")
1337+
1338+
self.log_error("\nAll SQLMesh state tables will be deleted")
1339+
self.log_error("\n" + "=" * 50 + "\n")
1340+
1341+
# Final confirmation with stronger warning
1342+
self.log_error(
1343+
"!!! WARNING: This action will DELETE ALL the above resources managed by SQLMesh\n"
1344+
"AND potentially external resources created by other tools in these schemas !!!\n"
1345+
)
1346+
1347+
if not self._confirm(
1348+
"Do you understand the risks and are you ABSOLUTELY SURE you want to proceed with deletion?"
1349+
):
1350+
self.log_error("Destroy operation cancelled.")
13011351
return False
13021352
return True
13031353

sqlmesh/core/context.py

Lines changed: 44 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,50 @@ def run_janitor(self, ignore_ttl: bool) -> bool:
849849
def destroy(self) -> bool:
850850
success = False
851851

852-
if self.console.start_destroy():
852+
# Collect resources to be deleted
853+
environments = self.state_reader.get_environments()
854+
schemas_to_delete = set()
855+
tables_to_delete = set()
856+
views_to_delete = set()
857+
all_snapshot_infos = set()
858+
859+
# For each environment find schemas and tables
860+
for environment in environments:
861+
all_snapshot_infos.update(environment.snapshots)
862+
snapshots = self.state_reader.get_snapshots(environment.snapshots).values()
863+
for snapshot in snapshots:
864+
if snapshot.is_model and not snapshot.is_symbolic:
865+
# Get the appropriate adapter
866+
if environment.gateway_managed and snapshot.model_gateway:
867+
adapter = self.engine_adapters.get(
868+
snapshot.model_gateway, self.engine_adapter
869+
)
870+
else:
871+
adapter = self.engine_adapter
872+
873+
if environment.suffix_target.is_schema or environment.suffix_target.is_catalog:
874+
schema = snapshot.qualified_view_name.schema_for_environment(
875+
environment.naming_info, dialect=adapter.dialect
876+
)
877+
catalog = snapshot.qualified_view_name.catalog_for_environment(
878+
environment.naming_info, dialect=adapter.dialect
879+
)
880+
if catalog:
881+
schemas_to_delete.add(f"{catalog}.{schema}")
882+
else:
883+
schemas_to_delete.add(schema)
884+
885+
if environment.suffix_target.is_table:
886+
view_name = snapshot.qualified_view_name.for_environment(
887+
environment.naming_info, dialect=adapter.dialect
888+
)
889+
views_to_delete.add(view_name)
890+
891+
# Add snapshot tables
892+
table_name = snapshot.table_name()
893+
tables_to_delete.add(table_name)
894+
895+
if self.console.start_destroy(schemas_to_delete, views_to_delete, tables_to_delete):
853896
try:
854897
success = self._destroy()
855898
finally:
@@ -2714,80 +2757,6 @@ def _context_diff(
27142757
)
27152758

27162759
def _destroy(self) -> bool:
2717-
environments = self.state_reader.get_environments()
2718-
2719-
schemas_to_delete = set()
2720-
tables_to_delete = set()
2721-
views_to_delete = set()
2722-
all_snapshot_infos = set()
2723-
2724-
# For each environment find schemas and tables
2725-
for environment in environments:
2726-
all_snapshot_infos.update(environment.snapshots)
2727-
snapshots = self.state_reader.get_snapshots(environment.snapshots).values()
2728-
for snapshot in snapshots:
2729-
if snapshot.is_model and not snapshot.is_symbolic:
2730-
# Get the appropriate adapter
2731-
if environment.gateway_managed and snapshot.model_gateway:
2732-
adapter = self.engine_adapters.get(
2733-
snapshot.model_gateway, self.engine_adapter
2734-
)
2735-
else:
2736-
adapter = self.engine_adapter
2737-
2738-
if environment.suffix_target.is_schema or environment.suffix_target.is_catalog:
2739-
schema = snapshot.qualified_view_name.schema_for_environment(
2740-
environment.naming_info, dialect=adapter.dialect
2741-
)
2742-
catalog = snapshot.qualified_view_name.catalog_for_environment(
2743-
environment.naming_info, dialect=adapter.dialect
2744-
)
2745-
if catalog:
2746-
schemas_to_delete.add(f"{catalog}.{schema}")
2747-
else:
2748-
schemas_to_delete.add(schema)
2749-
2750-
if environment.suffix_target.is_table:
2751-
view_name = snapshot.qualified_view_name.for_environment(
2752-
environment.naming_info, dialect=adapter.dialect
2753-
)
2754-
views_to_delete.add(view_name)
2755-
2756-
# Add snapshot tables
2757-
table_name = snapshot.table_name()
2758-
tables_to_delete.add(table_name)
2759-
2760-
# Display what will be deleted
2761-
self.console.log_error("\n" + "=" * 50 + "\n")
2762-
if schemas_to_delete:
2763-
self.console.log_error("Schemas to be deleted:")
2764-
for schema in sorted(schemas_to_delete):
2765-
self.console.log_error(f" • {schema}")
2766-
2767-
if views_to_delete:
2768-
self.console.log_error("\nEnvironment views to be deleted:")
2769-
for view in sorted(views_to_delete):
2770-
self.console.log_error(f" • {view}")
2771-
2772-
if tables_to_delete:
2773-
self.console.log_error("\nSnapshot tables to be deleted:")
2774-
for table in sorted(tables_to_delete):
2775-
self.console.log_error(f" • {table}")
2776-
2777-
self.console.log_error("\nAll SQLMesh state tables will be deleted")
2778-
self.console.log_error("\n" + "=" * 50 + "\n")
2779-
2780-
# Final confirmation with stronger warning
2781-
self.console.log_error(
2782-
"!!! CRITICAL WARNING: This action will PERMANENTLY DELETE ALL the above resources!\n"
2783-
"This includes ALL tables, views and schemas managed by SQLMesh AND potentially\n"
2784-
"external resources created by other tools in these schemas. This action is IRREVERSIBLE!\n"
2785-
)
2786-
2787-
if not self.console._confirm("Are you ABSOLUTELY SURE you want to proceed with deletion?"): # type: ignore
2788-
self.console.log_error("Destroy operation cancelled.")
2789-
return False
2790-
27912760
# Invalidate all environments, including prod
27922761
for environment in self.state_reader.get_environments():
27932762
self.state_sync.invalidate_environment(name=environment.name, protect_prod=False)

tests/integrations/jupyter/test_magics.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -900,31 +900,29 @@ def test_destroy(
900900
expected_messages = [
901901
(
902902
"!!! EXTREME CAUTION: DESTRUCTIVE OPERATION !!!\n\n"
903-
"The 'destroy' command will PERMANENTLY DELETE:\n"
903+
"The 'destroy' command will DELETE:\n"
904904
" • ALL state tables and metadata\n"
905905
" • ALL SQLMesh cache and build artifacts\n"
906906
" • ALL tables and views in the project's schemas/datasets\n"
907907
" • ALL schemas/datasets managed by SQLMesh in this project\n\n"
908908
"!!! WARNING: This includes external tables created or managed by other tools !!!\n\n"
909-
"The operation is irreversible and may disrupt any currently running or scheduled plans.\n"
909+
"The operation may disrupt any currently running or scheduled plans.\n"
910910
"Only use this command when you intend to COMPLETELY DESTROY the project."
911911
),
912-
"Do you understand the risks and want to see what will be deleted? [y/n]:",
913912
"Schemas to be deleted:",
914913
"• memory.sushi",
915914
"Snapshot tables to be deleted:",
916915
"All SQLMesh state tables will be deleted",
916+
"==================================================",
917917
(
918-
"!!! CRITICAL WARNING: This action will PERMANENTLY DELETE ALL the above resources!\n"
919-
"This includes ALL tables, views and schemas managed by SQLMesh AND potentially\n"
920-
"external resources created by other tools in these schemas. This action is IRREVERSIBLE!"
918+
"!!! WARNING: This action will DELETE ALL the above resources managed by SQLMesh\n"
919+
"AND potentially external resources created by other tools in these schemas !!!"
921920
),
922-
"Are you ABSOLUTELY SURE you want to proceed with deletion? [y/n]:",
921+
"Do you understand the risks and are you ABSOLUTELY SURE you want to proceed with deletion? [y/n]:",
923922
"Environment 'prod' invalidated.",
924923
"Deleted object memory.sushi",
925924
'Deleted object "memory"."raw"."model1"',
926925
'Deleted object "memory"."raw"."model2"',
927-
'Deleted object "memory"."raw"."demographics"',
928926
"State tables removed.",
929927
"Destroy completed successfully.",
930928
]

0 commit comments

Comments
 (0)