sqlmesh 0.213.1.dev1__py3-none-any.whl → 0.227.2.dev4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sqlmesh/__init__.py +12 -2
- sqlmesh/_version.py +2 -2
- sqlmesh/cli/main.py +0 -44
- sqlmesh/cli/project_init.py +11 -2
- sqlmesh/core/_typing.py +1 -0
- sqlmesh/core/audit/definition.py +8 -2
- sqlmesh/core/config/__init__.py +1 -1
- sqlmesh/core/config/connection.py +17 -5
- sqlmesh/core/config/dbt.py +13 -0
- sqlmesh/core/config/janitor.py +12 -0
- sqlmesh/core/config/loader.py +7 -0
- sqlmesh/core/config/model.py +2 -0
- sqlmesh/core/config/root.py +3 -0
- sqlmesh/core/console.py +81 -3
- sqlmesh/core/constants.py +1 -1
- sqlmesh/core/context.py +69 -26
- sqlmesh/core/dialect.py +3 -0
- sqlmesh/core/engine_adapter/_typing.py +2 -0
- sqlmesh/core/engine_adapter/base.py +322 -22
- sqlmesh/core/engine_adapter/base_postgres.py +17 -1
- sqlmesh/core/engine_adapter/bigquery.py +146 -7
- sqlmesh/core/engine_adapter/clickhouse.py +17 -13
- sqlmesh/core/engine_adapter/databricks.py +33 -2
- sqlmesh/core/engine_adapter/fabric.py +10 -29
- sqlmesh/core/engine_adapter/mixins.py +142 -48
- sqlmesh/core/engine_adapter/mssql.py +15 -4
- sqlmesh/core/engine_adapter/mysql.py +2 -2
- sqlmesh/core/engine_adapter/postgres.py +9 -3
- sqlmesh/core/engine_adapter/redshift.py +4 -0
- sqlmesh/core/engine_adapter/risingwave.py +1 -0
- sqlmesh/core/engine_adapter/shared.py +6 -0
- sqlmesh/core/engine_adapter/snowflake.py +82 -11
- sqlmesh/core/engine_adapter/spark.py +14 -10
- sqlmesh/core/engine_adapter/trino.py +4 -2
- sqlmesh/core/environment.py +2 -0
- sqlmesh/core/janitor.py +181 -0
- sqlmesh/core/lineage.py +1 -0
- sqlmesh/core/linter/definition.py +13 -13
- sqlmesh/core/linter/rules/builtin.py +29 -0
- sqlmesh/core/macros.py +35 -13
- sqlmesh/core/model/common.py +2 -0
- sqlmesh/core/model/definition.py +82 -28
- sqlmesh/core/model/kind.py +66 -2
- sqlmesh/core/model/meta.py +108 -4
- sqlmesh/core/node.py +101 -1
- sqlmesh/core/plan/builder.py +18 -10
- sqlmesh/core/plan/common.py +199 -2
- sqlmesh/core/plan/definition.py +25 -6
- sqlmesh/core/plan/evaluator.py +75 -113
- sqlmesh/core/plan/explainer.py +90 -8
- sqlmesh/core/plan/stages.py +42 -21
- sqlmesh/core/renderer.py +78 -32
- sqlmesh/core/scheduler.py +102 -22
- sqlmesh/core/selector.py +137 -9
- sqlmesh/core/signal.py +64 -1
- sqlmesh/core/snapshot/__init__.py +2 -0
- sqlmesh/core/snapshot/definition.py +146 -34
- sqlmesh/core/snapshot/evaluator.py +689 -124
- sqlmesh/core/state_sync/__init__.py +0 -1
- sqlmesh/core/state_sync/base.py +55 -33
- sqlmesh/core/state_sync/cache.py +12 -7
- sqlmesh/core/state_sync/common.py +216 -111
- sqlmesh/core/state_sync/db/environment.py +6 -4
- sqlmesh/core/state_sync/db/facade.py +42 -24
- sqlmesh/core/state_sync/db/interval.py +27 -7
- sqlmesh/core/state_sync/db/migrator.py +34 -16
- sqlmesh/core/state_sync/db/snapshot.py +177 -169
- sqlmesh/core/table_diff.py +2 -2
- sqlmesh/core/test/context.py +2 -0
- sqlmesh/core/test/definition.py +14 -9
- sqlmesh/dbt/adapter.py +22 -16
- sqlmesh/dbt/basemodel.py +75 -56
- sqlmesh/dbt/builtin.py +116 -12
- sqlmesh/dbt/column.py +17 -5
- sqlmesh/dbt/common.py +19 -5
- sqlmesh/dbt/context.py +14 -1
- sqlmesh/dbt/loader.py +61 -9
- sqlmesh/dbt/manifest.py +174 -16
- sqlmesh/dbt/model.py +183 -85
- sqlmesh/dbt/package.py +16 -1
- sqlmesh/dbt/profile.py +3 -3
- sqlmesh/dbt/project.py +12 -7
- sqlmesh/dbt/seed.py +6 -1
- sqlmesh/dbt/source.py +13 -1
- sqlmesh/dbt/target.py +25 -6
- sqlmesh/dbt/test.py +36 -5
- sqlmesh/migrations/v0000_baseline.py +95 -0
- sqlmesh/migrations/v0061_mysql_fix_blob_text_type.py +5 -7
- sqlmesh/migrations/v0062_add_model_gateway.py +5 -1
- sqlmesh/migrations/v0063_change_signals.py +5 -3
- sqlmesh/migrations/v0064_join_when_matched_strings.py +5 -3
- sqlmesh/migrations/v0065_add_model_optimize.py +5 -1
- sqlmesh/migrations/v0066_add_auto_restatements.py +8 -3
- sqlmesh/migrations/v0067_add_tsql_date_full_precision.py +5 -1
- sqlmesh/migrations/v0068_include_unrendered_query_in_metadata_hash.py +5 -1
- sqlmesh/migrations/v0069_update_dev_table_suffix.py +5 -3
- sqlmesh/migrations/v0070_include_grains_in_metadata_hash.py +5 -1
- sqlmesh/migrations/v0071_add_dev_version_to_intervals.py +9 -5
- sqlmesh/migrations/v0072_add_environment_statements.py +5 -3
- sqlmesh/migrations/v0073_remove_symbolic_disable_restatement.py +5 -3
- sqlmesh/migrations/v0074_add_partition_by_time_column_property.py +5 -1
- sqlmesh/migrations/v0075_remove_validate_query.py +5 -3
- sqlmesh/migrations/v0076_add_cron_tz.py +5 -1
- sqlmesh/migrations/v0077_fix_column_type_hash_calculation.py +5 -1
- sqlmesh/migrations/v0078_warn_if_non_migratable_python_env.py +5 -3
- sqlmesh/migrations/v0079_add_gateway_managed_property.py +10 -5
- sqlmesh/migrations/v0080_add_batch_size_to_scd_type_2_models.py +5 -1
- sqlmesh/migrations/v0081_update_partitioned_by.py +5 -3
- sqlmesh/migrations/v0082_warn_if_incorrectly_duplicated_statements.py +5 -3
- sqlmesh/migrations/v0083_use_sql_for_scd_time_data_type_data_hash.py +5 -1
- sqlmesh/migrations/v0084_normalize_quote_when_matched_and_merge_filter.py +5 -1
- sqlmesh/migrations/v0085_deterministic_repr.py +5 -3
- sqlmesh/migrations/v0086_check_deterministic_bug.py +5 -3
- sqlmesh/migrations/v0087_normalize_blueprint_variables.py +5 -3
- sqlmesh/migrations/v0088_warn_about_variable_python_env_diffs.py +5 -3
- sqlmesh/migrations/v0089_add_virtual_environment_mode.py +5 -1
- sqlmesh/migrations/v0090_add_forward_only_column.py +9 -5
- sqlmesh/migrations/v0091_on_additive_change.py +5 -1
- sqlmesh/migrations/v0092_warn_about_dbt_data_type_diff.py +5 -3
- sqlmesh/migrations/v0093_use_raw_sql_in_fingerprint.py +5 -1
- sqlmesh/migrations/v0094_add_dev_version_and_fingerprint_columns.py +123 -0
- sqlmesh/migrations/v0095_warn_about_dbt_raw_sql_diff.py +49 -0
- sqlmesh/migrations/v0096_remove_plan_dags_table.py +13 -0
- sqlmesh/migrations/v0097_add_dbt_name_in_node.py +9 -0
- sqlmesh/migrations/{v0060_move_audits_to_model.py → v0098_add_dbt_node_info_in_node.py} +33 -16
- sqlmesh/migrations/v0099_add_last_altered_to_intervals.py +25 -0
- sqlmesh/migrations/v0100_add_grants_and_grants_target_layer.py +9 -0
- sqlmesh/utils/__init__.py +8 -1
- sqlmesh/utils/cache.py +5 -1
- sqlmesh/utils/connection_pool.py +2 -1
- sqlmesh/utils/dag.py +65 -10
- sqlmesh/utils/date.py +8 -1
- sqlmesh/utils/errors.py +8 -0
- sqlmesh/utils/jinja.py +54 -4
- sqlmesh/utils/pydantic.py +6 -6
- sqlmesh/utils/windows.py +13 -3
- {sqlmesh-0.213.1.dev1.dist-info → sqlmesh-0.227.2.dev4.dist-info}/METADATA +7 -10
- sqlmesh-0.227.2.dev4.dist-info/RECORD +370 -0
- sqlmesh_dbt/cli.py +70 -7
- sqlmesh_dbt/console.py +14 -6
- sqlmesh_dbt/operations.py +103 -24
- sqlmesh_dbt/selectors.py +39 -1
- web/client/dist/assets/{Audits-Ucsx1GzF.js → Audits-CBiYyyx-.js} +1 -1
- web/client/dist/assets/{Banner-BWDzvavM.js → Banner-DSRbUlO5.js} +1 -1
- web/client/dist/assets/{ChevronDownIcon-D2VL13Ah.js → ChevronDownIcon-MK_nrjD_.js} +1 -1
- web/client/dist/assets/{ChevronRightIcon-DWGYbf1l.js → ChevronRightIcon-CLWtT22Q.js} +1 -1
- web/client/dist/assets/{Content-DdHDZM3I.js → Content-BNuGZN5l.js} +1 -1
- web/client/dist/assets/{Content-Bikfy8fh.js → Content-CSHJyW0n.js} +1 -1
- web/client/dist/assets/{Data-CzAJH7rW.js → Data-C1oRDbLx.js} +1 -1
- web/client/dist/assets/{DataCatalog-BJF11g8f.js → DataCatalog-HXyX2-_j.js} +1 -1
- web/client/dist/assets/{Editor-s0SBpV2y.js → Editor-BDyfpUuw.js} +1 -1
- web/client/dist/assets/{Editor-DgLhgKnm.js → Editor-D0jNItwC.js} +1 -1
- web/client/dist/assets/{Errors-D0m0O1d3.js → Errors-BfuFLcPi.js} +1 -1
- web/client/dist/assets/{FileExplorer-CEv0vXkt.js → FileExplorer-BR9IE3he.js} +1 -1
- web/client/dist/assets/{Footer-BwzXn8Ew.js → Footer-CgBEtiAh.js} +1 -1
- web/client/dist/assets/{Header-6heDkEqG.js → Header-DSqR6nSO.js} +1 -1
- web/client/dist/assets/{Input-obuJsD6k.js → Input-B-oZ6fGO.js} +1 -1
- web/client/dist/assets/Lineage-DYQVwDbD.js +1 -0
- web/client/dist/assets/{ListboxShow-HM9_qyrt.js → ListboxShow-BE5-xevs.js} +1 -1
- web/client/dist/assets/{ModelLineage-zWdKo0U2.js → ModelLineage-DkIFAYo4.js} +1 -1
- web/client/dist/assets/{Models-Bcu66SRz.js → Models-D5dWr8RB.js} +1 -1
- web/client/dist/assets/{Page-BWEEQfIt.js → Page-C-XfU5BR.js} +1 -1
- web/client/dist/assets/{Plan-C4gXCqlf.js → Plan-ZEuTINBq.js} +1 -1
- web/client/dist/assets/{PlusCircleIcon-CVDO651q.js → PlusCircleIcon-DVXAHG8_.js} +1 -1
- web/client/dist/assets/{ReportErrors-BT6xFwAr.js → ReportErrors-B7FEPzMB.js} +1 -1
- web/client/dist/assets/{Root-ryJoBK4h.js → Root-8aZyhPxF.js} +1 -1
- web/client/dist/assets/{SearchList-DB04sPb9.js → SearchList-W_iT2G82.js} +1 -1
- web/client/dist/assets/{SelectEnvironment-CUYcXUu6.js → SelectEnvironment-C65jALmO.js} +1 -1
- web/client/dist/assets/{SourceList-Doo_9ZGp.js → SourceList-DSLO6nVJ.js} +1 -1
- web/client/dist/assets/{SourceListItem-D5Mj7Dly.js → SourceListItem-BHt8d9-I.js} +1 -1
- web/client/dist/assets/{SplitPane-qHmkD1qy.js → SplitPane-CViaZmw6.js} +1 -1
- web/client/dist/assets/{Tests-DH1Z74ML.js → Tests-DhaVt5t1.js} +1 -1
- web/client/dist/assets/{Welcome-DqUJUNMF.js → Welcome-DvpjH-_4.js} +1 -1
- web/client/dist/assets/context-BctCsyGb.js +71 -0
- web/client/dist/assets/{context-Dr54UHLi.js → context-DFNeGsFF.js} +1 -1
- web/client/dist/assets/{editor-DYIP1yQ4.js → editor-CcO28cqd.js} +1 -1
- web/client/dist/assets/{file-DarlIDVi.js → file-CvJN3aZO.js} +1 -1
- web/client/dist/assets/{floating-ui.react-dom-BH3TFvkM.js → floating-ui.react-dom-CjE-JNW1.js} +1 -1
- web/client/dist/assets/{help-Bl8wqaQc.js → help-DuPhjipa.js} +1 -1
- web/client/dist/assets/{index-D1sR7wpN.js → index-C-dJH7yZ.js} +1 -1
- web/client/dist/assets/{index-O3mjYpnE.js → index-Dj0i1-CA.js} +2 -2
- web/client/dist/assets/{plan-CehRrJUG.js → plan-BTRSbjKn.js} +1 -1
- web/client/dist/assets/{popover-CqgMRE0G.js → popover-_Sf0yvOI.js} +1 -1
- web/client/dist/assets/{project-6gxepOhm.js → project-BvSOI8MY.js} +1 -1
- web/client/dist/index.html +1 -1
- sqlmesh/integrations/llm.py +0 -56
- sqlmesh/migrations/v0001_init.py +0 -60
- sqlmesh/migrations/v0002_remove_identify.py +0 -5
- sqlmesh/migrations/v0003_move_batch_size.py +0 -34
- sqlmesh/migrations/v0004_environmnent_add_finalized_at.py +0 -23
- sqlmesh/migrations/v0005_create_seed_table.py +0 -24
- sqlmesh/migrations/v0006_change_seed_hash.py +0 -5
- sqlmesh/migrations/v0007_env_table_info_to_kind.py +0 -99
- sqlmesh/migrations/v0008_create_intervals_table.py +0 -38
- sqlmesh/migrations/v0009_remove_pre_post_hooks.py +0 -62
- sqlmesh/migrations/v0010_seed_hash_batch_size.py +0 -5
- sqlmesh/migrations/v0011_add_model_kind_name.py +0 -63
- sqlmesh/migrations/v0012_update_jinja_expressions.py +0 -86
- sqlmesh/migrations/v0013_serde_using_model_dialects.py +0 -87
- sqlmesh/migrations/v0014_fix_dev_intervals.py +0 -14
- sqlmesh/migrations/v0015_environment_add_promoted_snapshot_ids.py +0 -26
- sqlmesh/migrations/v0016_fix_windows_path.py +0 -59
- sqlmesh/migrations/v0017_fix_windows_seed_path.py +0 -55
- sqlmesh/migrations/v0018_rename_snapshot_model_to_node.py +0 -53
- sqlmesh/migrations/v0019_add_env_suffix_target.py +0 -28
- sqlmesh/migrations/v0020_remove_redundant_attributes_from_dbt_models.py +0 -80
- sqlmesh/migrations/v0021_fix_table_properties.py +0 -62
- sqlmesh/migrations/v0022_move_project_to_model.py +0 -54
- sqlmesh/migrations/v0023_fix_added_models_with_forward_only_parents.py +0 -65
- sqlmesh/migrations/v0024_replace_model_kind_name_enum_with_value.py +0 -55
- sqlmesh/migrations/v0025_fix_intervals_and_missing_change_category.py +0 -117
- sqlmesh/migrations/v0026_remove_dialect_from_seed.py +0 -55
- sqlmesh/migrations/v0027_minute_interval_to_five.py +0 -57
- sqlmesh/migrations/v0028_add_plan_dags_table.py +0 -29
- sqlmesh/migrations/v0029_generate_schema_types_using_dialect.py +0 -69
- sqlmesh/migrations/v0030_update_unrestorable_snapshots.py +0 -65
- sqlmesh/migrations/v0031_remove_dbt_target_fields.py +0 -65
- sqlmesh/migrations/v0032_add_sqlmesh_version.py +0 -25
- sqlmesh/migrations/v0033_mysql_fix_blob_text_type.py +0 -45
- sqlmesh/migrations/v0034_add_default_catalog.py +0 -367
- sqlmesh/migrations/v0035_add_catalog_name_override.py +0 -22
- sqlmesh/migrations/v0036_delete_plan_dags_bug_fix.py +0 -14
- sqlmesh/migrations/v0037_remove_dbt_is_incremental_macro.py +0 -61
- sqlmesh/migrations/v0038_add_expiration_ts_to_snapshot.py +0 -73
- sqlmesh/migrations/v0039_include_environment_in_plan_dag_spec.py +0 -68
- sqlmesh/migrations/v0040_add_previous_finalized_snapshots.py +0 -26
- sqlmesh/migrations/v0041_remove_hash_raw_query_attribute.py +0 -59
- sqlmesh/migrations/v0042_trim_indirect_versions.py +0 -66
- sqlmesh/migrations/v0043_fix_remove_obsolete_attributes_in_plan_dags.py +0 -61
- sqlmesh/migrations/v0044_quote_identifiers_in_model_attributes.py +0 -5
- sqlmesh/migrations/v0045_move_gateway_variable.py +0 -70
- sqlmesh/migrations/v0046_add_batch_concurrency.py +0 -8
- sqlmesh/migrations/v0047_change_scd_string_to_column.py +0 -5
- sqlmesh/migrations/v0048_drop_indirect_versions.py +0 -59
- sqlmesh/migrations/v0049_replace_identifier_with_version_in_seeds_table.py +0 -57
- sqlmesh/migrations/v0050_drop_seeds_table.py +0 -11
- sqlmesh/migrations/v0051_rename_column_descriptions.py +0 -65
- sqlmesh/migrations/v0052_add_normalize_name_in_environment_naming_info.py +0 -28
- sqlmesh/migrations/v0053_custom_model_kind_extra_attributes.py +0 -5
- sqlmesh/migrations/v0054_fix_trailing_comments.py +0 -5
- sqlmesh/migrations/v0055_add_updated_ts_unpaused_ts_ttl_ms_unrestorable_to_snapshot.py +0 -132
- sqlmesh/migrations/v0056_restore_table_indexes.py +0 -118
- sqlmesh/migrations/v0057_add_table_format.py +0 -5
- sqlmesh/migrations/v0058_add_requirements.py +0 -26
- sqlmesh/migrations/v0059_add_physical_version.py +0 -5
- sqlmesh-0.213.1.dev1.dist-info/RECORD +0 -421
- web/client/dist/assets/Lineage-D0Hgdz2v.js +0 -1
- web/client/dist/assets/context-DgX0fp2E.js +0 -68
- {sqlmesh-0.213.1.dev1.dist-info → sqlmesh-0.227.2.dev4.dist-info}/WHEEL +0 -0
- {sqlmesh-0.213.1.dev1.dist-info → sqlmesh-0.227.2.dev4.dist-info}/entry_points.txt +0 -0
- {sqlmesh-0.213.1.dev1.dist-info → sqlmesh-0.227.2.dev4.dist-info}/licenses/LICENSE +0 -0
- {sqlmesh-0.213.1.dev1.dist-info → sqlmesh-0.227.2.dev4.dist-info}/top_level.txt +0 -0
sqlmesh/core/signal.py
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import typing as t
|
|
4
4
|
from sqlmesh.utils import UniqueKeyDict, registry_decorator
|
|
5
|
+
from sqlmesh.utils.errors import MissingSourceError
|
|
6
|
+
|
|
7
|
+
if t.TYPE_CHECKING:
|
|
8
|
+
from sqlmesh.core.context import ExecutionContext
|
|
9
|
+
from sqlmesh.core.snapshot.definition import Snapshot
|
|
10
|
+
from sqlmesh.utils.date import DatetimeRanges
|
|
11
|
+
from sqlmesh.core.snapshot.definition import DeployabilityIndex
|
|
5
12
|
|
|
6
13
|
|
|
7
14
|
class signal(registry_decorator):
|
|
@@ -33,3 +40,59 @@ class signal(registry_decorator):
|
|
|
33
40
|
|
|
34
41
|
|
|
35
42
|
SignalRegistry = UniqueKeyDict[str, signal]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@signal()
|
|
46
|
+
def freshness(
|
|
47
|
+
batch: DatetimeRanges,
|
|
48
|
+
snapshot: Snapshot,
|
|
49
|
+
context: ExecutionContext,
|
|
50
|
+
) -> bool:
|
|
51
|
+
"""
|
|
52
|
+
Implements model freshness as a signal, i.e it considers this model to be fresh if:
|
|
53
|
+
- Any upstream SQLMesh model has available intervals to compute i.e is fresh
|
|
54
|
+
- Any upstream external model has been altered since the last time the model was evaluated
|
|
55
|
+
"""
|
|
56
|
+
adapter = context.engine_adapter
|
|
57
|
+
if context.is_restatement or not adapter.SUPPORTS_METADATA_TABLE_LAST_MODIFIED_TS:
|
|
58
|
+
return True
|
|
59
|
+
|
|
60
|
+
deployability_index = context.deployability_index or DeployabilityIndex.all_deployable()
|
|
61
|
+
|
|
62
|
+
last_altered_ts = (
|
|
63
|
+
snapshot.last_altered_ts
|
|
64
|
+
if deployability_index.is_deployable(snapshot)
|
|
65
|
+
else snapshot.dev_last_altered_ts
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
if not last_altered_ts:
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
parent_snapshots = {context.snapshots[p.name] for p in snapshot.parents}
|
|
72
|
+
|
|
73
|
+
upstream_parent_snapshots = {p for p in parent_snapshots if not p.is_external}
|
|
74
|
+
external_parents = snapshot.node.depends_on - {p.name for p in upstream_parent_snapshots}
|
|
75
|
+
|
|
76
|
+
if context.parent_intervals:
|
|
77
|
+
# At least one upstream sqlmesh model has intervals to compute (i.e is fresh),
|
|
78
|
+
# so the current model is considered fresh too
|
|
79
|
+
return True
|
|
80
|
+
|
|
81
|
+
if external_parents:
|
|
82
|
+
external_last_altered_timestamps = adapter.get_table_last_modified_ts(
|
|
83
|
+
list(external_parents)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if len(external_last_altered_timestamps) != len(external_parents):
|
|
87
|
+
raise MissingSourceError(
|
|
88
|
+
f"Expected {len(external_parents)} sources to be present, but got {len(external_last_altered_timestamps)}."
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Finding new data means that the upstream depedencies have been altered
|
|
92
|
+
# since the last time the model was evaluated
|
|
93
|
+
return any(
|
|
94
|
+
external_last_altered_ts > last_altered_ts
|
|
95
|
+
for external_last_altered_ts in external_last_altered_timestamps
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return False
|
|
@@ -4,12 +4,14 @@ from sqlmesh.core.snapshot.definition import (
|
|
|
4
4
|
Node as Node,
|
|
5
5
|
QualifiedViewName as QualifiedViewName,
|
|
6
6
|
Snapshot as Snapshot,
|
|
7
|
+
SnapshotIdAndVersion as SnapshotIdAndVersion,
|
|
7
8
|
SnapshotChangeCategory as SnapshotChangeCategory,
|
|
8
9
|
SnapshotDataVersion as SnapshotDataVersion,
|
|
9
10
|
SnapshotFingerprint as SnapshotFingerprint,
|
|
10
11
|
SnapshotId as SnapshotId,
|
|
11
12
|
SnapshotIdBatch as SnapshotIdBatch,
|
|
12
13
|
SnapshotIdLike as SnapshotIdLike,
|
|
14
|
+
SnapshotIdAndVersionLike as SnapshotIdAndVersionLike,
|
|
13
15
|
SnapshotInfoLike as SnapshotInfoLike,
|
|
14
16
|
SnapshotIntervals as SnapshotIntervals,
|
|
15
17
|
SnapshotNameVersion as SnapshotNameVersion,
|
|
@@ -185,6 +185,8 @@ class SnapshotIntervals(PydanticModel):
|
|
|
185
185
|
intervals: Intervals = []
|
|
186
186
|
dev_intervals: Intervals = []
|
|
187
187
|
pending_restatement_intervals: Intervals = []
|
|
188
|
+
last_altered_ts: t.Optional[int] = None
|
|
189
|
+
dev_last_altered_ts: t.Optional[int] = None
|
|
188
190
|
|
|
189
191
|
@property
|
|
190
192
|
def snapshot_id(self) -> t.Optional[SnapshotId]:
|
|
@@ -205,6 +207,12 @@ class SnapshotIntervals(PydanticModel):
|
|
|
205
207
|
def add_pending_restatement_interval(self, start: int, end: int) -> None:
|
|
206
208
|
self._add_interval(start, end, "pending_restatement_intervals")
|
|
207
209
|
|
|
210
|
+
def update_last_altered_ts(self, last_altered_ts: t.Optional[int]) -> None:
|
|
211
|
+
self._update_last_altered_ts(last_altered_ts, "last_altered_ts")
|
|
212
|
+
|
|
213
|
+
def update_dev_last_altered_ts(self, last_altered_ts: t.Optional[int]) -> None:
|
|
214
|
+
self._update_last_altered_ts(last_altered_ts, "dev_last_altered_ts")
|
|
215
|
+
|
|
208
216
|
def remove_interval(self, start: int, end: int) -> None:
|
|
209
217
|
self._remove_interval(start, end, "intervals")
|
|
210
218
|
|
|
@@ -224,6 +232,13 @@ class SnapshotIntervals(PydanticModel):
|
|
|
224
232
|
target_intervals = merge_intervals([*target_intervals, (start, end)])
|
|
225
233
|
setattr(self, interval_attr, target_intervals)
|
|
226
234
|
|
|
235
|
+
def _update_last_altered_ts(
|
|
236
|
+
self, last_altered_ts: t.Optional[int], last_altered_attr: str
|
|
237
|
+
) -> None:
|
|
238
|
+
if last_altered_ts:
|
|
239
|
+
existing_last_altered_ts = getattr(self, last_altered_attr)
|
|
240
|
+
setattr(self, last_altered_attr, max(existing_last_altered_ts or 0, last_altered_ts))
|
|
241
|
+
|
|
227
242
|
def _remove_interval(self, start: int, end: int, interval_attr: str) -> None:
|
|
228
243
|
target_intervals = getattr(self, interval_attr)
|
|
229
244
|
target_intervals = remove_interval(target_intervals, start, end)
|
|
@@ -587,6 +602,67 @@ class SnapshotTableInfo(PydanticModel, SnapshotInfoMixin, frozen=True):
|
|
|
587
602
|
"""Returns the name and version of the snapshot."""
|
|
588
603
|
return SnapshotNameVersion(name=self.name, version=self.version)
|
|
589
604
|
|
|
605
|
+
@property
|
|
606
|
+
def id_and_version(self) -> SnapshotIdAndVersion:
|
|
607
|
+
return SnapshotIdAndVersion(
|
|
608
|
+
name=self.name,
|
|
609
|
+
kind_name=self.kind_name,
|
|
610
|
+
identifier=self.identifier,
|
|
611
|
+
version=self.version,
|
|
612
|
+
dev_version=self.dev_version,
|
|
613
|
+
fingerprint=self.fingerprint,
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
class SnapshotIdAndVersion(PydanticModel, ModelKindMixin):
|
|
618
|
+
"""A stripped down version of a snapshot that is used in situations where we want to fetch the main fields of the snapshots table
|
|
619
|
+
without the overhead of parsing the full snapshot payload and fetching intervals.
|
|
620
|
+
"""
|
|
621
|
+
|
|
622
|
+
name: str
|
|
623
|
+
version: str
|
|
624
|
+
kind_name_: t.Optional[ModelKindName] = Field(default=None, alias="kind_name")
|
|
625
|
+
dev_version_: t.Optional[str] = Field(alias="dev_version")
|
|
626
|
+
identifier: str
|
|
627
|
+
fingerprint_: t.Union[str, SnapshotFingerprint] = Field(alias="fingerprint")
|
|
628
|
+
|
|
629
|
+
@property
|
|
630
|
+
def snapshot_id(self) -> SnapshotId:
|
|
631
|
+
return SnapshotId(name=self.name, identifier=self.identifier)
|
|
632
|
+
|
|
633
|
+
@property
|
|
634
|
+
def id_and_version(self) -> SnapshotIdAndVersion:
|
|
635
|
+
return self
|
|
636
|
+
|
|
637
|
+
@property
|
|
638
|
+
def name_version(self) -> SnapshotNameVersion:
|
|
639
|
+
return SnapshotNameVersion(name=self.name, version=self.version)
|
|
640
|
+
|
|
641
|
+
@property
|
|
642
|
+
def fingerprint(self) -> SnapshotFingerprint:
|
|
643
|
+
value = self.fingerprint_
|
|
644
|
+
if isinstance(value, str):
|
|
645
|
+
self.fingerprint_ = value = SnapshotFingerprint.parse_raw(value)
|
|
646
|
+
return value
|
|
647
|
+
|
|
648
|
+
@property
|
|
649
|
+
def dev_version(self) -> str:
|
|
650
|
+
return self.dev_version_ or self.fingerprint.to_version()
|
|
651
|
+
|
|
652
|
+
@property
|
|
653
|
+
def model_kind_name(self) -> t.Optional[ModelKindName]:
|
|
654
|
+
return self.kind_name_
|
|
655
|
+
|
|
656
|
+
def display_name(
|
|
657
|
+
self,
|
|
658
|
+
environment_naming_info: EnvironmentNamingInfo,
|
|
659
|
+
default_catalog: t.Optional[str],
|
|
660
|
+
dialect: DialectType = None,
|
|
661
|
+
) -> str:
|
|
662
|
+
return model_display_name(
|
|
663
|
+
self.name, environment_naming_info, default_catalog, dialect=dialect
|
|
664
|
+
)
|
|
665
|
+
|
|
590
666
|
|
|
591
667
|
class Snapshot(PydanticModel, SnapshotInfoMixin):
|
|
592
668
|
"""A snapshot represents a node at a certain point in time.
|
|
@@ -652,6 +728,10 @@ class Snapshot(PydanticModel, SnapshotInfoMixin):
|
|
|
652
728
|
dev_table_suffix: str = "dev"
|
|
653
729
|
table_naming_convention: TableNamingConvention = TableNamingConvention.default
|
|
654
730
|
forward_only: bool = False
|
|
731
|
+
# Physical table last modified timestamp, not to be confused with the "updated_ts" field
|
|
732
|
+
# which is for the snapshot record itself
|
|
733
|
+
last_altered_ts: t.Optional[int] = None
|
|
734
|
+
dev_last_altered_ts: t.Optional[int] = None
|
|
655
735
|
|
|
656
736
|
@field_validator("ttl")
|
|
657
737
|
@classmethod
|
|
@@ -690,6 +770,7 @@ class Snapshot(PydanticModel, SnapshotInfoMixin):
|
|
|
690
770
|
)
|
|
691
771
|
for interval in snapshot_intervals:
|
|
692
772
|
snapshot.merge_intervals(interval)
|
|
773
|
+
|
|
693
774
|
result.append(snapshot)
|
|
694
775
|
|
|
695
776
|
return result
|
|
@@ -896,12 +977,20 @@ class Snapshot(PydanticModel, SnapshotInfoMixin):
|
|
|
896
977
|
if not apply_effective_from or end <= effective_from_ts:
|
|
897
978
|
self.add_interval(start, end)
|
|
898
979
|
|
|
980
|
+
if other.last_altered_ts:
|
|
981
|
+
self.last_altered_ts = max(self.last_altered_ts or 0, other.last_altered_ts)
|
|
982
|
+
|
|
899
983
|
if self.dev_version == other.dev_version:
|
|
900
984
|
# Merge dev intervals if the dev versions match which would mean
|
|
901
985
|
# that this and the other snapshot are pointing to the same dev table.
|
|
902
986
|
for start, end in other.dev_intervals:
|
|
903
987
|
self.add_interval(start, end, is_dev=True)
|
|
904
988
|
|
|
989
|
+
if other.dev_last_altered_ts:
|
|
990
|
+
self.dev_last_altered_ts = max(
|
|
991
|
+
self.dev_last_altered_ts or 0, other.dev_last_altered_ts
|
|
992
|
+
)
|
|
993
|
+
|
|
905
994
|
self.pending_restatement_intervals = merge_intervals(
|
|
906
995
|
[*self.pending_restatement_intervals, *other.pending_restatement_intervals]
|
|
907
996
|
)
|
|
@@ -1020,6 +1109,7 @@ class Snapshot(PydanticModel, SnapshotInfoMixin):
|
|
|
1020
1109
|
python_env=signals.python_env,
|
|
1021
1110
|
dialect=self.model.dialect,
|
|
1022
1111
|
path=self.model._path,
|
|
1112
|
+
snapshot=self,
|
|
1023
1113
|
kwargs=kwargs,
|
|
1024
1114
|
)
|
|
1025
1115
|
except SQLMeshError as e:
|
|
@@ -1393,6 +1483,10 @@ class Snapshot(PydanticModel, SnapshotInfoMixin):
|
|
|
1393
1483
|
"""Returns the name and version of the snapshot."""
|
|
1394
1484
|
return SnapshotNameVersion(name=self.name, version=self.version)
|
|
1395
1485
|
|
|
1486
|
+
@property
|
|
1487
|
+
def id_and_version(self) -> SnapshotIdAndVersion:
|
|
1488
|
+
return self.table_info.id_and_version
|
|
1489
|
+
|
|
1396
1490
|
@property
|
|
1397
1491
|
def disable_restatement(self) -> bool:
|
|
1398
1492
|
"""Is restatement disabled for the node"""
|
|
@@ -1412,19 +1506,19 @@ class Snapshot(PydanticModel, SnapshotInfoMixin):
|
|
|
1412
1506
|
check_categorical_relative_expression=False,
|
|
1413
1507
|
)
|
|
1414
1508
|
|
|
1509
|
+
@property
|
|
1510
|
+
def supports_schema_migration_in_prod(self) -> bool:
|
|
1511
|
+
"""Returns whether or not this snapshot supports schema migration when deployed to production."""
|
|
1512
|
+
return self.is_paused and self.is_model and not self.is_symbolic and not self.is_seed
|
|
1513
|
+
|
|
1415
1514
|
@property
|
|
1416
1515
|
def requires_schema_migration_in_prod(self) -> bool:
|
|
1417
1516
|
"""Returns whether or not this snapshot requires a schema migration when deployed to production."""
|
|
1418
|
-
return (
|
|
1419
|
-
self.
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
(self.previous_version and self.previous_version.version == self.version)
|
|
1424
|
-
or self.model.forward_only
|
|
1425
|
-
or bool(self.model.physical_version)
|
|
1426
|
-
or not self.virtual_environment_mode.is_full
|
|
1427
|
-
)
|
|
1517
|
+
return self.supports_schema_migration_in_prod and (
|
|
1518
|
+
(self.previous_version and self.previous_version.version == self.version)
|
|
1519
|
+
or self.model.forward_only
|
|
1520
|
+
or bool(self.model.physical_version)
|
|
1521
|
+
or not self.virtual_environment_mode.is_full
|
|
1428
1522
|
)
|
|
1429
1523
|
|
|
1430
1524
|
@property
|
|
@@ -1463,9 +1557,12 @@ class SnapshotTableCleanupTask(PydanticModel):
|
|
|
1463
1557
|
dev_table_only: bool
|
|
1464
1558
|
|
|
1465
1559
|
|
|
1466
|
-
SnapshotIdLike = t.Union[SnapshotId, SnapshotTableInfo, Snapshot]
|
|
1560
|
+
SnapshotIdLike = t.Union[SnapshotId, SnapshotIdAndVersion, SnapshotTableInfo, Snapshot]
|
|
1561
|
+
SnapshotIdAndVersionLike = t.Union[SnapshotIdAndVersion, SnapshotTableInfo, Snapshot]
|
|
1467
1562
|
SnapshotInfoLike = t.Union[SnapshotTableInfo, Snapshot]
|
|
1468
|
-
SnapshotNameVersionLike = t.Union[
|
|
1563
|
+
SnapshotNameVersionLike = t.Union[
|
|
1564
|
+
SnapshotNameVersion, SnapshotTableInfo, SnapshotIdAndVersion, Snapshot
|
|
1565
|
+
]
|
|
1469
1566
|
|
|
1470
1567
|
|
|
1471
1568
|
class DeployabilityIndex(PydanticModel, frozen=True):
|
|
@@ -1611,6 +1708,7 @@ class DeployabilityIndex(PydanticModel, frozen=True):
|
|
|
1611
1708
|
snapshot.is_valid_start(start, snapshot_start) if start is not None else True
|
|
1612
1709
|
)
|
|
1613
1710
|
|
|
1711
|
+
children_deployable = is_valid_start and not has_auto_restatement
|
|
1614
1712
|
if (
|
|
1615
1713
|
snapshot.is_forward_only
|
|
1616
1714
|
or snapshot.is_indirect_non_breaking
|
|
@@ -1627,15 +1725,9 @@ class DeployabilityIndex(PydanticModel, frozen=True):
|
|
|
1627
1725
|
):
|
|
1628
1726
|
# This snapshot represents what's currently deployed in prod.
|
|
1629
1727
|
representative_shared_version_ids.add(node)
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
is_valid_start
|
|
1634
|
-
and not (
|
|
1635
|
-
snapshot.is_paused and (snapshot.is_forward_only or is_forward_only_model)
|
|
1636
|
-
)
|
|
1637
|
-
and not has_auto_restatement
|
|
1638
|
-
)
|
|
1728
|
+
else:
|
|
1729
|
+
# If the parent is not representative then its children can't be deployable.
|
|
1730
|
+
children_deployable = False
|
|
1639
1731
|
else:
|
|
1640
1732
|
children_deployable = False
|
|
1641
1733
|
if not snapshots[node].is_paused:
|
|
@@ -1735,7 +1827,19 @@ def display_name(
|
|
|
1735
1827
|
"""
|
|
1736
1828
|
if snapshot_info_like.is_audit:
|
|
1737
1829
|
return snapshot_info_like.name
|
|
1738
|
-
|
|
1830
|
+
|
|
1831
|
+
return model_display_name(
|
|
1832
|
+
snapshot_info_like.name, environment_naming_info, default_catalog, dialect
|
|
1833
|
+
)
|
|
1834
|
+
|
|
1835
|
+
|
|
1836
|
+
def model_display_name(
|
|
1837
|
+
node_name: str,
|
|
1838
|
+
environment_naming_info: EnvironmentNamingInfo,
|
|
1839
|
+
default_catalog: t.Optional[str],
|
|
1840
|
+
dialect: DialectType = None,
|
|
1841
|
+
) -> str:
|
|
1842
|
+
view_name = exp.to_table(node_name)
|
|
1739
1843
|
|
|
1740
1844
|
catalog = (
|
|
1741
1845
|
None
|
|
@@ -1977,16 +2081,20 @@ def missing_intervals(
|
|
|
1977
2081
|
continue
|
|
1978
2082
|
snapshot_end_date = existing_interval_end
|
|
1979
2083
|
|
|
2084
|
+
snapshot_start_date = max(
|
|
2085
|
+
to_datetime(snapshot_start_date),
|
|
2086
|
+
to_datetime(start_date(snapshot, snapshots, cache, relative_to=snapshot_end_date)),
|
|
2087
|
+
)
|
|
2088
|
+
if snapshot_start_date > to_datetime(snapshot_end_date):
|
|
2089
|
+
continue
|
|
2090
|
+
|
|
1980
2091
|
missing_interval_end_date = snapshot_end_date
|
|
1981
2092
|
node_end_date = snapshot.node.end
|
|
1982
2093
|
if node_end_date and (to_datetime(node_end_date) < to_datetime(snapshot_end_date)):
|
|
1983
2094
|
missing_interval_end_date = node_end_date
|
|
1984
2095
|
|
|
1985
2096
|
intervals = snapshot.missing_intervals(
|
|
1986
|
-
|
|
1987
|
-
to_datetime(snapshot_start_date),
|
|
1988
|
-
to_datetime(start_date(snapshot, snapshots, cache, relative_to=snapshot_end_date)),
|
|
1989
|
-
),
|
|
2097
|
+
snapshot_start_date,
|
|
1990
2098
|
missing_interval_end_date,
|
|
1991
2099
|
execution_time=execution_time,
|
|
1992
2100
|
deployability_index=deployability_index,
|
|
@@ -2191,14 +2299,16 @@ def start_date(
|
|
|
2191
2299
|
if not isinstance(snapshots, dict):
|
|
2192
2300
|
snapshots = {snapshot.snapshot_id: snapshot for snapshot in snapshots}
|
|
2193
2301
|
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
if parent in snapshots
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2302
|
+
parent_starts = [
|
|
2303
|
+
start_date(snapshots[parent], snapshots, cache=cache, relative_to=relative_to)
|
|
2304
|
+
for parent in snapshot.parents
|
|
2305
|
+
if parent in snapshots
|
|
2306
|
+
]
|
|
2307
|
+
earliest = (
|
|
2308
|
+
min(parent_starts)
|
|
2309
|
+
if parent_starts
|
|
2310
|
+
else snapshot.node.cron_prev(snapshot.node.cron_floor(relative_to or now()))
|
|
2311
|
+
)
|
|
2202
2312
|
|
|
2203
2313
|
cache[key] = earliest
|
|
2204
2314
|
return earliest
|
|
@@ -2346,6 +2456,7 @@ def check_ready_intervals(
|
|
|
2346
2456
|
python_env: t.Dict[str, Executable],
|
|
2347
2457
|
dialect: DialectType = None,
|
|
2348
2458
|
path: t.Optional[Path] = None,
|
|
2459
|
+
snapshot: t.Optional[Snapshot] = None,
|
|
2349
2460
|
kwargs: t.Optional[t.Dict] = None,
|
|
2350
2461
|
) -> Intervals:
|
|
2351
2462
|
checked_intervals: Intervals = []
|
|
@@ -2361,6 +2472,7 @@ def check_ready_intervals(
|
|
|
2361
2472
|
provided_args=(batch,),
|
|
2362
2473
|
provided_kwargs=(kwargs or {}),
|
|
2363
2474
|
context=context,
|
|
2475
|
+
snapshot=snapshot,
|
|
2364
2476
|
)
|
|
2365
2477
|
except Exception as ex:
|
|
2366
2478
|
raise SignalEvalError(format_evaluated_code_exception(ex, python_env))
|