sqlmesh 0.209.1.dev13__py3-none-any.whl → 0.209.1.dev14__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.
sqlmesh/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.209.1.dev13'
32
- __version_tuple__ = version_tuple = (0, 209, 1, 'dev13')
31
+ __version__ = version = '0.209.1.dev14'
32
+ __version_tuple__ = version_tuple = (0, 209, 1, 'dev14')
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -49,6 +49,35 @@ class EnvironmentSuffixTarget(str, Enum):
49
49
  return str(self)
50
50
 
51
51
 
52
+ class VirtualEnvironmentMode(str, Enum):
53
+ """Mode for virtual environment behavior.
54
+
55
+ FULL: Use full virtual environment functionality with versioned table names and virtual layer updates.
56
+ DEV_ONLY: Bypass virtual environments in production, using original unversioned model names.
57
+ """
58
+
59
+ FULL = "full"
60
+ DEV_ONLY = "dev_only"
61
+
62
+ @property
63
+ def is_full(self) -> bool:
64
+ return self == VirtualEnvironmentMode.FULL
65
+
66
+ @property
67
+ def is_dev_only(self) -> bool:
68
+ return self == VirtualEnvironmentMode.DEV_ONLY
69
+
70
+ @classproperty
71
+ def default(cls) -> VirtualEnvironmentMode:
72
+ return VirtualEnvironmentMode.FULL
73
+
74
+ def __str__(self) -> str:
75
+ return self.name
76
+
77
+ def __repr__(self) -> str:
78
+ return str(self)
79
+
80
+
52
81
  class TableNamingConvention(str, Enum):
53
82
  # Causes table names at the physical layer to follow the convention:
54
83
  # <schema-name>__<table-name>__<fingerprint>
@@ -14,7 +14,11 @@ from sqlglot.optimizer.normalize_identifiers import normalize_identifiers
14
14
  from sqlmesh.cicd.config import CICDBotConfig
15
15
  from sqlmesh.core import constants as c
16
16
  from sqlmesh.core.console import get_console
17
- from sqlmesh.core.config import EnvironmentSuffixTarget, TableNamingConvention
17
+ from sqlmesh.core.config.common import (
18
+ EnvironmentSuffixTarget,
19
+ TableNamingConvention,
20
+ VirtualEnvironmentMode,
21
+ )
18
22
  from sqlmesh.core.config.base import BaseConfig, UpdateStrategy
19
23
  from sqlmesh.core.config.common import variables_validator, compile_regex_mapping
20
24
  from sqlmesh.core.config.connection import (
@@ -110,6 +114,7 @@ class Config(BaseConfig):
110
114
  physical_schema_mapping: A mapping from regular expressions to names of schemas in which physical tables for corresponding models will be placed.
111
115
  environment_suffix_target: Indicates whether to append the environment name to the schema or table name.
112
116
  physical_table_naming_convention: Indicates how tables should be named at the physical layer
117
+ virtual_environment_mode: Indicates how environments should be handled.
113
118
  gateway_managed_virtual_layer: Whether the models' views in the virtual layer are created by the model-specific gateway rather than the default gateway.
114
119
  infer_python_dependencies: Whether to statically analyze Python code to automatically infer Python package requirements.
115
120
  environment_catalog_mapping: A mapping from regular expressions to catalog names. The catalog name is used to determine the target catalog for a given environment.
@@ -148,12 +153,9 @@ class Config(BaseConfig):
148
153
  env_vars: t.Dict[str, str] = {}
149
154
  username: str = ""
150
155
  physical_schema_mapping: RegexKeyDict = {}
151
- environment_suffix_target: EnvironmentSuffixTarget = Field(
152
- default=EnvironmentSuffixTarget.default
153
- )
154
- physical_table_naming_convention: TableNamingConvention = Field(
155
- default=TableNamingConvention.default
156
- )
156
+ environment_suffix_target: EnvironmentSuffixTarget = EnvironmentSuffixTarget.default
157
+ physical_table_naming_convention: TableNamingConvention = TableNamingConvention.default
158
+ virtual_environment_mode: VirtualEnvironmentMode = VirtualEnvironmentMode.default
157
159
  gateway_managed_virtual_layer: bool = False
158
160
  infer_python_dependencies: bool = True
159
161
  environment_catalog_mapping: RegexKeyDict = {}
@@ -260,6 +262,11 @@ class Config(BaseConfig):
260
262
  "Please specify one or the other"
261
263
  )
262
264
 
265
+ if self.plan.use_finalized_state and not self.virtual_environment_mode.is_full:
266
+ raise ConfigError(
267
+ "Using the finalized state is only supported when `virtual_environment_mode` is set to `full`."
268
+ )
269
+
263
270
  if self.environment_catalog_mapping:
264
271
  _normalize_identifiers("environment_catalog_mapping")
265
272
  if self.physical_schema_mapping:
sqlmesh/core/context.py CHANGED
@@ -1616,6 +1616,11 @@ class GenericContext(BaseContext, t.Generic[C]):
1616
1616
  max_interval_end_per_model,
1617
1617
  )
1618
1618
 
1619
+ if not self.config.virtual_environment_mode.is_full:
1620
+ forward_only = True
1621
+ elif forward_only is None:
1622
+ forward_only = self.config.plan.forward_only
1623
+
1619
1624
  return self.PLAN_BUILDER_TYPE(
1620
1625
  context_diff=context_diff,
1621
1626
  start=start,
@@ -1628,9 +1633,7 @@ class GenericContext(BaseContext, t.Generic[C]):
1628
1633
  skip_backfill=skip_backfill,
1629
1634
  empty_backfill=empty_backfill,
1630
1635
  is_dev=is_dev,
1631
- forward_only=(
1632
- forward_only if forward_only is not None else self.config.plan.forward_only
1633
- ),
1636
+ forward_only=forward_only,
1634
1637
  allow_destructive_models=expanded_destructive_models,
1635
1638
  environment_ttl=environment_ttl,
1636
1639
  environment_suffix_target=self.config.environment_suffix_target,
@@ -2936,7 +2939,7 @@ class GenericContext(BaseContext, t.Generic[C]):
2936
2939
  def _plan_preview_enabled(self) -> bool:
2937
2940
  if self.config.plan.enable_preview is not None:
2938
2941
  return self.config.plan.enable_preview
2939
- # It is dangerous to enable preview by default for dbt projects that rely on engines that don’t support cloning.
2942
+ # It is dangerous to enable preview by default for dbt projects that rely on engines that don't support cloning.
2940
2943
  # Enabling previews in such cases can result in unintended full refreshes because dbt incremental models rely on
2941
2944
  # the maximum timestamp value in the target table.
2942
2945
  return self._project_type == c.NATIVE or self.engine_adapter.SUPPORTS_CLONING
@@ -32,6 +32,7 @@ from sqlmesh.core.engine_adapter.shared import (
32
32
  CommentCreationTable,
33
33
  CommentCreationView,
34
34
  DataObject,
35
+ DataObjectType,
35
36
  EngineRunMode,
36
37
  InsertOverwriteStrategy,
37
38
  SourceQuery,
@@ -39,13 +40,13 @@ from sqlmesh.core.engine_adapter.shared import (
39
40
  )
40
41
  from sqlmesh.core.model.kind import TimeColumn
41
42
  from sqlmesh.core.schema_diff import SchemaDiffer
42
- from sqlmesh.utils import columns_to_types_all_known, random_id, CorrelationId
43
- from sqlmesh.utils.connection_pool import create_connection_pool, ConnectionPool
43
+ from sqlmesh.utils import CorrelationId, columns_to_types_all_known, random_id
44
+ from sqlmesh.utils.connection_pool import ConnectionPool, create_connection_pool
44
45
  from sqlmesh.utils.date import TimeLike, make_inclusive, to_time_column
45
46
  from sqlmesh.utils.errors import (
47
+ MissingDefaultCatalogError,
46
48
  SQLMeshError,
47
49
  UnsupportedCatalogOperationError,
48
- MissingDefaultCatalogError,
49
50
  )
50
51
  from sqlmesh.utils.pandas import columns_to_types_from_df
51
52
 
@@ -54,8 +55,8 @@ if t.TYPE_CHECKING:
54
55
 
55
56
  from sqlmesh.core._typing import SchemaName, SessionProperties, TableName
56
57
  from sqlmesh.core.engine_adapter._typing import (
57
- BigframeSession,
58
58
  DF,
59
+ BigframeSession,
59
60
  PySparkDataFrame,
60
61
  PySparkSession,
61
62
  Query,
@@ -369,6 +370,12 @@ class EngineAdapter:
369
370
  kwargs: Optional create table properties.
370
371
  """
371
372
  target_table = exp.to_table(table_name)
373
+
374
+ target_data_object = self.get_data_object(target_table)
375
+ table_exists = target_data_object is not None
376
+ if self.drop_data_object_on_type_mismatch(target_data_object, DataObjectType.TABLE):
377
+ table_exists = False
378
+
372
379
  source_queries, columns_to_types = self._get_source_queries_and_columns_to_types(
373
380
  query_or_df, columns_to_types, target_table=target_table
374
381
  )
@@ -390,7 +397,7 @@ class EngineAdapter:
390
397
  )
391
398
  # All engines support `CREATE TABLE AS` so we use that if the table doesn't already exist and we
392
399
  # use `CREATE OR REPLACE TABLE AS` if the engine supports it
393
- if self.SUPPORTS_REPLACE_TABLE or not self.table_exists(target_table):
400
+ if self.SUPPORTS_REPLACE_TABLE or not table_exists:
394
401
  return self._create_table_from_source_queries(
395
402
  target_table,
396
403
  source_queries,
@@ -930,6 +937,28 @@ class EngineAdapter:
930
937
  )
931
938
  )
932
939
 
940
+ def drop_data_object(self, data_object: DataObject, ignore_if_not_exists: bool = True) -> None:
941
+ """Drops a data object of arbitrary type.
942
+
943
+ Args:
944
+ data_object: The data object to drop.
945
+ ignore_if_not_exists: If True, no error will be raised if the data object does not exist.
946
+ """
947
+ if data_object.type.is_view:
948
+ self.drop_view(data_object.to_table(), ignore_if_not_exists=ignore_if_not_exists)
949
+ elif data_object.type.is_materialized_view:
950
+ self.drop_view(
951
+ data_object.to_table(), ignore_if_not_exists=ignore_if_not_exists, materialized=True
952
+ )
953
+ elif data_object.type.is_table:
954
+ self.drop_table(data_object.to_table(), exists=ignore_if_not_exists)
955
+ elif data_object.type.is_managed_table:
956
+ self.drop_managed_table(data_object.to_table(), exists=ignore_if_not_exists)
957
+ else:
958
+ raise SQLMeshError(
959
+ f"Can't drop data object '{data_object.to_table().sql(dialect=self.dialect)}' of type '{data_object.type.value}'"
960
+ )
961
+
933
962
  def drop_table(self, table_name: TableName, exists: bool = True) -> None:
934
963
  """Drops a table.
935
964
 
@@ -1118,6 +1147,12 @@ class EngineAdapter:
1118
1147
  if properties.expressions:
1119
1148
  create_kwargs["properties"] = properties
1120
1149
 
1150
+ if replace:
1151
+ self.drop_data_object_on_type_mismatch(
1152
+ self.get_data_object(view_name),
1153
+ DataObjectType.VIEW if not materialized else DataObjectType.MATERIALIZED_VIEW,
1154
+ )
1155
+
1121
1156
  with source_queries[0] as query:
1122
1157
  self.execute(
1123
1158
  exp.Create(
@@ -2022,6 +2057,15 @@ class EngineAdapter:
2022
2057
  )
2023
2058
  self._rename_table(old_table_name, new_table_name)
2024
2059
 
2060
+ def get_data_object(self, target_name: TableName) -> t.Optional[DataObject]:
2061
+ target_table = exp.to_table(target_name)
2062
+ existing_data_objects = self.get_data_objects(
2063
+ schema_(target_table.db, target_table.catalog), {target_table.name}
2064
+ )
2065
+ if existing_data_objects:
2066
+ return existing_data_objects[0]
2067
+ return None
2068
+
2025
2069
  def get_data_objects(
2026
2070
  self, schema_name: SchemaName, object_names: t.Optional[t.Set[str]] = None
2027
2071
  ) -> t.List[DataObject]:
@@ -2483,6 +2527,30 @@ class EngineAdapter:
2483
2527
  table = exp.to_table(table_name)
2484
2528
  self.execute(f"TRUNCATE TABLE {table.sql(dialect=self.dialect, identify=True)}")
2485
2529
 
2530
+ def drop_data_object_on_type_mismatch(
2531
+ self, data_object: t.Optional[DataObject], expected_type: DataObjectType
2532
+ ) -> bool:
2533
+ """Drops a data object if it exists and is not of the expected type.
2534
+
2535
+ Args:
2536
+ data_object: The data object to check.
2537
+ expected_type: The expected type of the data object.
2538
+
2539
+ Returns:
2540
+ True if the data object was dropped, False otherwise.
2541
+ """
2542
+ if data_object is None or data_object.type == expected_type:
2543
+ return False
2544
+
2545
+ logger.warning(
2546
+ "Target data object '%s' is a %s and not a %s, dropping it",
2547
+ data_object.to_table().sql(dialect=self.dialect),
2548
+ data_object.type.value,
2549
+ expected_type.value,
2550
+ )
2551
+ self.drop_data_object(data_object)
2552
+ return True
2553
+
2486
2554
  def _replace_by_key(
2487
2555
  self,
2488
2556
  target_table: TableName,
@@ -262,7 +262,12 @@ class RedshiftEngineAdapter(
262
262
  """
263
263
  import pandas as pd
264
264
 
265
- if not isinstance(query_or_df, pd.DataFrame) or not self.table_exists(table_name):
265
+ target_data_object = self.get_data_object(table_name)
266
+ table_exists = target_data_object is not None
267
+ if self.drop_data_object_on_type_mismatch(target_data_object, DataObjectType.TABLE):
268
+ table_exists = False
269
+
270
+ if not isinstance(query_or_df, pd.DataFrame) or not table_exists:
266
271
  return super().replace_query(
267
272
  table_name,
268
273
  query_or_df,
@@ -171,6 +171,9 @@ class DataObject(PydanticModel):
171
171
  def is_clustered(self) -> bool:
172
172
  return bool(self.clustering_key)
173
173
 
174
+ def to_table(self) -> exp.Table:
175
+ return exp.table_(self.name, db=self.schema_name, catalog=self.catalog, quoted=True)
176
+
174
177
 
175
178
  class CatalogSupport(Enum):
176
179
  # The engine has no concept of catalogs
sqlmesh/core/loader.py CHANGED
@@ -603,6 +603,7 @@ class SqlMeshLoader(Loader):
603
603
  infer_names=self.config.model_naming.infer_names,
604
604
  signal_definitions=signals,
605
605
  default_catalog_per_gateway=self.context.default_catalog_per_gateway,
606
+ virtual_environment_mode=self.config.virtual_environment_mode,
606
607
  **loading_default_kwargs or {},
607
608
  )
608
609
 
@@ -683,6 +684,7 @@ class SqlMeshLoader(Loader):
683
684
  audit_definitions=audits,
684
685
  signal_definitions=signals,
685
686
  default_catalog_per_gateway=self.context.default_catalog_per_gateway,
687
+ virtual_environment_mode=self.config.virtual_environment_mode,
686
688
  ):
687
689
  if model.enabled:
688
690
  models[model.fqn] = model
@@ -8,6 +8,7 @@ import re
8
8
  from sqlglot import exp
9
9
  from sqlglot.dialects.dialect import DialectType
10
10
 
11
+ from sqlmesh.core.config.common import VirtualEnvironmentMode
11
12
  from sqlmesh.core.macros import MacroRegistry
12
13
  from sqlmesh.core.signal import SignalRegistry
13
14
  from sqlmesh.utils.jinja import JinjaMacroRegistry
@@ -154,6 +155,7 @@ class model(registry_decorator):
154
155
  variables: t.Optional[t.Dict[str, t.Any]] = None,
155
156
  infer_names: t.Optional[bool] = False,
156
157
  blueprint_variables: t.Optional[t.Dict[str, t.Any]] = None,
158
+ virtual_environment_mode: VirtualEnvironmentMode = VirtualEnvironmentMode.default,
157
159
  ) -> Model:
158
160
  """Get the model registered by this function."""
159
161
  env: t.Dict[str, t.Tuple[t.Any, t.Optional[bool]]] = {}
@@ -228,6 +230,7 @@ class model(registry_decorator):
228
230
  "audit_definitions": audit_definitions,
229
231
  "signal_definitions": signal_definitions,
230
232
  "blueprint_variables": blueprint_variables,
233
+ "virtual_environment_mode": virtual_environment_mode,
231
234
  **rendered_fields,
232
235
  }
233
236
 
@@ -1062,6 +1062,7 @@ class _Model(ModelMeta, frozen=True):
1062
1062
  self.gateway,
1063
1063
  self.interval_unit.value if self.interval_unit is not None else None,
1064
1064
  str(self.optimize_query) if self.optimize_query is not None else None,
1065
+ self.virtual_environment_mode.value,
1065
1066
  ]
1066
1067
 
1067
1068
  for column_name, column_type in (self.columns_to_types_ or {}).items():
@@ -2957,6 +2958,7 @@ META_FIELD_CONVERTER: t.Dict[str, t.Callable] = {
2957
2958
  )
2958
2959
  ),
2959
2960
  "formatting": str,
2961
+ "virtual_environment_mode": lambda value: exp.Literal.string(value.value),
2960
2962
  }
2961
2963
 
2962
2964
 
@@ -10,6 +10,7 @@ from sqlglot.helper import ensure_collection, ensure_list
10
10
  from sqlglot.optimizer.normalize_identifiers import normalize_identifiers
11
11
 
12
12
  from sqlmesh.core import dialect as d
13
+ from sqlmesh.core.config.common import VirtualEnvironmentMode
13
14
  from sqlmesh.core.config.linter import LinterConfig
14
15
  from sqlmesh.core.dialect import normalize_model_name
15
16
  from sqlmesh.core.model.common import (
@@ -83,6 +84,7 @@ class ModelMeta(_Node):
83
84
  default=None, exclude=True, alias="ignored_rules"
84
85
  )
85
86
  formatting: t.Optional[bool] = Field(default=None, exclude=True)
87
+ virtual_environment_mode: VirtualEnvironmentMode = VirtualEnvironmentMode.default
86
88
 
87
89
  _bool_validator = bool_validator
88
90
  _model_kind_validator = model_kind_validator
@@ -16,6 +16,7 @@ from sqlmesh.core.config import (
16
16
  )
17
17
  from sqlmesh.core.context_diff import ContextDiff
18
18
  from sqlmesh.core.environment import EnvironmentNamingInfo
19
+ from sqlmesh.core.plan.common import should_force_rebuild
19
20
  from sqlmesh.core.plan.definition import (
20
21
  Plan,
21
22
  SnapshotMapping,
@@ -162,7 +163,7 @@ class PlanBuilder:
162
163
 
163
164
  self._start = start
164
165
  if not self._start and (
165
- self._forward_only_preview_needed or self._auto_restatement_preview_needed
166
+ self._forward_only_preview_needed or self._non_forward_only_preview_needed
166
167
  ):
167
168
  self._start = default_start or yesterday_ds()
168
169
 
@@ -267,7 +268,6 @@ class PlanBuilder:
267
268
  self._ensure_no_new_snapshots_with_restatements()
268
269
  self._ensure_new_env_with_changes()
269
270
  self._ensure_valid_date_range()
270
- self._ensure_no_forward_only_revert()
271
271
  self._ensure_no_broken_references()
272
272
 
273
273
  self._apply_effective_from()
@@ -277,7 +277,7 @@ class PlanBuilder:
277
277
 
278
278
  self._check_destructive_changes(directly_modified)
279
279
  self._categorize_snapshots(dag, indirectly_modified)
280
- self._adjust_new_snapshot_intervals()
280
+ self._adjust_snapshot_intervals()
281
281
 
282
282
  deployability_index = (
283
283
  DeployabilityIndex.create(
@@ -509,21 +509,22 @@ class PlanBuilder:
509
509
  ).sorted
510
510
  }
511
511
 
512
- def _adjust_new_snapshot_intervals(self) -> None:
513
- old_snapshots = {
514
- (old.name, old.version_get_or_generate()): old
515
- for _, old in self._context_diff.modified_snapshots.values()
516
- }
517
-
518
- for new in self._context_diff.new_snapshots.values():
519
- new.intervals = []
520
- new.dev_intervals = []
521
- old = old_snapshots.get((new.name, new.version_get_or_generate()))
522
- if not old:
512
+ def _adjust_snapshot_intervals(self) -> None:
513
+ for new, old in self._context_diff.modified_snapshots.values():
514
+ if not new.is_model or not old.is_model:
523
515
  continue
524
- new.merge_intervals(old)
525
- if new.is_forward_only:
526
- new.dev_intervals = new.intervals.copy()
516
+ is_same_version = old.version_get_or_generate() == new.version_get_or_generate()
517
+ if is_same_version and should_force_rebuild(old, new):
518
+ # If the difference between 2 snapshots requires a full rebuild,
519
+ # then clear the intervals for the new snapshot.
520
+ self._context_diff.snapshots[new.snapshot_id].intervals = []
521
+ elif new.snapshot_id in self._context_diff.new_snapshots:
522
+ new.intervals = []
523
+ new.dev_intervals = []
524
+ if is_same_version:
525
+ new.merge_intervals(old)
526
+ if new.is_forward_only:
527
+ new.dev_intervals = new.intervals.copy()
527
528
 
528
529
  def _check_destructive_changes(self, directly_modified: t.Set[SnapshotId]) -> None:
529
530
  for s_id in sorted(directly_modified):
@@ -587,7 +588,12 @@ class PlanBuilder:
587
588
  if not snapshot or not self._is_new_snapshot(snapshot):
588
589
  continue
589
590
 
590
- forward_only = self._is_forward_only_change(s_id) or self._forward_only
591
+ forward_only = self._forward_only or self._is_forward_only_change(s_id)
592
+ if forward_only and s_id.name in self._context_diff.modified_snapshots:
593
+ new, old = self._context_diff.modified_snapshots[s_id.name]
594
+ if should_force_rebuild(old, new) or snapshot.is_seed:
595
+ # Breaking kind changes and seed changes can't be forward-only.
596
+ forward_only = False
591
597
 
592
598
  if s_id in self._choices:
593
599
  snapshot.categorize_as(self._choices[s_id], forward_only)
@@ -608,15 +614,10 @@ class PlanBuilder:
608
614
  s_id = snapshot.snapshot_id
609
615
 
610
616
  if self._context_diff.directly_modified(s_id.name):
611
- new, old = self._context_diff.modified_snapshots[s_id.name]
612
- is_breaking_kind_change = _is_breaking_kind_change(old, new)
613
- if is_breaking_kind_change or snapshot.is_seed:
614
- # Breaking kind changes and seed changes can't be forward-only.
615
- forward_only = False
616
-
617
617
  if self._auto_categorization_enabled:
618
- if is_breaking_kind_change:
619
- snapshot.categorize_as(SnapshotChangeCategory.BREAKING, forward_only)
618
+ new, old = self._context_diff.modified_snapshots[s_id.name]
619
+ if should_force_rebuild(old, new):
620
+ snapshot.categorize_as(SnapshotChangeCategory.BREAKING, False)
620
621
  return
621
622
 
622
623
  s_id_with_missing_columns: t.Optional[SnapshotId] = None
@@ -773,7 +774,7 @@ class PlanBuilder:
773
774
  if snapshot.name in self._context_diff.modified_snapshots:
774
775
  _, old = self._context_diff.modified_snapshots[snapshot.name]
775
776
  # If the model kind has changed in a breaking way, then we can't consider this to be a forward-only change.
776
- if snapshot.is_model and _is_breaking_kind_change(old, snapshot):
777
+ if snapshot.is_model and should_force_rebuild(old, snapshot):
777
778
  return False
778
779
  return (
779
780
  snapshot.is_model and snapshot.model.forward_only and bool(snapshot.previous_versions)
@@ -801,27 +802,6 @@ class PlanBuilder:
801
802
  f"Plan end date: '{time_like_to_str(end)}' cannot be in the future (execution time: '{time_like_to_str(self.execution_time)}')"
802
803
  )
803
804
 
804
- def _ensure_no_forward_only_revert(self) -> None:
805
- """Ensures that a previously superseded breaking / non-breaking snapshot is not being
806
- used again to replace an existing forward-only snapshot with the same version.
807
-
808
- In other words there is no going back to the original non-forward-only snapshot with
809
- the same version once a forward-only change for that version has been introduced.
810
- """
811
- for name, (candidate, promoted) in self._context_diff.modified_snapshots.items():
812
- if (
813
- candidate.snapshot_id not in self._context_diff.new_snapshots
814
- and candidate.is_model
815
- and not candidate.model.forward_only
816
- and promoted.is_forward_only
817
- and not promoted.is_paused
818
- and not candidate.is_no_rebuild
819
- and promoted.version == candidate.version
820
- ):
821
- raise PlanError(
822
- f"Attempted to revert to an unrevertable version of model '{name}'. Run `sqlmesh plan` again to mitigate the issue."
823
- )
824
-
825
805
  def _ensure_no_broken_references(self) -> None:
826
806
  for snapshot in self._context_diff.snapshots.values():
827
807
  broken_references = {
@@ -871,12 +851,18 @@ class PlanBuilder:
871
851
  )
872
852
 
873
853
  @cached_property
874
- def _auto_restatement_preview_needed(self) -> bool:
875
- return self._is_dev and any(
876
- snapshot.model.auto_restatement_cron is not None
877
- for snapshot in self._modified_and_added_snapshots
878
- if snapshot.is_model
879
- )
854
+ def _non_forward_only_preview_needed(self) -> bool:
855
+ if not self._is_dev:
856
+ return False
857
+ for snapshot in self._modified_and_added_snapshots:
858
+ if not snapshot.is_model:
859
+ continue
860
+ if (
861
+ not snapshot.virtual_environment_mode.is_full
862
+ or snapshot.model.auto_restatement_cron is not None
863
+ ):
864
+ return True
865
+ return False
880
866
 
881
867
  @cached_property
882
868
  def _modified_and_added_snapshots(self) -> t.List[Snapshot]:
@@ -886,16 +872,3 @@ class PlanBuilder:
886
872
  if snapshot.name in self._context_diff.modified_snapshots
887
873
  or snapshot.snapshot_id in self._context_diff.added
888
874
  ]
889
-
890
-
891
- def _is_breaking_kind_change(old: Snapshot, new: Snapshot) -> bool:
892
- if old.model.kind.name == new.model.kind.name:
893
- # If the kind hasn't changed, then it's not a breaking change
894
- return False
895
- if not old.is_incremental or not new.is_incremental:
896
- # If either is not incremental, then it's a breaking change
897
- return True
898
- if old.model.partitioned_by == new.model.partitioned_by:
899
- # If the partitioning hasn't changed, then it's not a breaking change
900
- return False
901
- return True
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ from sqlmesh.core.snapshot import Snapshot
4
+
5
+
6
+ def should_force_rebuild(old: Snapshot, new: Snapshot) -> bool:
7
+ if old.virtual_environment_mode != new.virtual_environment_mode:
8
+ # If the virtual environment mode has changed, then we need to rebuild
9
+ return True
10
+ if old.model.kind.name == new.model.kind.name:
11
+ # If the kind hasn't changed, then we don't need to rebuild
12
+ return False
13
+ if not old.is_incremental or not new.is_incremental:
14
+ # If either is not incremental, then we need to rebuild
15
+ return True
16
+ if old.model.partitioned_by == new.model.partitioned_by:
17
+ # If the partitioning hasn't changed, then we don't need to rebuild
18
+ return False
19
+ return True
@@ -1,7 +1,8 @@
1
1
  import typing as t
2
2
 
3
3
  from dataclasses import dataclass
4
- from sqlmesh.core.environment import EnvironmentStatements, EnvironmentNamingInfo
4
+ from sqlmesh.core.environment import EnvironmentStatements, EnvironmentNamingInfo, Environment
5
+ from sqlmesh.core.plan.common import should_force_rebuild
5
6
  from sqlmesh.core.plan.definition import EvaluatablePlan
6
7
  from sqlmesh.core.state_sync import StateReader
7
8
  from sqlmesh.core.scheduler import merged_missing_intervals, SnapshotToIntervals
@@ -230,8 +231,9 @@ class PlanStagesBuilder:
230
231
  all_selected_for_backfill_snapshots = {
231
232
  s.snapshot_id for s in snapshots.values() if plan.is_selected_for_backfill(s.name)
232
233
  }
234
+ existing_environment = self.state_reader.get_environment(plan.environment.name)
233
235
 
234
- self._adjust_intervals(snapshots_by_name, plan)
236
+ self._adjust_intervals(snapshots_by_name, plan, existing_environment)
235
237
 
236
238
  deployability_index = DeployabilityIndex.create(snapshots, start=plan.start)
237
239
  deployability_index_for_creation = deployability_index
@@ -269,7 +271,7 @@ class PlanStagesBuilder:
269
271
  missing_intervals_after_promote[snapshot] = intervals
270
272
 
271
273
  promoted_snapshots, demoted_snapshots, demoted_environment_naming_info = (
272
- self._get_promoted_demoted_snapshots(plan)
274
+ self._get_promoted_demoted_snapshots(plan, existing_environment)
273
275
  )
274
276
 
275
277
  stages: t.List[PlanStage] = []
@@ -358,6 +360,7 @@ class PlanStagesBuilder:
358
360
  demoted_environment_naming_info,
359
361
  snapshots | full_demoted_snapshots,
360
362
  deployability_index,
363
+ plan.is_dev,
361
364
  )
362
365
  if virtual_layer_update_stage:
363
366
  stages.append(virtual_layer_update_stage)
@@ -437,11 +440,18 @@ class PlanStagesBuilder:
437
440
  demoted_environment_naming_info: t.Optional[EnvironmentNamingInfo],
438
441
  all_snapshots: t.Dict[SnapshotId, Snapshot],
439
442
  deployability_index: DeployabilityIndex,
443
+ is_dev: bool,
440
444
  ) -> t.Optional[VirtualLayerUpdateStage]:
441
- promoted_snapshots = {s for s in promoted_snapshots if s.is_model and not s.is_symbolic}
442
- demoted_snapshots = {s for s in demoted_snapshots if s.is_model and not s.is_symbolic}
445
+ def _should_update_virtual_layer(snapshot: SnapshotTableInfo) -> bool:
446
+ # Skip virtual layer update for snapshots with virtual environment support disabled
447
+ virtual_environment_enabled = is_dev or snapshot.virtual_environment_mode.is_full
448
+ return snapshot.is_model and not snapshot.is_symbolic and virtual_environment_enabled
449
+
450
+ promoted_snapshots = {s for s in promoted_snapshots if _should_update_virtual_layer(s)}
451
+ demoted_snapshots = {s for s in demoted_snapshots if _should_update_virtual_layer(s)}
443
452
  if not promoted_snapshots and not demoted_snapshots:
444
453
  return None
454
+
445
455
  return VirtualLayerUpdateStage(
446
456
  promoted_snapshots=promoted_snapshots,
447
457
  demoted_snapshots=demoted_snapshots,
@@ -451,11 +461,10 @@ class PlanStagesBuilder:
451
461
  )
452
462
 
453
463
  def _get_promoted_demoted_snapshots(
454
- self, plan: EvaluatablePlan
464
+ self, plan: EvaluatablePlan, existing_environment: t.Optional[Environment]
455
465
  ) -> t.Tuple[
456
466
  t.Set[SnapshotTableInfo], t.Set[SnapshotTableInfo], t.Optional[EnvironmentNamingInfo]
457
467
  ]:
458
- existing_environment = self.state_reader.get_environment(plan.environment.name)
459
468
  if existing_environment:
460
469
  new_table_infos = {
461
470
  table_info.name: table_info for table_info in plan.environment.promoted_snapshots
@@ -571,10 +580,40 @@ class PlanStagesBuilder:
571
580
  return [s for s in snapshots.values() if _should_create(s)]
572
581
 
573
582
  def _adjust_intervals(
574
- self, snapshots_by_name: t.Dict[str, Snapshot], plan: EvaluatablePlan
583
+ self,
584
+ snapshots_by_name: t.Dict[str, Snapshot],
585
+ plan: EvaluatablePlan,
586
+ existing_environment: t.Optional[Environment],
575
587
  ) -> None:
576
588
  # Make sure the intervals are up to date and restatements are reflected
577
589
  self.state_reader.refresh_snapshot_intervals(snapshots_by_name.values())
590
+
591
+ if existing_environment:
592
+ new_snapshot_ids = set()
593
+ new_snapshot_versions = set()
594
+ for s in snapshots_by_name.values():
595
+ if s.is_model:
596
+ new_snapshot_ids.add(s.snapshot_id)
597
+ new_snapshot_versions.add(s.name_version)
598
+ # Only compare to old snapshots that share the same version as the new snapshots
599
+ old_snapshot_ids = {
600
+ s.snapshot_id
601
+ for s in existing_environment.snapshots
602
+ if s.is_model
603
+ and s.name_version in new_snapshot_versions
604
+ and s.snapshot_id not in new_snapshot_ids
605
+ }
606
+ if old_snapshot_ids:
607
+ old_snapshots = self.state_reader.get_snapshots(old_snapshot_ids)
608
+ for old in old_snapshots.values():
609
+ new = snapshots_by_name.get(old.name)
610
+ if not new or old.version != new.version:
611
+ continue
612
+ if should_force_rebuild(old, new):
613
+ # If the difference between 2 snapshots requires a full rebuild,
614
+ # then clear the intervals for the new snapshot.
615
+ new.intervals = []
616
+
578
617
  for new_snapshot in plan.new_snapshots:
579
618
  if new_snapshot.is_forward_only:
580
619
  # Forward-only snapshots inherit intervals in dev because of cloning
@@ -13,7 +13,7 @@ from pydantic import Field
13
13
  from sqlglot import exp
14
14
  from sqlglot.optimizer.normalize_identifiers import normalize_identifiers
15
15
 
16
- from sqlmesh.core.config import TableNamingConvention
16
+ from sqlmesh.core.config.common import TableNamingConvention, VirtualEnvironmentMode
17
17
  from sqlmesh.core import constants as c
18
18
  from sqlmesh.core.audit import StandaloneAudit
19
19
  from sqlmesh.core.environment import EnvironmentSuffixTarget
@@ -230,6 +230,7 @@ class SnapshotDataVersion(PydanticModel, frozen=True):
230
230
  physical_schema_: t.Optional[str] = Field(default=None, alias="physical_schema")
231
231
  dev_table_suffix: str
232
232
  table_naming_convention: TableNamingConvention = Field(default=TableNamingConvention.default)
233
+ virtual_environment_mode: VirtualEnvironmentMode = Field(default=VirtualEnvironmentMode.default)
233
234
 
234
235
  def snapshot_id(self, name: str) -> SnapshotId:
235
236
  return SnapshotId(name=name, identifier=self.fingerprint.to_identifier())
@@ -336,7 +337,7 @@ class SnapshotInfoMixin(ModelKindMixin):
336
337
  # This can be removed from this model once Pydantic 1 support is dropped (must remain in `Snapshot` though)
337
338
  base_table_name_override: t.Optional[str]
338
339
  dev_table_suffix: str
339
- table_naming_convention: TableNamingConvention = Field(default=TableNamingConvention.default)
340
+ table_naming_convention: TableNamingConvention
340
341
  forward_only: bool
341
342
 
342
343
  @cached_property
@@ -383,6 +384,10 @@ class SnapshotInfoMixin(ModelKindMixin):
383
384
  def fully_qualified_table(self) -> t.Optional[exp.Table]:
384
385
  raise NotImplementedError
385
386
 
387
+ @property
388
+ def virtual_environment_mode(self) -> VirtualEnvironmentMode:
389
+ raise NotImplementedError
390
+
386
391
  @property
387
392
  def is_forward_only(self) -> bool:
388
393
  return self.forward_only or self.change_category == SnapshotChangeCategory.FORWARD_ONLY
@@ -443,6 +448,10 @@ class SnapshotInfoMixin(ModelKindMixin):
443
448
  if self.is_external:
444
449
  return self.name
445
450
 
451
+ if is_deployable and self.virtual_environment_mode.is_dev_only:
452
+ # Use the model name as is if the target is deployable and the virtual environment mode is set to dev-only
453
+ return self.name
454
+
446
455
  is_dev_table = not is_deployable
447
456
  if is_dev_table:
448
457
  version = self.dev_version
@@ -459,6 +468,7 @@ class SnapshotInfoMixin(ModelKindMixin):
459
468
  fqt = self.fully_qualified_table.copy()
460
469
  fqt.set("catalog", None)
461
470
  base_table_name = fqt.sql()
471
+
462
472
  return table_name(
463
473
  self.physical_schema,
464
474
  base_table_name,
@@ -499,6 +509,10 @@ class SnapshotTableInfo(PydanticModel, SnapshotInfoMixin, frozen=True):
499
509
  dev_table_suffix: str
500
510
  model_gateway: t.Optional[str] = None
501
511
  forward_only: bool = False
512
+ table_naming_convention: TableNamingConvention = TableNamingConvention.default
513
+ virtual_environment_mode_: VirtualEnvironmentMode = Field(
514
+ default=VirtualEnvironmentMode.default, alias="virtual_environment_mode"
515
+ )
502
516
 
503
517
  def __lt__(self, other: SnapshotTableInfo) -> bool:
504
518
  return self.name < other.name
@@ -530,6 +544,10 @@ class SnapshotTableInfo(PydanticModel, SnapshotInfoMixin, frozen=True):
530
544
  """Helper method to return self."""
531
545
  return self
532
546
 
547
+ @property
548
+ def virtual_environment_mode(self) -> VirtualEnvironmentMode:
549
+ return self.virtual_environment_mode_
550
+
533
551
  @property
534
552
  def data_version(self) -> SnapshotDataVersion:
535
553
  return SnapshotDataVersion(
@@ -540,6 +558,7 @@ class SnapshotTableInfo(PydanticModel, SnapshotInfoMixin, frozen=True):
540
558
  physical_schema=self.physical_schema,
541
559
  dev_table_suffix=self.dev_table_suffix,
542
560
  table_naming_convention=self.table_naming_convention,
561
+ virtual_environment_mode=self.virtual_environment_mode,
543
562
  )
544
563
 
545
564
  @property
@@ -623,9 +642,7 @@ class Snapshot(PydanticModel, SnapshotInfoMixin):
623
642
  base_table_name_override: t.Optional[str] = None
624
643
  next_auto_restatement_ts: t.Optional[int] = None
625
644
  dev_table_suffix: str = "dev"
626
- table_naming_convention_: TableNamingConvention = Field(
627
- default=TableNamingConvention.default, alias="table_naming_convention"
628
- )
645
+ table_naming_convention: TableNamingConvention = TableNamingConvention.default
629
646
  forward_only: bool = False
630
647
 
631
648
  @field_validator("ttl")
@@ -878,10 +895,9 @@ class Snapshot(PydanticModel, SnapshotInfoMixin):
878
895
  """
879
896
  effective_from_ts = self.normalized_effective_from_ts or 0
880
897
  apply_effective_from = effective_from_ts > 0 and self.identifier != other.identifier
881
-
882
898
  for start, end in other.intervals:
883
899
  # If the effective_from is set, then intervals that come after it must come from
884
- # the current snapshost.
900
+ # the current snapshots.
885
901
  if apply_effective_from and start < effective_from_ts:
886
902
  end = min(end, effective_from_ts)
887
903
  if not apply_effective_from or end <= effective_from_ts:
@@ -1035,7 +1051,10 @@ class Snapshot(PydanticModel, SnapshotInfoMixin):
1035
1051
  SnapshotChangeCategory.INDIRECT_NON_BREAKING,
1036
1052
  SnapshotChangeCategory.METADATA,
1037
1053
  )
1038
- if self.is_model and self.model.physical_version:
1054
+ if self.is_model and not self.virtual_environment_mode.is_full:
1055
+ # Hardcode the version if the virtual environment is not fully enabled.
1056
+ self.version = "novde"
1057
+ elif self.is_model and self.model.physical_version:
1039
1058
  # If the model has a pinned version then use that.
1040
1059
  self.version = self.model.physical_version
1041
1060
  elif is_no_rebuild and self.previous_version:
@@ -1239,6 +1258,7 @@ class Snapshot(PydanticModel, SnapshotInfoMixin):
1239
1258
  model_gateway=self.model_gateway,
1240
1259
  table_naming_convention=self.table_naming_convention, # type: ignore
1241
1260
  forward_only=self.forward_only,
1261
+ virtual_environment_mode=self.virtual_environment_mode,
1242
1262
  )
1243
1263
 
1244
1264
  @property
@@ -1252,6 +1272,7 @@ class Snapshot(PydanticModel, SnapshotInfoMixin):
1252
1272
  physical_schema=self.physical_schema,
1253
1273
  dev_table_suffix=self.dev_table_suffix,
1254
1274
  table_naming_convention=self.table_naming_convention,
1275
+ virtual_environment_mode=self.virtual_environment_mode,
1255
1276
  )
1256
1277
 
1257
1278
  @property
@@ -1383,6 +1404,7 @@ class Snapshot(PydanticModel, SnapshotInfoMixin):
1383
1404
  or self.model.forward_only
1384
1405
  or bool(self.model.physical_version)
1385
1406
  or self.is_view
1407
+ or not self.virtual_environment_mode.is_full
1386
1408
  )
1387
1409
  )
1388
1410
 
@@ -1396,6 +1418,12 @@ class Snapshot(PydanticModel, SnapshotInfoMixin):
1396
1418
  return t.cast(CustomKind, self.model.kind).materialization
1397
1419
  return None
1398
1420
 
1421
+ @property
1422
+ def virtual_environment_mode(self) -> VirtualEnvironmentMode:
1423
+ return (
1424
+ self.model.virtual_environment_mode if self.is_model else VirtualEnvironmentMode.default
1425
+ )
1426
+
1399
1427
  def _ensure_categorized(self) -> None:
1400
1428
  if not self.change_category:
1401
1429
  raise SQLMeshError(f"Snapshot {self.snapshot_id} has not been categorized yet.")
@@ -1535,14 +1563,20 @@ class DeployabilityIndex(PydanticModel, frozen=True):
1535
1563
  for node in dag:
1536
1564
  if node not in snapshots:
1537
1565
  continue
1538
- # Make sure that the node is deployable according to all its parents
1539
- this_deployable = all(
1540
- children_deployability_mapping[p_id]
1541
- for p_id in snapshots[node].parents
1542
- if p_id in children_deployability_mapping
1543
- )
1566
+ snapshot = snapshots[node]
1567
+
1568
+ if not snapshot.virtual_environment_mode.is_full:
1569
+ # If the virtual environment is not fully enabled, then the snapshot can never be deployable
1570
+ this_deployable = False
1571
+ else:
1572
+ # Make sure that the node is deployable according to all its parents
1573
+ this_deployable = all(
1574
+ children_deployability_mapping[p_id]
1575
+ for p_id in snapshots[node].parents
1576
+ if p_id in children_deployability_mapping
1577
+ )
1578
+
1544
1579
  if this_deployable:
1545
- snapshot = snapshots[node]
1546
1580
  is_forward_only_model = (
1547
1581
  snapshot.is_model and snapshot.model.forward_only and not snapshot.is_metadata
1548
1582
  )
@@ -38,7 +38,7 @@ from sqlmesh.core import dialect as d
38
38
  from sqlmesh.core.audit import Audit, StandaloneAudit
39
39
  from sqlmesh.core.dialect import schema_
40
40
  from sqlmesh.core.engine_adapter import EngineAdapter
41
- from sqlmesh.core.engine_adapter.shared import InsertOverwriteStrategy
41
+ from sqlmesh.core.engine_adapter.shared import InsertOverwriteStrategy, DataObjectType
42
42
  from sqlmesh.core.macros import RuntimeStage
43
43
  from sqlmesh.core.model import (
44
44
  AuditResult,
@@ -934,7 +934,14 @@ class SnapshotEvaluator:
934
934
  adapter.transaction(),
935
935
  adapter.session(snapshot.model.render_session_properties(**render_kwargs)),
936
936
  ):
937
- if adapter.table_exists(target_table_name):
937
+ target_data_object = adapter.get_data_object(target_table_name)
938
+ table_exists = target_data_object is not None
939
+ if adapter.drop_data_object_on_type_mismatch(
940
+ target_data_object, _snapshot_to_data_object_type(snapshot)
941
+ ):
942
+ table_exists = False
943
+
944
+ if table_exists:
938
945
  evaluation_strategy = _evaluation_strategy(snapshot, adapter)
939
946
  tmp_table_name = snapshot.table_name(is_deployable=False)
940
947
  logger.info(
@@ -2274,8 +2281,10 @@ def _check_destructive_schema_change(
2274
2281
  alter_expressions: t.List[exp.Alter],
2275
2282
  allow_destructive_snapshots: t.Set[str],
2276
2283
  ) -> None:
2277
- if snapshot.needs_destructive_check(allow_destructive_snapshots) and has_drop_alteration(
2278
- alter_expressions
2284
+ if (
2285
+ snapshot.is_no_rebuild
2286
+ and snapshot.needs_destructive_check(allow_destructive_snapshots)
2287
+ and has_drop_alteration(alter_expressions)
2279
2288
  ):
2280
2289
  snapshot_name = snapshot.name
2281
2290
  dropped_column_names = get_dropped_column_names(alter_expressions)
@@ -2305,3 +2314,15 @@ def _check_table_db_is_physical_schema(table_name: str, physical_schema: str) ->
2305
2314
  raise SQLMeshError(
2306
2315
  f"Table '{table_name}' is not a part of the physical schema '{physical_schema}' and so can't be dropped."
2307
2316
  )
2317
+
2318
+
2319
+ def _snapshot_to_data_object_type(snapshot: Snapshot) -> DataObjectType:
2320
+ if snapshot.is_managed:
2321
+ return DataObjectType.MANAGED_TABLE
2322
+ if snapshot.is_materialized_view:
2323
+ return DataObjectType.MATERIALIZED_VIEW
2324
+ if snapshot.is_view:
2325
+ return DataObjectType.VIEW
2326
+ if snapshot.is_materialized:
2327
+ return DataObjectType.TABLE
2328
+ return DataObjectType.UNKNOWN
@@ -173,10 +173,9 @@ class SnapshotState:
173
173
  snapshot.set_unpaused_ts(None)
174
174
  paused_snapshots.append(snapshot.snapshot_id)
175
175
 
176
- if (
177
- not snapshot.is_forward_only
178
- and target_snapshot.is_forward_only
179
- and not snapshot.unrestorable
176
+ if not snapshot.unrestorable and (
177
+ (target_snapshot.is_forward_only and not snapshot.is_forward_only)
178
+ or (snapshot.is_forward_only and not target_snapshot.is_forward_only)
180
179
  ):
181
180
  logger.info("Marking snapshot %s as unrestorable", snapshot.snapshot_id)
182
181
  snapshot.unrestorable = True
sqlmesh/dbt/basemodel.py CHANGED
@@ -10,6 +10,7 @@ from sqlglot.helper import ensure_list
10
10
 
11
11
  from sqlmesh.core import dialect as d
12
12
  from sqlmesh.core.config.base import UpdateStrategy
13
+ from sqlmesh.core.config.common import VirtualEnvironmentMode
13
14
  from sqlmesh.core.model import Model
14
15
  from sqlmesh.dbt.column import (
15
16
  ColumnConfig,
@@ -345,6 +346,9 @@ class BaseModelConfig(GeneralConfig):
345
346
 
346
347
  @abstractmethod
347
348
  def to_sqlmesh(
348
- self, context: DbtContext, audit_definitions: t.Optional[t.Dict[str, ModelAudit]] = None
349
+ self,
350
+ context: DbtContext,
351
+ audit_definitions: t.Optional[t.Dict[str, ModelAudit]] = None,
352
+ virtual_environment_mode: VirtualEnvironmentMode = VirtualEnvironmentMode.default,
349
353
  ) -> Model:
350
354
  """Convert DBT model into sqlmesh Model"""
sqlmesh/dbt/loader.py CHANGED
@@ -117,7 +117,11 @@ class DbtLoader(Loader):
117
117
 
118
118
  def _to_sqlmesh(config: BMC, context: DbtContext) -> Model:
119
119
  logger.debug("Converting '%s' to sqlmesh format", config.canonical_name(context))
120
- return config.to_sqlmesh(context, audit_definitions=audits)
120
+ return config.to_sqlmesh(
121
+ context,
122
+ audit_definitions=audits,
123
+ virtual_environment_mode=self.config.virtual_environment_mode,
124
+ )
121
125
 
122
126
  for project in self._load_projects():
123
127
  context = project.context.copy()
sqlmesh/dbt/model.py CHANGED
@@ -8,6 +8,7 @@ from sqlglot.helper import ensure_list
8
8
 
9
9
  from sqlmesh.core import dialect as d
10
10
  from sqlmesh.core.config.base import UpdateStrategy
11
+ from sqlmesh.core.config.common import VirtualEnvironmentMode
11
12
  from sqlmesh.core.console import get_console
12
13
  from sqlmesh.core.model import (
13
14
  EmbeddedKind,
@@ -421,7 +422,10 @@ class ModelConfig(BaseModelConfig):
421
422
  }
422
423
 
423
424
  def to_sqlmesh(
424
- self, context: DbtContext, audit_definitions: t.Optional[t.Dict[str, ModelAudit]] = None
425
+ self,
426
+ context: DbtContext,
427
+ audit_definitions: t.Optional[t.Dict[str, ModelAudit]] = None,
428
+ virtual_environment_mode: VirtualEnvironmentMode = VirtualEnvironmentMode.default,
425
429
  ) -> Model:
426
430
  """Converts the dbt model into a SQLMesh model."""
427
431
  model_dialect = self.dialect(context)
@@ -573,6 +577,7 @@ class ModelConfig(BaseModelConfig):
573
577
  # Note: any table dependencies that are not referenced using the `ref` macro will not be included.
574
578
  extract_dependencies_from_query=False,
575
579
  allow_partials=allow_partials,
580
+ virtual_environment_mode=virtual_environment_mode,
576
581
  **optional_kwargs,
577
582
  **model_kwargs,
578
583
  )
sqlmesh/dbt/seed.py CHANGED
@@ -15,6 +15,7 @@ except ImportError:
15
15
  SUPPORTS_DELIMITER = False
16
16
  from sqlglot import exp
17
17
 
18
+ from sqlmesh.core.config.common import VirtualEnvironmentMode
18
19
  from sqlmesh.core.model import Model, SeedKind, create_seed_model
19
20
  from sqlmesh.dbt.basemodel import BaseModelConfig
20
21
  from sqlmesh.dbt.column import ColumnConfig
@@ -38,7 +39,10 @@ class SeedConfig(BaseModelConfig):
38
39
  quote_columns: t.Optional[bool] = False
39
40
 
40
41
  def to_sqlmesh(
41
- self, context: DbtContext, audit_definitions: t.Optional[t.Dict[str, ModelAudit]] = None
42
+ self,
43
+ context: DbtContext,
44
+ audit_definitions: t.Optional[t.Dict[str, ModelAudit]] = None,
45
+ virtual_environment_mode: VirtualEnvironmentMode = VirtualEnvironmentMode.default,
42
46
  ) -> Model:
43
47
  """Converts the dbt seed into a SQLMesh model."""
44
48
  seed_path = self.path.absolute().as_posix()
@@ -83,6 +87,7 @@ class SeedConfig(BaseModelConfig):
83
87
  SeedKind(path=seed_path),
84
88
  dialect=self.dialect(context),
85
89
  audit_definitions=audit_definitions,
90
+ virtual_environment_mode=virtual_environment_mode,
86
91
  **kwargs,
87
92
  )
88
93
 
@@ -0,0 +1,5 @@
1
+ """Add virtual_environment_mode to the model definition."""
2
+
3
+
4
+ def migrate(state_sync, **kwargs): # type: ignore
5
+ pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlmesh
3
- Version: 0.209.1.dev13
3
+ Version: 0.209.1.dev14
4
4
  Summary: Next-generation data transformation framework
5
5
  Author-email: "TobikoData Inc." <engineering@tobikodata.com>
6
6
  License: Apache License
@@ -1,5 +1,5 @@
1
1
  sqlmesh/__init__.py,sha256=QjwaamJIdW7voXoS880yAx7tHm-Im4_mqGA-XJgafos,7631
2
- sqlmesh/_version.py,sha256=Z-QEmLpn7WCZTovKI1S7q9PMPW5I4VTOb_KNgW5Qf5Y,723
2
+ sqlmesh/_version.py,sha256=bHG9Na0Ngc7db0PR46aKmXwZV8939jGLj8kRw2qwQDE,723
3
3
  sqlmesh/magics.py,sha256=bEzYqXHcM0P3CjyUZ1O9arlmXAdLAhoglKbQ29fD9AE,41842
4
4
  sqlmesh/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  sqlmesh/cicd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -13,12 +13,12 @@ sqlmesh/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  sqlmesh/core/_typing.py,sha256=e1f7xO2aZCy38M_3VoDy-XI3shyd99-IFctgIFSgXWM,497
14
14
  sqlmesh/core/console.py,sha256=KbIXoxt5hpDENEolY-DpBlPmcWtJ9vzEgF52mjG2jPE,165710
15
15
  sqlmesh/core/constants.py,sha256=Wg6qApCRhENA1me7ELUS5GnArKlTBH-8192gQYKQnE8,2759
16
- sqlmesh/core/context.py,sha256=bFAvnjg3YENIgWVDYfflKA5yW6o6gMEZhiSKBPIrBk8,127613
16
+ sqlmesh/core/context.py,sha256=HFEIAMbxDa7gBvyvR2B6OTJsRuD-JXO2fOKHIAFoDLY,127702
17
17
  sqlmesh/core/context_diff.py,sha256=_Ur55fGLJ58Icsp44I4pVhdi7_JHiHq45mWIco9oFzk,21792
18
18
  sqlmesh/core/dialect.py,sha256=wJ2cDoxz72LIRJ3iBZdCqtnKMbMUdIftl-AvbVldI28,52413
19
19
  sqlmesh/core/environment.py,sha256=odus40R-YzdQdS_IiUq_tFouUXIhnpwmNwo8dE8Aw1k,13041
20
20
  sqlmesh/core/lineage.py,sha256=zjB0Zfamo2Fja2r5SSZPMbrEKTXr1WjozZPVqvxdybI,3143
21
- sqlmesh/core/loader.py,sha256=z8KNT5igXc_g-5X1BPPL9XAbpve3ZQp7OkkX63iT0Gc,41179
21
+ sqlmesh/core/loader.py,sha256=WDceZAqm2ijcZHlEnG1old7p5c9LKvkr4YpbPZ-7hk4,41349
22
22
  sqlmesh/core/macros.py,sha256=G1TD1Lv-iTNOh1JtHwRFOrnThqDBFuAQgQJtp6UDe0I,63023
23
23
  sqlmesh/core/node.py,sha256=3ChBz1PIMJhp-jzed_GLBrtmEF2v_BVDEM5WNe0ssPk,14658
24
24
  sqlmesh/core/notification_target.py,sha256=PPGoDrgbRKxr27vJEu03XqNTQLYTw0ZF_b0yAapxGeI,16158
@@ -40,7 +40,7 @@ sqlmesh/core/audit/definition.py,sha256=iPpGEOwFIXFHQFQGMyapCm7jnlotwMa-gNaCgTwz
40
40
  sqlmesh/core/config/__init__.py,sha256=5zxTXjUMuxBj4XlX6bmpIWTp4GsZY5rIdKSCTYbxRBM,2070
41
41
  sqlmesh/core/config/base.py,sha256=t8NQmsgQoZSc-k0dlDiCb8t1jj0AMYdGZ-6se9q_Pks,4898
42
42
  sqlmesh/core/config/categorizer.py,sha256=6vzUoNLjR6GOEb_2mYVz2TwmMv2BfldgHX2u-Le5HZs,1975
43
- sqlmesh/core/config/common.py,sha256=HZxek5DrzdFQl22BypSqB_8amsIT8tqUFSgh66Ibxwk,5158
43
+ sqlmesh/core/config/common.py,sha256=Zg0f3-iEfSE4FcJOlSQzbG8gL6CpKpFLCe8dJJgBmJs,5931
44
44
  sqlmesh/core/config/connection.py,sha256=NcS1qB0MKIBCfw6vUNLhalVKZTo2igS7VTrcV_T-VgA,88607
45
45
  sqlmesh/core/config/feature_flag.py,sha256=JRcD31OG_t2cYc24meKbHDGB0iVID0P07V2oOeB_mgA,203
46
46
  sqlmesh/core/config/format.py,sha256=6CXFbvnor56xbldKE-Vrm9k_ABRoY4v6vgIb3mCihiQ,1355
@@ -52,14 +52,14 @@ sqlmesh/core/config/migration.py,sha256=3lZIlmIcQH3_hIEm3Y3jFtvpw0YXzRzAnk4Odikl
52
52
  sqlmesh/core/config/model.py,sha256=xDKIHD8WIzsPu7DfpybPM3bLobppttCZjvD3TTVnwps,4182
53
53
  sqlmesh/core/config/naming.py,sha256=KVGaxUgswoQajXR_cHFJvgkmEFcU1V182GkOH1SJxuE,326
54
54
  sqlmesh/core/config/plan.py,sha256=wJEDfocMcKSMN0-5gVojjIcmJZ0376LJU3tkGMTm-Zc,1558
55
- sqlmesh/core/config/root.py,sha256=qLb2woEixtmB32dTAAIbKlAVt1yNTMUVhZbRzrbS924,16906
55
+ sqlmesh/core/config/root.py,sha256=xzQxFov0cvmxw90id6YE_OGJEJh38-lbHhA5kWvSh8E,17308
56
56
  sqlmesh/core/config/run.py,sha256=rIK0QnN_vArrwps2TXGVEbvao2AnGZgzXo7EliJa42E,852
57
57
  sqlmesh/core/config/scheduler.py,sha256=LVEHIln7dqv8ygo4uniji4j9-gfuJlk-WpQU9FAqILs,7031
58
58
  sqlmesh/core/config/ui.py,sha256=jsO-S6_d9NkLZGG5pT4mgKgxMF34KzkDociZAMvCX3U,278
59
59
  sqlmesh/core/engine_adapter/__init__.py,sha256=two_WTs4UnSNp-fscJh8vOunJq0Xi37EuDuMjKsKuNE,2281
60
60
  sqlmesh/core/engine_adapter/_typing.py,sha256=_63pYCThfC39UtPVHY3N0H6qJ1YBT32OqwwR3swolGM,1037
61
61
  sqlmesh/core/engine_adapter/athena.py,sha256=AS1w_l_VrFZsc2CIicElg1dtS9y6q5hHHmTg4ECH-xs,26453
62
- sqlmesh/core/engine_adapter/base.py,sha256=QUHO4z-5IibBD1pKPfYr04yTroDZ4katsV-GHUmFC-Y,104590
62
+ sqlmesh/core/engine_adapter/base.py,sha256=qefboMkuzRn4ctUyuMZiFtL3tSHBZoMzKcpm_90Nvxw,107386
63
63
  sqlmesh/core/engine_adapter/base_postgres.py,sha256=DOsOdCJyQAVfA9S0GznuH0J1bonMqYVP_yJLDYcxFYM,6772
64
64
  sqlmesh/core/engine_adapter/bigquery.py,sha256=oL3xISn6Mw2jnJareKkeoG3XFCGW4lVof4OorXdQfBA,51468
65
65
  sqlmesh/core/engine_adapter/clickhouse.py,sha256=LoPdm_s_s-ZxS1XJPA3Et0iOHeJ1LgqTAFbwBdiqE7A,34912
@@ -69,9 +69,9 @@ sqlmesh/core/engine_adapter/mixins.py,sha256=2f6I97iRoeEX2c_BB5Pu8ruIrozwtCZPrxN
69
69
  sqlmesh/core/engine_adapter/mssql.py,sha256=N-l9RReP45hg9CNKCQTrg4BmwKtN-v9xK68JJW-xYc8,17647
70
70
  sqlmesh/core/engine_adapter/mysql.py,sha256=lVC-siXZK3iSfqCmBjZNw8uy09Z8la8v8_oUZ-E2OwI,6966
71
71
  sqlmesh/core/engine_adapter/postgres.py,sha256=4eXBDKXb6uiu8B7nD60b2p9pbkc8Z1Ycgavw7Necxj4,4626
72
- sqlmesh/core/engine_adapter/redshift.py,sha256=35T2ZLKozakRNeD0zykWk8RvXomfX9xi4Zg5Y4ZAVhg,16779
72
+ sqlmesh/core/engine_adapter/redshift.py,sha256=RdEPTBmW2zrruiY7mbbbCJnvTCKHcHBep1MgpXoOZl4,17005
73
73
  sqlmesh/core/engine_adapter/risingwave.py,sha256=GY0u085TWerl8kTRNJpEutdD5FBsoPijQ0CcOkWOJEE,2359
74
- sqlmesh/core/engine_adapter/shared.py,sha256=zL2W2t9af9mo80aSO0fz5TqV6mY8_kI5VYLswbKdZyk,13369
74
+ sqlmesh/core/engine_adapter/shared.py,sha256=pvYU-sC2WEIOENm4oG4UrOCWXF56h-FEmlAqAR8TYoA,13500
75
75
  sqlmesh/core/engine_adapter/snowflake.py,sha256=IEEzepxNpfqBIMe4ziz2k8fjOcJPeQPZyM3XljymAj0,28874
76
76
  sqlmesh/core/engine_adapter/spark.py,sha256=sYkVOPS0X43UPbI0z7Hh_EpKlI36qkOcr4NNFOTQlLs,21045
77
77
  sqlmesh/core/engine_adapter/trino.py,sha256=Tws3GxqnC6BmXFohqru1TDxsgPH-21qV6Oky7FaaLpE,15908
@@ -87,23 +87,24 @@ sqlmesh/core/metric/rewriter.py,sha256=GiSTHfn2kinqCfNPYgZPRk93JFLzVaaejHtHDQ0yX
87
87
  sqlmesh/core/model/__init__.py,sha256=C8GRZ53xuXEA9hQv3BQS9pNNyd9rZ06R_B96UYGhDu4,1644
88
88
  sqlmesh/core/model/cache.py,sha256=csun0RJguHzKX6-qITcOs4fVP4f8_Ts8qiUVV4sHY6Q,7869
89
89
  sqlmesh/core/model/common.py,sha256=xCMdwkJXnwjE9-56wNb1HiER0KHmWbBzc2Tm--2u168,25610
90
- sqlmesh/core/model/decorator.py,sha256=wUUg5t0K559tV9Pqt6FE2EWXt5TPGb4wF2Yg4B4hX4w,9251
91
- sqlmesh/core/model/definition.py,sha256=uouwIbWA-m2cFXxvDrBVHKxR0ycSAq8Rz3Qv3xY7ewA,113852
90
+ sqlmesh/core/model/decorator.py,sha256=bL-JuNrdBAikZSjVxnXqeB9i0e9qC7jm7yLjwiZ38aU,9470
91
+ sqlmesh/core/model/definition.py,sha256=IxapmZQkSPFpqGA9_mfzA5xGu1AZVxgsR0P1f1-ngus,113980
92
92
  sqlmesh/core/model/kind.py,sha256=uN73ycANfM94CxcOdkUTclLansNvwo3wySkkl--QzY0,36322
93
- sqlmesh/core/model/meta.py,sha256=IE-b_OkCcKzXuQBsopiaf0l5a5Ay903N_64Z1s4cXGk,20064
93
+ sqlmesh/core/model/meta.py,sha256=PCYfLMevlWExRHQtYs1H_158x3I5K-yUXbdjRzH2YQw,20212
94
94
  sqlmesh/core/model/schema.py,sha256=_HMYfzK9wWXh7_CQDIIGnuQUD4aiX3o5D2cRp2sERzc,3387
95
95
  sqlmesh/core/model/seed.py,sha256=a0M-1zY1gOkN5ph2GQyataEdBSCtq50YjeFk2LyvInI,5058
96
96
  sqlmesh/core/plan/__init__.py,sha256=NKSvM7ZBVjw9ho3J65M1wFvG3KURB8PJ0FHHLmtSF44,443
97
- sqlmesh/core/plan/builder.py,sha256=2Nv3D8SujJfMiDLXZR8uUv95lKZZx95RHVbeJ_H2iLo,41988
97
+ sqlmesh/core/plan/builder.py,sha256=CliBMyjTFTp4pVjV04ocxjLh3i0LjJevWdarZj3af8A,40954
98
+ sqlmesh/core/plan/common.py,sha256=xchdSb_XqQBu0S6OqnpeLBR5XB8Rmr9FRCfO_v4Tl50,768
98
99
  sqlmesh/core/plan/definition.py,sha256=yrX_uZMXi29ltkuowWhNUOi1kd0Uq0TQoM574EI6JHo,14019
99
100
  sqlmesh/core/plan/evaluator.py,sha256=0oqepq0qZ_tPhAye6Q9gLenBD_8yE3E8AAwt553lz7U,22163
100
101
  sqlmesh/core/plan/explainer.py,sha256=aBkvtM9eog_2NIglazSDdG_i9TTCdFSVOkCw5N8O-1k,12028
101
- sqlmesh/core/plan/stages.py,sha256=n2gBzOdal2oMgwHgL2pns9vLZmkKQb1j6qBZnk5y8NE,21830
102
+ sqlmesh/core/plan/stages.py,sha256=ZxaHdV6G1P8g9fk2kgsFBGJFe8JPPICdoG_8U1FbklY,23743
102
103
  sqlmesh/core/snapshot/__init__.py,sha256=Z5HTetNCZGoKF1WAKXk4LWL9dc8EC8lwS2O4TtoBmt4,1317
103
104
  sqlmesh/core/snapshot/cache.py,sha256=bgqCR2hyf6r2A_8QP1EnXFK25gDX37-Zg0YeMuETWxg,3934
104
105
  sqlmesh/core/snapshot/categorizer.py,sha256=qGG5870mu1jR2whKisSBH_GFbsyh96RnzJDgkN7DV9g,2482
105
- sqlmesh/core/snapshot/definition.py,sha256=iYQAIkrAUQmMt7_HTVGcXsdXK4waBVWR8UhqEJ7yLdc,89669
106
- sqlmesh/core/snapshot/evaluator.py,sha256=brtESRAypTYpltOKyQYQZ8JwpT9OmBOXUR3zR5hJ6qE,91709
106
+ sqlmesh/core/snapshot/definition.py,sha256=dbx9n4vI1Zdddiu6EXB-eM6KjSsepnpVWdjC_2aPcrg,91305
107
+ sqlmesh/core/snapshot/evaluator.py,sha256=h0WoXKWj4ZrehBDJty8AsQ7mq5xvUCCywdpRjH9B1d8,92460
107
108
  sqlmesh/core/state_sync/__init__.py,sha256=B60xj-_Xk-4jas3UphLvwkuf04G-9AyGckcblXE0aRg,869
108
109
  sqlmesh/core/state_sync/base.py,sha256=AdqWmbhnvRFqVcFK6L01uCvpBVAYGuMrm4P0D3u_NaU,18334
109
110
  sqlmesh/core/state_sync/cache.py,sha256=0AsYdQyBqbLdJvkBQ-hDAipsvnn_jSps-nya-Q4XeL0,5337
@@ -114,7 +115,7 @@ sqlmesh/core/state_sync/db/environment.py,sha256=iTxrdNlKB36S54PUdlqEqlxiAZJtKNE
114
115
  sqlmesh/core/state_sync/db/facade.py,sha256=WWditN46A9DXHDcHT7kUZaCfkkjcaDfMC9qO1EfH4Og,25266
115
116
  sqlmesh/core/state_sync/db/interval.py,sha256=aMQSF42bVOUU05g7oizcrKR6TBVzR6TJppxzlxkmJFU,17985
116
117
  sqlmesh/core/state_sync/db/migrator.py,sha256=2LNUwOBNFkA1ntp-Z93S_OWxFQhOBj3ZD_ZqPCyPEAE,18139
117
- sqlmesh/core/state_sync/db/snapshot.py,sha256=SpVg55CB4FB4DX1ZXVSJrjdek_W0RNYEkgrQBkCPJIg,29534
118
+ sqlmesh/core/state_sync/db/snapshot.py,sha256=x7JxtOttjE9NccWfjIWURixR2y8AT6OjgEL6FiQV1Jc,29586
118
119
  sqlmesh/core/state_sync/db/utils.py,sha256=204OpT3ISFPCwWP9Aqq8nr6ETl24sbG5uOyo86IJv6E,3819
119
120
  sqlmesh/core/state_sync/db/version.py,sha256=fl5g1tNMYu8O6WTN4J3bciwjGq0c5nZlRDlxxRKGU78,2210
120
121
  sqlmesh/core/test/__init__.py,sha256=e83TJPwPRR_rAG29Y0OVbZb-5oWVBzz-_wrcd22Qk10,418
@@ -125,19 +126,19 @@ sqlmesh/core/test/result.py,sha256=6gOKEsERciHhcrw9TedtNr7g1ynTO7UwA5-PPrzvYuM,4
125
126
  sqlmesh/core/test/runner.py,sha256=8I-cL7Q9CggLvET_GPkrXB2YjlyCIHrvbFbbRDnSHRE,6169
126
127
  sqlmesh/dbt/__init__.py,sha256=KUv-lW5sG9D2ceXAIzA4MLcjyhzq3E-7qJP4P_PH2EU,144
127
128
  sqlmesh/dbt/adapter.py,sha256=pV8b8m_wqK3YJo_6D5b6cZBwgewFAPvAlvLO51UCBl4,16948
128
- sqlmesh/dbt/basemodel.py,sha256=XuiK9-r5JWzvZGEk98Jt4ukR2exfMDsotGZqYx5xluc,12561
129
+ sqlmesh/dbt/basemodel.py,sha256=YmrgE53nrRdo4kCFkuivOPRnqhmAnQ1_XVqxYjGmNRI,12731
129
130
  sqlmesh/dbt/builtin.py,sha256=p4E1rmSLM0B5PHcFWA9J7FwScR90pIPqiMN8G4M3sBM,15173
130
131
  sqlmesh/dbt/column.py,sha256=Kvj_N3D_QegcoNQG7VqRiqy-FRfupud-6ilhlykBOgE,1873
131
132
  sqlmesh/dbt/common.py,sha256=g4PAL-sEdlnfb_zT-WW3xh-oamyCUoK8dUiDEMVwDWI,7804
132
133
  sqlmesh/dbt/context.py,sha256=dxClM-G1IeMbP0v5yxC_va8QNU-uKghW2dBlSZHJOk0,10331
133
- sqlmesh/dbt/loader.py,sha256=EqPcPT-RgLo52iMiDc9eUb8tWrsvdR8RoRJG06FStBw,15576
134
+ sqlmesh/dbt/loader.py,sha256=Loc-yNHop-Gop80tDhziTPJwOQ01jlW6CcAV1vFsS_Q,15702
134
135
  sqlmesh/dbt/manifest.py,sha256=jlafUYdoG7gS2pzvJrt9JfC2afbAMORhBbBUaLl-ZTM,24894
135
- sqlmesh/dbt/model.py,sha256=FgQpl8pUNOnihGUo5PYdqOqqOX3XtfDHX3xEJc-CVC4,25782
136
+ sqlmesh/dbt/model.py,sha256=eAkTnj0nh7huBFXGAO8ckr5chvdAFrqbw-gOs6uZ_NE,26015
136
137
  sqlmesh/dbt/package.py,sha256=IBBQueVB4_4C9tIk0-GUL-O2uibNxXu6W2a4AeHHcOU,4322
137
138
  sqlmesh/dbt/profile.py,sha256=Fz2c0dJNN0xBgYEogCIKBTByjNvlA2BVBzOLQCfdEfM,3788
138
139
  sqlmesh/dbt/project.py,sha256=a21czHhdcm2J80Zllj2q6VvH-W_rDtlXHa8wmvHaLTo,4036
139
140
  sqlmesh/dbt/relation.py,sha256=rgDJQUuENjqq7kpMJJ6AdlgwYmcYjA1oFj6LCYCS-Hg,227
140
- sqlmesh/dbt/seed.py,sha256=E7pFhtGwRrC9U_VZJNABwLTa-Nk4OeD-gOtjSpvYmDA,4251
141
+ sqlmesh/dbt/seed.py,sha256=UuPUTXpj3nL3JxX1fAReS263zPo31Hmj8dfOctF1oh8,4484
141
142
  sqlmesh/dbt/source.py,sha256=IZK-aOV-KdWIOPuXqRjJZAZ7phW1HUXoHtE-3clgkx4,4125
142
143
  sqlmesh/dbt/target.py,sha256=aHPt0jSb-Rsqg-uGYZY_rwIXUCmJ8YmsaGh2HiczWbI,42831
143
144
  sqlmesh/dbt/test.py,sha256=aOTKqYpVafff-TU4Xhbc9eZr6QAuFLMxIu_sV-18JLQ,8282
@@ -264,6 +265,7 @@ sqlmesh/migrations/v0084_normalize_quote_when_matched_and_merge_filter.py,sha256
264
265
  sqlmesh/migrations/v0085_deterministic_repr.py,sha256=mkbZV0rzXJ5bmN1Y75kFI9T6gbnx_sy-zn_UzbOL1Sg,4265
265
266
  sqlmesh/migrations/v0086_check_deterministic_bug.py,sha256=WiYcmhQD434GXWiiakQtcy9ESmB35spXMltV43nvU7Q,2953
266
267
  sqlmesh/migrations/v0087_normalize_blueprint_variables.py,sha256=GI6KKA-6PtONUoOnyvLpRTUmPjbpa-CKhDa0iqJW01U,4421
268
+ sqlmesh/migrations/v0088_add_virtual_environment_mode.py,sha256=rm6k7CRy2uHozo1Oy17D8WhiV1JldPNCpJ8PukoHSwM,122
267
269
  sqlmesh/migrations/v0088_warn_about_variable_python_env_diffs.py,sha256=zCLT6Yqwny73SlfACWjO1bZfs8lZ7gJBov5Iz4akwiQ,3254
268
270
  sqlmesh/schedulers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
269
271
  sqlmesh/utils/__init__.py,sha256=51VqA2etTZ6kX54250s1hN5ui7D89OeUTUQ9kgMze7s,11554
@@ -289,7 +291,7 @@ sqlmesh/utils/pydantic.py,sha256=o_NsXbIpDqNpUA1Uc5xF0ZzoXQYYB0DfHwdRxBwXPNk,120
289
291
  sqlmesh/utils/rich.py,sha256=cwQ5nJ6sgz64xHtoh6_ec7ReV5YpsOGhMtUJnwoRfEI,3549
290
292
  sqlmesh/utils/windows.py,sha256=r7Yin4WEwbvH7sKA3hOJtRsclmscNuBeCklCfVPvtQs,611
291
293
  sqlmesh/utils/yaml.py,sha256=hvo5lmybYLcIx4p6CQwufS8X4MbwS60lEBnCmYJCXmU,2336
292
- sqlmesh-0.209.1.dev13.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
294
+ sqlmesh-0.209.1.dev14.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
293
295
  sqlmesh_dbt/__init__.py,sha256=awYS5y5mz-1NUmx6i5h5NSTJ7tidRl9NC0FAnFWSF6U,350
294
296
  sqlmesh_dbt/cli.py,sha256=Ekn77WLMmyyr31aoDoWrZp5DS0rfiddy7pNohBK8G0s,2115
295
297
  sqlmesh_dbt/console.py,sha256=1B0B5_R1DlrEkyWVDl8G78-B6_K-ZtLPsQEhqPCfLEU,188
@@ -411,8 +413,8 @@ web/server/api/endpoints/models.py,sha256=kwj0s7uve3iZSMfmjkoPVMFMeY1sD0peTeyrWf
411
413
  web/server/api/endpoints/modules.py,sha256=8hqqgonGay_mJmpCw0IdbjsPhWlQH2VLdKAqha-myac,468
412
414
  web/server/api/endpoints/plan.py,sha256=bbbY50W_2MsZSTxOHWMKz0tbIm75nsRSlPy8GI2fg9Q,9306
413
415
  web/server/api/endpoints/table_diff.py,sha256=SCy3-pr5x6bEEiOaZGCDbiHQhpgxwmnz3Acqa2Ie6jI,2090
414
- sqlmesh-0.209.1.dev13.dist-info/METADATA,sha256=xyA8vDp0_wasj_KFmp6AZoegsW7YBPLiaOnzE_ndzf4,26686
415
- sqlmesh-0.209.1.dev13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
416
- sqlmesh-0.209.1.dev13.dist-info/entry_points.txt,sha256=sHAf6tQczIM8xZoduN4qaUjV7QEPVUUW_LCT8EDUMv4,155
417
- sqlmesh-0.209.1.dev13.dist-info/top_level.txt,sha256=RQ-33FPe2IgL0rgossAfJkCRtqslz9b7wFARqiWLC5Q,24
418
- sqlmesh-0.209.1.dev13.dist-info/RECORD,,
416
+ sqlmesh-0.209.1.dev14.dist-info/METADATA,sha256=dL1K4Bz229XuuJr3I_ZJNxFcMBlII5PcA4wJIxAGHHw,26686
417
+ sqlmesh-0.209.1.dev14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
418
+ sqlmesh-0.209.1.dev14.dist-info/entry_points.txt,sha256=sHAf6tQczIM8xZoduN4qaUjV7QEPVUUW_LCT8EDUMv4,155
419
+ sqlmesh-0.209.1.dev14.dist-info/top_level.txt,sha256=RQ-33FPe2IgL0rgossAfJkCRtqslz9b7wFARqiWLC5Q,24
420
+ sqlmesh-0.209.1.dev14.dist-info/RECORD,,