From b3f1be7d4b73054a8ce2d1ad5da26539e2361f05 Mon Sep 17 00:00:00 2001 From: Colin Date: Thu, 18 Apr 2024 14:58:52 -0700 Subject: [PATCH 1/6] make aliasing optional for empty/limit rendering --- .../dbt/tests/adapter/empty/test_empty.py | 18 ++++++++++++++++++ dbt/adapters/base/relation.py | 13 +++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) 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..db632897 100644 --- a/dbt-tests-adapter/dbt/tests/adapter/empty/test_empty.py +++ b/dbt-tests-adapter/dbt/tests/adapter/empty/test_empty.py @@ -71,6 +71,24 @@ def test_run_with_empty(self, project): run_dbt(["run", "--empty"]) 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..a6d98ee2 100644 --- a/dbt/adapters/base/relation.py +++ b/dbt/adapters/base/relation.py @@ -47,6 +47,7 @@ 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 +206,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( From 6fd033b74b7a3e0867aa9d9f5c98a950699a05ea Mon Sep 17 00:00:00 2001 From: Colin Date: Thu, 18 Apr 2024 15:20:06 -0700 Subject: [PATCH 2/6] update unit test --- tests/unit/test_relation.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) 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 From 567e496e0f5bef8618c25fb7eafa0447fc4272d3 Mon Sep 17 00:00:00 2001 From: Colin Date: Thu, 18 Apr 2024 15:31:40 -0700 Subject: [PATCH 3/6] fix test_empty.py --- dbt-tests-adapter/dbt/tests/adapter/empty/test_empty.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 db632897..2249d98d 100644 --- a/dbt-tests-adapter/dbt/tests/adapter/empty/test_empty.py +++ b/dbt-tests-adapter/dbt/tests/adapter/empty/test_empty.py @@ -71,7 +71,8 @@ def test_run_with_empty(self, project): run_dbt(["run", "--empty"]) self.assert_row_count(project, "model", 0) -class BaseTestEmptyInlineSourceRef(BaseTestEmpty) + +class BaseTestEmptyInlineSourceRef(BaseTestEmpty): @pytest.fixture(scope="class") def models(self): model_sql = """ From e93d20818bb4decee7dbe0cd16ab108af1ff29ec Mon Sep 17 00:00:00 2001 From: Colin Date: Thu, 18 Apr 2024 15:51:42 -0700 Subject: [PATCH 4/6] add changie --- .changes/unreleased/Features-20240418-155123.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changes/unreleased/Features-20240418-155123.yaml 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" From a98912d53079ac89b20658d42bba3756b1b10c64 Mon Sep 17 00:00:00 2001 From: Colin Date: Thu, 18 Apr 2024 15:55:43 -0700 Subject: [PATCH 5/6] add second changie --- .changes/unreleased/Features-20240418-155531.yaml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changes/unreleased/Features-20240418-155531.yaml 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' From 80b9309ef5e23331f8e8ddb9ee025553d4caa31b Mon Sep 17 00:00:00 2001 From: Colin Date: Mon, 22 Apr 2024 09:15:22 -0700 Subject: [PATCH 6/6] reformat relation.py --- dbt/adapters/base/relation.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dbt/adapters/base/relation.py b/dbt/adapters/base/relation.py index a6d98ee2..210a2dcd 100644 --- a/dbt/adapters/base/relation.py +++ b/dbt/adapters/base/relation.py @@ -47,7 +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 + 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 @@ -207,7 +209,7 @@ def render(self) -> str: 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 + """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: