Skip to content

Commit 6be88d0

Browse files
Chore: Show resources to be deleted and enhance warnings in destroy
1 parent 5dfdeca commit 6be88d0

6 files changed

Lines changed: 115 additions & 16 deletions

File tree

docs/reference/cli.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ Options:
149149
```
150150
Usage: sqlmesh destroy
151151
152-
Removes all project resources, including warehouse objects, state tables, the SQLMesh cache and any build artifacts.
152+
Removes all state tables, the SQLMesh cache and all project resources, including warehouse objects. This includes all tables, views and schemas managed by SQLMesh, as well as any external resources that may have been created by other tools within those schemas.
153153
154154
Options:
155155
--help Show this message and exit.

docs/reference/notebook.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ options:
250250
```
251251
%destroy
252252
253-
Removes all project resources, including warehouse objects, state tables, the SQLMesh cache and any build artifacts.
253+
Removes all state tables, the SQLMesh cache, and other project resources, including warehouse objects. This includes all tables, views, and schemas managed by SQLMesh, as well as any external resources that may have been created by other tools within those schemas.
254254
```
255255

256256
#### dlt_refresh

sqlmesh/core/console.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,14 +1283,20 @@ def stop_cleanup(self, success: bool = False) -> None:
12831283
self.log_error("Cleanup failed!")
12841284

12851285
def start_destroy(self) -> bool:
1286-
self.log_warning(
1286+
self.log_error(
12871287
(
1288-
"This will permanently delete all engine-managed objects, state tables and SQLMesh cache.\n"
1288+
"!!! EXTREME CAUTION: DESTRUCTIVE OPERATION !!!\n\n"
1289+
"The 'destroy' command will PERMANENTLY DELETE:\n"
1290+
" • ALL state tables and metadata\n"
1291+
" • ALL SQLMesh cache and build artifacts\n"
1292+
" • ALL tables and views in the project's schemas/datasets\n"
1293+
" • ALL schemas/datasets managed by SQLMesh in this project\n\n"
1294+
"!!! WARNING: This includes external tables created or managed by other tools !!!\n\n"
12891295
"The operation is irreversible and may disrupt any currently running or scheduled plans.\n"
1290-
"Use this command only when you intend to fully reset the project."
1296+
"Only use this command when you intend to COMPLETELY DESTROY the project.\n"
12911297
)
12921298
)
1293-
if not self._confirm("Proceed?"):
1299+
if not self._confirm("Do you understand the risks and want to see what will be deleted?"):
12941300
self.log_error("Destroy aborted!")
12951301
return False
12961302
return True

sqlmesh/core/context.py

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -851,8 +851,7 @@ def destroy(self) -> bool:
851851

852852
if self.console.start_destroy():
853853
try:
854-
self._destroy()
855-
success = True
854+
success = self._destroy()
856855
finally:
857856
self.console.stop_destroy(success=success)
858857

@@ -2714,7 +2713,81 @@ def _context_diff(
27142713
always_recreate_environment=always_recreate_environment,
27152714
)
27162715

2717-
def _destroy(self) -> None:
2716+
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+
27182791
# Invalidate all environments, including prod
27192792
for environment in self.state_reader.get_environments():
27202793
self.state_sync.invalidate_environment(name=environment.name, protect_prod=False)
@@ -2730,6 +2803,8 @@ def _destroy(self) -> None:
27302803
# Finally clear caches
27312804
self.clear_caches()
27322805

2806+
return True
2807+
27332808
def _run_janitor(self, ignore_ttl: bool = False) -> None:
27342809
current_ts = now_timestamp()
27352810

tests/core/test_integration.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6354,7 +6354,9 @@ def test_destroy(copy_to_temp_path):
63546354
context.fetchdf(f"SELECT * FROM db_1.first_schema.model_two")
63556355

63566356
# Use the destroy command to remove all data objects and state
6357-
context._destroy()
6357+
# Mock the console confirmation to automatically return True
6358+
with patch.object(context.console, "_confirm", return_value=True):
6359+
context._destroy()
63586360

63596361
# Ensure all tables have been removed
63606362
for table_name in state_tables:

tests/integrations/jupyter/test_magics.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -898,16 +898,32 @@ def test_destroy(
898898
assert not output.stderr
899899
text_output = convert_all_html_output_to_text(output)
900900
expected_messages = [
901-
"[WARNING] This will permanently delete all engine-managed objects, state tables and SQLMesh cache.\n"
902-
"The operation is irreversible and may disrupt any currently running or scheduled plans.\n"
903-
"Use this command only when you intend to fully reset the project.",
901+
(
902+
"!!! EXTREME CAUTION: DESTRUCTIVE OPERATION !!!\n\n"
903+
"The 'destroy' command will PERMANENTLY DELETE:\n"
904+
" • ALL state tables and metadata\n"
905+
" • ALL SQLMesh cache and build artifacts\n"
906+
" • ALL tables and views in the project's schemas/datasets\n"
907+
" • ALL schemas/datasets managed by SQLMesh in this project\n\n"
908+
"!!! 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"
910+
"Only use this command when you intend to COMPLETELY DESTROY the project."
911+
),
912+
"Do you understand the risks and want to see what will be deleted? [y/n]:",
913+
"Schemas to be deleted:",
914+
"• memory.sushi",
915+
"Snapshot tables to be deleted:",
916+
"All SQLMesh state tables will be deleted",
917+
(
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!"
921+
),
922+
"Are you ABSOLUTELY SURE you want to proceed with deletion? [y/n]:",
904923
"Environment 'prod' invalidated.",
905924
"Deleted object memory.sushi",
906925
'Deleted object "memory"."raw"."model1"',
907-
'Deleted object "memory"."raw"."model1"',
908926
'Deleted object "memory"."raw"."model2"',
909-
'Deleted object "memory"."raw"."model2"',
910-
'Deleted object "memory"."raw"."demographics"',
911927
'Deleted object "memory"."raw"."demographics"',
912928
"State tables removed.",
913929
"Destroy completed successfully.",

0 commit comments

Comments
 (0)