Skip to content

feat: add Microsoft Fabric and SQL Server support to elementary CLI#2140

Merged
haritamar merged 21 commits intomasterfrom
devin/ELE-5285-1772890704
Mar 8, 2026
Merged

feat: add Microsoft Fabric and SQL Server support to elementary CLI#2140
haritamar merged 21 commits intomasterfrom
devin/ELE-5285-1772890704

Conversation

@devin-ai-integration
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot commented Mar 7, 2026

feat: add Microsoft Fabric and SQL Server support to elementary CLI

Summary

CLI-side counterpart to ELE-5282 (dbt-data-reliability Fabric/SQL Server support). Wires in dbt-fabric and dbt-sqlserver as optional dependencies, adds adapter-specific overrides needed for edr commands, and integrates both adapters into the CI test matrix.

Changes:

  • pyproject.toml — Added dbt-fabric and dbt-sqlserver as optional deps with fabric and sqlserver extras (pip install "elementary-data[fabric]" / pip install "elementary-data[sqlserver]")
  • packages.yml — Points dbt-data-reliability to merged commit 534afc63c75d28b87d7cbd3b222dd3ea9a980f7b on master (ELE-5282 merged)
  • get_adapter_type_and_unique_id.sql — Added fabric__ dispatch macro returning target.server (sqlserver inherits via adapter fallback)
  • transient_errors.py — Added shared _TSQL_TRANSIENT constant with T-SQL/ODBC transient error patterns, referenced by both fabric and sqlserver adapter entries
  • profiles.yml.j2 — Added local Docker test profiles for both adapters (connecting to SQL Server on localhost:1433)
  • schema.yml — Added fabric and sqlserver to the varchar data type branches in test models
  • docker-compose.yml — Added SQL Server 2022 Docker service with healthcheck (used by both fabric and sqlserver targets)
  • test-all-warehouses.yml — Added fabric and sqlserver to the CI warehouse-type matrix
  • test-warehouse.yml — Added SQL Server container startup using docker compose up -d --wait (healthcheck-based), ODBC driver installation, and marked both adapters as Docker-based for schema naming

T-SQL compatibility fixes

Several T-SQL syntax incompatibilities were discovered and fixed to get edr monitor report working on Fabric/SQL Server:

  • ||dbt.concat() — T-SQL doesn't support || for string concatenation. Replaced with dbt.concat() (the dbt cross-DB macro) in test_alerts.sql, current_tests_run_results_query.sql, and get_test_results.sql. This generates || on Postgres/Redshift/Snowflake and concat() on T-SQL adapters, so it works across all warehouses.
  • LIMITTOP — T-SQL uses TOP N instead of LIMIT N. Added conditional branches using elementary.is_tsql() in get_latest_invocation.sql, get_test_last_invocation.sql, and test_conn.sql.
  • Nested CTEs — T-SQL forbids WITH clauses inside subqueries or other CTEs. Added fabric__get_test_results macro that materializes intermediate queries as temp tables (test_results_base, ordered_test_results) and cleans them up after use. sqlserver inherits this automatically via adapter fallback.
  • ORDER BY in views — T-SQL doesn't allow ORDER BY in view/subquery definitions without TOP or OFFSET. Moved ORDER BY clauses outside of create_temp_table calls to the final run_query instead.
  • Reserved keywordsschema and database are T-SQL reserved words. Created edr_quote_identifier macro in macros/utils/edr_quote_identifier.sql that wraps identifiers in square brackets ([identifier]) for T-SQL adapters and returns them bare for all others. Applied to get_models_runs, get_model_resources, and get_source_resources — eliminating the need for full fabric__ duplicate macros.

CI Status: 11/13 warehouse adapters passing — fabric, sqlserver, spark, snowflake, redshift, databricks, postgres, clickhouse, trino, athena, dremio all green. Two transient failures (duckdb network timeout, bigquery pre-existing test failures) unrelated to this PR's changes.

Updates since last revision

Post-merge cleanup (commits 54614537, 69898366):

  • Updated packages.yml from dev branch reference (devin/ELE-5282-1772640713) to merged master commit hash (534afc63c75d28b87d7cbd3b222dd3ea9a980f7b)
  • Removed hardcoded dev branch pin from CI workflow (test-all-warehouses.yml)
  • Refactored SQL Server startup in CI to use docker compose up -d --wait sqlserver instead of manual timeout/sqlcmd polling loop — leverages existing healthcheck for more robust startup detection

Code quality refactoring (commit 39a3c8d4):

  • Extracted _TSQL_TRANSIENT shared tuple in transient_errors.py to eliminate duplicate error patterns between fabric and sqlserver (net -12 lines)
  • Removed redundant sqlserver__get_adapter_unique_id dispatch — sqlserver automatically falls back to fabric__ implementation (net -4 lines)
  • Refactored fabric__get_test_results to use select * in Step 3 instead of 35 explicit columns — eliminates column drift maintenance burden
  • Extracted _process_raw_test_results helper macro to deduplicate Jinja post-processing logic across default, fabric, and clickhouse dispatches (net -77 lines)
  • Total: -93 lines of code removed while preserving functionality

Refactored T-SQL reserved keyword quoting (commit d566f902):

  • Created edr_quote_identifier(identifier) macro to conditionally wrap identifiers in [...] for T-SQL adapters
  • Removed full fabric__ duplicate macros for get_models_runs, get_model_resources, and get_source_resources
  • Net -93 lines of code by eliminating duplication

Replaced inline T-SQL checks with elementary.is_tsql() macro (commit 40c8044d):

  • Replaced all 4 occurrences of target.type in ('fabric', 'sqlserver') with calls to elementary.is_tsql() macro (from dbt-data-reliability master)
  • Updated edr_quote_identifier.sql, get_test_last_invocation.sql, get_latest_invocation.sql, and test_conn.sql
  • This uses adapter dispatch pattern from dbt-data-reliability for cleaner, more maintainable T-SQL detection

Review & Testing Checklist for Human

  • Review _process_raw_test_results helper macro — This new shared helper is used by default, fabric, and clickhouse dispatches. Verify the logic is equivalent to what was in each dispatch previously and that consolidating them doesn't introduce subtle behavioral differences.
  • Review fabric__get_test_results Step 3 select * change — Changed from 35 explicit columns to select *. Verify that this doesn't introduce unexpected columns or break downstream logic. Consider whether there are cases where the upstream temp table might have extra columns that shouldn't be included.
  • Test end-to-end on Fabric/SQL Server — While CI passes (11/13), manual testing of edr monitor report on a real Fabric or SQL Server warehouse would provide additional confidence. Verify alerts, test results, and model/source resources are fetched correctly.
  • Verify dbt-data-reliability commit hash — Confirm that 534afc63c75d28b87d7cbd3b222dd3ea9a980f7b is the correct merged commit on master and includes all required elementary.is_tsql() and Fabric/SQL Server support.

Notes

  • ELE-5282 now merged to master — this PR uses commit hash 534afc63c75d28b87d7cbd3b222dd3ea9a980f7b from dbt-data-reliability
  • The _TSQL_TRANSIENT constant is now shared between fabric and sqlserver adapter entries in _ADAPTER_PATTERNS — both use identical T-SQL/ODBC error patterns.
  • Both fabric and sqlserver share the same SQL Server Docker container in CI. The container starts using healthcheck-based startup (--wait flag) for robustness.
  • sqlserver adapter automatically falls back to fabric__ implementations — per dbt-sqlserver's design, if a sqlserver__ dispatch doesn't exist, it automatically uses fabric__. This means only fabric__ variants are needed for T-SQL compatibility.
  • The edr_quote_identifier macro now uses elementary.is_tsql() (adapter dispatch from dbt-data-reliability) instead of inline target.type checks for cleaner maintainability.
  • Two CI test failures (duckdb, bigquery) are transient/pre-existing and unrelated to this PR — duckdb had a network timeout during dbt deps, bigquery has 60 pre-existing test failures
  • Link to Devin Session
  • Requested by: @haritamar

Summary by CodeRabbit

  • New Features
    • Added support for Microsoft Fabric and SQL Server adapters, including transient error recognition and server-based adapter identifiers.
    • Added T-SQL compatibility fixes for string concatenation (using dbt.concat()), LIMIT/TOP, nested CTEs, ORDER BY in views, and reserved keyword quoting.
    • Created edr_quote_identifier macro to eliminate code duplication for T-SQL reserved keyword handling.
    • Replaced inline T-SQL checks with elementary.is_tsql() macro for cleaner maintainability.
    • Extracted _TSQL_TRANSIENT shared tuple to eliminate duplicate transient error patterns.
    • Created _process_raw_test_results helper macro to deduplicate Jinja post-processing logic.
    • Refactored SQL Server CI startup to use healthcheck-based approach with docker compose --wait
  • Tests
    • Expanded e2e and CI test matrices to include Fabric and SQL Server, added local SQL Server service definitions, profile targets, and workflow steps (healthcheck-based readiness, ODBC drivers).
    • Broadened test model type mappings to cover Fabric/SQL Server.
  • Chores
    • Added optional dbt-fabric/dbt-sqlserver packages and updated package source declarations to merged master commit.
  • Bug Fixes
    • Standardized SQL string concatenation using dbt.concat() cross-DB macro to ensure compatibility across all warehouse adapters (Postgres, Redshift, Snowflake, T-SQL, etc.).
    • Fixed nested CTE, ORDER BY, and reserved keyword issues for T-SQL compatibility.
  • Refactoring
    • Refactored T-SQL reserved keyword quoting to use edr_quote_identifier macro, reducing code by ~93 lines.
    • Replaced inline target.type in ('fabric', 'sqlserver') checks with elementary.is_tsql() macro calls across 4 macro files.
    • Extracted shared transient error tuple and removed redundant sqlserver dispatch, reducing code by ~93 lines.
    • Refactored fabric__get_test_results to use select * and extracted shared processing helper, reducing code by ~77 lines.

Open with Devin

devin-ai-integration bot and others added 4 commits March 7, 2026 13:40
- Add dbt-fabric as optional dependency with fabric extra in pyproject.toml
- Update packages.yml to point to Fabric-enabled dbt-data-reliability branch
- Add fabric__get_adapter_unique_id macro (uses target.server)
- Add Fabric transient error patterns to transient_errors.py
- Add Fabric to data type mappings in test schema.yml
- Update package-lock.yml for new dbt-data-reliability revision

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
Add local Docker SQL Server profile for Fabric adapter testing.
Uses same local Docker test credentials pattern as other adapters
(e.g. postgres admin/admin, dremio dremio123).

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
- Add dbt-sqlserver as optional dependency with sqlserver extra in pyproject.toml
- Add sqlserver__get_adapter_unique_id macro (uses target.server)
- Add SQL Server transient error patterns to transient_errors.py
- Add sqlserver to data type mappings in test schema.yml

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
Add local Docker SQL Server profile for sqlserver adapter testing,
alongside the existing fabric profile.

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
@linear
Copy link

linear bot commented Mar 7, 2026

@devin-ai-integration
Copy link
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@github-actions
Copy link
Contributor

github-actions bot commented Mar 7, 2026

👋 @devin-ai-integration[bot]
Thank you for raising your pull request.
Please make sure to add tests and document all user-facing changes.
You can do this by editing the docs files in this pull request.

@coderabbitai
Copy link

coderabbitai bot commented Mar 7, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Azure Fabric and SQL Server support across transient-error patterns, adapter-identity macros, optional dbt packages/extras, CI/workflows, docker-compose for e2e, test profiles, model type mappings, and several SQL macro concatenation normalizations.

Changes

Cohort / File(s) Summary
Adapter transient errors
elementary/clients/dbt/transient_errors.py
Added adapter-specific transient error substrings for fabric and sqlserver.
Adapter identity macros
elementary/monitor/dbt_project/macros/get_adapter_type_and_unique_id.sql
Added fabric__get_adapter_unique_id() and sqlserver__get_adapter_unique_id() returning target.server.
Optional dbt packages / extras
pyproject.toml
Added optional dependencies dbt-fabric, dbt-sqlserver; new extras fabric, sqlserver; included them in all extras.
DBT package manifests
elementary/monitor/dbt_project/package-lock.yml, elementary/monitor/dbt_project/packages.yml
Reworked dbt_utils entry to include name; updated elementary git revision/sha1; updated dbt-data-reliability git ref.
CI workflows
.github/workflows/test-all-warehouses.yml, .github/workflows/test-warehouse.yml
Added fabric and sqlserver to warehouse matrices/inputs; added SQL Server startup and ODBC driver install steps; extended Docker-backed adapter handling and profile/schema logic.
E2E compose & model mappings
tests/e2e_dbt_project/docker-compose.yml, tests/e2e_dbt_project/models/schema.yml
Added sqlserver service to docker-compose; expanded model data_type mapping to treat fabric and sqlserver as varchar.
Test profiles
tests/profiles/profiles.yml.j2
Added fabric and sqlserver output targets and included them in test target lists and cloud mappings.
SQL concatenation normalizations
elementary/monitor/dbt_project/macros/.../test_alerts.sql, .../current_tests_run_results_query.sql, .../get_test_results.sql
Replaced `

Sequence Diagram(s)

sequenceDiagram
participant CI as CI Workflow
participant Docker as Docker Host
participant SQLSvc as SQL Server container
participant Runner as Runner Host
participant DBT as dbt (macros & profiles)
participant Tests as Test Suite

CI->>Docker: start sqlserver service
Docker-->>SQLSvc: container runs + healthcheck
SQLSvc-->>CI: healthcheck OK
CI->>Runner: install ODBC driver (msodbcsql18) for sqlserver/fabric
CI->>DBT: invoke dbt with profile (uses target.server via new macros)
DBT->>Tests: macros return adapter id
Tests->>DBT: run tests (transient error patterns considered per adapter)
DBT-->>CI: test results
CI->>Tests: trigger e2e workflows / report
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through configs, added two new peers,
Fabric and SQL, now part of our gears.
Containers wake, macros fetch server name,
Patterns and profiles aligned in the same game.
A little thump—tests pass, carrots for the gains! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main objective: adding Microsoft Fabric and SQL Server support to the elementary CLI, which aligns with the comprehensive changes across dependencies, macros, adapters, and CI workflows.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch devin/ELE-5285-1772890704

Comment @coderabbitai help to get the list of available commands and usage tips.

- Add fabric and sqlserver to warehouse-type matrix in test-all-warehouses.yml
- Add fabric and sqlserver to workflow_dispatch options in test-warehouse.yml
- Add SQL Server Docker service to docker-compose.yml
- Add SQL Server startup step and ODBC driver install to CI workflow
- Mark fabric/sqlserver as Docker-based adapters for schema naming

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
elementary/clients/dbt/transient_errors.py (1)

112-127: LGTM!

The transient error patterns for fabric and sqlserver are appropriate for T-SQL/ODBC connections. The patterns cover common connectivity issues like timeouts, connection failures, and SSL errors.

The patterns are identical between both adapters—consider extracting them into a shared tuple (similar to _DATABRICKS_PATTERNS) if future maintenance is a concern, though keeping them separate is also valid for independent evolution.

♻️ Optional: Extract shared patterns
+_MSSQL_PATTERNS: Tuple[str, ...] = (
+    "connection timed out",
+    "could not connect to the server",
+    "ssl syscall error",
+    "communication link failure",
+    "tcp provider: an existing connection was forcibly closed",
+    "login timeout expired",
+)
+
 _ADAPTER_PATTERNS: Dict[str, Tuple[str, ...]] = {
     # ... existing entries ...
-    "fabric": (
-        "connection timed out",
-        "could not connect to the server",
-        "ssl syscall error",
-        "communication link failure",
-        "tcp provider: an existing connection was forcibly closed",
-        "login timeout expired",
-    ),
-    "sqlserver": (
-        "connection timed out",
-        "could not connect to the server",
-        "ssl syscall error",
-        "communication link failure",
-        "tcp provider: an existing connection was forcibly closed",
-        "login timeout expired",
-    ),
+    "fabric": _MSSQL_PATTERNS,
+    "sqlserver": _MSSQL_PATTERNS,
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@elementary/clients/dbt/transient_errors.py` around lines 112 - 127, The two
identical pattern tuples for "fabric" and "sqlserver" should be extracted to a
shared constant to avoid duplication and ease maintenance: create a shared tuple
(e.g. _TSQL_PATTERNS) containing the repeated strings and replace the current
per-adapter tuples for the "fabric" and "sqlserver" entries with a reference to
that constant; update any import/usage comments to mirror the existing style
used for _DATABRICKS_PATTERNS so the semantics remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@elementary/monitor/dbt_project/macros/get_adapter_type_and_unique_id.sql`:
- Around line 24-31: The two adapter macros fabric__get_adapter_unique_id and
sqlserver__get_adapter_unique_id currently read target.server directly which
breaks for dbt-sqlserver where the canonical field is host; update both macros
to return a tolerant lookup using target.get('host') or target.get('server')
(i.e., return target.get('host') or target.get('server')) so the macros work for
configurations that use either host or server.

In `@elementary/monitor/dbt_project/packages.yml`:
- Line 5: The packages.yml currently pins the dependency using a temporary
branch name in the revision field (revision: devin/ELE-5282-1772640713); update
that revision to a stable identifier by replacing the branch reference with the
final commit hash or release tag for the merged ELE-5282 change. Locate the
revision entry in packages.yml and substitute the branch string with the
concrete commit SHA or version (e.g., <commit-hash> or vX.Y.Z) so builds are
reproducible, then re-run dependency resolution to verify the lockfile resolves
correctly.

In `@pyproject.toml`:
- Around line 59-60: The pyproject.toml entries for dbt-fabric and dbt-sqlserver
currently allow a broad range (dbt-fabric and dbt-sqlserver keys) which can
permit adapter versions incompatible with the dbt-core version; update those two
dependency constraints to tightly track the dbt-core major.minor you support
(e.g., restrict each adapter to the same major.minor as your dbt-core range or
use an exact major.minor-compatible constraint) so adapters must be the same
dbt-core minor series as the project's dbt-core.

---

Nitpick comments:
In `@elementary/clients/dbt/transient_errors.py`:
- Around line 112-127: The two identical pattern tuples for "fabric" and
"sqlserver" should be extracted to a shared constant to avoid duplication and
ease maintenance: create a shared tuple (e.g. _TSQL_PATTERNS) containing the
repeated strings and replace the current per-adapter tuples for the "fabric" and
"sqlserver" entries with a reference to that constant; update any import/usage
comments to mirror the existing style used for _DATABRICKS_PATTERNS so the
semantics remain unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 756529fe-ba59-49e7-bee0-5ed79287d689

📥 Commits

Reviewing files that changed from the base of the PR and between e8dabbf and b54a7a1.

📒 Files selected for processing (7)
  • elementary/clients/dbt/transient_errors.py
  • elementary/monitor/dbt_project/macros/get_adapter_type_and_unique_id.sql
  • elementary/monitor/dbt_project/package-lock.yml
  • elementary/monitor/dbt_project/packages.yml
  • pyproject.toml
  • tests/e2e_dbt_project/models/schema.yml
  • tests/profiles/profiles.yml.j2

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration bot and others added 3 commits March 7, 2026 14:03
… for dbt-data-reliability

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
…rver as Docker target

dbt-fabric only supports Azure AD authentication methods and cannot connect
to a local SQL Server Docker container with SQL auth. Move fabric from
Docker targets to cloud targets using ServicePrincipal authentication
(matching the dbt-data-reliability configuration).

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
SQL Server / T-SQL does not support the || operator for string
concatenation. Replace all occurrences with the cross-database
concat() function which is supported by all target databases.

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@elementary/monitor/dbt_project/macros/alerts/population/test_alerts.sql`:
- Around line 131-137: The handling of failed_tests.column_name is inconsistent:
change uses of coalesce(failed_tests.column_name, 'None') in the expressions
producing alert_class_id and elementary_unique_id so that empty string values
are treated the same as NULL; specifically replace
coalesce(failed_tests.column_name, 'None') with
coalesce(nullif(failed_tests.column_name, ''), 'None') in the concat for
alert_class_id and in the else branch used for elementary_unique_id (while
keeping the special cases for schema_change and tests.short_name =
'dimension_anomalies' intact) so alert_class_id and elementary_unique_id are
consistent with current_tests_run_results_query.sql and get_test_results.sql.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c7c5ff1d-1680-4d29-9da0-eee73438a1b1

📥 Commits

Reviewing files that changed from the base of the PR and between cfc9b68 and 7b51cd7.

📒 Files selected for processing (3)
  • elementary/monitor/dbt_project/macros/alerts/population/test_alerts.sql
  • elementary/monitor/dbt_project/macros/base_queries/current_tests_run_results_query.sql
  • elementary/monitor/dbt_project/macros/get_test_results.sql

Comment on lines 131 to 137
concat(coalesce(failed_tests.test_unique_id, 'None'), '.', coalesce(failed_tests.column_name, 'None'), '.', coalesce(failed_tests.sub_type, 'None')) as alert_class_id,
case
when failed_tests.test_type = 'schema_change' then failed_tests.test_unique_id
{# In old versions of elementary, elementary_test_results doesn't contain test_short_name, so we use dbt_test short_name. #}
when tests.short_name = 'dimension_anomalies' then failed_tests.test_unique_id
else coalesce(failed_tests.test_unique_id, 'None') || '.' || coalesce(failed_tests.column_name, 'None') || '.' || coalesce(failed_tests.sub_type, 'None')
else concat(coalesce(failed_tests.test_unique_id, 'None'), '.', coalesce(failed_tests.column_name, 'None'), '.', coalesce(failed_tests.sub_type, 'None'))
end as elementary_unique_id,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Inconsistent empty string handling for column_name.

The column_name handling differs from other files in this PR:

  • Here: coalesce(failed_tests.column_name, 'None') — empty string '' remains ''
  • In current_tests_run_results_query.sql and get_test_results.sql: coalesce(nullif(column_name, ''), 'None') — empty string becomes 'None'

This could produce mismatched alert_class_id / elementary_unique_id values for the same test when column_name is an empty string vs NULL.

🔧 Proposed fix to add nullif for consistency
-        concat(coalesce(failed_tests.test_unique_id, 'None'), '.', coalesce(failed_tests.column_name, 'None'), '.', coalesce(failed_tests.sub_type, 'None')) as alert_class_id,
+        concat(coalesce(failed_tests.test_unique_id, 'None'), '.', coalesce(nullif(failed_tests.column_name, ''), 'None'), '.', coalesce(failed_tests.sub_type, 'None')) as alert_class_id,
         case
             when failed_tests.test_type = 'schema_change' then failed_tests.test_unique_id
             {# In old versions of elementary, elementary_test_results doesn't contain test_short_name, so we use dbt_test short_name. #}
             when tests.short_name = 'dimension_anomalies' then failed_tests.test_unique_id
-            else concat(coalesce(failed_tests.test_unique_id, 'None'), '.', coalesce(failed_tests.column_name, 'None'), '.', coalesce(failed_tests.sub_type, 'None'))
+            else concat(coalesce(failed_tests.test_unique_id, 'None'), '.', coalesce(nullif(failed_tests.column_name, ''), 'None'), '.', coalesce(failed_tests.sub_type, 'None'))
         end as elementary_unique_id,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@elementary/monitor/dbt_project/macros/alerts/population/test_alerts.sql`
around lines 131 - 137, The handling of failed_tests.column_name is
inconsistent: change uses of coalesce(failed_tests.column_name, 'None') in the
expressions producing alert_class_id and elementary_unique_id so that empty
string values are treated the same as NULL; specifically replace
coalesce(failed_tests.column_name, 'None') with
coalesce(nullif(failed_tests.column_name, ''), 'None') in the concat for
alert_class_id and in the else branch used for elementary_unique_id (while
keeping the special cases for schema_change and tests.short_name =
'dimension_anomalies' intact) so alert_class_id and elementary_unique_id are
consistent with current_tests_run_results_query.sql and get_test_results.sql.

devin-ai-integration bot and others added 8 commits March 7, 2026 14:41
Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
T-SQL does not allow nested CTEs (WITH inside WITH). The default
get_test_results macro wraps current_tests_run_results_query (which
starts with WITH) inside another WITH clause, creating nested CTEs.

The fabric__ dispatch materialises the inner query into a temp table
first, then builds ordered_test_results on top without nesting.
sqlserver inherits from fabric automatically, so no sqlserver__
override is needed.

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
…lity

T-SQL forbids ORDER BY in views/subqueries/CTEs unless TOP or OFFSET
is also specified. The fabric__create_temp_table uses a VIEW workaround,
so ORDER BY must be in the final SELECT, not in the materialised query.

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
…atibility

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
…ble-quotes

Double-quoted identifiers break Spark SQL. Use adapter dispatch with
fabric__ variants that quote 'schema' and 'database' using T-SQL square
brackets, while the default keeps the original unquoted aliases.

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
… compatibility

Redshift's concat() only accepts 2 arguments. The dbt.concat() macro
generates the correct SQL per adapter (|| on Postgres/Redshift/Snowflake,
concat() on others).

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>


{% macro fabric__get_model_resources(exclude_elementary=true) %}
{# T-SQL: 'schema' and 'database' are reserved keywords — quote with square brackets #}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have a generic macros for quoting instead of duplicating?
Maybe edr_wuote already exists? Not sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I checked — edr_quote in dbt-data-reliability is for quoting string values (wraps in single quotes), not identifiers. edr_quote_column uses adapter.quote() which does adapter-native identifier quoting ([col] on T-SQL, "col" on Postgres).

We could create a small helper like:

{% macro edr_quote_alias(alias_name) %}
    {% if target.type in ('fabric', 'sqlserver') %}
        {{ return('[' ~ alias_name ~ ']') }}
    {% else %}
        {{ return(alias_name) }}
    {% endif %}
{% endmacro %}

Then the default macros could use schema_name as {{ edr_quote_alias('schema') }} without needing full fabric__ duplicates. This would eliminate the duplication for get_models_runs, get_model_resources, and get_source_resources.

Alternatively, adapter.quote() could work but it would add quotes on all adapters (e.g., "schema" on Postgres), which changes case sensitivity behavior and — as we saw — breaks Spark SQL.

Want me to implement the edr_quote_alias approach to remove the duplication?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — pushed d566f902. Created edr_quote_identifier(identifier) macro in macros/utils/edr_quote_identifier.sql that returns [identifier] on T-SQL and identifier on all others. Removed the adapter.dispatch + default__ + fabric__ pattern entirely from get_models_runs, get_model_resources, and get_source_resources. Net -93 lines.



{% macro fabric__get_source_resources(exclude_elementary=true) %}
{# T-SQL: 'schema' and 'database' are reserved keywords — quote with square brackets #}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment regarding quoting

…ates

Replaces the adapter dispatch + fabric__ duplication pattern with a single
edr_quote_identifier('schema') helper that returns [schema] on T-SQL and
schema on all other adapters. Applied to get_models_runs, get_model_resources,
and get_source_resources.

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>

test_invocation as (
select distinct invocation_id, detected_at
select {% if target.type in ('fabric', 'sqlserver') %}top 1{% endif %} distinct invocation_id, detected_at
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use elementary.is_tsql
(Was just added to dbt-data-reliability)
Also anywhere else that does this condition

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — pushed 40c8044d. Replaced all 4 occurrences of target.type in ('fabric', 'sqlserver') with elementary.is_tsql() across edr_quote_identifier.sql, get_test_last_invocation.sql, get_latest_invocation.sql, and test_conn.sql.

Replace all 'target.type in (fabric, sqlserver)' conditions with
elementary.is_tsql() macro from dbt-data-reliability.

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
Copy link
Collaborator

@haritamar haritamar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work on the Fabric/SQL Server support. The T-SQL compatibility approach is well-structured — dbt.concat(), elementary.is_tsql(), and edr_quote_identifier are the right patterns.

Main feedback:

  • CI default for dbt-data-reliability-ref leaks the dev branch to all warehouse runs — needs cleanup before/after merge.
  • Duplicate transient error tuples — extract a shared _TSQL_TRANSIENT tuple.
  • sqlserver__get_adapter_unique_id is redundant given the automatic fabric fallback.
  • fabric__get_test_results — the biggest concern. The explicit column list will drift, and the post-processing Jinja loop is copy-pasted. Suggest extracting a shared processing helper and using select * in the temp table step to keep the fabric dispatch focused on just the T-SQL materialization strategy.

warehouse-type: ${{ matrix.warehouse-type }}
elementary-ref: ${{ inputs.elementary-ref || ((github.event_name == 'pull_request_target' || github.event_name == 'pull_request') && github.event.pull_request.head.sha) || '' }}
dbt-data-reliability-ref: ${{ inputs.dbt-data-reliability-ref }}
dbt-data-reliability-ref: ${{ inputs.dbt-data-reliability-ref || 'devin/ELE-5282-1772640713' }}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the default dbt-data-reliability-ref for all warehouse CI runs (not just fabric/sqlserver). Once ELE-5282 is merged, this default needs to be reverted to empty — otherwise all CI runs will pin to a stale branch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged — the default will need to be reverted to empty once ELE-5282 is merged. I'll leave a TODO note or we can address it when that branch lands.

Comment on lines 112 to 130
"fabric": (
"connection timed out",
"could not connect to the server",
"ssl syscall error",
"communication link failure",
"tcp provider: an existing connection was forcibly closed",
"login timeout expired",
),
"sqlserver": (
"connection timed out",
"could not connect to the server",
"ssl syscall error",
"communication link failure",
"tcp provider: an existing connection was forcibly closed",
"login timeout expired",
),
}

# Pre-computed union of all adapter-specific patterns for the fallback path
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two entries are byte-for-byte identical. Consider extracting a shared tuple to reduce maintenance burden:

_TSQL_TRANSIENT = (
    "connection timed out",
    "could not connect to the server",
    "ssl syscall error",
    "communication link failure",
    "tcp provider: an existing connection was forcibly closed",
    "login timeout expired",
)

# Then in the dict:
"fabric": _TSQL_TRANSIENT,
"sqlserver": _TSQL_TRANSIENT,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 39a3c8d4 — extracted _TSQL_TRANSIENT shared tuple, both "fabric" and "sqlserver" now reference it.

Comment on lines +28 to +30

{% macro sqlserver__get_adapter_unique_id() %}
{{ return(target.server) }}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the PR notes that sqlserver automatically falls back to fabric__ dispatch, this sqlserver__ variant is redundant and can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 39a3c8d4 — removed the redundant sqlserver__get_adapter_unique_id. sqlserver will now fall back to fabric__get_adapter_unique_id automatically.

…e redundant sqlserver dispatch, deduplicate get_test_results processing

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
Copy link
Contributor Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

View 6 additional findings in Devin Review.

Open in Devin Review

warehouse-type: ${{ matrix.warehouse-type }}
elementary-ref: ${{ inputs.elementary-ref || ((github.event_name == 'pull_request_target' || github.event_name == 'pull_request') && github.event.pull_request.head.sha) || '' }}
dbt-data-reliability-ref: ${{ inputs.dbt-data-reliability-ref }}
dbt-data-reliability-ref: ${{ inputs.dbt-data-reliability-ref || 'devin/ELE-5282-1772640713' }}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 CI workflow hardcodes a development branch for dbt-data-reliability-ref

The dbt-data-reliability-ref input in the CI workflow defaults to the development branch devin/ELE-5282-1772640713 instead of an empty string (which would use the repo's default branch). This means every PR-triggered and pull_request_target-triggered CI run will check out this development branch of dbt-data-reliability instead of master/main. This branch may be deleted or force-pushed at any time, breaking all CI runs.

Suggested change
dbt-data-reliability-ref: ${{ inputs.dbt-data-reliability-ref || 'devin/ELE-5282-1772640713' }}
dbt-data-reliability-ref: ${{ inputs.dbt-data-reliability-ref }}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional and already documented in the PR description — this PR depends on the devin/ELE-5282-1772640713 branch (ELE-5282) which adds Fabric/SQL Server support to dbt-data-reliability. Both packages.yml and the CI workflow default need to reference this branch until ELE-5282 is merged, at which point they'll be updated to a stable commit hash/removed.

version: [">=0.8.0", "<0.9.0"]
- git: https://github.com/elementary-data/dbt-data-reliability.git
revision: ab21363935c42490a60a779557ba99bed96b754c
revision: devin/ELE-5282-1772640713
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 packages.yml pins to a development branch instead of a stable commit hash

The packages.yml shipped with the CLI sets revision: devin/ELE-5282-1772640713 — a development branch name — instead of a pinned commit hash. This means anyone installing the Elementary CLI and running dbt deps will pull from a volatile branch that can be rebased, force-pushed, or deleted. The previous code correctly used a commit hash (ab21363935c42490a60a779557ba99bed96b754c). This should be reverted to a stable commit hash before merging.

Prompt for agents
In elementary/monitor/dbt_project/packages.yml line 5, replace the development branch name `devin/ELE-5282-1772640713` with a stable commit hash from the dbt-data-reliability repository that contains the fabric/sqlserver support changes. Also update the package-lock.yml (elementary/monitor/dbt_project/package-lock.yml line 7) to match the chosen commit hash.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above — this is a known, documented dependency. The PR description explicitly states: "Must be reverted to a commit hash or release once ELE-5282 is merged." Will be updated before final merge.

… branch pin

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
working-directory: ${{ env.E2E_DBT_PROJECT_DIR }}
run: |
docker compose up -d sqlserver
timeout 120 bash -c 'until docker exec sqlserver /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "${MSSQL_SA_PASSWORD:-Elementary123!}" -C -Q "SELECT 1" -b 2>/dev/null; do sleep 5; done'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a healthcheck instead in the docker compose, and then wait for the docker to be healthy?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — the docker-compose already had a healthcheck defined, so I've updated the CI step to use docker compose up -d --wait sqlserver which waits for the healthcheck to pass. Removed the manual timeout/polling loop.

…nual polling

Co-Authored-By: Itamar Hartstein <haritamar@gmail.com>
@haritamar haritamar merged commit 13a4e0a into master Mar 8, 2026
48 of 52 checks passed
@haritamar haritamar deleted the devin/ELE-5285-1772890704 branch March 8, 2026 21:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant