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
|
@@ -6,7 +6,6 @@ import logging
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from collections import defaultdict
|
|
8
8
|
from sqlglot import exp
|
|
9
|
-
from pydantic import Field
|
|
10
9
|
|
|
11
10
|
from sqlmesh.core.engine_adapter import EngineAdapter
|
|
12
11
|
from sqlmesh.core.state_sync.db.utils import (
|
|
@@ -15,9 +14,7 @@ from sqlmesh.core.state_sync.db.utils import (
|
|
|
15
14
|
snapshot_id_filter,
|
|
16
15
|
fetchone,
|
|
17
16
|
fetchall,
|
|
18
|
-
create_batches,
|
|
19
17
|
)
|
|
20
|
-
from sqlmesh.core.node import IntervalUnit
|
|
21
18
|
from sqlmesh.core.environment import Environment
|
|
22
19
|
from sqlmesh.core.model import SeedModel, ModelKindName
|
|
23
20
|
from sqlmesh.core.snapshot.cache import SnapshotCache
|
|
@@ -28,13 +25,18 @@ from sqlmesh.core.snapshot import (
|
|
|
28
25
|
SnapshotNameVersion,
|
|
29
26
|
SnapshotInfoLike,
|
|
30
27
|
Snapshot,
|
|
28
|
+
SnapshotIdAndVersion,
|
|
31
29
|
SnapshotId,
|
|
32
30
|
SnapshotFingerprint,
|
|
33
|
-
|
|
31
|
+
)
|
|
32
|
+
from sqlmesh.core.state_sync.common import (
|
|
33
|
+
RowBoundary,
|
|
34
|
+
ExpiredSnapshotBatch,
|
|
35
|
+
ExpiredBatchRange,
|
|
36
|
+
LimitBoundary,
|
|
34
37
|
)
|
|
35
38
|
from sqlmesh.utils.migration import index_text_type, blob_text_type
|
|
36
39
|
from sqlmesh.utils.date import now_timestamp, TimeLike, to_timestamp
|
|
37
|
-
from sqlmesh.utils.pydantic import PydanticModel
|
|
38
40
|
from sqlmesh.utils import unique
|
|
39
41
|
|
|
40
42
|
if t.TYPE_CHECKING:
|
|
@@ -63,6 +65,7 @@ class SnapshotState:
|
|
|
63
65
|
"name": exp.DataType.build(index_type),
|
|
64
66
|
"identifier": exp.DataType.build(index_type),
|
|
65
67
|
"version": exp.DataType.build(index_type),
|
|
68
|
+
"dev_version": exp.DataType.build(index_type),
|
|
66
69
|
"snapshot": exp.DataType.build(blob_type),
|
|
67
70
|
"kind_name": exp.DataType.build("text"),
|
|
68
71
|
"updated_ts": exp.DataType.build("bigint"),
|
|
@@ -70,6 +73,7 @@ class SnapshotState:
|
|
|
70
73
|
"ttl_ms": exp.DataType.build("bigint"),
|
|
71
74
|
"unrestorable": exp.DataType.build("boolean"),
|
|
72
75
|
"forward_only": exp.DataType.build("boolean"),
|
|
76
|
+
"fingerprint": exp.DataType.build(blob_type),
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
self._auto_restatement_columns_to_types = {
|
|
@@ -164,94 +168,136 @@ class SnapshotState:
|
|
|
164
168
|
self,
|
|
165
169
|
environments: t.Iterable[Environment],
|
|
166
170
|
current_ts: int,
|
|
167
|
-
ignore_ttl: bool
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
and are no longer in use within an environment.
|
|
173
|
-
|
|
174
|
-
Returns:
|
|
175
|
-
The set of expired snapshot ids.
|
|
176
|
-
The list of table cleanup tasks.
|
|
177
|
-
"""
|
|
178
|
-
_, cleanup_targets = self._get_expired_snapshots(
|
|
179
|
-
environments=environments,
|
|
180
|
-
current_ts=current_ts,
|
|
181
|
-
ignore_ttl=ignore_ttl,
|
|
171
|
+
ignore_ttl: bool,
|
|
172
|
+
batch_range: ExpiredBatchRange,
|
|
173
|
+
) -> t.Optional[ExpiredSnapshotBatch]:
|
|
174
|
+
expired_query = exp.select("name", "identifier", "version", "updated_ts").from_(
|
|
175
|
+
self.snapshots_table
|
|
182
176
|
)
|
|
183
|
-
return cleanup_targets
|
|
184
|
-
|
|
185
|
-
def _get_expired_snapshots(
|
|
186
|
-
self,
|
|
187
|
-
environments: t.Iterable[Environment],
|
|
188
|
-
current_ts: int,
|
|
189
|
-
ignore_ttl: bool = False,
|
|
190
|
-
) -> t.Tuple[t.Set[SnapshotId], t.List[SnapshotTableCleanupTask]]:
|
|
191
|
-
expired_query = exp.select("name", "identifier", "version").from_(self.snapshots_table)
|
|
192
177
|
|
|
193
178
|
if not ignore_ttl:
|
|
194
179
|
expired_query = expired_query.where(
|
|
195
180
|
(exp.column("updated_ts") + exp.column("ttl_ms")) <= current_ts
|
|
196
181
|
)
|
|
197
182
|
|
|
183
|
+
expired_query = expired_query.where(batch_range.where_filter)
|
|
184
|
+
|
|
185
|
+
promoted_snapshot_ids = {
|
|
186
|
+
snapshot.snapshot_id
|
|
187
|
+
for environment in environments
|
|
188
|
+
for snapshot in (
|
|
189
|
+
environment.snapshots
|
|
190
|
+
if environment.finalized_ts is not None
|
|
191
|
+
# If the environment is not finalized, check both the current snapshots and the previous finalized snapshots
|
|
192
|
+
else [*environment.snapshots, *(environment.previous_finalized_snapshots or [])]
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if promoted_snapshot_ids:
|
|
197
|
+
not_in_conditions = [
|
|
198
|
+
exp.not_(condition)
|
|
199
|
+
for condition in snapshot_id_filter(
|
|
200
|
+
self.engine_adapter,
|
|
201
|
+
promoted_snapshot_ids,
|
|
202
|
+
batch_size=self.SNAPSHOT_BATCH_SIZE,
|
|
203
|
+
)
|
|
204
|
+
]
|
|
205
|
+
expired_query = expired_query.where(exp.and_(*not_in_conditions))
|
|
206
|
+
|
|
207
|
+
expired_query = expired_query.order_by(
|
|
208
|
+
exp.column("updated_ts"), exp.column("name"), exp.column("identifier")
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
if isinstance(batch_range.end, LimitBoundary):
|
|
212
|
+
expired_query = expired_query.limit(batch_range.end.batch_size)
|
|
213
|
+
|
|
214
|
+
rows = fetchall(self.engine_adapter, expired_query)
|
|
215
|
+
|
|
216
|
+
if not rows:
|
|
217
|
+
return None
|
|
218
|
+
|
|
198
219
|
expired_candidates = {
|
|
199
220
|
SnapshotId(name=name, identifier=identifier): SnapshotNameVersion(
|
|
200
221
|
name=name, version=version
|
|
201
222
|
)
|
|
202
|
-
for name, identifier, version in
|
|
223
|
+
for name, identifier, version, _ in rows
|
|
203
224
|
}
|
|
204
225
|
if not expired_candidates:
|
|
205
|
-
return
|
|
226
|
+
return None
|
|
206
227
|
|
|
207
|
-
|
|
208
|
-
snapshot.snapshot_id
|
|
209
|
-
for environment in environments
|
|
210
|
-
for snapshot in environment.snapshots
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
def _is_snapshot_used(snapshot: SharedVersionSnapshot) -> bool:
|
|
228
|
+
def _is_snapshot_used(snapshot: SnapshotIdAndVersion) -> bool:
|
|
214
229
|
return (
|
|
215
230
|
snapshot.snapshot_id in promoted_snapshot_ids
|
|
216
231
|
or snapshot.snapshot_id not in expired_candidates
|
|
217
232
|
)
|
|
218
233
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
234
|
+
# Extract cursor values from last row for pagination
|
|
235
|
+
last_row = rows[-1]
|
|
236
|
+
last_row_boundary = RowBoundary(
|
|
237
|
+
updated_ts=last_row[3],
|
|
238
|
+
name=last_row[0],
|
|
239
|
+
identifier=last_row[1],
|
|
222
240
|
)
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
241
|
+
# The returned batch_range represents the actual range of rows in this batch
|
|
242
|
+
result_batch_range = ExpiredBatchRange(
|
|
243
|
+
start=batch_range.start,
|
|
244
|
+
end=last_row_boundary,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
unique_expired_versions = unique(expired_candidates.values())
|
|
248
|
+
expired_snapshot_ids: t.Set[SnapshotId] = set()
|
|
249
|
+
cleanup_tasks: t.List[SnapshotTableCleanupTask] = []
|
|
250
|
+
|
|
251
|
+
snapshots = self._get_snapshots_with_same_version(unique_expired_versions)
|
|
252
|
+
|
|
253
|
+
snapshots_by_version = defaultdict(set)
|
|
254
|
+
snapshots_by_dev_version = defaultdict(set)
|
|
255
|
+
for s in snapshots:
|
|
256
|
+
snapshots_by_version[(s.name, s.version)].add(s.snapshot_id)
|
|
257
|
+
snapshots_by_dev_version[(s.name, s.dev_version)].add(s.snapshot_id)
|
|
258
|
+
|
|
259
|
+
expired_snapshots = [s for s in snapshots if not _is_snapshot_used(s)]
|
|
260
|
+
all_expired_snapshot_ids = {s.snapshot_id for s in expired_snapshots}
|
|
261
|
+
|
|
262
|
+
cleanup_targets: t.List[t.Tuple[SnapshotId, bool]] = []
|
|
263
|
+
for snapshot in expired_snapshots:
|
|
264
|
+
shared_version_snapshots = snapshots_by_version[(snapshot.name, snapshot.version)]
|
|
265
|
+
shared_version_snapshots.discard(snapshot.snapshot_id)
|
|
266
|
+
|
|
267
|
+
shared_dev_version_snapshots = snapshots_by_dev_version[
|
|
268
|
+
(snapshot.name, snapshot.dev_version)
|
|
269
|
+
]
|
|
270
|
+
shared_dev_version_snapshots.discard(snapshot.snapshot_id)
|
|
271
|
+
|
|
272
|
+
if not shared_dev_version_snapshots:
|
|
273
|
+
dev_table_only = bool(shared_version_snapshots)
|
|
274
|
+
cleanup_targets.append((snapshot.snapshot_id, dev_table_only))
|
|
275
|
+
|
|
276
|
+
snapshot_ids_to_cleanup = [snapshot_id for snapshot_id, _ in cleanup_targets]
|
|
277
|
+
full_snapshots = self._get_snapshots(snapshot_ids_to_cleanup)
|
|
278
|
+
for snapshot_id, dev_table_only in cleanup_targets:
|
|
279
|
+
if snapshot_id in full_snapshots:
|
|
280
|
+
cleanup_tasks.append(
|
|
281
|
+
SnapshotTableCleanupTask(
|
|
282
|
+
snapshot=full_snapshots[snapshot_id].table_info,
|
|
283
|
+
dev_table_only=dev_table_only,
|
|
252
284
|
)
|
|
285
|
+
)
|
|
286
|
+
expired_snapshot_ids.add(snapshot_id)
|
|
287
|
+
all_expired_snapshot_ids.discard(snapshot_id)
|
|
288
|
+
|
|
289
|
+
# Add any remaining expired snapshots that don't require cleanup
|
|
290
|
+
if all_expired_snapshot_ids:
|
|
291
|
+
expired_snapshot_ids.update(all_expired_snapshot_ids)
|
|
292
|
+
|
|
293
|
+
if expired_snapshot_ids or cleanup_tasks:
|
|
294
|
+
return ExpiredSnapshotBatch(
|
|
295
|
+
expired_snapshot_ids=expired_snapshot_ids,
|
|
296
|
+
cleanup_tasks=cleanup_tasks,
|
|
297
|
+
batch_range=result_batch_range,
|
|
298
|
+
)
|
|
253
299
|
|
|
254
|
-
return
|
|
300
|
+
return None
|
|
255
301
|
|
|
256
302
|
def delete_snapshots(self, snapshot_ids: t.Iterable[SnapshotIdLike]) -> None:
|
|
257
303
|
"""Deletes snapshots.
|
|
@@ -288,6 +334,55 @@ class SnapshotState:
|
|
|
288
334
|
"""
|
|
289
335
|
return self._get_snapshots(snapshot_ids)
|
|
290
336
|
|
|
337
|
+
def get_snapshots_by_names(
|
|
338
|
+
self,
|
|
339
|
+
snapshot_names: t.Iterable[str],
|
|
340
|
+
current_ts: t.Optional[int] = None,
|
|
341
|
+
exclude_expired: bool = True,
|
|
342
|
+
) -> t.Set[SnapshotIdAndVersion]:
|
|
343
|
+
"""Return the snapshot records for all versions of the specified snapshot names.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
snapshot_names: Iterable of snapshot names to fetch all snapshot records for
|
|
347
|
+
current_ts: Sets the current time for identifying which snapshots have expired so they can be excluded (only relevant if :exclude_expired=True)
|
|
348
|
+
exclude_expired: Whether or not to return the snapshot id's of expired snapshots in the result
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
A set containing all the matched snapshot records. To fetch full snapshots, pass it into StateSync.get_snapshots()
|
|
352
|
+
"""
|
|
353
|
+
if not snapshot_names:
|
|
354
|
+
return set()
|
|
355
|
+
|
|
356
|
+
if exclude_expired:
|
|
357
|
+
current_ts = current_ts or now_timestamp()
|
|
358
|
+
unexpired_expr = (exp.column("updated_ts") + exp.column("ttl_ms")) > current_ts
|
|
359
|
+
else:
|
|
360
|
+
unexpired_expr = None
|
|
361
|
+
|
|
362
|
+
return {
|
|
363
|
+
SnapshotIdAndVersion(
|
|
364
|
+
name=name,
|
|
365
|
+
identifier=identifier,
|
|
366
|
+
version=version,
|
|
367
|
+
kind_name=kind_name or None,
|
|
368
|
+
dev_version=dev_version,
|
|
369
|
+
fingerprint=fingerprint,
|
|
370
|
+
)
|
|
371
|
+
for where in snapshot_name_filter(
|
|
372
|
+
snapshot_names=snapshot_names,
|
|
373
|
+
batch_size=self.SNAPSHOT_BATCH_SIZE,
|
|
374
|
+
)
|
|
375
|
+
for name, identifier, version, kind_name, dev_version, fingerprint in fetchall(
|
|
376
|
+
self.engine_adapter,
|
|
377
|
+
exp.select(
|
|
378
|
+
"name", "identifier", "version", "kind_name", "dev_version", "fingerprint"
|
|
379
|
+
)
|
|
380
|
+
.from_(self.snapshots_table)
|
|
381
|
+
.where(where)
|
|
382
|
+
.and_(unexpired_expr),
|
|
383
|
+
)
|
|
384
|
+
}
|
|
385
|
+
|
|
291
386
|
def snapshots_exist(self, snapshot_ids: t.Iterable[SnapshotIdLike]) -> t.Set[SnapshotId]:
|
|
292
387
|
"""Checks if snapshots exist.
|
|
293
388
|
|
|
@@ -571,7 +666,7 @@ class SnapshotState:
|
|
|
571
666
|
self,
|
|
572
667
|
snapshots: t.Collection[SnapshotNameVersionLike],
|
|
573
668
|
lock_for_update: bool = False,
|
|
574
|
-
) -> t.List[
|
|
669
|
+
) -> t.List[SnapshotIdAndVersion]:
|
|
575
670
|
"""Fetches all snapshots that share the same version as the snapshots.
|
|
576
671
|
|
|
577
672
|
The output includes the snapshots with the specified identifiers.
|
|
@@ -593,14 +688,12 @@ class SnapshotState:
|
|
|
593
688
|
):
|
|
594
689
|
query = (
|
|
595
690
|
exp.select(
|
|
596
|
-
"snapshot",
|
|
597
691
|
"name",
|
|
598
692
|
"identifier",
|
|
599
693
|
"version",
|
|
600
|
-
"
|
|
601
|
-
"
|
|
602
|
-
"
|
|
603
|
-
"forward_only",
|
|
694
|
+
"kind_name",
|
|
695
|
+
"dev_version",
|
|
696
|
+
"fingerprint",
|
|
604
697
|
)
|
|
605
698
|
.from_(exp.to_table(self.snapshots_table).as_("snapshots"))
|
|
606
699
|
.where(where)
|
|
@@ -611,17 +704,15 @@ class SnapshotState:
|
|
|
611
704
|
snapshot_rows.extend(fetchall(self.engine_adapter, query))
|
|
612
705
|
|
|
613
706
|
return [
|
|
614
|
-
|
|
707
|
+
SnapshotIdAndVersion(
|
|
615
708
|
name=name,
|
|
616
709
|
identifier=identifier,
|
|
617
710
|
version=version,
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
forward_only=forward_only,
|
|
622
|
-
snapshot=snapshot,
|
|
711
|
+
kind_name=kind_name or None,
|
|
712
|
+
dev_version=dev_version,
|
|
713
|
+
fingerprint=SnapshotFingerprint.parse_raw(fingerprint),
|
|
623
714
|
)
|
|
624
|
-
for
|
|
715
|
+
for name, identifier, version, kind_name, dev_version, fingerprint in snapshot_rows
|
|
625
716
|
]
|
|
626
717
|
|
|
627
718
|
|
|
@@ -676,6 +767,8 @@ def _snapshots_to_df(snapshots: t.Iterable[Snapshot]) -> pd.DataFrame:
|
|
|
676
767
|
"ttl_ms": snapshot.ttl_ms,
|
|
677
768
|
"unrestorable": snapshot.unrestorable,
|
|
678
769
|
"forward_only": snapshot.forward_only,
|
|
770
|
+
"dev_version": snapshot.dev_version,
|
|
771
|
+
"fingerprint": snapshot.fingerprint.json(),
|
|
679
772
|
}
|
|
680
773
|
for snapshot in snapshots
|
|
681
774
|
]
|
|
@@ -695,88 +788,3 @@ def _auto_restatements_to_df(auto_restatements: t.Dict[SnapshotNameVersion, int]
|
|
|
695
788
|
for name_version, ts in auto_restatements.items()
|
|
696
789
|
]
|
|
697
790
|
)
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
class SharedVersionSnapshot(PydanticModel):
|
|
701
|
-
"""A stripped down version of a snapshot that is used for fetching snapshots that share the same version
|
|
702
|
-
with a significantly reduced parsing overhead.
|
|
703
|
-
"""
|
|
704
|
-
|
|
705
|
-
name: str
|
|
706
|
-
version: str
|
|
707
|
-
dev_version_: t.Optional[str] = Field(alias="dev_version")
|
|
708
|
-
identifier: str
|
|
709
|
-
fingerprint: SnapshotFingerprint
|
|
710
|
-
interval_unit: IntervalUnit
|
|
711
|
-
change_category: SnapshotChangeCategory
|
|
712
|
-
updated_ts: int
|
|
713
|
-
unpaused_ts: t.Optional[int]
|
|
714
|
-
unrestorable: bool
|
|
715
|
-
disable_restatement: bool
|
|
716
|
-
effective_from: t.Optional[TimeLike]
|
|
717
|
-
raw_snapshot: t.Dict[str, t.Any]
|
|
718
|
-
forward_only: bool
|
|
719
|
-
|
|
720
|
-
@property
|
|
721
|
-
def snapshot_id(self) -> SnapshotId:
|
|
722
|
-
return SnapshotId(name=self.name, identifier=self.identifier)
|
|
723
|
-
|
|
724
|
-
@property
|
|
725
|
-
def is_forward_only(self) -> bool:
|
|
726
|
-
return self.forward_only or self.change_category == SnapshotChangeCategory.FORWARD_ONLY
|
|
727
|
-
|
|
728
|
-
@property
|
|
729
|
-
def normalized_effective_from_ts(self) -> t.Optional[int]:
|
|
730
|
-
return (
|
|
731
|
-
to_timestamp(self.interval_unit.cron_floor(self.effective_from))
|
|
732
|
-
if self.effective_from
|
|
733
|
-
else None
|
|
734
|
-
)
|
|
735
|
-
|
|
736
|
-
@property
|
|
737
|
-
def dev_version(self) -> str:
|
|
738
|
-
return self.dev_version_ or self.fingerprint.to_version()
|
|
739
|
-
|
|
740
|
-
@property
|
|
741
|
-
def full_snapshot(self) -> Snapshot:
|
|
742
|
-
return Snapshot(
|
|
743
|
-
**{
|
|
744
|
-
**self.raw_snapshot,
|
|
745
|
-
"updated_ts": self.updated_ts,
|
|
746
|
-
"unpaused_ts": self.unpaused_ts,
|
|
747
|
-
"unrestorable": self.unrestorable,
|
|
748
|
-
"forward_only": self.forward_only,
|
|
749
|
-
}
|
|
750
|
-
)
|
|
751
|
-
|
|
752
|
-
@classmethod
|
|
753
|
-
def from_snapshot_record(
|
|
754
|
-
cls,
|
|
755
|
-
*,
|
|
756
|
-
name: str,
|
|
757
|
-
identifier: str,
|
|
758
|
-
version: str,
|
|
759
|
-
updated_ts: int,
|
|
760
|
-
unpaused_ts: t.Optional[int],
|
|
761
|
-
unrestorable: bool,
|
|
762
|
-
forward_only: bool,
|
|
763
|
-
snapshot: str,
|
|
764
|
-
) -> SharedVersionSnapshot:
|
|
765
|
-
raw_snapshot = json.loads(snapshot)
|
|
766
|
-
raw_node = raw_snapshot["node"]
|
|
767
|
-
return SharedVersionSnapshot(
|
|
768
|
-
name=name,
|
|
769
|
-
version=version,
|
|
770
|
-
dev_version=raw_snapshot.get("dev_version"),
|
|
771
|
-
identifier=identifier,
|
|
772
|
-
fingerprint=raw_snapshot["fingerprint"],
|
|
773
|
-
interval_unit=raw_node.get("interval_unit", IntervalUnit.from_cron(raw_node["cron"])),
|
|
774
|
-
change_category=raw_snapshot["change_category"],
|
|
775
|
-
updated_ts=updated_ts,
|
|
776
|
-
unpaused_ts=unpaused_ts,
|
|
777
|
-
unrestorable=unrestorable,
|
|
778
|
-
disable_restatement=raw_node.get("kind", {}).get("disable_restatement", False),
|
|
779
|
-
effective_from=raw_snapshot.get("effective_from"),
|
|
780
|
-
raw_snapshot=raw_snapshot,
|
|
781
|
-
forward_only=forward_only,
|
|
782
|
-
)
|
sqlmesh/core/table_diff.py
CHANGED
|
@@ -367,8 +367,8 @@ class TableDiff:
|
|
|
367
367
|
column_type = matched_columns[name]
|
|
368
368
|
qualified_column = exp.column(name, table)
|
|
369
369
|
|
|
370
|
-
if column_type.is_type(*exp.DataType.
|
|
371
|
-
return
|
|
370
|
+
if column_type.is_type(*exp.DataType.REAL_TYPES):
|
|
371
|
+
return self.adapter._normalize_decimal_value(qualified_column, self.decimals)
|
|
372
372
|
if column_type.is_type(*exp.DataType.NESTED_TYPES):
|
|
373
373
|
return self.adapter._normalize_nested_value(qualified_column)
|
|
374
374
|
|
sqlmesh/core/test/context.py
CHANGED
|
@@ -18,6 +18,8 @@ class TestExecutionContext(ExecutionContext):
|
|
|
18
18
|
models: All upstream models to use for expansion and mapping of physical locations.
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
+
__test__ = False # prevent pytest trying to collect this as a test class
|
|
22
|
+
|
|
21
23
|
def __init__(
|
|
22
24
|
self,
|
|
23
25
|
engine_adapter: EngineAdapter,
|
sqlmesh/core/test/definition.py
CHANGED
|
@@ -100,8 +100,11 @@ class ModelTest(unittest.TestCase):
|
|
|
100
100
|
self._validate_and_normalize_test()
|
|
101
101
|
|
|
102
102
|
if self.engine_adapter.default_catalog:
|
|
103
|
-
self._fixture_catalog: t.Optional[exp.Identifier] =
|
|
104
|
-
|
|
103
|
+
self._fixture_catalog: t.Optional[exp.Identifier] = normalize_identifiers(
|
|
104
|
+
exp.parse_identifier(
|
|
105
|
+
self.engine_adapter.default_catalog, dialect=self._test_adapter_dialect
|
|
106
|
+
),
|
|
107
|
+
dialect=self._test_adapter_dialect,
|
|
105
108
|
)
|
|
106
109
|
else:
|
|
107
110
|
self._fixture_catalog = None
|
|
@@ -451,6 +454,9 @@ class ModelTest(unittest.TestCase):
|
|
|
451
454
|
query = outputs.get("query")
|
|
452
455
|
partial = outputs.pop("partial", None)
|
|
453
456
|
|
|
457
|
+
if ctes is None and query is None:
|
|
458
|
+
_raise_error("Incomplete test, outputs must contain 'query' or 'ctes'", self.path)
|
|
459
|
+
|
|
454
460
|
def _normalize_rows(
|
|
455
461
|
values: t.List[Row] | t.Dict,
|
|
456
462
|
name: str,
|
|
@@ -641,16 +647,16 @@ class ModelTest(unittest.TestCase):
|
|
|
641
647
|
return self._execute(query)
|
|
642
648
|
|
|
643
649
|
rows = values["rows"]
|
|
650
|
+
columns_str: t.Optional[t.List[str]] = None
|
|
644
651
|
if columns:
|
|
652
|
+
columns_str = [str(c) for c in columns]
|
|
645
653
|
referenced_columns = list(dict.fromkeys(col for row in rows for col in row))
|
|
646
654
|
_raise_if_unexpected_columns(columns, referenced_columns)
|
|
647
655
|
|
|
648
656
|
if partial:
|
|
649
|
-
|
|
657
|
+
columns_str = [c for c in columns_str if c in referenced_columns]
|
|
650
658
|
|
|
651
|
-
return pd.DataFrame.from_records(
|
|
652
|
-
rows, columns=[str(c) for c in columns] if columns else None
|
|
653
|
-
)
|
|
659
|
+
return pd.DataFrame.from_records(rows, columns=columns_str)
|
|
654
660
|
|
|
655
661
|
def _add_missing_columns(
|
|
656
662
|
self, query: exp.Query, all_columns: t.Optional[t.Collection[str]] = None
|
|
@@ -801,7 +807,7 @@ class PythonModelTest(ModelTest):
|
|
|
801
807
|
actual_df.reset_index(drop=True, inplace=True)
|
|
802
808
|
expected = self._create_df(values, columns=self.model.columns_to_types, partial=partial)
|
|
803
809
|
|
|
804
|
-
self.assert_equal(expected, actual_df, sort=
|
|
810
|
+
self.assert_equal(expected, actual_df, sort=True, partial=partial)
|
|
805
811
|
|
|
806
812
|
def _execute_model(self) -> pd.DataFrame:
|
|
807
813
|
"""Executes the python model and returns a DataFrame."""
|
|
@@ -919,8 +925,7 @@ def generate_test(
|
|
|
919
925
|
cte_output = test._execute(cte_query)
|
|
920
926
|
ctes[cte.alias] = (
|
|
921
927
|
pandas_timestamp_to_pydatetime(
|
|
922
|
-
cte_output.apply(lambda col: col.map(_normalize_df_value)),
|
|
923
|
-
cte_query.named_selects,
|
|
928
|
+
df=cte_output.apply(lambda col: col.map(_normalize_df_value)),
|
|
924
929
|
)
|
|
925
930
|
.replace({np.nan: None})
|
|
926
931
|
.to_dict(orient="records")
|
sqlmesh/dbt/adapter.py
CHANGED
|
@@ -115,30 +115,39 @@ class BaseAdapter(abc.ABC):
|
|
|
115
115
|
"""Returns the value quoted according to the quote policy."""
|
|
116
116
|
return self.quote(value) if getattr(self.quote_policy, component_type, False) else value
|
|
117
117
|
|
|
118
|
-
def dispatch(
|
|
118
|
+
def dispatch(
|
|
119
|
+
self,
|
|
120
|
+
macro_name: str,
|
|
121
|
+
macro_namespace: t.Optional[str] = None,
|
|
122
|
+
) -> t.Callable:
|
|
119
123
|
"""Returns a dialect-specific version of a macro with the given name."""
|
|
120
124
|
target_type = self.jinja_globals["target"]["type"]
|
|
121
|
-
macro_suffix = f"__{
|
|
125
|
+
macro_suffix = f"__{macro_name}"
|
|
122
126
|
|
|
123
127
|
def _relevance(package_name_pair: t.Tuple[t.Optional[str], str]) -> t.Tuple[int, int]:
|
|
124
128
|
"""Lower scores more relevant."""
|
|
125
|
-
macro_package,
|
|
129
|
+
macro_package, name = package_name_pair
|
|
126
130
|
|
|
127
|
-
package_score = 0 if macro_package ==
|
|
131
|
+
package_score = 0 if macro_package == macro_namespace else 1
|
|
128
132
|
name_score = 1
|
|
129
133
|
|
|
130
|
-
if
|
|
134
|
+
if name.startswith("default"):
|
|
131
135
|
name_score = 2
|
|
132
|
-
elif
|
|
136
|
+
elif name.startswith(target_type):
|
|
133
137
|
name_score = 0
|
|
134
138
|
|
|
135
139
|
return name_score, package_score
|
|
136
140
|
|
|
137
141
|
jinja_env = self.jinja_macros.build_environment(**self.jinja_globals).globals
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
+
|
|
143
|
+
packages_to_check: t.List[t.Optional[str]] = [None]
|
|
144
|
+
if macro_namespace is not None:
|
|
145
|
+
if macro_namespace in jinja_env:
|
|
146
|
+
packages_to_check = [self.jinja_macros.root_package_name, macro_namespace]
|
|
147
|
+
|
|
148
|
+
# Add dbt packages as fallback
|
|
149
|
+
packages_to_check.extend(k for k in jinja_env if k.startswith("dbt"))
|
|
150
|
+
|
|
142
151
|
candidates = {}
|
|
143
152
|
for macro_package in packages_to_check:
|
|
144
153
|
macros = jinja_env.get(macro_package, {}) if macro_package else jinja_env
|
|
@@ -156,7 +165,7 @@ class BaseAdapter(abc.ABC):
|
|
|
156
165
|
sorted_candidates = sorted(candidates, key=_relevance)
|
|
157
166
|
return candidates[sorted_candidates[0]]
|
|
158
167
|
|
|
159
|
-
raise ConfigError(f"Macro '{
|
|
168
|
+
raise ConfigError(f"Macro '{macro_name}', package '{macro_namespace}' was not found.")
|
|
160
169
|
|
|
161
170
|
def type(self) -> str:
|
|
162
171
|
return self.project_dialect or ""
|
|
@@ -168,7 +177,8 @@ class BaseAdapter(abc.ABC):
|
|
|
168
177
|
|
|
169
178
|
@property
|
|
170
179
|
def graph(self) -> t.Any:
|
|
171
|
-
|
|
180
|
+
flat_graph = self.jinja_globals.get("flat_graph", None)
|
|
181
|
+
return flat_graph or AttributeDict(
|
|
172
182
|
{
|
|
173
183
|
"exposures": {},
|
|
174
184
|
"groups": {},
|
|
@@ -276,10 +286,6 @@ class RuntimeAdapter(BaseAdapter):
|
|
|
276
286
|
**table_mapping,
|
|
277
287
|
}
|
|
278
288
|
|
|
279
|
-
@property
|
|
280
|
-
def graph(self) -> t.Any:
|
|
281
|
-
return self.jinja_globals.get("flat_graph", super().graph)
|
|
282
|
-
|
|
283
289
|
def get_relation(
|
|
284
290
|
self, database: t.Optional[str], schema: str, identifier: str
|
|
285
291
|
) -> t.Optional[BaseRelation]:
|