Skip to content

Commit 57895ea

Browse files
authored
Merge pull request #5 from axellpadilla/patch/migrate-to-dbt-fabric-1.9.3-to-1.9.6
Subject chore: bump dbt-fabric to v1.9.6 and fix SQL Server unit-test materialization
2 parents d0e2db8 + b948397 commit 57895ea

File tree

5 files changed

+113
-8
lines changed

5 files changed

+113
-8
lines changed

dbt/adapters/sqlserver/sqlserver_connections.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from dbt.adapters.fabric.fabric_connection_manager import (
1212
AZURE_CREDENTIAL_SCOPE,
1313
bool_to_connection_string_arg,
14-
get_pyodbc_attrs_before_accesstoken,
1514
get_pyodbc_attrs_before_credentials,
1615
)
1716

@@ -136,10 +135,7 @@ def open(cls, connection: Connection) -> Connection:
136135
def connect():
137136
logger.debug(f"Using connection string: {con_str_display}")
138137

139-
if credentials.authentication == "ActiveDirectoryAccessToken":
140-
attrs_before = get_pyodbc_attrs_before_accesstoken(credentials.access_token)
141-
else:
142-
attrs_before = get_pyodbc_attrs_before_credentials(credentials)
138+
attrs_before = get_pyodbc_attrs_before_credentials(credentials)
143139

144140
handle = pyodbc.connect(
145141
con_str_concat,

dbt/adapters/sqlserver/sqlserver_relation.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ def render_limited(self) -> str:
3030
if self.limit is None:
3131
return rendered
3232
elif self.limit == 0:
33-
return f"(select * from {rendered} where 1=0) {self._render_limited_alias()}"
33+
return f"(select * from {rendered} where 1=0) AS {self._render_limited_alias()}"
3434
else:
35-
return f"(select TOP {self.limit} * from {rendered}) {self._render_limited_alias()}"
35+
return f"(select TOP {self.limit} * from {rendered}) AS {self._render_limited_alias()}"
3636

3737
def __post_init__(self):
3838
# Check for length of Redshift table/view names.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{% macro check_for_nested_cte(sql) %}
2+
{% if execute %} {# Ensure this runs only at execution time #}
3+
{% set cleaned_sql = sql | lower | replace("\n", " ") %} {# Convert to lowercase and remove newlines #}
4+
{% set cte_count = cleaned_sql.count("with ") %} {# Count occurrences of "WITH " #}
5+
{% if cte_count > 1 %}
6+
{{ return(True) }}
7+
{% else %}
8+
{{ return(False) }} {# No nested CTEs found #}
9+
{% endif %}
10+
{% else %}
11+
{{ return(False) }} {# Return False during parsing #}
12+
{% endif %}
13+
{% endmacro %}
14+
15+
{% macro sqlserver__unit_test_create_table_as(temporary, relation, sql) -%}
16+
{%- set query_label = apply_label() -%}
17+
{%- set contract_config = config.get('contract') -%}
18+
{%- set is_nested_cte = check_for_nested_cte(sql) -%}
19+
20+
{%- if is_nested_cte -%}
21+
{{ exceptions.warn(
22+
"Nested CTE warning: Nested CTEs do not support CTAS. However, 2-level nested CTEs are supported due to a code bug. Please expect this fix in the future."
23+
) }}
24+
{%- endif -%}
25+
26+
{%- if is_nested_cte and contract_config.enforced -%}
27+
28+
{{ exceptions.raise_compiler_error(
29+
"Unit test Materialization error: Since the contract is enforced and the model contains a nested CTE, unit tests cannot be materialized. Please refactor your model or unenforce model and try again."
30+
) }}
31+
32+
{%- elif not is_nested_cte and contract_config.enforced -%}
33+
34+
{# Build CREATE TABLE + INSERT using a temporary view to avoid CTAS semantics #}
35+
CREATE TABLE {{ relation }}
36+
{{ build_columns_constraints(relation) }}
37+
{{ get_assert_columns_equivalent(sql) }};
38+
39+
{%- set listColumns -%}
40+
{%- for column in model['columns'] -%}
41+
{{ "["~column~"]" }}{{ ", " if not loop.last }}
42+
{%- endfor -%}
43+
{%- endset -%}
44+
45+
{%- set tmp_vw_relation = relation.incorporate(path={"identifier": relation.identifier ~ '__dbt_tmp_vw'}, type='view') -%}
46+
{%- do adapter.drop_relation(tmp_vw_relation) -%}
47+
{{ get_create_view_as_sql(tmp_vw_relation, sql) }}
48+
49+
INSERT INTO {{ relation }} ({{ listColumns }})
50+
SELECT {{ listColumns }} FROM {{ tmp_vw_relation }} {{ query_label }};
51+
52+
DROP VIEW IF EXISTS {{ tmp_vw_relation.schema }}.{{ tmp_vw_relation.identifier }};
53+
54+
{%- else -%}
55+
56+
{# Default: use SELECT INTO from an intermediate view so CTEs are preserved and labels are placed inside the selectable statement #}
57+
{%- set tmp_vw_relation = relation.incorporate(path={"identifier": relation.identifier ~ '__dbt_tmp_vw'}, type='view') -%}
58+
{%- do adapter.drop_relation(tmp_vw_relation) -%}
59+
60+
{{ get_create_view_as_sql(tmp_vw_relation, sql) }}
61+
62+
SELECT * INTO {{ relation }} FROM {{ tmp_vw_relation }} {{ query_label }};
63+
64+
DROP VIEW IF EXISTS {{ tmp_vw_relation.schema }}.{{ tmp_vw_relation.identifier }};
65+
66+
{%- endif -%}
67+
68+
{%- endmacro %}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{%- materialization unit, adapter='sqlserver' -%}
2+
3+
{% set relations = [] %}
4+
5+
{% set expected_rows = config.get('expected_rows') %}
6+
{% set expected_sql = config.get('expected_sql') %}
7+
{% set tested_expected_column_names = expected_rows[0].keys() if (expected_rows | length ) > 0 else get_columns_in_query(sql) %}
8+
9+
{%- set target_relation = this.incorporate(type='table') -%}
10+
{%- set temp_relation = make_temp_relation(target_relation)-%}
11+
{% do run_query(sqlserver__unit_test_create_table_as(True, temp_relation, get_empty_subquery_sql(sql))) %}
12+
{%- set columns_in_relation = adapter.get_columns_in_relation(temp_relation) -%}
13+
{%- set column_name_to_data_types = {} -%}
14+
{%- set column_name_to_quoted = {} -%}
15+
{%- for column in columns_in_relation -%}
16+
{%- do column_name_to_data_types.update({column.name|lower: column.data_type}) -%}
17+
{%- do column_name_to_quoted.update({column.name|lower: column.quoted}) -%}
18+
{%- endfor -%}
19+
20+
{%- set expected_column_names_quoted = [] -%}
21+
{%- for column_name in tested_expected_column_names -%}
22+
{%- do expected_column_names_quoted.append(column_name_to_quoted[column_name]) -%}
23+
{%- endfor -%}
24+
25+
{% if not expected_sql %}
26+
{% set expected_sql = get_expected_sql(expected_rows, column_name_to_data_types) %}
27+
{# column_name_to_quoted can be added once supported by get_expected_sql #}
28+
{% endif %}
29+
{% set unit_test_sql = get_unit_test_sql(sql, expected_sql, expected_column_names_quoted) %}
30+
31+
{% call statement('main', fetch_result=True) -%}
32+
33+
{{ unit_test_sql }}
34+
35+
{%- endcall %}
36+
37+
{% do adapter.drop_relation(temp_relation) %}
38+
39+
{{ return({'relations': relations}) }}
40+
41+
{%- endmaterialization -%}

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def run(self):
6666
packages=find_namespace_packages(include=["dbt", "dbt.*"]),
6767
include_package_data=True,
6868
install_requires=[
69-
"dbt-fabric==1.9.3",
69+
"dbt-fabric==1.9.6",
7070
"dbt-core>=1.9.0,<2.0",
7171
"dbt-common>=1.0,<2.0",
7272
"dbt-adapters>=1.11.0,<2.0",

0 commit comments

Comments
 (0)