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

Make aliasing subquery configurable for render_limited #179

Merged
merged 9 commits into from
Apr 22, 2024
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20240418-155123.yaml
Original file line number Diff line number Diff line change
@@ -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"
7 changes: 7 additions & 0 deletions .changes/unreleased/Features-20240418-155531.yaml
Original file line number Diff line number Diff line change
@@ -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'
19 changes: 19 additions & 0 deletions dbt-tests-adapter/dbt/tests/adapter/empty/test_empty.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
13 changes: 11 additions & 2 deletions dbt/adapters/base/relation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}"
colin-rogers-dbt marked this conversation as resolved.
Show resolved Hide resolved
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(
Expand Down
23 changes: 18 additions & 5 deletions tests/unit/test_relation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading