Skip to content

Commit

Permalink
Make aliasing subquery configurable for render_limited (#179)
Browse files Browse the repository at this point in the history
  • Loading branch information
colin-rogers-dbt authored Apr 22, 2024
1 parent 9cfb21e commit b61d66d
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 7 deletions.
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
15 changes: 13 additions & 2 deletions dbt/adapters/base/relation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
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

0 comments on commit b61d66d

Please sign in to comment.