diff --git a/.changes/unreleased/Features-20240418-155123.yaml b/.changes/unreleased/Features-20240418-155123.yaml new file mode 100644 index 00000000..a3d9a56d --- /dev/null +++ b/.changes/unreleased/Features-20240418-155123.yaml @@ -0,0 +1,6 @@ +kind: Features +body: Allow adapters to opt out of aliasing the subquery generated by render_limited +time: 2024-04-18T15:51:23.584295-07:00 +custom: + Author: colin-rogers-dbt + Issue: "124" diff --git a/.changes/unreleased/Features-20240418-155531.yaml b/.changes/unreleased/Features-20240418-155531.yaml new file mode 100644 index 00000000..02614b08 --- /dev/null +++ b/.changes/unreleased/Features-20240418-155531.yaml @@ -0,0 +1,7 @@ +kind: Features +body: subquery alias generated by render_limited now includes the relation name to + mitigate duplicate aliasing +time: 2024-04-18T15:55:31.826729-07:00 +custom: + Author: colin-rogers-dbt + Issue: ' 124' diff --git a/dbt-tests-adapter/dbt/tests/adapter/empty/test_empty.py b/dbt-tests-adapter/dbt/tests/adapter/empty/test_empty.py index 373a13ee..2249d98d 100644 --- a/dbt-tests-adapter/dbt/tests/adapter/empty/test_empty.py +++ b/dbt-tests-adapter/dbt/tests/adapter/empty/test_empty.py @@ -72,5 +72,24 @@ def test_run_with_empty(self, project): self.assert_row_count(project, "model", 0) +class BaseTestEmptyInlineSourceRef(BaseTestEmpty): + @pytest.fixture(scope="class") + def models(self): + model_sql = """ + select * from {{ source('seed_sources', 'raw_source') }} as raw_source + """ + + return { + "model.sql": model_sql, + "sources.yml": schema_sources_yml, + } + + def test_run_with_empty(self, project): + # create source from seed + run_dbt(["seed"]) + run_dbt(["run", "--empty", "--debug"]) + self.assert_row_count(project, "model", 0) + + class TestEmpty(BaseTestEmpty): pass diff --git a/dbt/adapters/base/relation.py b/dbt/adapters/base/relation.py index d74b29bd..210a2dcd 100644 --- a/dbt/adapters/base/relation.py +++ b/dbt/adapters/base/relation.py @@ -47,6 +47,9 @@ class BaseRelation(FakeAPIObject, Hashable): quote_policy: Policy = field(default_factory=lambda: Policy()) dbt_created: bool = False limit: Optional[int] = None + require_alias: bool = ( + True # used to govern whether to add an alias when render_limited is called + ) # register relation types that can be renamed for the purpose of replacing relations using stages and backups # adding a relation type here also requires defining the associated rename macro @@ -205,14 +208,22 @@ def render(self) -> str: # if there is nothing set, this will return the empty string. return ".".join(part for _, part in self._render_iterator() if part is not None) + def _render_limited_alias(self) -> str: + """Some databases require an alias for subqueries (postgres, mysql) for all others we want to avoid adding + an alias as it has the potential to introduce issues with the query if the user also defines an alias. + """ + if self.require_alias: + return f" _dbt_limit_subq_{self.table}" + return "" + def render_limited(self) -> str: rendered = self.render() if self.limit is None: return rendered elif self.limit == 0: - return f"(select * from {rendered} where false limit 0) _dbt_limit_subq" + return f"(select * from {rendered} where false limit 0){self._render_limited_alias()}" else: - return f"(select * from {rendered} limit {self.limit}) _dbt_limit_subq" + return f"(select * from {rendered} limit {self.limit}){self._render_limited_alias()}" def quoted(self, identifier): return "{quote_char}{identifier}{quote_char}".format( diff --git a/tests/unit/test_relation.py b/tests/unit/test_relation.py index aa9cda25..a1c01c5c 100644 --- a/tests/unit/test_relation.py +++ b/tests/unit/test_relation.py @@ -43,25 +43,38 @@ def test_can_be_replaced_default(): @pytest.mark.parametrize( - "limit,expected_result", + "limit,require_alias,expected_result", [ - (None, '"test_database"."test_schema"."test_identifier"'), + (None, False, '"test_database"."test_schema"."test_identifier"'), ( 0, - '(select * from "test_database"."test_schema"."test_identifier" where false limit 0) _dbt_limit_subq', + True, + '(select * from "test_database"."test_schema"."test_identifier" where false limit 0) _dbt_limit_subq_test_identifier', ), ( 1, - '(select * from "test_database"."test_schema"."test_identifier" limit 1) _dbt_limit_subq', + True, + '(select * from "test_database"."test_schema"."test_identifier" limit 1) _dbt_limit_subq_test_identifier', + ), + ( + 0, + False, + '(select * from "test_database"."test_schema"."test_identifier" where false limit 0)', + ), + ( + 1, + False, + '(select * from "test_database"."test_schema"."test_identifier" limit 1)', ), ], ) -def test_render_limited(limit, expected_result): +def test_render_limited(limit, require_alias, expected_result): my_relation = BaseRelation.create( database="test_database", schema="test_schema", identifier="test_identifier", limit=limit, + require_alias=require_alias, ) actual_result = my_relation.render_limited() assert actual_result == expected_result