sqlmesh 0.213.1.dev1__py3-none-any.whl → 0.227.2.dev4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. sqlmesh/__init__.py +12 -2
  2. sqlmesh/_version.py +2 -2
  3. sqlmesh/cli/main.py +0 -44
  4. sqlmesh/cli/project_init.py +11 -2
  5. sqlmesh/core/_typing.py +1 -0
  6. sqlmesh/core/audit/definition.py +8 -2
  7. sqlmesh/core/config/__init__.py +1 -1
  8. sqlmesh/core/config/connection.py +17 -5
  9. sqlmesh/core/config/dbt.py +13 -0
  10. sqlmesh/core/config/janitor.py +12 -0
  11. sqlmesh/core/config/loader.py +7 -0
  12. sqlmesh/core/config/model.py +2 -0
  13. sqlmesh/core/config/root.py +3 -0
  14. sqlmesh/core/console.py +81 -3
  15. sqlmesh/core/constants.py +1 -1
  16. sqlmesh/core/context.py +69 -26
  17. sqlmesh/core/dialect.py +3 -0
  18. sqlmesh/core/engine_adapter/_typing.py +2 -0
  19. sqlmesh/core/engine_adapter/base.py +322 -22
  20. sqlmesh/core/engine_adapter/base_postgres.py +17 -1
  21. sqlmesh/core/engine_adapter/bigquery.py +146 -7
  22. sqlmesh/core/engine_adapter/clickhouse.py +17 -13
  23. sqlmesh/core/engine_adapter/databricks.py +33 -2
  24. sqlmesh/core/engine_adapter/fabric.py +10 -29
  25. sqlmesh/core/engine_adapter/mixins.py +142 -48
  26. sqlmesh/core/engine_adapter/mssql.py +15 -4
  27. sqlmesh/core/engine_adapter/mysql.py +2 -2
  28. sqlmesh/core/engine_adapter/postgres.py +9 -3
  29. sqlmesh/core/engine_adapter/redshift.py +4 -0
  30. sqlmesh/core/engine_adapter/risingwave.py +1 -0
  31. sqlmesh/core/engine_adapter/shared.py +6 -0
  32. sqlmesh/core/engine_adapter/snowflake.py +82 -11
  33. sqlmesh/core/engine_adapter/spark.py +14 -10
  34. sqlmesh/core/engine_adapter/trino.py +4 -2
  35. sqlmesh/core/environment.py +2 -0
  36. sqlmesh/core/janitor.py +181 -0
  37. sqlmesh/core/lineage.py +1 -0
  38. sqlmesh/core/linter/definition.py +13 -13
  39. sqlmesh/core/linter/rules/builtin.py +29 -0
  40. sqlmesh/core/macros.py +35 -13
  41. sqlmesh/core/model/common.py +2 -0
  42. sqlmesh/core/model/definition.py +82 -28
  43. sqlmesh/core/model/kind.py +66 -2
  44. sqlmesh/core/model/meta.py +108 -4
  45. sqlmesh/core/node.py +101 -1
  46. sqlmesh/core/plan/builder.py +18 -10
  47. sqlmesh/core/plan/common.py +199 -2
  48. sqlmesh/core/plan/definition.py +25 -6
  49. sqlmesh/core/plan/evaluator.py +75 -113
  50. sqlmesh/core/plan/explainer.py +90 -8
  51. sqlmesh/core/plan/stages.py +42 -21
  52. sqlmesh/core/renderer.py +78 -32
  53. sqlmesh/core/scheduler.py +102 -22
  54. sqlmesh/core/selector.py +137 -9
  55. sqlmesh/core/signal.py +64 -1
  56. sqlmesh/core/snapshot/__init__.py +2 -0
  57. sqlmesh/core/snapshot/definition.py +146 -34
  58. sqlmesh/core/snapshot/evaluator.py +689 -124
  59. sqlmesh/core/state_sync/__init__.py +0 -1
  60. sqlmesh/core/state_sync/base.py +55 -33
  61. sqlmesh/core/state_sync/cache.py +12 -7
  62. sqlmesh/core/state_sync/common.py +216 -111
  63. sqlmesh/core/state_sync/db/environment.py +6 -4
  64. sqlmesh/core/state_sync/db/facade.py +42 -24
  65. sqlmesh/core/state_sync/db/interval.py +27 -7
  66. sqlmesh/core/state_sync/db/migrator.py +34 -16
  67. sqlmesh/core/state_sync/db/snapshot.py +177 -169
  68. sqlmesh/core/table_diff.py +2 -2
  69. sqlmesh/core/test/context.py +2 -0
  70. sqlmesh/core/test/definition.py +14 -9
  71. sqlmesh/dbt/adapter.py +22 -16
  72. sqlmesh/dbt/basemodel.py +75 -56
  73. sqlmesh/dbt/builtin.py +116 -12
  74. sqlmesh/dbt/column.py +17 -5
  75. sqlmesh/dbt/common.py +19 -5
  76. sqlmesh/dbt/context.py +14 -1
  77. sqlmesh/dbt/loader.py +61 -9
  78. sqlmesh/dbt/manifest.py +174 -16
  79. sqlmesh/dbt/model.py +183 -85
  80. sqlmesh/dbt/package.py +16 -1
  81. sqlmesh/dbt/profile.py +3 -3
  82. sqlmesh/dbt/project.py +12 -7
  83. sqlmesh/dbt/seed.py +6 -1
  84. sqlmesh/dbt/source.py +13 -1
  85. sqlmesh/dbt/target.py +25 -6
  86. sqlmesh/dbt/test.py +36 -5
  87. sqlmesh/migrations/v0000_baseline.py +95 -0
  88. sqlmesh/migrations/v0061_mysql_fix_blob_text_type.py +5 -7
  89. sqlmesh/migrations/v0062_add_model_gateway.py +5 -1
  90. sqlmesh/migrations/v0063_change_signals.py +5 -3
  91. sqlmesh/migrations/v0064_join_when_matched_strings.py +5 -3
  92. sqlmesh/migrations/v0065_add_model_optimize.py +5 -1
  93. sqlmesh/migrations/v0066_add_auto_restatements.py +8 -3
  94. sqlmesh/migrations/v0067_add_tsql_date_full_precision.py +5 -1
  95. sqlmesh/migrations/v0068_include_unrendered_query_in_metadata_hash.py +5 -1
  96. sqlmesh/migrations/v0069_update_dev_table_suffix.py +5 -3
  97. sqlmesh/migrations/v0070_include_grains_in_metadata_hash.py +5 -1
  98. sqlmesh/migrations/v0071_add_dev_version_to_intervals.py +9 -5
  99. sqlmesh/migrations/v0072_add_environment_statements.py +5 -3
  100. sqlmesh/migrations/v0073_remove_symbolic_disable_restatement.py +5 -3
  101. sqlmesh/migrations/v0074_add_partition_by_time_column_property.py +5 -1
  102. sqlmesh/migrations/v0075_remove_validate_query.py +5 -3
  103. sqlmesh/migrations/v0076_add_cron_tz.py +5 -1
  104. sqlmesh/migrations/v0077_fix_column_type_hash_calculation.py +5 -1
  105. sqlmesh/migrations/v0078_warn_if_non_migratable_python_env.py +5 -3
  106. sqlmesh/migrations/v0079_add_gateway_managed_property.py +10 -5
  107. sqlmesh/migrations/v0080_add_batch_size_to_scd_type_2_models.py +5 -1
  108. sqlmesh/migrations/v0081_update_partitioned_by.py +5 -3
  109. sqlmesh/migrations/v0082_warn_if_incorrectly_duplicated_statements.py +5 -3
  110. sqlmesh/migrations/v0083_use_sql_for_scd_time_data_type_data_hash.py +5 -1
  111. sqlmesh/migrations/v0084_normalize_quote_when_matched_and_merge_filter.py +5 -1
  112. sqlmesh/migrations/v0085_deterministic_repr.py +5 -3
  113. sqlmesh/migrations/v0086_check_deterministic_bug.py +5 -3
  114. sqlmesh/migrations/v0087_normalize_blueprint_variables.py +5 -3
  115. sqlmesh/migrations/v0088_warn_about_variable_python_env_diffs.py +5 -3
  116. sqlmesh/migrations/v0089_add_virtual_environment_mode.py +5 -1
  117. sqlmesh/migrations/v0090_add_forward_only_column.py +9 -5
  118. sqlmesh/migrations/v0091_on_additive_change.py +5 -1
  119. sqlmesh/migrations/v0092_warn_about_dbt_data_type_diff.py +5 -3
  120. sqlmesh/migrations/v0093_use_raw_sql_in_fingerprint.py +5 -1
  121. sqlmesh/migrations/v0094_add_dev_version_and_fingerprint_columns.py +123 -0
  122. sqlmesh/migrations/v0095_warn_about_dbt_raw_sql_diff.py +49 -0
  123. sqlmesh/migrations/v0096_remove_plan_dags_table.py +13 -0
  124. sqlmesh/migrations/v0097_add_dbt_name_in_node.py +9 -0
  125. sqlmesh/migrations/{v0060_move_audits_to_model.py → v0098_add_dbt_node_info_in_node.py} +33 -16
  126. sqlmesh/migrations/v0099_add_last_altered_to_intervals.py +25 -0
  127. sqlmesh/migrations/v0100_add_grants_and_grants_target_layer.py +9 -0
  128. sqlmesh/utils/__init__.py +8 -1
  129. sqlmesh/utils/cache.py +5 -1
  130. sqlmesh/utils/connection_pool.py +2 -1
  131. sqlmesh/utils/dag.py +65 -10
  132. sqlmesh/utils/date.py +8 -1
  133. sqlmesh/utils/errors.py +8 -0
  134. sqlmesh/utils/jinja.py +54 -4
  135. sqlmesh/utils/pydantic.py +6 -6
  136. sqlmesh/utils/windows.py +13 -3
  137. {sqlmesh-0.213.1.dev1.dist-info → sqlmesh-0.227.2.dev4.dist-info}/METADATA +7 -10
  138. sqlmesh-0.227.2.dev4.dist-info/RECORD +370 -0
  139. sqlmesh_dbt/cli.py +70 -7
  140. sqlmesh_dbt/console.py +14 -6
  141. sqlmesh_dbt/operations.py +103 -24
  142. sqlmesh_dbt/selectors.py +39 -1
  143. web/client/dist/assets/{Audits-Ucsx1GzF.js → Audits-CBiYyyx-.js} +1 -1
  144. web/client/dist/assets/{Banner-BWDzvavM.js → Banner-DSRbUlO5.js} +1 -1
  145. web/client/dist/assets/{ChevronDownIcon-D2VL13Ah.js → ChevronDownIcon-MK_nrjD_.js} +1 -1
  146. web/client/dist/assets/{ChevronRightIcon-DWGYbf1l.js → ChevronRightIcon-CLWtT22Q.js} +1 -1
  147. web/client/dist/assets/{Content-DdHDZM3I.js → Content-BNuGZN5l.js} +1 -1
  148. web/client/dist/assets/{Content-Bikfy8fh.js → Content-CSHJyW0n.js} +1 -1
  149. web/client/dist/assets/{Data-CzAJH7rW.js → Data-C1oRDbLx.js} +1 -1
  150. web/client/dist/assets/{DataCatalog-BJF11g8f.js → DataCatalog-HXyX2-_j.js} +1 -1
  151. web/client/dist/assets/{Editor-s0SBpV2y.js → Editor-BDyfpUuw.js} +1 -1
  152. web/client/dist/assets/{Editor-DgLhgKnm.js → Editor-D0jNItwC.js} +1 -1
  153. web/client/dist/assets/{Errors-D0m0O1d3.js → Errors-BfuFLcPi.js} +1 -1
  154. web/client/dist/assets/{FileExplorer-CEv0vXkt.js → FileExplorer-BR9IE3he.js} +1 -1
  155. web/client/dist/assets/{Footer-BwzXn8Ew.js → Footer-CgBEtiAh.js} +1 -1
  156. web/client/dist/assets/{Header-6heDkEqG.js → Header-DSqR6nSO.js} +1 -1
  157. web/client/dist/assets/{Input-obuJsD6k.js → Input-B-oZ6fGO.js} +1 -1
  158. web/client/dist/assets/Lineage-DYQVwDbD.js +1 -0
  159. web/client/dist/assets/{ListboxShow-HM9_qyrt.js → ListboxShow-BE5-xevs.js} +1 -1
  160. web/client/dist/assets/{ModelLineage-zWdKo0U2.js → ModelLineage-DkIFAYo4.js} +1 -1
  161. web/client/dist/assets/{Models-Bcu66SRz.js → Models-D5dWr8RB.js} +1 -1
  162. web/client/dist/assets/{Page-BWEEQfIt.js → Page-C-XfU5BR.js} +1 -1
  163. web/client/dist/assets/{Plan-C4gXCqlf.js → Plan-ZEuTINBq.js} +1 -1
  164. web/client/dist/assets/{PlusCircleIcon-CVDO651q.js → PlusCircleIcon-DVXAHG8_.js} +1 -1
  165. web/client/dist/assets/{ReportErrors-BT6xFwAr.js → ReportErrors-B7FEPzMB.js} +1 -1
  166. web/client/dist/assets/{Root-ryJoBK4h.js → Root-8aZyhPxF.js} +1 -1
  167. web/client/dist/assets/{SearchList-DB04sPb9.js → SearchList-W_iT2G82.js} +1 -1
  168. web/client/dist/assets/{SelectEnvironment-CUYcXUu6.js → SelectEnvironment-C65jALmO.js} +1 -1
  169. web/client/dist/assets/{SourceList-Doo_9ZGp.js → SourceList-DSLO6nVJ.js} +1 -1
  170. web/client/dist/assets/{SourceListItem-D5Mj7Dly.js → SourceListItem-BHt8d9-I.js} +1 -1
  171. web/client/dist/assets/{SplitPane-qHmkD1qy.js → SplitPane-CViaZmw6.js} +1 -1
  172. web/client/dist/assets/{Tests-DH1Z74ML.js → Tests-DhaVt5t1.js} +1 -1
  173. web/client/dist/assets/{Welcome-DqUJUNMF.js → Welcome-DvpjH-_4.js} +1 -1
  174. web/client/dist/assets/context-BctCsyGb.js +71 -0
  175. web/client/dist/assets/{context-Dr54UHLi.js → context-DFNeGsFF.js} +1 -1
  176. web/client/dist/assets/{editor-DYIP1yQ4.js → editor-CcO28cqd.js} +1 -1
  177. web/client/dist/assets/{file-DarlIDVi.js → file-CvJN3aZO.js} +1 -1
  178. web/client/dist/assets/{floating-ui.react-dom-BH3TFvkM.js → floating-ui.react-dom-CjE-JNW1.js} +1 -1
  179. web/client/dist/assets/{help-Bl8wqaQc.js → help-DuPhjipa.js} +1 -1
  180. web/client/dist/assets/{index-D1sR7wpN.js → index-C-dJH7yZ.js} +1 -1
  181. web/client/dist/assets/{index-O3mjYpnE.js → index-Dj0i1-CA.js} +2 -2
  182. web/client/dist/assets/{plan-CehRrJUG.js → plan-BTRSbjKn.js} +1 -1
  183. web/client/dist/assets/{popover-CqgMRE0G.js → popover-_Sf0yvOI.js} +1 -1
  184. web/client/dist/assets/{project-6gxepOhm.js → project-BvSOI8MY.js} +1 -1
  185. web/client/dist/index.html +1 -1
  186. sqlmesh/integrations/llm.py +0 -56
  187. sqlmesh/migrations/v0001_init.py +0 -60
  188. sqlmesh/migrations/v0002_remove_identify.py +0 -5
  189. sqlmesh/migrations/v0003_move_batch_size.py +0 -34
  190. sqlmesh/migrations/v0004_environmnent_add_finalized_at.py +0 -23
  191. sqlmesh/migrations/v0005_create_seed_table.py +0 -24
  192. sqlmesh/migrations/v0006_change_seed_hash.py +0 -5
  193. sqlmesh/migrations/v0007_env_table_info_to_kind.py +0 -99
  194. sqlmesh/migrations/v0008_create_intervals_table.py +0 -38
  195. sqlmesh/migrations/v0009_remove_pre_post_hooks.py +0 -62
  196. sqlmesh/migrations/v0010_seed_hash_batch_size.py +0 -5
  197. sqlmesh/migrations/v0011_add_model_kind_name.py +0 -63
  198. sqlmesh/migrations/v0012_update_jinja_expressions.py +0 -86
  199. sqlmesh/migrations/v0013_serde_using_model_dialects.py +0 -87
  200. sqlmesh/migrations/v0014_fix_dev_intervals.py +0 -14
  201. sqlmesh/migrations/v0015_environment_add_promoted_snapshot_ids.py +0 -26
  202. sqlmesh/migrations/v0016_fix_windows_path.py +0 -59
  203. sqlmesh/migrations/v0017_fix_windows_seed_path.py +0 -55
  204. sqlmesh/migrations/v0018_rename_snapshot_model_to_node.py +0 -53
  205. sqlmesh/migrations/v0019_add_env_suffix_target.py +0 -28
  206. sqlmesh/migrations/v0020_remove_redundant_attributes_from_dbt_models.py +0 -80
  207. sqlmesh/migrations/v0021_fix_table_properties.py +0 -62
  208. sqlmesh/migrations/v0022_move_project_to_model.py +0 -54
  209. sqlmesh/migrations/v0023_fix_added_models_with_forward_only_parents.py +0 -65
  210. sqlmesh/migrations/v0024_replace_model_kind_name_enum_with_value.py +0 -55
  211. sqlmesh/migrations/v0025_fix_intervals_and_missing_change_category.py +0 -117
  212. sqlmesh/migrations/v0026_remove_dialect_from_seed.py +0 -55
  213. sqlmesh/migrations/v0027_minute_interval_to_five.py +0 -57
  214. sqlmesh/migrations/v0028_add_plan_dags_table.py +0 -29
  215. sqlmesh/migrations/v0029_generate_schema_types_using_dialect.py +0 -69
  216. sqlmesh/migrations/v0030_update_unrestorable_snapshots.py +0 -65
  217. sqlmesh/migrations/v0031_remove_dbt_target_fields.py +0 -65
  218. sqlmesh/migrations/v0032_add_sqlmesh_version.py +0 -25
  219. sqlmesh/migrations/v0033_mysql_fix_blob_text_type.py +0 -45
  220. sqlmesh/migrations/v0034_add_default_catalog.py +0 -367
  221. sqlmesh/migrations/v0035_add_catalog_name_override.py +0 -22
  222. sqlmesh/migrations/v0036_delete_plan_dags_bug_fix.py +0 -14
  223. sqlmesh/migrations/v0037_remove_dbt_is_incremental_macro.py +0 -61
  224. sqlmesh/migrations/v0038_add_expiration_ts_to_snapshot.py +0 -73
  225. sqlmesh/migrations/v0039_include_environment_in_plan_dag_spec.py +0 -68
  226. sqlmesh/migrations/v0040_add_previous_finalized_snapshots.py +0 -26
  227. sqlmesh/migrations/v0041_remove_hash_raw_query_attribute.py +0 -59
  228. sqlmesh/migrations/v0042_trim_indirect_versions.py +0 -66
  229. sqlmesh/migrations/v0043_fix_remove_obsolete_attributes_in_plan_dags.py +0 -61
  230. sqlmesh/migrations/v0044_quote_identifiers_in_model_attributes.py +0 -5
  231. sqlmesh/migrations/v0045_move_gateway_variable.py +0 -70
  232. sqlmesh/migrations/v0046_add_batch_concurrency.py +0 -8
  233. sqlmesh/migrations/v0047_change_scd_string_to_column.py +0 -5
  234. sqlmesh/migrations/v0048_drop_indirect_versions.py +0 -59
  235. sqlmesh/migrations/v0049_replace_identifier_with_version_in_seeds_table.py +0 -57
  236. sqlmesh/migrations/v0050_drop_seeds_table.py +0 -11
  237. sqlmesh/migrations/v0051_rename_column_descriptions.py +0 -65
  238. sqlmesh/migrations/v0052_add_normalize_name_in_environment_naming_info.py +0 -28
  239. sqlmesh/migrations/v0053_custom_model_kind_extra_attributes.py +0 -5
  240. sqlmesh/migrations/v0054_fix_trailing_comments.py +0 -5
  241. sqlmesh/migrations/v0055_add_updated_ts_unpaused_ts_ttl_ms_unrestorable_to_snapshot.py +0 -132
  242. sqlmesh/migrations/v0056_restore_table_indexes.py +0 -118
  243. sqlmesh/migrations/v0057_add_table_format.py +0 -5
  244. sqlmesh/migrations/v0058_add_requirements.py +0 -26
  245. sqlmesh/migrations/v0059_add_physical_version.py +0 -5
  246. sqlmesh-0.213.1.dev1.dist-info/RECORD +0 -421
  247. web/client/dist/assets/Lineage-D0Hgdz2v.js +0 -1
  248. web/client/dist/assets/context-DgX0fp2E.js +0 -68
  249. {sqlmesh-0.213.1.dev1.dist-info → sqlmesh-0.227.2.dev4.dist-info}/WHEEL +0 -0
  250. {sqlmesh-0.213.1.dev1.dist-info → sqlmesh-0.227.2.dev4.dist-info}/entry_points.txt +0 -0
  251. {sqlmesh-0.213.1.dev1.dist-info → sqlmesh-0.227.2.dev4.dist-info}/licenses/LICENSE +0 -0
  252. {sqlmesh-0.213.1.dev1.dist-info → sqlmesh-0.227.2.dev4.dist-info}/top_level.txt +0 -0
@@ -22,19 +22,19 @@ import typing as t
22
22
  from pathlib import Path
23
23
  from datetime import datetime
24
24
 
25
- from sqlglot import exp
26
25
 
27
26
  from sqlmesh.core.console import Console, get_console
28
27
  from sqlmesh.core.engine_adapter import EngineAdapter
29
28
  from sqlmesh.core.environment import Environment, EnvironmentStatements, EnvironmentSummary
30
29
  from sqlmesh.core.snapshot import (
31
30
  Snapshot,
31
+ SnapshotIdAndVersion,
32
32
  SnapshotId,
33
33
  SnapshotIdLike,
34
+ SnapshotIdAndVersionLike,
34
35
  SnapshotInfoLike,
35
36
  SnapshotIntervals,
36
37
  SnapshotNameVersion,
37
- SnapshotTableCleanupTask,
38
38
  SnapshotTableInfo,
39
39
  start_date,
40
40
  )
@@ -42,7 +42,6 @@ from sqlmesh.core.snapshot.definition import (
42
42
  Interval,
43
43
  )
44
44
  from sqlmesh.core.state_sync.base import (
45
- PromotionResult,
46
45
  StateSync,
47
46
  Versions,
48
47
  )
@@ -54,6 +53,9 @@ from sqlmesh.core.state_sync.common import (
54
53
  StateStream,
55
54
  chunk_iterable,
56
55
  EnvironmentWithStatements,
56
+ ExpiredSnapshotBatch,
57
+ PromotionResult,
58
+ ExpiredBatchRange,
57
59
  )
58
60
  from sqlmesh.core.state_sync.db.interval import IntervalState
59
61
  from sqlmesh.core.state_sync.db.environment import EnvironmentState
@@ -89,7 +91,6 @@ class EngineAdapterStateSync(StateSync):
89
91
  console: t.Optional[Console] = None,
90
92
  cache_dir: Path = Path(),
91
93
  ):
92
- self.plan_dags_table = exp.table_("_plan_dags", db=schema)
93
94
  self.interval_state = IntervalState(engine_adapter, schema=schema)
94
95
  self.environment_state = EnvironmentState(engine_adapter, schema=schema)
95
96
  self.snapshot_state = SnapshotState(engine_adapter, schema=schema, cache_dir=cache_dir)
@@ -100,7 +101,6 @@ class EngineAdapterStateSync(StateSync):
100
101
  snapshot_state=self.snapshot_state,
101
102
  environment_state=self.environment_state,
102
103
  interval_state=self.interval_state,
103
- plan_dags_table=self.plan_dags_table,
104
104
  console=console,
105
105
  )
106
106
  # Make sure that if an empty string is provided that we treat it as None
@@ -262,10 +262,18 @@ class EngineAdapterStateSync(StateSync):
262
262
  self.environment_state.invalidate_environment(name, protect_prod)
263
263
 
264
264
  def get_expired_snapshots(
265
- self, current_ts: int, ignore_ttl: bool = False
266
- ) -> t.List[SnapshotTableCleanupTask]:
265
+ self,
266
+ *,
267
+ batch_range: ExpiredBatchRange,
268
+ current_ts: t.Optional[int] = None,
269
+ ignore_ttl: bool = False,
270
+ ) -> t.Optional[ExpiredSnapshotBatch]:
271
+ current_ts = current_ts or now_timestamp()
267
272
  return self.snapshot_state.get_expired_snapshots(
268
- self.environment_state.get_environments(), current_ts=current_ts, ignore_ttl=ignore_ttl
273
+ environments=self.environment_state.get_environments(),
274
+ current_ts=current_ts,
275
+ ignore_ttl=ignore_ttl,
276
+ batch_range=batch_range,
269
277
  )
270
278
 
271
279
  def get_expired_environments(self, current_ts: int) -> t.List[EnvironmentSummary]:
@@ -273,17 +281,19 @@ class EngineAdapterStateSync(StateSync):
273
281
 
274
282
  @transactional()
275
283
  def delete_expired_snapshots(
276
- self, ignore_ttl: bool = False, current_ts: t.Optional[int] = None
277
- ) -> t.List[SnapshotTableCleanupTask]:
278
- current_ts = current_ts or now_timestamp()
279
- expired_snapshot_ids, cleanup_targets = self.snapshot_state._get_expired_snapshots(
280
- self.environment_state.get_environments(), ignore_ttl=ignore_ttl, current_ts=current_ts
284
+ self,
285
+ batch_range: ExpiredBatchRange,
286
+ ignore_ttl: bool = False,
287
+ current_ts: t.Optional[int] = None,
288
+ ) -> None:
289
+ batch = self.get_expired_snapshots(
290
+ ignore_ttl=ignore_ttl,
291
+ current_ts=current_ts,
292
+ batch_range=batch_range,
281
293
  )
282
-
283
- self.snapshot_state.delete_snapshots(expired_snapshot_ids)
284
- self.interval_state.cleanup_intervals(cleanup_targets, expired_snapshot_ids)
285
-
286
- return cleanup_targets
294
+ if batch and batch.expired_snapshot_ids:
295
+ self.snapshot_state.delete_snapshots(batch.expired_snapshot_ids)
296
+ self.interval_state.cleanup_intervals(batch.cleanup_tasks, batch.expired_snapshot_ids)
287
297
 
288
298
  @transactional()
289
299
  def delete_expired_environments(
@@ -309,7 +319,6 @@ class EngineAdapterStateSync(StateSync):
309
319
  self.environment_state.environments_table,
310
320
  self.environment_state.environment_statements_table,
311
321
  self.interval_state.intervals_table,
312
- self.plan_dags_table,
313
322
  self.version_state.versions_table,
314
323
  ):
315
324
  self.engine_adapter.drop_table(table)
@@ -368,6 +377,16 @@ class EngineAdapterStateSync(StateSync):
368
377
  Snapshot.hydrate_with_intervals_by_version(snapshots.values(), intervals)
369
378
  return snapshots
370
379
 
380
+ def get_snapshots_by_names(
381
+ self,
382
+ snapshot_names: t.Iterable[str],
383
+ current_ts: t.Optional[int] = None,
384
+ exclude_expired: bool = True,
385
+ ) -> t.Set[SnapshotIdAndVersion]:
386
+ return self.snapshot_state.get_snapshots_by_names(
387
+ snapshot_names=snapshot_names, current_ts=current_ts, exclude_expired=exclude_expired
388
+ )
389
+
371
390
  @transactional()
372
391
  def add_interval(
373
392
  self,
@@ -375,8 +394,9 @@ class EngineAdapterStateSync(StateSync):
375
394
  start: TimeLike,
376
395
  end: TimeLike,
377
396
  is_dev: bool = False,
397
+ last_altered_ts: t.Optional[int] = None,
378
398
  ) -> None:
379
- super().add_interval(snapshot, start, end, is_dev)
399
+ super().add_interval(snapshot, start, end, is_dev, last_altered_ts)
380
400
 
381
401
  @transactional()
382
402
  def add_snapshots_intervals(self, snapshots_intervals: t.Sequence[SnapshotIntervals]) -> None:
@@ -402,7 +422,7 @@ class EngineAdapterStateSync(StateSync):
402
422
  @transactional()
403
423
  def remove_intervals(
404
424
  self,
405
- snapshot_intervals: t.Sequence[t.Tuple[SnapshotInfoLike, Interval]],
425
+ snapshot_intervals: t.Sequence[t.Tuple[SnapshotIdAndVersionLike, Interval]],
406
426
  remove_shared_versions: bool = False,
407
427
  ) -> None:
408
428
  self.interval_state.remove_intervals(snapshot_intervals, remove_shared_versions)
@@ -444,14 +464,12 @@ class EngineAdapterStateSync(StateSync):
444
464
  @transactional()
445
465
  def migrate(
446
466
  self,
447
- default_catalog: t.Optional[str],
448
467
  skip_backup: bool = False,
449
468
  promoted_snapshots_only: bool = True,
450
469
  ) -> None:
451
470
  """Migrate the state sync to the latest SQLMesh / SQLGlot version."""
452
471
  self.migrator.migrate(
453
- self,
454
- default_catalog,
472
+ self.schema,
455
473
  skip_backup=skip_backup,
456
474
  promoted_snapshots_only=promoted_snapshots_only,
457
475
  )
@@ -15,10 +15,10 @@ from sqlmesh.core.state_sync.db.utils import (
15
15
  from sqlmesh.core.snapshot import (
16
16
  SnapshotIntervals,
17
17
  SnapshotIdLike,
18
+ SnapshotIdAndVersionLike,
18
19
  SnapshotNameVersionLike,
19
20
  SnapshotTableCleanupTask,
20
21
  SnapshotNameVersion,
21
- SnapshotInfoLike,
22
22
  Snapshot,
23
23
  )
24
24
  from sqlmesh.core.snapshot.definition import Interval
@@ -60,6 +60,7 @@ class IntervalState:
60
60
  "is_removed": exp.DataType.build("boolean"),
61
61
  "is_compacted": exp.DataType.build("boolean"),
62
62
  "is_pending_restatement": exp.DataType.build("boolean"),
63
+ "last_altered_ts": exp.DataType.build("bigint"),
63
64
  }
64
65
 
65
66
  def add_snapshots_intervals(self, snapshots_intervals: t.Sequence[SnapshotIntervals]) -> None:
@@ -68,11 +69,11 @@ class IntervalState:
68
69
 
69
70
  def remove_intervals(
70
71
  self,
71
- snapshot_intervals: t.Sequence[t.Tuple[SnapshotInfoLike, Interval]],
72
+ snapshot_intervals: t.Sequence[t.Tuple[SnapshotIdAndVersionLike, Interval]],
72
73
  remove_shared_versions: bool = False,
73
74
  ) -> None:
74
75
  intervals_to_remove: t.Sequence[
75
- t.Tuple[t.Union[SnapshotInfoLike, SnapshotIntervals], Interval]
76
+ t.Tuple[t.Union[SnapshotIdAndVersionLike, SnapshotIntervals], Interval]
76
77
  ] = snapshot_intervals
77
78
  if remove_shared_versions:
78
79
  name_version_mapping = {s.name_version: interval for s, interval in snapshot_intervals}
@@ -215,13 +216,23 @@ class IntervalState:
215
216
  for start_ts, end_ts in snapshot.intervals:
216
217
  new_intervals.append(
217
218
  _interval_to_df(
218
- snapshot, start_ts, end_ts, is_dev=False, is_compacted=is_compacted
219
+ snapshot,
220
+ start_ts,
221
+ end_ts,
222
+ is_dev=False,
223
+ is_compacted=is_compacted,
224
+ last_altered_ts=snapshot.last_altered_ts,
219
225
  )
220
226
  )
221
227
  for start_ts, end_ts in snapshot.dev_intervals:
222
228
  new_intervals.append(
223
229
  _interval_to_df(
224
- snapshot, start_ts, end_ts, is_dev=True, is_compacted=is_compacted
230
+ snapshot,
231
+ start_ts,
232
+ end_ts,
233
+ is_dev=True,
234
+ is_compacted=is_compacted,
235
+ last_altered_ts=snapshot.dev_last_altered_ts,
225
236
  )
226
237
  )
227
238
 
@@ -236,6 +247,7 @@ class IntervalState:
236
247
  is_dev=False,
237
248
  is_compacted=is_compacted,
238
249
  is_pending_restatement=True,
250
+ last_altered_ts=snapshot.last_altered_ts,
239
251
  )
240
252
  )
241
253
 
@@ -284,6 +296,7 @@ class IntervalState:
284
296
  is_dev,
285
297
  is_removed,
286
298
  is_pending_restatement,
299
+ last_altered_ts,
287
300
  ) in rows:
288
301
  interval_ids.add(interval_id)
289
302
  merge_key = (name, version, dev_version, identifier)
@@ -318,8 +331,10 @@ class IntervalState:
318
331
  else:
319
332
  if is_dev:
320
333
  intervals[merge_key].add_dev_interval(start, end)
334
+ intervals[merge_key].update_dev_last_altered_ts(last_altered_ts)
321
335
  else:
322
336
  intervals[merge_key].add_interval(start, end)
337
+ intervals[merge_key].update_last_altered_ts(last_altered_ts)
323
338
  # Remove all pending restatement intervals recorded before the current interval has been added
324
339
  intervals[
325
340
  pending_restatement_interval_merge_key
@@ -340,6 +355,7 @@ class IntervalState:
340
355
  "is_dev",
341
356
  "is_removed",
342
357
  "is_pending_restatement",
358
+ "last_altered_ts",
343
359
  )
344
360
  .from_(exp.to_table(self.intervals_table).as_("intervals"))
345
361
  .order_by(
@@ -431,7 +447,9 @@ class IntervalState:
431
447
 
432
448
 
433
449
  def _intervals_to_df(
434
- snapshot_intervals: t.Sequence[t.Tuple[t.Union[SnapshotInfoLike, SnapshotIntervals], Interval]],
450
+ snapshot_intervals: t.Sequence[
451
+ t.Tuple[t.Union[SnapshotIdAndVersionLike, SnapshotIntervals], Interval]
452
+ ],
435
453
  is_dev: bool,
436
454
  is_removed: bool,
437
455
  ) -> pd.DataFrame:
@@ -451,13 +469,14 @@ def _intervals_to_df(
451
469
 
452
470
 
453
471
  def _interval_to_df(
454
- snapshot: t.Union[SnapshotInfoLike, SnapshotIntervals],
472
+ snapshot: t.Union[SnapshotIdAndVersionLike, SnapshotIntervals],
455
473
  start_ts: int,
456
474
  end_ts: int,
457
475
  is_dev: bool = False,
458
476
  is_removed: bool = False,
459
477
  is_compacted: bool = False,
460
478
  is_pending_restatement: bool = False,
479
+ last_altered_ts: t.Optional[int] = None,
461
480
  ) -> t.Dict[str, t.Any]:
462
481
  return {
463
482
  "id": random_id(),
@@ -472,4 +491,5 @@ def _interval_to_df(
472
491
  "is_removed": is_removed,
473
492
  "is_compacted": is_compacted,
474
493
  "is_pending_restatement": is_pending_restatement,
494
+ "last_altered_ts": last_altered_ts,
475
495
  }
@@ -27,8 +27,9 @@ from sqlmesh.core.snapshot.definition import (
27
27
  )
28
28
  from sqlmesh.core.state_sync.base import (
29
29
  MIGRATIONS,
30
+ MIN_SCHEMA_VERSION,
31
+ MIN_SQLMESH_VERSION,
30
32
  )
31
- from sqlmesh.core.state_sync.base import StateSync
32
33
  from sqlmesh.core.state_sync.db.environment import EnvironmentState
33
34
  from sqlmesh.core.state_sync.db.interval import IntervalState
34
35
  from sqlmesh.core.state_sync.db.snapshot import SnapshotState
@@ -41,7 +42,7 @@ from sqlmesh.core.state_sync.db.utils import (
41
42
  from sqlmesh.utils import major_minor
42
43
  from sqlmesh.utils.dag import DAG
43
44
  from sqlmesh.utils.date import now_timestamp
44
- from sqlmesh.utils.errors import SQLMeshError
45
+ from sqlmesh.utils.errors import SQLMeshError, StateMigrationError
45
46
 
46
47
  logger = logging.getLogger(__name__)
47
48
 
@@ -61,7 +62,6 @@ class StateMigrator:
61
62
  snapshot_state: SnapshotState,
62
63
  environment_state: EnvironmentState,
63
64
  interval_state: IntervalState,
64
- plan_dags_table: TableName,
65
65
  console: t.Optional[Console] = None,
66
66
  ):
67
67
  self.engine_adapter = engine_adapter
@@ -70,7 +70,6 @@ class StateMigrator:
70
70
  self.snapshot_state = snapshot_state
71
71
  self.environment_state = environment_state
72
72
  self.interval_state = interval_state
73
- self.plan_dags_table = plan_dags_table
74
73
 
75
74
  self._state_tables = [
76
75
  self.snapshot_state.snapshots_table,
@@ -79,15 +78,13 @@ class StateMigrator:
79
78
  ]
80
79
  self._optional_state_tables = [
81
80
  self.interval_state.intervals_table,
82
- self.plan_dags_table,
83
81
  self.snapshot_state.auto_restatements_table,
84
82
  self.environment_state.environment_statements_table,
85
83
  ]
86
84
 
87
85
  def migrate(
88
86
  self,
89
- state_sync: StateSync,
90
- default_catalog: t.Optional[str],
87
+ schema: t.Optional[str],
91
88
  skip_backup: bool = False,
92
89
  promoted_snapshots_only: bool = True,
93
90
  ) -> None:
@@ -96,15 +93,13 @@ class StateMigrator:
96
93
  migration_start_ts = time.perf_counter()
97
94
 
98
95
  try:
99
- migrate_rows = self._apply_migrations(state_sync, default_catalog, skip_backup)
96
+ migrate_rows = self._apply_migrations(schema, skip_backup)
100
97
 
101
98
  if not migrate_rows and major_minor(SQLMESH_VERSION) == versions.minor_sqlmesh_version:
102
99
  return
103
100
 
104
101
  if migrate_rows:
105
102
  self._migrate_rows(promoted_snapshots_only)
106
- # Cleanup plan DAGs since we currently don't migrate snapshot records that are in there.
107
- self.engine_adapter.delete_from(self.plan_dags_table, "TRUE")
108
103
  self.version_state.update_versions()
109
104
 
110
105
  analytics.collector.on_migration_end(
@@ -126,6 +121,8 @@ class StateMigrator:
126
121
  )
127
122
 
128
123
  self.console.log_migration_status(success=False)
124
+ if isinstance(e, StateMigrationError):
125
+ raise
129
126
  raise SQLMeshError("SQLMesh migration failed.") from e
130
127
 
131
128
  self.console.log_migration_status()
@@ -155,12 +152,21 @@ class StateMigrator:
155
152
 
156
153
  def _apply_migrations(
157
154
  self,
158
- state_sync: StateSync,
159
- default_catalog: t.Optional[str],
155
+ schema: t.Optional[str],
160
156
  skip_backup: bool,
161
157
  ) -> bool:
162
158
  versions = self.version_state.get_versions()
163
- migrations = MIGRATIONS[versions.schema_version :]
159
+ first_script_index = 0
160
+ if versions.schema_version and versions.schema_version < MIN_SCHEMA_VERSION:
161
+ raise StateMigrationError(
162
+ "The current state belongs to an old version of SQLMesh that is no longer supported. "
163
+ f"Please upgrade to {MIN_SQLMESH_VERSION} first before upgrading to {SQLMESH_VERSION}."
164
+ )
165
+ elif versions.schema_version > 0:
166
+ # -1 to skip the baseline migration script
167
+ first_script_index = versions.schema_version - (MIN_SCHEMA_VERSION - 1)
168
+
169
+ migrations = MIGRATIONS[first_script_index:]
164
170
  should_backup = any(
165
171
  [
166
172
  migrations,
@@ -173,9 +179,14 @@ class StateMigrator:
173
179
 
174
180
  snapshot_count_before = self.snapshot_state.count() if versions.schema_version else None
175
181
 
182
+ state_table_exist = any(self.engine_adapter.table_exists(t) for t in self._state_tables)
183
+
176
184
  for migration in migrations:
177
185
  logger.info(f"Applying migration {migration}")
178
- migration.migrate(state_sync, default_catalog=default_catalog)
186
+ migration.migrate_schemas(engine_adapter=self.engine_adapter, schema=schema)
187
+ if state_table_exist:
188
+ # No need to run DML for the initial migration since all tables are empty
189
+ migration.migrate_rows(engine_adapter=self.engine_adapter, schema=schema)
179
190
 
180
191
  snapshot_count_after = self.snapshot_state.count()
181
192
 
@@ -217,6 +228,7 @@ class StateMigrator:
217
228
  "updated_ts": updated_ts,
218
229
  "unpaused_ts": unpaused_ts,
219
230
  "unrestorable": unrestorable,
231
+ "forward_only": forward_only,
220
232
  }
221
233
  for where in (
222
234
  snapshot_id_filter(
@@ -225,10 +237,16 @@ class StateMigrator:
225
237
  if snapshots is not None
226
238
  else [None]
227
239
  )
228
- for name, identifier, raw_snapshot, updated_ts, unpaused_ts, unrestorable in fetchall(
240
+ for name, identifier, raw_snapshot, updated_ts, unpaused_ts, unrestorable, forward_only in fetchall(
229
241
  self.engine_adapter,
230
242
  exp.select(
231
- "name", "identifier", "snapshot", "updated_ts", "unpaused_ts", "unrestorable"
243
+ "name",
244
+ "identifier",
245
+ "snapshot",
246
+ "updated_ts",
247
+ "unpaused_ts",
248
+ "unrestorable",
249
+ "forward_only",
232
250
  )
233
251
  .from_(self.snapshot_state.snapshots_table)
234
252
  .where(where)