diff --git a/sqlmesh/core/engine_adapter/snowflake.py b/sqlmesh/core/engine_adapter/snowflake.py index cb9ec5398f..404228289b 100644 --- a/sqlmesh/core/engine_adapter/snowflake.py +++ b/sqlmesh/core/engine_adapter/snowflake.py @@ -178,7 +178,7 @@ def _drop_catalog(self, catalog_name: exp.Identifier) -> None: .from_(exp.to_table("information_schema.databases")) .where( exp.and_( - exp.column("database_name").eq(exp.Literal.string(catalog_name)), + exp.column("database_name").eq(exp.Literal.string(catalog_name.name)), exp.column("comment").eq(exp.Literal.string(c.SQLMESH_MANAGED)), ) ) diff --git a/tests/core/engine_adapter/integration/test_integration_snowflake.py b/tests/core/engine_adapter/integration/test_integration_snowflake.py index 7f3c38be46..c9ce95654c 100644 --- a/tests/core/engine_adapter/integration/test_integration_snowflake.py +++ b/tests/core/engine_adapter/integration/test_integration_snowflake.py @@ -315,6 +315,33 @@ def fetch_database_names() -> t.Set[str]: assert fetch_database_names() == {non_sqlmesh_managed_catalog} +def test_create_drop_catalog_quoted_name(ctx: TestContext, engine_adapter: SnowflakeEngineAdapter): + # a name with a hyphen has to be quoted, so it only matches the existence check in + # drop_catalog if the check compares against the bare name + sqlmesh_managed_catalog = ctx.add_test_suffix("env-dev") + + initial_catalog = engine_adapter.get_current_catalog() + assert initial_catalog + + ctx._catalogs.append(sqlmesh_managed_catalog) # so it still gets cleaned up if the test fails + engine_adapter.create_catalog(sqlmesh_managed_catalog) + + def fetch_database_names() -> t.Set[str]: + engine_adapter.set_current_catalog(initial_catalog) + return { + str(r[0]) + for r in engine_adapter.fetchall( + f"select database_name from information_schema.databases where database_name like '%{ctx.test_id}'" + ) + } + + # the database is stored under the bare name, without identifier quotes + assert fetch_database_names() == {sqlmesh_managed_catalog} + + engine_adapter.drop_catalog(sqlmesh_managed_catalog) + assert fetch_database_names() == set() + + def test_rows_tracker( ctx: TestContext, engine_adapter: SnowflakeEngineAdapter, mocker: MockerFixture ): diff --git a/tests/core/engine_adapter/test_snowflake.py b/tests/core/engine_adapter/test_snowflake.py index 8e1c510521..384d6bae5e 100644 --- a/tests/core/engine_adapter/test_snowflake.py +++ b/tests/core/engine_adapter/test_snowflake.py @@ -1195,6 +1195,41 @@ def test_drop_catalog(snowflake_mocked_engine_adapter: SnowflakeEngineAdapter) - ] +@pytest.mark.parametrize( + "catalog_name, expected_name", + [ + # names that need quoting are parsed into quoted identifiers + ("my-db__dev", "my-db__dev"), + (exp.to_identifier("my db", quoted=True), "my db"), + (exp.to_identifier("MyDb", quoted=True), "MyDb"), + ], +) +def test_drop_catalog_quoted_name( + snowflake_mocked_engine_adapter: SnowflakeEngineAdapter, + catalog_name: t.Union[str, exp.Identifier], + expected_name: str, +) -> None: + adapter = snowflake_mocked_engine_adapter + exists_check = f"""SELECT 1 FROM "INFORMATION_SCHEMA"."DATABASES" WHERE "DATABASE_NAME" = '{expected_name}' AND "COMMENT" = 'sqlmesh_managed'""" + + # the existence check finds the sqlmesh_managed database, so it is dropped + adapter.cursor.fetchone.return_value = (1,) + adapter.drop_catalog(catalog_name) + + # the existence check must compare against the bare database name, without identifier quotes + assert to_sql_calls(adapter) == [ + exists_check, + f'DROP DATABASE IF EXISTS "{expected_name}"', + ] + + # the existence check finds nothing, so the database is left alone + adapter.cursor.execute.reset_mock() + adapter.cursor.fetchone.return_value = None + adapter.drop_catalog(catalog_name) + + assert to_sql_calls(adapter) == [exists_check] + + def _make_snowflake_clustering_adapter( make_mocked_engine_adapter: t.Callable, mocker: MockerFixture,