Skip to content

Commit 5d1f952

Browse files
committed
Fix: Only apply partition interval unit if partitioned_by is not set by the user explicitly (#3636)
1 parent 5d08cdf commit 5d1f952

File tree

4 files changed

+73
-24
lines changed

4 files changed

+73
-24
lines changed

sqlmesh/core/model/definition.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from sqlmesh.core import constants as c
2424
from sqlmesh.core import dialect as d
2525
from sqlmesh.core.audit import Audit, ModelAudit
26+
from sqlmesh.core.node import IntervalUnit
2627
from sqlmesh.core.macros import MacroRegistry, macro
2728
from sqlmesh.core.model.common import (
2829
expression_validator,
@@ -1044,9 +1045,7 @@ def full_depends_on(self) -> t.Set[str]:
10441045
@property
10451046
def partitioned_by(self) -> t.List[exp.Expression]:
10461047
"""Columns to partition the model by, including the time column if it is not already included."""
1047-
if self.time_column and self.time_column.column not in {
1048-
col for expr in self.partitioned_by_ for col in expr.find_all(exp.Column)
1049-
}:
1048+
if self.time_column and not self._is_time_column_in_partitioned_by:
10501049
return [
10511050
TIME_COL_PARTITION_FUNC.get(self.dialect, lambda x, y: x)(
10521051
self.time_column.column, self.columns_to_types
@@ -1055,6 +1054,16 @@ def partitioned_by(self) -> t.List[exp.Expression]:
10551054
]
10561055
return self.partitioned_by_
10571056

1057+
@property
1058+
def partition_interval_unit(self) -> t.Optional[IntervalUnit]:
1059+
"""The interval unit to use for partitioning if applicable."""
1060+
# Only return the interval unit for partitioning if the partitioning
1061+
# wasn't explicitly set by the user. Otherwise, the user-provided
1062+
# value should always take precedence.
1063+
if self.time_column and not self._is_time_column_in_partitioned_by:
1064+
return self.interval_unit
1065+
return None
1066+
10581067
@property
10591068
def audits_with_args(self) -> t.List[t.Tuple[Audit, t.Dict[str, exp.Expression]]]:
10601069
from sqlmesh.core.audit.builtin import BUILT_IN_AUDITS
@@ -1071,6 +1080,12 @@ def audits_with_args(self) -> t.List[t.Tuple[Audit, t.Dict[str, exp.Expression]]
10711080

10721081
return list(audits_with_args.values())
10731082

1083+
@property
1084+
def _is_time_column_in_partitioned_by(self) -> bool:
1085+
return self.time_column is not None and self.time_column.column in {
1086+
col for expr in self.partitioned_by_ for col in expr.find_all(exp.Column)
1087+
}
1088+
10741089

10751090
class SqlModel(_Model):
10761091
"""The model definition which relies on a SQL query to fetch the data.

sqlmesh/core/snapshot/evaluator.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,7 +1156,7 @@ def _replace_query_for_model(self, model: Model, name: str, query_or_df: QueryOr
11561156
table_format=model.table_format,
11571157
storage_format=model.storage_format,
11581158
partitioned_by=model.partitioned_by,
1159-
partition_interval_unit=model.interval_unit,
1159+
partition_interval_unit=model.partition_interval_unit,
11601160
clustered_by=model.clustered_by,
11611161
table_properties=model.physical_properties,
11621162
table_description=model.description,
@@ -1286,7 +1286,7 @@ def create(
12861286
table_format=model.table_format,
12871287
storage_format=model.storage_format,
12881288
partitioned_by=model.partitioned_by,
1289-
partition_interval_unit=model.interval_unit,
1289+
partition_interval_unit=model.partition_interval_unit,
12901290
clustered_by=model.clustered_by,
12911291
table_properties=model.physical_properties,
12921292
table_description=model.description if is_table_deployable else None,
@@ -1310,7 +1310,7 @@ def create(
13101310
table_format=model.table_format,
13111311
storage_format=model.storage_format,
13121312
partitioned_by=model.partitioned_by,
1313-
partition_interval_unit=model.interval_unit,
1313+
partition_interval_unit=model.partition_interval_unit,
13141314
clustered_by=model.clustered_by,
13151315
table_properties=model.physical_properties,
13161316
table_description=model.description if is_table_deployable else None,
@@ -1519,7 +1519,7 @@ def create(
15191519
table_format=model.table_format,
15201520
storage_format=model.storage_format,
15211521
partitioned_by=model.partitioned_by,
1522-
partition_interval_unit=model.interval_unit,
1522+
partition_interval_unit=model.partition_interval_unit,
15231523
clustered_by=model.clustered_by,
15241524
table_properties=model.physical_properties,
15251525
table_description=model.description if is_table_deployable else None,
@@ -1712,7 +1712,7 @@ def create(
17121712
materialized_properties = {
17131713
"partitioned_by": model.partitioned_by,
17141714
"clustered_by": model.clustered_by,
1715-
"partition_interval_unit": model.interval_unit,
1715+
"partition_interval_unit": model.partition_interval_unit,
17161716
}
17171717
self.adapter.create_view(
17181718
table_name,

tests/core/test_model.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6677,3 +6677,37 @@ def dummy_model_entry(evaluator: MacroEvaluator) -> exp.Select:
66776677
)
66786678
assert isinstance(context._get_engine_adapter("duckdb"), DuckDBEngineAdapter)
66796679
assert len(context._engine_adapters) == 2
6680+
6681+
6682+
def test_partition_interval_unit():
6683+
expressions = d.parse(
6684+
"""
6685+
MODEL (
6686+
name test,
6687+
kind INCREMENTAL_BY_TIME_RANGE(
6688+
time_column ds,
6689+
),
6690+
cron '0 0 1 * *'
6691+
);
6692+
SELECT '2024-01-01' AS ds;
6693+
"""
6694+
)
6695+
model = load_sql_based_model(expressions)
6696+
assert model.partition_interval_unit == IntervalUnit.MONTH
6697+
6698+
# Partitioning was explicitly set by the user
6699+
expressions = d.parse(
6700+
"""
6701+
MODEL (
6702+
name test,
6703+
kind INCREMENTAL_BY_TIME_RANGE(
6704+
time_column ds,
6705+
),
6706+
cron '0 0 1 * *',
6707+
partitioned_by (ds)
6708+
);
6709+
SELECT '2024-01-01' AS ds;
6710+
"""
6711+
)
6712+
model = load_sql_based_model(expressions)
6713+
assert model.partition_interval_unit is None

tests/core/test_snapshot_evaluator.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ def test_evaluate_incremental_unmanaged_no_intervals(
679679
clustered_by=[],
680680
column_descriptions={},
681681
columns_to_types=table_columns,
682-
partition_interval_unit=model.interval_unit,
682+
partition_interval_unit=model.partition_interval_unit,
683683
partitioned_by=model.partitioned_by,
684684
table_format=None,
685685
storage_format=None,
@@ -891,7 +891,7 @@ def test_create_prod_table_exists_forward_only(mocker: MockerFixture, adapter_mo
891891
table_format=None,
892892
storage_format=None,
893893
partitioned_by=[],
894-
partition_interval_unit=IntervalUnit.DAY,
894+
partition_interval_unit=None,
895895
clustered_by=[],
896896
table_properties={},
897897
table_description=None,
@@ -965,7 +965,7 @@ def test_create_materialized_view(mocker: MockerFixture, adapter_mock, make_snap
965965
materialized=True,
966966
materialized_properties={
967967
"clustered_by": [],
968-
"partition_interval_unit": IntervalUnit.DAY,
968+
"partition_interval_unit": None,
969969
"partitioned_by": [],
970970
},
971971
view_properties={},
@@ -1014,7 +1014,7 @@ def test_create_view_with_properties(mocker: MockerFixture, adapter_mock, make_s
10141014
},
10151015
materialized_properties={
10161016
"clustered_by": [],
1017-
"partition_interval_unit": IntervalUnit.DAY,
1017+
"partition_interval_unit": None,
10181018
"partitioned_by": [],
10191019
},
10201020
table_description=None,
@@ -1667,7 +1667,7 @@ def test_create_scd_type_2_by_time(adapter_mock, make_snapshot):
16671667
table_format=None,
16681668
storage_format=None,
16691669
partitioned_by=[],
1670-
partition_interval_unit=IntervalUnit.DAY,
1670+
partition_interval_unit=None,
16711671
clustered_by=[],
16721672
table_properties={},
16731673
table_description=None,
@@ -1722,7 +1722,7 @@ def test_create_ctas_scd_type_2_by_time(adapter_mock, make_snapshot):
17221722
table_format=None,
17231723
storage_format=None,
17241724
partitioned_by=[],
1725-
partition_interval_unit=IntervalUnit.DAY,
1725+
partition_interval_unit=None,
17261726
clustered_by=[],
17271727
table_properties={},
17281728
table_description=None,
@@ -1843,7 +1843,7 @@ def test_create_scd_type_2_by_column(adapter_mock, make_snapshot):
18431843
table_format=None,
18441844
storage_format=None,
18451845
partitioned_by=[],
1846-
partition_interval_unit=IntervalUnit.DAY,
1846+
partition_interval_unit=None,
18471847
clustered_by=[],
18481848
table_properties={},
18491849
table_description=None,
@@ -1892,7 +1892,7 @@ def test_create_ctas_scd_type_2_by_column(adapter_mock, make_snapshot):
18921892
table_format=None,
18931893
storage_format=None,
18941894
partitioned_by=[],
1895-
partition_interval_unit=IntervalUnit.DAY,
1895+
partition_interval_unit=None,
18961896
clustered_by=[],
18971897
table_properties={},
18981898
table_description=None,
@@ -2166,7 +2166,7 @@ def test_create_incremental_by_unique_no_intervals(adapter_mock, make_snapshot):
21662166
clustered_by=[],
21672167
column_descriptions={},
21682168
columns_to_types=table_columns,
2169-
partition_interval_unit=model.interval_unit,
2169+
partition_interval_unit=model.partition_interval_unit,
21702170
partitioned_by=model.partitioned_by,
21712171
table_format=None,
21722172
storage_format=None,
@@ -2271,7 +2271,7 @@ def test_create_seed(mocker: MockerFixture, adapter_mock, make_snapshot):
22712271
table_format=None,
22722272
storage_format=None,
22732273
partitioned_by=[],
2274-
partition_interval_unit=IntervalUnit.DAY,
2274+
partition_interval_unit=None,
22752275
clustered_by=[],
22762276
table_properties={},
22772277
table_description=None,
@@ -2346,7 +2346,7 @@ def test_create_seed_on_error(mocker: MockerFixture, adapter_mock, make_snapshot
23462346
table_format=None,
23472347
storage_format=None,
23482348
partitioned_by=[],
2349-
partition_interval_unit=IntervalUnit.DAY,
2349+
partition_interval_unit=None,
23502350
clustered_by=[],
23512351
table_properties={},
23522352
table_description=None,
@@ -2402,7 +2402,7 @@ def test_create_seed_no_intervals(mocker: MockerFixture, adapter_mock, make_snap
24022402
table_format=None,
24032403
storage_format=None,
24042404
partitioned_by=[],
2405-
partition_interval_unit=IntervalUnit.DAY,
2405+
partition_interval_unit=None,
24062406
clustered_by=[],
24072407
table_properties={},
24082408
table_description=None,
@@ -2808,7 +2808,7 @@ def test_create_managed(adapter_mock, make_snapshot, mocker: MockerFixture):
28082808
table_format=model.table_format,
28092809
storage_format=model.storage_format,
28102810
partitioned_by=model.partitioned_by,
2811-
partition_interval_unit=model.interval_unit,
2811+
partition_interval_unit=model.partition_interval_unit,
28122812
clustered_by=model.clustered_by,
28132813
table_properties=model.physical_properties,
28142814
table_description=None,
@@ -2894,7 +2894,7 @@ def test_evaluate_managed(adapter_mock, make_snapshot, mocker: MockerFixture):
28942894
table_format=model.table_format,
28952895
storage_format=model.storage_format,
28962896
partitioned_by=model.partitioned_by,
2897-
partition_interval_unit=model.interval_unit,
2897+
partition_interval_unit=model.partition_interval_unit,
28982898
clustered_by=model.clustered_by,
28992899
table_properties=model.physical_properties,
29002900
table_description=model.description,
@@ -3061,7 +3061,7 @@ def test_create_snapshot(
30613061
table_format=None,
30623062
storage_format=None,
30633063
partitioned_by=[],
3064-
partition_interval_unit=IntervalUnit.DAY,
3064+
partition_interval_unit=None,
30653065
clustered_by=[],
30663066
table_properties={},
30673067
table_description=None,
@@ -3108,7 +3108,7 @@ def test_migrate_snapshot(snapshot: Snapshot, mocker: MockerFixture, adapter_moc
31083108
table_format=None,
31093109
storage_format=None,
31103110
partitioned_by=[],
3111-
partition_interval_unit=IntervalUnit.DAY,
3111+
partition_interval_unit=None,
31123112
clustered_by=[],
31133113
table_properties={},
31143114
table_description=None,

0 commit comments

Comments
 (0)