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 +2 -2
- sqlmesh/core/config/common.py +29 -0
- sqlmesh/core/config/root.py +14 -7
- sqlmesh/core/context.py +7 -4
- sqlmesh/core/engine_adapter/base.py +73 -5
- sqlmesh/core/engine_adapter/redshift.py +6 -1
- sqlmesh/core/engine_adapter/shared.py +3 -0
- sqlmesh/core/loader.py +2 -0
- sqlmesh/core/model/decorator.py +3 -0
- sqlmesh/core/model/definition.py +2 -0
- sqlmesh/core/model/meta.py +2 -0
- sqlmesh/core/plan/builder.py +40 -67
- sqlmesh/core/plan/common.py +19 -0
- sqlmesh/core/plan/stages.py +47 -8
- sqlmesh/core/snapshot/definition.py +49 -15
- sqlmesh/core/snapshot/evaluator.py +25 -4
- sqlmesh/core/state_sync/db/snapshot.py +3 -4
- sqlmesh/dbt/basemodel.py +5 -1
- sqlmesh/dbt/loader.py +5 -1
- sqlmesh/dbt/model.py +6 -1
- sqlmesh/dbt/seed.py +6 -1
- sqlmesh/migrations/v0088_add_virtual_environment_mode.py +5 -0
- {sqlmesh-0.209.1.dev13.dist-info → sqlmesh-0.209.1.dev14.dist-info}/METADATA +1 -1
- {sqlmesh-0.209.1.dev13.dist-info → sqlmesh-0.209.1.dev14.dist-info}/RECORD +28 -26
- {sqlmesh-0.209.1.dev13.dist-info → sqlmesh-0.209.1.dev14.dist-info}/WHEEL +0 -0
- {sqlmesh-0.209.1.dev13.dist-info → sqlmesh-0.209.1.dev14.dist-info}/entry_points.txt +0 -0
- {sqlmesh-0.209.1.dev13.dist-info → sqlmesh-0.209.1.dev14.dist-info}/licenses/LICENSE +0 -0
- {sqlmesh-0.209.1.dev13.dist-info → sqlmesh-0.209.1.dev14.dist-info}/top_level.txt +0 -0
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.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 209, 1, '
|
|
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
|
sqlmesh/core/config/common.py
CHANGED
|
@@ -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>
|
sqlmesh/core/config/root.py
CHANGED
|
@@ -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
|
|
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 =
|
|
152
|
-
|
|
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
|
|
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
|
|
43
|
-
from sqlmesh.utils.connection_pool import
|
|
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
|
|
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
|
-
|
|
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
|
sqlmesh/core/model/decorator.py
CHANGED
|
@@ -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
|
|
sqlmesh/core/model/definition.py
CHANGED
|
@@ -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
|
|
sqlmesh/core/model/meta.py
CHANGED
|
@@ -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
|
sqlmesh/core/plan/builder.py
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
|
513
|
-
|
|
514
|
-
|
|
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.
|
|
525
|
-
if new
|
|
526
|
-
|
|
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.
|
|
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
|
-
|
|
619
|
-
|
|
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
|
|
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
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
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
|
sqlmesh/core/plan/stages.py
CHANGED
|
@@ -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
|
-
|
|
442
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
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
|
-
|
|
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
|
|
2278
|
-
|
|
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,
|
|
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(
|
|
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,
|
|
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,
|
|
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
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
sqlmesh/__init__.py,sha256=QjwaamJIdW7voXoS880yAx7tHm-Im4_mqGA-XJgafos,7631
|
|
2
|
-
sqlmesh/_version.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
91
|
-
sqlmesh/core/model/definition.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
106
|
-
sqlmesh/core/snapshot/evaluator.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
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.
|
|
415
|
-
sqlmesh-0.209.1.
|
|
416
|
-
sqlmesh-0.209.1.
|
|
417
|
-
sqlmesh-0.209.1.
|
|
418
|
-
sqlmesh-0.209.1.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|