sqlmesh 0.217.1.dev1__py3-none-any.whl → 0.227.2.dev20__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 (190) hide show
  1. sqlmesh/__init__.py +12 -2
  2. sqlmesh/_version.py +2 -2
  3. sqlmesh/cli/project_init.py +10 -2
  4. sqlmesh/core/_typing.py +1 -0
  5. sqlmesh/core/audit/definition.py +8 -2
  6. sqlmesh/core/config/__init__.py +1 -1
  7. sqlmesh/core/config/connection.py +20 -5
  8. sqlmesh/core/config/dbt.py +13 -0
  9. sqlmesh/core/config/janitor.py +12 -0
  10. sqlmesh/core/config/loader.py +7 -0
  11. sqlmesh/core/config/model.py +2 -0
  12. sqlmesh/core/config/root.py +3 -0
  13. sqlmesh/core/console.py +80 -2
  14. sqlmesh/core/constants.py +1 -1
  15. sqlmesh/core/context.py +112 -35
  16. sqlmesh/core/dialect.py +3 -0
  17. sqlmesh/core/engine_adapter/_typing.py +2 -0
  18. sqlmesh/core/engine_adapter/base.py +330 -23
  19. sqlmesh/core/engine_adapter/base_postgres.py +17 -1
  20. sqlmesh/core/engine_adapter/bigquery.py +146 -7
  21. sqlmesh/core/engine_adapter/clickhouse.py +17 -13
  22. sqlmesh/core/engine_adapter/databricks.py +50 -2
  23. sqlmesh/core/engine_adapter/fabric.py +110 -29
  24. sqlmesh/core/engine_adapter/mixins.py +142 -48
  25. sqlmesh/core/engine_adapter/mssql.py +15 -4
  26. sqlmesh/core/engine_adapter/mysql.py +2 -2
  27. sqlmesh/core/engine_adapter/postgres.py +9 -3
  28. sqlmesh/core/engine_adapter/redshift.py +4 -0
  29. sqlmesh/core/engine_adapter/risingwave.py +1 -0
  30. sqlmesh/core/engine_adapter/shared.py +6 -0
  31. sqlmesh/core/engine_adapter/snowflake.py +82 -11
  32. sqlmesh/core/engine_adapter/spark.py +14 -10
  33. sqlmesh/core/engine_adapter/trino.py +5 -2
  34. sqlmesh/core/janitor.py +181 -0
  35. sqlmesh/core/lineage.py +1 -0
  36. sqlmesh/core/linter/rules/builtin.py +15 -0
  37. sqlmesh/core/loader.py +17 -30
  38. sqlmesh/core/macros.py +35 -13
  39. sqlmesh/core/model/common.py +2 -0
  40. sqlmesh/core/model/definition.py +72 -4
  41. sqlmesh/core/model/kind.py +66 -2
  42. sqlmesh/core/model/meta.py +107 -2
  43. sqlmesh/core/node.py +101 -2
  44. sqlmesh/core/plan/builder.py +15 -10
  45. sqlmesh/core/plan/common.py +196 -2
  46. sqlmesh/core/plan/definition.py +21 -6
  47. sqlmesh/core/plan/evaluator.py +72 -113
  48. sqlmesh/core/plan/explainer.py +90 -8
  49. sqlmesh/core/plan/stages.py +42 -21
  50. sqlmesh/core/renderer.py +26 -18
  51. sqlmesh/core/scheduler.py +60 -19
  52. sqlmesh/core/selector.py +137 -9
  53. sqlmesh/core/signal.py +64 -1
  54. sqlmesh/core/snapshot/__init__.py +1 -0
  55. sqlmesh/core/snapshot/definition.py +109 -25
  56. sqlmesh/core/snapshot/evaluator.py +610 -50
  57. sqlmesh/core/state_sync/__init__.py +0 -1
  58. sqlmesh/core/state_sync/base.py +31 -27
  59. sqlmesh/core/state_sync/cache.py +12 -4
  60. sqlmesh/core/state_sync/common.py +216 -111
  61. sqlmesh/core/state_sync/db/facade.py +30 -15
  62. sqlmesh/core/state_sync/db/interval.py +27 -7
  63. sqlmesh/core/state_sync/db/migrator.py +14 -8
  64. sqlmesh/core/state_sync/db/snapshot.py +119 -87
  65. sqlmesh/core/table_diff.py +2 -2
  66. sqlmesh/core/test/definition.py +14 -9
  67. sqlmesh/core/test/discovery.py +4 -0
  68. sqlmesh/dbt/adapter.py +20 -11
  69. sqlmesh/dbt/basemodel.py +52 -41
  70. sqlmesh/dbt/builtin.py +27 -11
  71. sqlmesh/dbt/column.py +17 -5
  72. sqlmesh/dbt/common.py +4 -2
  73. sqlmesh/dbt/context.py +14 -1
  74. sqlmesh/dbt/loader.py +60 -8
  75. sqlmesh/dbt/manifest.py +136 -8
  76. sqlmesh/dbt/model.py +105 -25
  77. sqlmesh/dbt/package.py +16 -1
  78. sqlmesh/dbt/profile.py +3 -3
  79. sqlmesh/dbt/project.py +12 -7
  80. sqlmesh/dbt/seed.py +1 -1
  81. sqlmesh/dbt/source.py +6 -1
  82. sqlmesh/dbt/target.py +25 -6
  83. sqlmesh/dbt/test.py +31 -1
  84. sqlmesh/integrations/github/cicd/controller.py +6 -2
  85. sqlmesh/lsp/context.py +4 -2
  86. sqlmesh/magics.py +1 -1
  87. sqlmesh/migrations/v0000_baseline.py +3 -6
  88. sqlmesh/migrations/v0061_mysql_fix_blob_text_type.py +2 -5
  89. sqlmesh/migrations/v0062_add_model_gateway.py +2 -2
  90. sqlmesh/migrations/v0063_change_signals.py +2 -4
  91. sqlmesh/migrations/v0064_join_when_matched_strings.py +2 -4
  92. sqlmesh/migrations/v0065_add_model_optimize.py +2 -2
  93. sqlmesh/migrations/v0066_add_auto_restatements.py +2 -6
  94. sqlmesh/migrations/v0067_add_tsql_date_full_precision.py +2 -2
  95. sqlmesh/migrations/v0068_include_unrendered_query_in_metadata_hash.py +2 -2
  96. sqlmesh/migrations/v0069_update_dev_table_suffix.py +2 -4
  97. sqlmesh/migrations/v0070_include_grains_in_metadata_hash.py +2 -2
  98. sqlmesh/migrations/v0071_add_dev_version_to_intervals.py +2 -6
  99. sqlmesh/migrations/v0072_add_environment_statements.py +2 -4
  100. sqlmesh/migrations/v0073_remove_symbolic_disable_restatement.py +2 -4
  101. sqlmesh/migrations/v0074_add_partition_by_time_column_property.py +2 -2
  102. sqlmesh/migrations/v0075_remove_validate_query.py +2 -4
  103. sqlmesh/migrations/v0076_add_cron_tz.py +2 -2
  104. sqlmesh/migrations/v0077_fix_column_type_hash_calculation.py +2 -2
  105. sqlmesh/migrations/v0078_warn_if_non_migratable_python_env.py +2 -4
  106. sqlmesh/migrations/v0079_add_gateway_managed_property.py +7 -9
  107. sqlmesh/migrations/v0080_add_batch_size_to_scd_type_2_models.py +2 -2
  108. sqlmesh/migrations/v0081_update_partitioned_by.py +2 -4
  109. sqlmesh/migrations/v0082_warn_if_incorrectly_duplicated_statements.py +2 -4
  110. sqlmesh/migrations/v0083_use_sql_for_scd_time_data_type_data_hash.py +2 -2
  111. sqlmesh/migrations/v0084_normalize_quote_when_matched_and_merge_filter.py +2 -2
  112. sqlmesh/migrations/v0085_deterministic_repr.py +2 -4
  113. sqlmesh/migrations/v0086_check_deterministic_bug.py +2 -4
  114. sqlmesh/migrations/v0087_normalize_blueprint_variables.py +2 -4
  115. sqlmesh/migrations/v0088_warn_about_variable_python_env_diffs.py +2 -4
  116. sqlmesh/migrations/v0089_add_virtual_environment_mode.py +2 -2
  117. sqlmesh/migrations/v0090_add_forward_only_column.py +2 -6
  118. sqlmesh/migrations/v0091_on_additive_change.py +2 -2
  119. sqlmesh/migrations/v0092_warn_about_dbt_data_type_diff.py +2 -4
  120. sqlmesh/migrations/v0093_use_raw_sql_in_fingerprint.py +2 -2
  121. sqlmesh/migrations/v0094_add_dev_version_and_fingerprint_columns.py +2 -6
  122. sqlmesh/migrations/v0095_warn_about_dbt_raw_sql_diff.py +2 -4
  123. sqlmesh/migrations/v0096_remove_plan_dags_table.py +2 -4
  124. sqlmesh/migrations/v0097_add_dbt_name_in_node.py +2 -2
  125. sqlmesh/migrations/v0098_add_dbt_node_info_in_node.py +103 -0
  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/date.py +1 -1
  131. sqlmesh/utils/errors.py +4 -0
  132. sqlmesh/utils/git.py +3 -1
  133. sqlmesh/utils/jinja.py +25 -2
  134. sqlmesh/utils/pydantic.py +6 -6
  135. sqlmesh/utils/windows.py +13 -3
  136. {sqlmesh-0.217.1.dev1.dist-info → sqlmesh-0.227.2.dev20.dist-info}/METADATA +5 -5
  137. {sqlmesh-0.217.1.dev1.dist-info → sqlmesh-0.227.2.dev20.dist-info}/RECORD +188 -183
  138. sqlmesh_dbt/cli.py +70 -7
  139. sqlmesh_dbt/console.py +14 -6
  140. sqlmesh_dbt/operations.py +103 -24
  141. sqlmesh_dbt/selectors.py +39 -1
  142. web/client/dist/assets/{Audits-Ucsx1GzF.js → Audits-CBiYyyx-.js} +1 -1
  143. web/client/dist/assets/{Banner-BWDzvavM.js → Banner-DSRbUlO5.js} +1 -1
  144. web/client/dist/assets/{ChevronDownIcon-D2VL13Ah.js → ChevronDownIcon-MK_nrjD_.js} +1 -1
  145. web/client/dist/assets/{ChevronRightIcon-DWGYbf1l.js → ChevronRightIcon-CLWtT22Q.js} +1 -1
  146. web/client/dist/assets/{Content-DdHDZM3I.js → Content-BNuGZN5l.js} +1 -1
  147. web/client/dist/assets/{Content-Bikfy8fh.js → Content-CSHJyW0n.js} +1 -1
  148. web/client/dist/assets/{Data-CzAJH7rW.js → Data-C1oRDbLx.js} +1 -1
  149. web/client/dist/assets/{DataCatalog-BJF11g8f.js → DataCatalog-HXyX2-_j.js} +1 -1
  150. web/client/dist/assets/{Editor-s0SBpV2y.js → Editor-BDyfpUuw.js} +1 -1
  151. web/client/dist/assets/{Editor-DgLhgKnm.js → Editor-D0jNItwC.js} +1 -1
  152. web/client/dist/assets/{Errors-D0m0O1d3.js → Errors-BfuFLcPi.js} +1 -1
  153. web/client/dist/assets/{FileExplorer-CEv0vXkt.js → FileExplorer-BR9IE3he.js} +1 -1
  154. web/client/dist/assets/{Footer-BwzXn8Ew.js → Footer-CgBEtiAh.js} +1 -1
  155. web/client/dist/assets/{Header-6heDkEqG.js → Header-DSqR6nSO.js} +1 -1
  156. web/client/dist/assets/{Input-obuJsD6k.js → Input-B-oZ6fGO.js} +1 -1
  157. web/client/dist/assets/Lineage-DYQVwDbD.js +1 -0
  158. web/client/dist/assets/{ListboxShow-HM9_qyrt.js → ListboxShow-BE5-xevs.js} +1 -1
  159. web/client/dist/assets/{ModelLineage-zWdKo0U2.js → ModelLineage-DkIFAYo4.js} +1 -1
  160. web/client/dist/assets/{Models-Bcu66SRz.js → Models-D5dWr8RB.js} +1 -1
  161. web/client/dist/assets/{Page-BWEEQfIt.js → Page-C-XfU5BR.js} +1 -1
  162. web/client/dist/assets/{Plan-C4gXCqlf.js → Plan-ZEuTINBq.js} +1 -1
  163. web/client/dist/assets/{PlusCircleIcon-CVDO651q.js → PlusCircleIcon-DVXAHG8_.js} +1 -1
  164. web/client/dist/assets/{ReportErrors-BT6xFwAr.js → ReportErrors-B7FEPzMB.js} +1 -1
  165. web/client/dist/assets/{Root-ryJoBK4h.js → Root-8aZyhPxF.js} +1 -1
  166. web/client/dist/assets/{SearchList-DB04sPb9.js → SearchList-W_iT2G82.js} +1 -1
  167. web/client/dist/assets/{SelectEnvironment-CUYcXUu6.js → SelectEnvironment-C65jALmO.js} +1 -1
  168. web/client/dist/assets/{SourceList-Doo_9ZGp.js → SourceList-DSLO6nVJ.js} +1 -1
  169. web/client/dist/assets/{SourceListItem-D5Mj7Dly.js → SourceListItem-BHt8d9-I.js} +1 -1
  170. web/client/dist/assets/{SplitPane-qHmkD1qy.js → SplitPane-CViaZmw6.js} +1 -1
  171. web/client/dist/assets/{Tests-DH1Z74ML.js → Tests-DhaVt5t1.js} +1 -1
  172. web/client/dist/assets/{Welcome-DqUJUNMF.js → Welcome-DvpjH-_4.js} +1 -1
  173. web/client/dist/assets/context-BctCsyGb.js +71 -0
  174. web/client/dist/assets/{context-Dr54UHLi.js → context-DFNeGsFF.js} +1 -1
  175. web/client/dist/assets/{editor-DYIP1yQ4.js → editor-CcO28cqd.js} +1 -1
  176. web/client/dist/assets/{file-DarlIDVi.js → file-CvJN3aZO.js} +1 -1
  177. web/client/dist/assets/{floating-ui.react-dom-BH3TFvkM.js → floating-ui.react-dom-CjE-JNW1.js} +1 -1
  178. web/client/dist/assets/{help-Bl8wqaQc.js → help-DuPhjipa.js} +1 -1
  179. web/client/dist/assets/{index-D1sR7wpN.js → index-C-dJH7yZ.js} +1 -1
  180. web/client/dist/assets/{index-O3mjYpnE.js → index-Dj0i1-CA.js} +2 -2
  181. web/client/dist/assets/{plan-CehRrJUG.js → plan-BTRSbjKn.js} +1 -1
  182. web/client/dist/assets/{popover-CqgMRE0G.js → popover-_Sf0yvOI.js} +1 -1
  183. web/client/dist/assets/{project-6gxepOhm.js → project-BvSOI8MY.js} +1 -1
  184. web/client/dist/index.html +1 -1
  185. web/client/dist/assets/Lineage-D0Hgdz2v.js +0 -1
  186. web/client/dist/assets/context-DgX0fp2E.js +0 -68
  187. {sqlmesh-0.217.1.dev1.dist-info → sqlmesh-0.227.2.dev20.dist-info}/WHEEL +0 -0
  188. {sqlmesh-0.217.1.dev1.dist-info → sqlmesh-0.227.2.dev20.dist-info}/entry_points.txt +0 -0
  189. {sqlmesh-0.217.1.dev1.dist-info → sqlmesh-0.227.2.dev20.dist-info}/licenses/LICENSE +0 -0
  190. {sqlmesh-0.217.1.dev1.dist-info → sqlmesh-0.227.2.dev20.dist-info}/top_level.txt +0 -0
sqlmesh/core/context.py CHANGED
@@ -93,7 +93,7 @@ from sqlmesh.core.plan.definition import UserProvidedFlags
93
93
  from sqlmesh.core.reference import ReferenceGraph
94
94
  from sqlmesh.core.scheduler import Scheduler, CompletionStatus
95
95
  from sqlmesh.core.schema_loader import create_external_models_file
96
- from sqlmesh.core.selector import Selector
96
+ from sqlmesh.core.selector import Selector, NativeSelector
97
97
  from sqlmesh.core.snapshot import (
98
98
  DeployabilityIndex,
99
99
  Snapshot,
@@ -107,14 +107,15 @@ from sqlmesh.core.state_sync import (
107
107
  CachingStateSync,
108
108
  StateReader,
109
109
  StateSync,
110
- cleanup_expired_views,
111
110
  )
111
+ from sqlmesh.core.janitor import cleanup_expired_views, delete_expired_snapshots
112
112
  from sqlmesh.core.table_diff import TableDiff
113
113
  from sqlmesh.core.test import (
114
114
  ModelTextTestResult,
115
115
  ModelTestMetadata,
116
116
  generate_test,
117
117
  run_tests,
118
+ filter_tests_by_patterns,
118
119
  )
119
120
  from sqlmesh.core.user import User
120
121
  from sqlmesh.utils import UniqueKeyDict, Verbosity
@@ -139,6 +140,7 @@ from sqlmesh.utils.errors import (
139
140
  )
140
141
  from sqlmesh.utils.config import print_config
141
142
  from sqlmesh.utils.jinja import JinjaMacroRegistry
143
+ from sqlmesh.utils.windows import IS_WINDOWS, fix_windows_path
142
144
 
143
145
  if t.TYPE_CHECKING:
144
146
  import pandas as pd
@@ -153,6 +155,8 @@ if t.TYPE_CHECKING:
153
155
  )
154
156
  from sqlmesh.core.snapshot import Node
155
157
 
158
+ from sqlmesh.core.snapshot.definition import Intervals
159
+
156
160
  ModelOrSnapshot = t.Union[str, Model, Snapshot]
157
161
  NodeOrSnapshot = t.Union[str, Model, StandaloneAudit, Snapshot]
158
162
 
@@ -274,6 +278,8 @@ class ExecutionContext(BaseContext):
274
278
  deployability_index: t.Optional[DeployabilityIndex] = None,
275
279
  default_dialect: t.Optional[str] = None,
276
280
  default_catalog: t.Optional[str] = None,
281
+ is_restatement: t.Optional[bool] = None,
282
+ parent_intervals: t.Optional[Intervals] = None,
277
283
  variables: t.Optional[t.Dict[str, t.Any]] = None,
278
284
  blueprint_variables: t.Optional[t.Dict[str, t.Any]] = None,
279
285
  ):
@@ -284,6 +290,8 @@ class ExecutionContext(BaseContext):
284
290
  self._default_dialect = default_dialect
285
291
  self._variables = variables or {}
286
292
  self._blueprint_variables = blueprint_variables or {}
293
+ self._is_restatement = is_restatement
294
+ self._parent_intervals = parent_intervals
287
295
 
288
296
  @property
289
297
  def default_dialect(self) -> t.Optional[str]:
@@ -308,6 +316,14 @@ class ExecutionContext(BaseContext):
308
316
  """Returns the gateway name."""
309
317
  return self.var(c.GATEWAY)
310
318
 
319
+ @property
320
+ def is_restatement(self) -> t.Optional[bool]:
321
+ return self._is_restatement
322
+
323
+ @property
324
+ def parent_intervals(self) -> t.Optional[Intervals]:
325
+ return self._parent_intervals
326
+
311
327
  def var(self, var_name: str, default: t.Optional[t.Any] = None) -> t.Optional[t.Any]:
312
328
  """Returns a variable value."""
313
329
  return self._variables.get(var_name.lower(), default)
@@ -328,6 +344,7 @@ class ExecutionContext(BaseContext):
328
344
  self.deployability_index,
329
345
  self._default_dialect,
330
346
  self._default_catalog,
347
+ self._is_restatement,
331
348
  variables=variables,
332
349
  blueprint_variables=blueprint_variables,
333
350
  )
@@ -368,6 +385,7 @@ class GenericContext(BaseContext, t.Generic[C]):
368
385
  load: bool = True,
369
386
  users: t.Optional[t.List[User]] = None,
370
387
  config_loader_kwargs: t.Optional[t.Dict[str, t.Any]] = None,
388
+ selector: t.Optional[t.Type[Selector]] = None,
371
389
  ):
372
390
  self.configs = (
373
391
  config
@@ -381,6 +399,11 @@ class GenericContext(BaseContext, t.Generic[C]):
381
399
  self._standalone_audits: UniqueKeyDict[str, StandaloneAudit] = UniqueKeyDict(
382
400
  "standaloneaudits"
383
401
  )
402
+ self._model_test_metadata: t.List[ModelTestMetadata] = []
403
+ self._model_test_metadata_path_index: t.Dict[Path, t.List[ModelTestMetadata]] = {}
404
+ self._model_test_metadata_fully_qualified_name_index: t.Dict[str, ModelTestMetadata] = {}
405
+ self._models_with_tests: t.Set[str] = set()
406
+
384
407
  self._macros: UniqueKeyDict[str, ExecutableOrMacro] = UniqueKeyDict("macros")
385
408
  self._metrics: UniqueKeyDict[str, Metric] = UniqueKeyDict("metrics")
386
409
  self._jinja_macros = JinjaMacroRegistry()
@@ -390,6 +413,7 @@ class GenericContext(BaseContext, t.Generic[C]):
390
413
  self._engine_adapter: t.Optional[EngineAdapter] = None
391
414
  self._linters: t.Dict[str, Linter] = {}
392
415
  self._loaded: bool = False
416
+ self._selector_cls = selector or NativeSelector
393
417
 
394
418
  self.path, self.config = t.cast(t.Tuple[Path, C], next(iter(self.configs.items())))
395
419
 
@@ -618,6 +642,10 @@ class GenericContext(BaseContext, t.Generic[C]):
618
642
  self._excluded_requirements.clear()
619
643
  self._linters.clear()
620
644
  self._environment_statements = []
645
+ self._model_test_metadata.clear()
646
+ self._model_test_metadata_path_index.clear()
647
+ self._model_test_metadata_fully_qualified_name_index.clear()
648
+ self._models_with_tests.clear()
621
649
 
622
650
  for loader, project in zip(self._loaders, loaded_projects):
623
651
  self._jinja_macros = self._jinja_macros.merge(project.jinja_macros)
@@ -630,6 +658,16 @@ class GenericContext(BaseContext, t.Generic[C]):
630
658
  self._excluded_requirements.update(project.excluded_requirements)
631
659
  self._environment_statements.extend(project.environment_statements)
632
660
 
661
+ self._model_test_metadata.extend(project.model_test_metadata)
662
+ for metadata in project.model_test_metadata:
663
+ if metadata.path not in self._model_test_metadata_path_index:
664
+ self._model_test_metadata_path_index[metadata.path] = []
665
+ self._model_test_metadata_path_index[metadata.path].append(metadata)
666
+ self._model_test_metadata_fully_qualified_name_index[
667
+ metadata.fully_qualified_test_name
668
+ ] = metadata
669
+ self._models_with_tests.add(metadata.model_name)
670
+
633
671
  config = loader.config
634
672
  self._linters[config.project] = Linter.from_rules(
635
673
  BUILTIN_RULES.union(project.user_rules), config.linter
@@ -1031,6 +1069,11 @@ class GenericContext(BaseContext, t.Generic[C]):
1031
1069
  """Returns all registered standalone audits in this context."""
1032
1070
  return MappingProxyType(self._standalone_audits)
1033
1071
 
1072
+ @property
1073
+ def models_with_tests(self) -> t.Set[str]:
1074
+ """Returns all models with tests in this context."""
1075
+ return self._models_with_tests
1076
+
1034
1077
  @property
1035
1078
  def snapshots(self) -> t.Dict[str, Snapshot]:
1036
1079
  """Generates and returns snapshots based on models registered in this context.
@@ -1429,6 +1472,7 @@ class GenericContext(BaseContext, t.Generic[C]):
1429
1472
  explain: t.Optional[bool] = None,
1430
1473
  ignore_cron: t.Optional[bool] = None,
1431
1474
  min_intervals: t.Optional[int] = None,
1475
+ always_include_local_changes: t.Optional[bool] = None,
1432
1476
  ) -> PlanBuilder:
1433
1477
  """Creates a plan builder.
1434
1478
 
@@ -1467,6 +1511,8 @@ class GenericContext(BaseContext, t.Generic[C]):
1467
1511
  diff_rendered: Whether the diff should compare raw vs rendered models
1468
1512
  min_intervals: Adjust the plan start date on a per-model basis in order to ensure at least this many intervals are covered
1469
1513
  on every model when checking for missing intervals
1514
+ always_include_local_changes: Usually when restatements are present, local changes in the filesystem are ignored.
1515
+ However, it can be desirable to deploy changes + restatements in the same plan, so this flag overrides the default behaviour.
1470
1516
 
1471
1517
  Returns:
1472
1518
  The plan builder.
@@ -1583,13 +1629,20 @@ class GenericContext(BaseContext, t.Generic[C]):
1583
1629
  "Selector did not return any models. Please check your model selection and try again."
1584
1630
  )
1585
1631
 
1632
+ if always_include_local_changes is None:
1633
+ # default behaviour - if restatements are detected; we operate entirely out of state and ignore local changes
1634
+ force_no_diff = restate_models is not None or (
1635
+ backfill_models is not None and not backfill_models
1636
+ )
1637
+ else:
1638
+ force_no_diff = not always_include_local_changes
1639
+
1586
1640
  snapshots = self._snapshots(models_override)
1587
1641
  context_diff = self._context_diff(
1588
1642
  environment or c.PROD,
1589
1643
  snapshots=snapshots,
1590
1644
  create_from=create_from,
1591
- force_no_diff=restate_models is not None
1592
- or (backfill_models is not None and not backfill_models),
1645
+ force_no_diff=force_no_diff,
1593
1646
  ensure_finalized_snapshots=self.config.plan.use_finalized_state,
1594
1647
  diff_rendered=diff_rendered,
1595
1648
  always_recreate_environment=self.config.plan.always_recreate_environment,
@@ -1644,6 +1697,14 @@ class GenericContext(BaseContext, t.Generic[C]):
1644
1697
  elif forward_only is None:
1645
1698
  forward_only = self.config.plan.forward_only
1646
1699
 
1700
+ # When handling prod restatements, only clear intervals from other model versions if we are using full virtual environments
1701
+ # If we are not, then there is no point, because none of the data in dev environments can be promoted by definition
1702
+ restate_all_snapshots = (
1703
+ expanded_restate_models is not None
1704
+ and not is_dev
1705
+ and self.config.virtual_environment_mode.is_full
1706
+ )
1707
+
1647
1708
  return self.PLAN_BUILDER_TYPE(
1648
1709
  context_diff=context_diff,
1649
1710
  start=start,
@@ -1651,6 +1712,7 @@ class GenericContext(BaseContext, t.Generic[C]):
1651
1712
  execution_time=execution_time,
1652
1713
  apply=self.apply,
1653
1714
  restate_models=expanded_restate_models,
1715
+ restate_all_snapshots=restate_all_snapshots,
1654
1716
  backfill_models=backfill_models,
1655
1717
  no_gaps=no_gaps,
1656
1718
  skip_backfill=skip_backfill,
@@ -1678,9 +1740,9 @@ class GenericContext(BaseContext, t.Generic[C]):
1678
1740
  console=self.console,
1679
1741
  user_provided_flags=user_provided_flags,
1680
1742
  selected_models={
1681
- dbt_name
1743
+ dbt_unique_id
1682
1744
  for model in model_selector.expand_model_selections(select_models or "*")
1683
- if (dbt_name := snapshots[model].node.dbt_name)
1745
+ if (dbt_unique_id := snapshots[model].node.dbt_unique_id)
1684
1746
  },
1685
1747
  explain=explain or False,
1686
1748
  ignore_cron=ignore_cron or False,
@@ -2183,7 +2245,7 @@ class GenericContext(BaseContext, t.Generic[C]):
2183
2245
 
2184
2246
  pd.set_option("display.max_columns", None)
2185
2247
 
2186
- test_meta = self.load_model_tests(tests=tests, patterns=match_patterns)
2248
+ test_meta = self.select_tests(tests=tests, patterns=match_patterns)
2187
2249
 
2188
2250
  result = run_tests(
2189
2251
  model_test_metadata=test_meta,
@@ -2242,6 +2304,7 @@ class GenericContext(BaseContext, t.Generic[C]):
2242
2304
  snapshot=snapshot,
2243
2305
  start=start,
2244
2306
  end=end,
2307
+ execution_time=execution_time,
2245
2308
  snapshots=self.snapshots,
2246
2309
  ):
2247
2310
  audit_id = f"{audit_result.audit.name}"
@@ -2562,12 +2625,15 @@ class GenericContext(BaseContext, t.Generic[C]):
2562
2625
  )
2563
2626
 
2564
2627
  def clear_caches(self) -> None:
2565
- for path in self.configs:
2566
- cache_path = path / c.CACHE
2567
- if cache_path.exists():
2568
- rmtree(cache_path)
2569
- if self.cache_dir.exists():
2570
- rmtree(self.cache_dir)
2628
+ paths_to_remove = [path / c.CACHE for path in self.configs]
2629
+ paths_to_remove.append(self.cache_dir)
2630
+
2631
+ if IS_WINDOWS:
2632
+ paths_to_remove = [fix_windows_path(path) for path in paths_to_remove]
2633
+
2634
+ for path in paths_to_remove:
2635
+ if path.exists():
2636
+ rmtree(path)
2571
2637
 
2572
2638
  if isinstance(self._state_sync, CachingStateSync):
2573
2639
  self._state_sync.clear_cache()
@@ -2824,19 +2890,14 @@ class GenericContext(BaseContext, t.Generic[C]):
2824
2890
  # Clean up expired environments by removing their views and schemas
2825
2891
  self._cleanup_environments(current_ts=current_ts)
2826
2892
 
2827
- cleanup_targets = self.state_sync.get_expired_snapshots(
2828
- ignore_ttl=ignore_ttl, current_ts=current_ts
2829
- )
2830
-
2831
- # Remove the expired snapshots tables
2832
- self.snapshot_evaluator.cleanup(
2833
- target_snapshots=cleanup_targets,
2834
- on_complete=self.console.update_cleanup_progress,
2893
+ delete_expired_snapshots(
2894
+ self.state_sync,
2895
+ self.snapshot_evaluator,
2896
+ current_ts=current_ts,
2897
+ ignore_ttl=ignore_ttl,
2898
+ console=self.console,
2899
+ batch_size=self.config.janitor.expired_snapshots_batch_size,
2835
2900
  )
2836
-
2837
- # Delete the expired snapshot records from the state sync
2838
- self.state_sync.delete_expired_snapshots(ignore_ttl=ignore_ttl, current_ts=current_ts)
2839
-
2840
2901
  self.state_sync.compact_intervals()
2841
2902
 
2842
2903
  def _cleanup_environments(self, current_ts: t.Optional[int] = None) -> None:
@@ -2874,7 +2935,7 @@ class GenericContext(BaseContext, t.Generic[C]):
2874
2935
  def _new_selector(
2875
2936
  self, models: t.Optional[UniqueKeyDict[str, Model]] = None, dag: t.Optional[DAG[str]] = None
2876
2937
  ) -> Selector:
2877
- return Selector(
2938
+ return self._selector_cls(
2878
2939
  self.state_reader,
2879
2940
  models=models or self._models,
2880
2941
  context_path=self.path,
@@ -3157,18 +3218,34 @@ class GenericContext(BaseContext, t.Generic[C]):
3157
3218
 
3158
3219
  return all_violations
3159
3220
 
3160
- def load_model_tests(
3161
- self, tests: t.Optional[t.List[str]] = None, patterns: list[str] | None = None
3221
+ def select_tests(
3222
+ self,
3223
+ tests: t.Optional[t.List[str]] = None,
3224
+ patterns: t.Optional[t.List[str]] = None,
3162
3225
  ) -> t.List[ModelTestMetadata]:
3163
- # If a set of specific test path(s) are provided, we can use a single loader
3164
- # since it's not required to walk every tests/ folder in each repo
3165
- loaders = [self._loaders[0]] if tests else self._loaders
3226
+ """Filter pre-loaded test metadata based on tests and patterns."""
3227
+
3228
+ test_meta = self._model_test_metadata
3229
+
3230
+ if tests:
3231
+ filtered_tests = []
3232
+ for test in tests:
3233
+ if "::" in test:
3234
+ if test in self._model_test_metadata_fully_qualified_name_index:
3235
+ filtered_tests.append(
3236
+ self._model_test_metadata_fully_qualified_name_index[test]
3237
+ )
3238
+ else:
3239
+ test_path = Path(test)
3240
+ if test_path in self._model_test_metadata_path_index:
3241
+ filtered_tests.extend(self._model_test_metadata_path_index[test_path])
3242
+
3243
+ test_meta = filtered_tests
3166
3244
 
3167
- model_tests = []
3168
- for loader in loaders:
3169
- model_tests.extend(loader.load_model_tests(tests=tests, patterns=patterns))
3245
+ if patterns:
3246
+ test_meta = filter_tests_by_patterns(test_meta, patterns)
3170
3247
 
3171
- return model_tests
3248
+ return test_meta
3172
3249
 
3173
3250
 
3174
3251
  class Context(GenericContext[Config]):
sqlmesh/core/dialect.py CHANGED
@@ -174,6 +174,7 @@ def _parse_id_var(
174
174
 
175
175
  while (
176
176
  identifier
177
+ and not identifier.args.get("quoted")
177
178
  and self._is_connected()
178
179
  and (
179
180
  self._match_texts(("{", SQLMESH_MACRO_PREFIX))
@@ -349,6 +350,7 @@ def _parse_select(
349
350
  parse_subquery_alias: bool = True,
350
351
  parse_set_operation: bool = True,
351
352
  consume_pipe: bool = True,
353
+ from_: t.Optional[exp.From] = None,
352
354
  ) -> t.Optional[exp.Expression]:
353
355
  select = self.__parse_select( # type: ignore
354
356
  nested=nested,
@@ -356,6 +358,7 @@ def _parse_select(
356
358
  parse_subquery_alias=parse_subquery_alias,
357
359
  parse_set_operation=parse_set_operation,
358
360
  consume_pipe=consume_pipe,
361
+ from_=from_,
359
362
  )
360
363
 
361
364
  if (
@@ -30,3 +30,5 @@ if t.TYPE_CHECKING:
30
30
  ]
31
31
 
32
32
  QueryOrDF = t.Union[Query, DF]
33
+ GrantsConfig = t.Dict[str, t.List[str]]
34
+ DCL = t.TypeVar("DCL", exp.Grant, exp.Revoke)