|
7 | 7 | import typing as t |
8 | 8 | import shutil |
9 | 9 | from datetime import datetime, timedelta, date |
| 10 | +from unittest import mock |
10 | 11 | from unittest.mock import patch |
| 12 | +import logging |
| 13 | + |
11 | 14 | import numpy as np # noqa: TID253 |
12 | 15 | import pandas as pd # noqa: TID253 |
13 | 16 | import pytest |
@@ -3748,3 +3751,65 @@ def _set_config(gateway: str, config: Config) -> None: |
3748 | 3751 | "incremental_model", |
3749 | 3752 | "seed_model", |
3750 | 3753 | ] |
| 3754 | + |
| 3755 | + |
| 3756 | +def test_materialized_view_evaluation(ctx: TestContext, mocker: MockerFixture): |
| 3757 | + adapter = ctx.engine_adapter |
| 3758 | + dialect = ctx.dialect |
| 3759 | + |
| 3760 | + if not adapter.SUPPORTS_MATERIALIZED_VIEWS: |
| 3761 | + pytest.skip(f"Skipping engine {dialect} as it does not support materialized views") |
| 3762 | + elif dialect in ("snowflake", "databricks"): |
| 3763 | + pytest.skip(f"Skipping {dialect} as they're not enabled on standard accounts") |
| 3764 | + |
| 3765 | + model_name = ctx.table("test_tbl") |
| 3766 | + mview_name = ctx.table("test_mview") |
| 3767 | + |
| 3768 | + sqlmesh = ctx.create_context() |
| 3769 | + |
| 3770 | + sqlmesh.upsert_model( |
| 3771 | + load_sql_based_model( |
| 3772 | + d.parse( |
| 3773 | + f""" |
| 3774 | + MODEL (name {model_name}, kind FULL); |
| 3775 | +
|
| 3776 | + SELECT 1 AS col |
| 3777 | + """ |
| 3778 | + ) |
| 3779 | + ) |
| 3780 | + ) |
| 3781 | + |
| 3782 | + sqlmesh.upsert_model( |
| 3783 | + load_sql_based_model( |
| 3784 | + d.parse( |
| 3785 | + f""" |
| 3786 | + MODEL (name {mview_name}, kind VIEW (materialized true)); |
| 3787 | +
|
| 3788 | + SELECT * FROM {model_name} |
| 3789 | + """ |
| 3790 | + ) |
| 3791 | + ) |
| 3792 | + ) |
| 3793 | + |
| 3794 | + def _assert_mview_value(value: int): |
| 3795 | + df = adapter.fetchdf(f"SELECT * FROM {mview_name.sql(dialect=dialect)}") |
| 3796 | + assert df["col"][0] == value |
| 3797 | + |
| 3798 | + # Case 1: Ensure that plan is successful and we can query the materialized view |
| 3799 | + sqlmesh.plan(auto_apply=True, no_prompts=True) |
| 3800 | + |
| 3801 | + _assert_mview_value(value=1) |
| 3802 | + |
| 3803 | + # Case 2: Ensure that we can change the underlying table and the materialized view is recreated |
| 3804 | + sqlmesh.upsert_model( |
| 3805 | + load_sql_based_model(d.parse(f"""MODEL (name {model_name}, kind FULL); SELECT 2 AS col""")) |
| 3806 | + ) |
| 3807 | + |
| 3808 | + logger = logging.getLogger("sqlmesh.core.snapshot.evaluator") |
| 3809 | + |
| 3810 | + with mock.patch.object(logger, "info") as mock_logger: |
| 3811 | + sqlmesh.plan(auto_apply=True, no_prompts=True) |
| 3812 | + |
| 3813 | + assert any("Replacing view" in call[0][0] for call in mock_logger.call_args_list) |
| 3814 | + |
| 3815 | + _assert_mview_value(value=2) |
0 commit comments