|
21 | 21 | from sqlmesh.core.config import load_config_from_paths |
22 | 22 | from sqlmesh.core.config.connection import ConnectionConfig |
23 | 23 | import sqlmesh.core.dialect as d |
| 24 | +from sqlmesh.core.environment import EnvironmentSuffixTarget |
24 | 25 | from sqlmesh.core.dialect import select_from_values |
25 | 26 | from sqlmesh.core.model import Model, load_sql_based_model |
26 | 27 | from sqlmesh.core.engine_adapter.shared import DataObject, DataObjectType |
@@ -2333,11 +2334,7 @@ def _normalize_snowflake(name: str, prefix_regex: str = "(sqlmesh__)(.*)"): |
2333 | 2334 | k: [_normalize_snowflake(name) for name in v] for k, v in object_names.items() |
2334 | 2335 | } |
2335 | 2336 |
|
2336 | | - if ctx.mark.startswith("gcp_postgres"): |
2337 | | - engine_type = "gcp_postgres" |
2338 | | - else: |
2339 | | - engine_type = ctx.mark.split("_")[0] |
2340 | | - init_example_project(tmp_path, engine_type, schema_name=schema_name) |
| 2337 | + init_example_project(tmp_path, ctx.engine_type, schema_name=schema_name) |
2341 | 2338 |
|
2342 | 2339 | config = load_config_from_paths( |
2343 | 2340 | Config, |
@@ -3557,3 +3554,137 @@ def test_identifier_length_limit(ctx: TestContext): |
3557 | 3554 | match=re.escape(match), |
3558 | 3555 | ): |
3559 | 3556 | adapter.create_table(long_table_name, {"col": exp.DataType.build("int")}) |
| 3557 | + |
| 3558 | + |
| 3559 | +@pytest.mark.parametrize( |
| 3560 | + "environment_suffix_target", |
| 3561 | + [ |
| 3562 | + EnvironmentSuffixTarget.TABLE, |
| 3563 | + EnvironmentSuffixTarget.SCHEMA, |
| 3564 | + EnvironmentSuffixTarget.CATALOG, |
| 3565 | + ], |
| 3566 | +) |
| 3567 | +def test_janitor( |
| 3568 | + ctx: TestContext, tmp_path: pathlib.Path, environment_suffix_target: EnvironmentSuffixTarget |
| 3569 | +): |
| 3570 | + if ( |
| 3571 | + environment_suffix_target == EnvironmentSuffixTarget.CATALOG |
| 3572 | + and not ctx.engine_adapter.SUPPORTS_CREATE_DROP_CATALOG |
| 3573 | + ): |
| 3574 | + pytest.skip("Engine does not support catalog-based virtual environments") |
| 3575 | + |
| 3576 | + schema = ctx.schema() # catalog.schema |
| 3577 | + parsed_schema = d.to_schema(schema) |
| 3578 | + |
| 3579 | + init_example_project(tmp_path, ctx.engine_type, schema_name=parsed_schema.db) |
| 3580 | + |
| 3581 | + def _set_config(_gateway: str, config: Config) -> None: |
| 3582 | + config.environment_suffix_target = environment_suffix_target |
| 3583 | + config.model_defaults.dialect = ctx.dialect |
| 3584 | + |
| 3585 | + sqlmesh = ctx.create_context(path=tmp_path, config_mutator=_set_config) |
| 3586 | + |
| 3587 | + sqlmesh.plan(auto_apply=True) |
| 3588 | + |
| 3589 | + # create a new model in dev |
| 3590 | + (tmp_path / "models" / "new_model.sql").write_text(f""" |
| 3591 | + MODEL ( |
| 3592 | + name {schema}.new_model, |
| 3593 | + kind FULL |
| 3594 | + ); |
| 3595 | +
|
| 3596 | + select * from {schema}.full_model |
| 3597 | + """) |
| 3598 | + sqlmesh.load() |
| 3599 | + |
| 3600 | + result = sqlmesh.plan(environment="dev", auto_apply=True) |
| 3601 | + assert result.context_diff.is_new_environment |
| 3602 | + assert len(result.context_diff.new_snapshots) == 1 |
| 3603 | + new_model = list(result.context_diff.new_snapshots.values())[0] |
| 3604 | + assert "new_model" in new_model.name.lower() |
| 3605 | + |
| 3606 | + # check physical objects |
| 3607 | + snapshot_table_name = exp.to_table(new_model.table_name(), dialect=ctx.dialect) |
| 3608 | + snapshot_schema = snapshot_table_name.db |
| 3609 | + |
| 3610 | + prod_schema = normalize_identifiers(d.to_schema(schema), dialect=ctx.dialect) |
| 3611 | + dev_env_schema = prod_schema.copy() |
| 3612 | + if environment_suffix_target == EnvironmentSuffixTarget.CATALOG: |
| 3613 | + dev_env_schema.set("catalog", exp.to_identifier(f"{prod_schema.catalog}__dev")) |
| 3614 | + else: |
| 3615 | + dev_env_schema.set("db", exp.to_identifier(f"{prod_schema.db}__dev")) |
| 3616 | + normalize_identifiers(dev_env_schema, dialect=ctx.dialect) |
| 3617 | + |
| 3618 | + md = ctx.get_metadata_results(prod_schema) |
| 3619 | + if environment_suffix_target == EnvironmentSuffixTarget.TABLE: |
| 3620 | + assert sorted([v.lower() for v in md.views]) == [ |
| 3621 | + "full_model", |
| 3622 | + "incremental_model", |
| 3623 | + "new_model__dev", |
| 3624 | + "seed_model", |
| 3625 | + ] |
| 3626 | + else: |
| 3627 | + assert sorted([v.lower() for v in md.views]) == [ |
| 3628 | + "full_model", |
| 3629 | + "incremental_model", |
| 3630 | + "seed_model", |
| 3631 | + ] |
| 3632 | + assert not md.tables |
| 3633 | + assert not md.managed_tables |
| 3634 | + |
| 3635 | + if environment_suffix_target != EnvironmentSuffixTarget.TABLE: |
| 3636 | + # note: this is "catalog__dev.schema" for EnvironmentSuffixTarget.CATALOG and "catalog.schema__dev" for EnvironmentSuffixTarget.SCHEMA |
| 3637 | + md = ctx.get_metadata_results(dev_env_schema) |
| 3638 | + assert [v.lower() for v in md.views] == ["new_model"] |
| 3639 | + assert not md.tables |
| 3640 | + assert not md.managed_tables |
| 3641 | + |
| 3642 | + md = ctx.get_metadata_results(snapshot_schema) |
| 3643 | + assert not md.views |
| 3644 | + assert not md.managed_tables |
| 3645 | + assert sorted(t.split("__")[1].lower() for t in md.tables) == [ |
| 3646 | + "full_model", |
| 3647 | + "incremental_model", |
| 3648 | + "new_model", |
| 3649 | + "seed_model", |
| 3650 | + ] |
| 3651 | + |
| 3652 | + # invalidate dev and run the janitor to clean it up |
| 3653 | + sqlmesh.invalidate_environment("dev") |
| 3654 | + assert sqlmesh.run_janitor( |
| 3655 | + ignore_ttl=True |
| 3656 | + ) # ignore_ttl to delete the new_model snapshot even though it hasnt expired yet |
| 3657 | + |
| 3658 | + # there should be no dev environment or dev tables / schemas |
| 3659 | + md = ctx.get_metadata_results(prod_schema) |
| 3660 | + assert sorted([v.lower() for v in md.views]) == [ |
| 3661 | + "full_model", |
| 3662 | + "incremental_model", |
| 3663 | + "seed_model", |
| 3664 | + ] |
| 3665 | + assert not md.tables |
| 3666 | + assert not md.managed_tables |
| 3667 | + |
| 3668 | + if environment_suffix_target != EnvironmentSuffixTarget.TABLE: |
| 3669 | + if environment_suffix_target == EnvironmentSuffixTarget.SCHEMA: |
| 3670 | + md = ctx.get_metadata_results(dev_env_schema) |
| 3671 | + else: |
| 3672 | + try: |
| 3673 | + md = ctx.get_metadata_results(dev_env_schema) |
| 3674 | + except Exception as e: |
| 3675 | + # Most engines will raise an error when @set_catalog tries to set a catalog that doesnt exist |
| 3676 | + # in this case, we just swallow the error. We know this call already worked before in the earlier checks |
| 3677 | + md = MetadataResults() |
| 3678 | + |
| 3679 | + assert not md.views |
| 3680 | + assert not md.tables |
| 3681 | + assert not md.managed_tables |
| 3682 | + |
| 3683 | + md = ctx.get_metadata_results(snapshot_schema) |
| 3684 | + assert not md.views |
| 3685 | + assert not md.managed_tables |
| 3686 | + assert sorted(t.split("__")[1].lower() for t in md.tables) == [ |
| 3687 | + "full_model", |
| 3688 | + "incremental_model", |
| 3689 | + "seed_model", |
| 3690 | + ] |
0 commit comments