Skip to content

Commit 13a4e0a

Browse files
feat: add Microsoft Fabric and SQL Server support to elementary CLI (#2140)
1 parent e8dabbf commit 13a4e0a

19 files changed

+212
-72
lines changed

.github/workflows/test-all-warehouses.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ jobs:
100100
trino,
101101
dremio,
102102
spark,
103+
fabric,
104+
sqlserver,
103105
]
104106
uses: ./.github/workflows/test-warehouse.yml
105107
with:

.github/workflows/test-warehouse.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ on:
1818
- duckdb
1919
- trino
2020
- dremio
21+
- fabric
22+
- sqlserver
2123
elementary-ref:
2224
type: string
2325
required: false
@@ -159,6 +161,12 @@ jobs:
159161
run: |
160162
docker compose up -d --build --wait spark-thrift
161163
164+
- name: Start SQL Server
165+
if: inputs.warehouse-type == 'sqlserver'
166+
working-directory: ${{ env.E2E_DBT_PROJECT_DIR }}
167+
run: |
168+
docker compose up -d --wait sqlserver
169+
162170
- name: Setup Python
163171
uses: actions/setup-python@v5
164172
with:
@@ -168,6 +176,14 @@ jobs:
168176
if: inputs.warehouse-type == 'spark'
169177
run: sudo apt-get install -y python3-dev libsasl2-dev gcc
170178

179+
- name: Install ODBC driver for SQL Server
180+
if: inputs.warehouse-type == 'fabric' || inputs.warehouse-type == 'sqlserver'
181+
run: |
182+
curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc > /dev/null
183+
curl -fsSL https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/prod.list | sudo tee /etc/apt/sources.list.d/mssql-release.list > /dev/null
184+
sudo apt-get update
185+
sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18 unixodbc-dev
186+
171187
- name: Install dbt
172188
run: >
173189
pip install
@@ -188,7 +204,7 @@ jobs:
188204
# This enables caching the seeded database state between runs.
189205
IS_DOCKER=false
190206
case "${{ inputs.warehouse-type }}" in
191-
postgres|clickhouse|trino|dremio|duckdb|spark) IS_DOCKER=true ;;
207+
postgres|clickhouse|trino|dremio|duckdb|spark|sqlserver) IS_DOCKER=true ;;
192208
esac
193209
194210
if [ "$IS_DOCKER" = "true" ]; then

elementary/clients/dbt/transient_errors.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@
4545
"service unavailable",
4646
)
4747

48+
_TSQL_TRANSIENT: Tuple[str, ...] = (
49+
"connection timed out",
50+
"could not connect to the server",
51+
"ssl syscall error",
52+
"communication link failure",
53+
"tcp provider: an existing connection was forcibly closed",
54+
"login timeout expired",
55+
)
56+
4857
_ADAPTER_PATTERNS: Dict[str, Tuple[str, ...]] = {
4958
"bigquery": (
5059
# Streaming-buffer delay after a streaming insert.
@@ -109,6 +118,8 @@
109118
# DuckDB runs in-process; transient errors are rare.
110119
# Common patterns (connection reset, broken pipe) are in _COMMON.
111120
),
121+
"fabric": _TSQL_TRANSIENT,
122+
"sqlserver": _TSQL_TRANSIENT,
112123
}
113124

114125
# Pre-computed union of all adapter-specific patterns for the fallback path

elementary/monitor/dbt_project/macros/alerts/population/test_alerts.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,12 @@
128128
select distinct
129129
failed_tests.alert_id,
130130
{# Generate elementary unique id which is used to identify between tests, and set it as alert_class_id #}
131-
coalesce(failed_tests.test_unique_id, 'None') || '.' || coalesce(failed_tests.column_name, 'None') || '.' || coalesce(failed_tests.sub_type, 'None') as alert_class_id,
131+
{{ dbt.concat(["coalesce(failed_tests.test_unique_id, 'None')", "'.'", "coalesce(failed_tests.column_name, 'None')", "'.'", "coalesce(failed_tests.sub_type, 'None')"]) }} as alert_class_id,
132132
case
133133
when failed_tests.test_type = 'schema_change' then failed_tests.test_unique_id
134134
{# In old versions of elementary, elementary_test_results doesn't contain test_short_name, so we use dbt_test short_name. #}
135135
when tests.short_name = 'dimension_anomalies' then failed_tests.test_unique_id
136-
else coalesce(failed_tests.test_unique_id, 'None') || '.' || coalesce(failed_tests.column_name, 'None') || '.' || coalesce(failed_tests.sub_type, 'None')
136+
else {{ dbt.concat(["coalesce(failed_tests.test_unique_id, 'None')", "'.'", "coalesce(failed_tests.column_name, 'None')", "'.'", "coalesce(failed_tests.sub_type, 'None')"]) }}
137137
end as elementary_unique_id,
138138
failed_tests.data_issue_id,
139139
failed_tests.test_execution_id,

elementary/monitor/dbt_project/macros/base_queries/current_tests_run_results_query.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
when elementary_test_results.test_type = 'schema_change' then elementary_test_results.test_unique_id
5050
{# In old versions of elementary, elementary_test_results doesn't contain test_short_name, so we use dbt_test short_name. #}
5151
when dbt_tests.short_name = 'dimension_anomalies' then elementary_test_results.test_unique_id
52-
else coalesce(elementary_test_results.test_unique_id, 'None') || '.' || coalesce(nullif(elementary_test_results.column_name, ''), 'None') || '.' || coalesce(elementary_test_results.test_sub_type, 'None')
52+
else {{ dbt.concat(["coalesce(elementary_test_results.test_unique_id, 'None')", "'.'", "coalesce(nullif(elementary_test_results.column_name, ''), 'None')", "'.'", "coalesce(elementary_test_results.test_sub_type, 'None')"]) }}
5353
end as elementary_unique_id,
5454
elementary_test_results.invocation_id,
5555
elementary_test_results.data_issue_id,

elementary/monitor/dbt_project/macros/base_queries/resources.sql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
select
88
unique_id,
99
name,
10-
schema_name as schema,
10+
schema_name as {{ elementary_cli.edr_quote_identifier('schema') }},
1111
tags,
1212
owner as owners,
13-
database_name as database
13+
database_name as {{ elementary_cli.edr_quote_identifier('database') }}
1414
from dbt_models
1515
{% if exclude_elementary %}
1616
where package_name != 'elementary'
@@ -31,10 +31,10 @@
3131
unique_id,
3232
name,
3333
source_name,
34-
schema_name AS schema,
34+
schema_name AS {{ elementary_cli.edr_quote_identifier('schema') }},
3535
tags,
3636
owner AS owners,
37-
database_name as database
37+
database_name as {{ elementary_cli.edr_quote_identifier('database') }}
3838
from dbt_sources
3939
{% if exclude_elementary %}
4040
where package_name != 'elementary'

elementary/monitor/dbt_project/macros/get_adapter_type_and_unique_id.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@
2121
{% macro duckdb__get_adapter_unique_id() %}
2222
{{ return(target.path) }}
2323
{% endmacro %}
24+
25+
{% macro fabric__get_adapter_unique_id() %}
26+
{{ return(target.server) }}
27+
{% endmacro %}

elementary/monitor/dbt_project/macros/get_latest_invocation.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
{% endif %}
88

99
{% set get_pkg_version_query %}
10+
{% if elementary.is_tsql() %}
11+
select top 1 * from {{ invocations_relation }} order by generated_at desc
12+
{% else %}
1013
select * from {{ invocations_relation }} order by generated_at desc limit 1
14+
{% endif %}
1115
{% endset %}
1216
{% set result = elementary.run_query(get_pkg_version_query) %}
1317
{% if not result %}

elementary/monitor/dbt_project/macros/get_models_runs.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
unique_id,
1212
invocation_id,
1313
name,
14-
schema_name as schema,
14+
schema_name as {{ elementary_cli.edr_quote_identifier('schema') }},
1515
status,
1616
case
1717
when status != 'success' then 0

elementary/monitor/dbt_project/macros/get_test_last_invocation.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
),
99

1010
test_invocation as (
11-
select distinct invocation_id, detected_at
11+
select {% if elementary.is_tsql() %}top 1{% endif %} distinct invocation_id, detected_at
1212
from elementary_test_results
1313
{% if invocation_id %}
1414
where invocation_id = {{ "'" ~ invocation_id ~ "'" }}
1515
{% elif invocation_max_time %}
1616
where detected_at < {{ "'" ~ invocation_max_time ~ "'" }}
1717
{% endif %}
1818
order by detected_at desc
19-
limit 1
19+
{% if not elementary.is_tsql() %}limit 1{% endif %}
2020
)
2121

2222
{% if invocations_relation %}

0 commit comments

Comments
 (0)