Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(fix) generate_database_name macro should be case insensitive #453

Merged
merged 6 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions dbt/include/duckdb/macros/adapters.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{%- call statement('create_schema') -%}
{% set sql %}
select type from duckdb_databases()
where database_name='{{ relation.database }}'
where lower(database_name)='{{ relation.database | lower }}'
and type='sqlite'
{% endset %}
{% set results = run_query(sql) %}
Expand Down Expand Up @@ -30,7 +30,7 @@
select schema_name
from system.information_schema.schemata
{% if database is not none %}
where catalog_name = '{{ database }}'
where lower(catalog_name) = '{{ database | lower }}'
{% endif %}
{% endset %}
{{ return(run_query(sql)) }}
Expand Down Expand Up @@ -135,7 +135,7 @@ def materialize(df, con):
and table_schema = '{{ relation.schema }}'
{% endif %}
{% if relation.database %}
and table_catalog = '{{ relation.database }}'
and lower(table_catalog) = '{{ relation.database | lower }}'
{% endif %}
order by ordinal_position

Expand All @@ -157,7 +157,7 @@ def materialize(df, con):
END as type
from system.information_schema.tables
where table_schema = '{{ schema_relation.schema }}'
guenp marked this conversation as resolved.
Show resolved Hide resolved
and table_catalog = '{{ schema_relation.database }}'
and lower(table_catalog) = '{{ schema_relation.database | lower }}'
{% endcall %}
{{ return(load_result('list_relations_without_caching').table) }}
{% endmacro %}
Expand Down
27 changes: 27 additions & 0 deletions tests/functional/plugins/motherduck/fixtures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#
# Models
#

models__gen_data_macro = """
select * from {{ ref("seed") }}
"""

#
# Macros
#

macros__generate_database_name = """
{% macro generate_database_name(custom_database_name=none, node=none) -%}
{{ target.database | trim }}_{{ var("build_env") | trim }}_{{ var("org_prefix") | trim }}
{%- endmacro %}
"""

#
# Seeds
#

seeds__example_seed_csv = """a,b,c
1,2,3
4,5,6
7,8,9
"""
111 changes: 111 additions & 0 deletions tests/functional/plugins/motherduck/test_macros.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""
Test that the generate database name macro is case insensitive

See DuckDB docs: https://duckdb.org/docs/sql/dialect/keywords_and_identifiers.html

"Identifiers in DuckDB are always case-insensitive, similarly to PostgreSQL.
However, unlike PostgreSQL (and some other major SQL implementations), DuckDB also
treats quoted identifiers as case-insensitive."
"""
from urllib.parse import urlparse
import pytest

from dbt.tests.util import (
run_dbt,
check_result_nodes_by_name
)
from tests.functional.plugins.motherduck.fixtures import (
models__gen_data_macro,
macros__generate_database_name,
seeds__example_seed_csv,
)


@pytest.mark.skip_profile("buenavista", "file", "memory")
class TestMacrosGenerateDatabaseName:
@pytest.fixture(scope="class")
def database_name(self, dbt_profile_target, request):
return urlparse(dbt_profile_target["path"]).path + "_ducky_ducky"

@pytest.fixture(autouse=True)
def run_dbt_scope(self, project, database_name):
project.run_sql(f"CREATE DATABASE IF NOT EXISTS {database_name}")
yield
project.run_sql(f"DROP DATABASE {database_name}")

@pytest.fixture(scope="class")
def seeds(self):
return {
"seed.csv": seeds__example_seed_csv,
}

@pytest.fixture(scope="class")
def models(self):
return {
"model.sql": models__gen_data_macro
}

@pytest.fixture(scope="class")
def macros(self):
return {"db_name.sql": macros__generate_database_name}

@staticmethod
def gen_project_config_update(build_env, org_prefix):
return {
"config-version": 2,
"vars": {
"test": {
"build_env": build_env,
"org_prefix": org_prefix
},
},
"macro-paths": ["macros"],
}

@pytest.fixture(scope="class")
def project_config_update(self):
return self.gen_project_config_update("ducky", "ducky")

def test_dbname_macro(self, project):
# seed command
results = run_dbt(["seed"])
assert len(results) == 1
check_result_nodes_by_name(results, ["seed"])

for _ in range(3):
results = run_dbt(["run"])
assert len(results) == 1
check_result_nodes_by_name(results, ["model"])


@pytest.mark.skip_profile("buenavista", "file", "memory")
class TestMacrosGenerateDatabaseNameUpperCase(TestMacrosGenerateDatabaseName):
@pytest.fixture(scope="class")
def database_name(self, dbt_profile_target, request):
return urlparse(dbt_profile_target["path"]).path + "_ducky_ducky"

@pytest.fixture(scope="class")
def project_config_update(self):
return self.gen_project_config_update("DUCKY", "DUCKY")


@pytest.mark.skip_profile("buenavista", "file", "memory")
class TestMacrosGenerateDatabaseNameLowerCase(TestMacrosGenerateDatabaseName):
@pytest.fixture(scope="class")
def database_name(self, dbt_profile_target, request):
return urlparse(dbt_profile_target["path"]).path + "_DUCKY_DUCKY"

@pytest.fixture(scope="class")
def project_config_update(self):
return self.gen_project_config_update("ducky", "ducky")


@pytest.mark.skip_profile("buenavista", "file", "memory")
class TestMacrosGenerateDatabaseNameAllMixedCase(TestMacrosGenerateDatabaseName):
@pytest.fixture(scope="class")
def database_name(self, dbt_profile_target, request):
return urlparse(dbt_profile_target["path"]).path + "_dUcKy_DUckY"

@pytest.fixture(scope="class")
def project_config_update(self):
return self.gen_project_config_update("DuCkY", "dUcKy")
Loading