From 160b1c8c965c3c8ff176c0fb9e10d8234cf3b9b0 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Thu, 27 Mar 2025 08:47:48 +0000 Subject: [PATCH 1/2] Update SQL server relations to not require an alias --- dbt/adapters/sqlserver/sqlserver_relation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dbt/adapters/sqlserver/sqlserver_relation.py b/dbt/adapters/sqlserver/sqlserver_relation.py index 279634f3..0340fddd 100644 --- a/dbt/adapters/sqlserver/sqlserver_relation.py +++ b/dbt/adapters/sqlserver/sqlserver_relation.py @@ -20,6 +20,7 @@ class SQLServerRelation(BaseRelation): default_factory=lambda: SQLServerIncludePolicy() ) quote_policy: SQLServerQuotePolicy = field(default_factory=lambda: SQLServerQuotePolicy()) + require_alias: bool = False @classproperty def get_relation_type(cls) -> Type[SQLServerRelationType]: From 61f949f480e0eb4e973ef2d55b082a907b391a96 Mon Sep 17 00:00:00 2001 From: Axell Padilla <68310020+axellpadilla@users.noreply.github.com> Date: Fri, 22 May 2026 23:34:29 +0000 Subject: [PATCH 2/2] feat: Enable SQL Server limited-relation no-alias behavior by default set dbt_sqlserver_disable_empty_relation_aliases default to True keep SQL Server relation _render_limited_alias() hook for no-alias behavior update empty-mode fixture SQL to use explicit user aliases so default no-alias behavior is valid preserve opt-in flag path/test for user validation --- dbt/adapters/sqlserver/sqlserver_adapter.py | 12 +++++ dbt/adapters/sqlserver/sqlserver_relation.py | 14 +++-- tests/functional/adapter/dbt/test_empty.py | 56 ++++++++++++++++++-- 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/dbt/adapters/sqlserver/sqlserver_adapter.py b/dbt/adapters/sqlserver/sqlserver_adapter.py index 894c4336..58e2ad12 100644 --- a/dbt/adapters/sqlserver/sqlserver_adapter.py +++ b/dbt/adapters/sqlserver/sqlserver_adapter.py @@ -50,6 +50,9 @@ class SQLServerAdapter(SQLAdapter): def __init__(self, config, mp_context=None): super().__init__(config, mp_context) + SQLServerRelation.disable_empty_relation_aliases = ( + self.behavior.dbt_sqlserver_disable_empty_relation_aliases + ) if self.behavior.dbt_sqlserver_use_native_string_types: self.Column = SQLServerColumnNative @@ -76,6 +79,15 @@ def _behavior_flags(self) -> List[BehaviorFlag]: "macro in your project instead." ), }, + { + "name": "dbt_sqlserver_disable_empty_relation_aliases", + "default": True, + "description": ( + "When True, SQL Server limited relations used by --empty and sample mode " + "do not automatically receive dbt-generated aliases. Set this false to opt " + "out of alias generation temporarily for testing." + ), + }, { "name": "dbt_sqlserver_use_native_string_types", "default": False, diff --git a/dbt/adapters/sqlserver/sqlserver_relation.py b/dbt/adapters/sqlserver/sqlserver_relation.py index f2a84bf9..ff5cedae 100644 --- a/dbt/adapters/sqlserver/sqlserver_relation.py +++ b/dbt/adapters/sqlserver/sqlserver_relation.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import Optional, Type +from typing import ClassVar, Optional, Type from dbt_common.exceptions import DbtRuntimeError @@ -20,20 +20,26 @@ class SQLServerRelation(BaseRelation): default_factory=lambda: SQLServerIncludePolicy() ) quote_policy: SQLServerQuotePolicy = field(default_factory=lambda: SQLServerQuotePolicy()) - require_alias: bool = False + disable_empty_relation_aliases: ClassVar[bool] = True @classproperty def get_relation_type(cls) -> Type[SQLServerRelationType]: return SQLServerRelationType + def _render_limited_alias(self) -> str: + if self.disable_empty_relation_aliases: + return "" + + return super()._render_limited_alias() + 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 1=0) AS {self._render_limited_alias()}" + return f"(select * from {rendered} where 1=0){self._render_limited_alias()}" else: - return f"(select TOP {self.limit} * from {rendered}) AS {self._render_limited_alias()}" + return f"(select TOP {self.limit} * from {rendered}){self._render_limited_alias()}" def __post_init__(self): # Check for length of Redshift table/view names. diff --git a/tests/functional/adapter/dbt/test_empty.py b/tests/functional/adapter/dbt/test_empty.py index 1f3cfc01..c4732599 100644 --- a/tests/functional/adapter/dbt/test_empty.py +++ b/tests/functional/adapter/dbt/test_empty.py @@ -12,14 +12,21 @@ model_sql_sqlserver = """ select * -from {{ ref('model_input') }} +from {{ ref('model_input') }} as model_input_alias union all select * -from {{ source('seed_sources', 'raw_source') }} +from {{ source('seed_sources', 'raw_source') }} as raw_source_alias """ model_inline_sql_sqlserver = """ -select * from {{ source('seed_sources', 'raw_source') }} +select * from {{ source('seed_sources', 'raw_source') }} as raw_source_alias +""" + +model_sql_user_alias_sqlserver = """ +select user_alias.id as id +from {{ ref('model_input') }} as user_alias +inner join {{ source('seed_sources', 'raw_source') }} as source_alias + on user_alias.id = source_alias.id """ @@ -47,6 +54,49 @@ def test_run_with_empty(self, project): self.assert_row_count(project, "model", 0) +class TestEmptyWithUserAlias(BaseTestEmpty): + @pytest.fixture(scope="class") + def models(self): + return { + "model_input.sql": model_input_sql, + "model.sql": model_sql_user_alias_sqlserver, + "sources.yml": schema_sources_yml, + } + + def test_run_with_empty(self, project): + run_dbt(["seed"]) + + run_dbt(["run", "--empty"]) + self.assert_row_count(project, "model", 0) + + +@pytest.mark.xfail( + reason="Upstream dbt empty-mode alias handling needs to be contextual aware.", +) +class TestEmptyWithUserAliasAndNoAliasFlag(BaseTestEmpty): + @pytest.fixture(scope="class") + def models(self): + return { + "model_input.sql": model_input_sql, + "model.sql": model_sql_user_alias_sqlserver, + "sources.yml": schema_sources_yml, + } + + @pytest.fixture(scope="class") + def project_config_update(self): + return { + "flags": { + "dbt_sqlserver_disable_empty_relation_aliases": False, + } + } + + def test_run_with_empty(self, project): + run_dbt(["seed"]) + + run_dbt(["run", "--empty"]) + self.assert_row_count(project, "model", 0) + + class TestemptyInlineSourceRef(BaseTestEmptyInlineSourceRef): @pytest.fixture(scope="class") def models(self):