From 2634f0b5f6e96cffcb81044566be40fc01472a83 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:01:16 +0000 Subject: [PATCH 1/4] Fix: normalize rows_affected to handle string values from dbt-databricks When using dbt-databricks with view_update_via_alter enabled, skipped views return rows_affected as a string '-1' instead of an integer. This causes Spark's VALUES clause to fail with INVALID_INLINE_TABLE.INCOMPATIBLE_TYPES_IN_INLINE_TABLE when building bulk INSERTs for dbt_run_results. The fix adds a normalize_rows_affected macro that: - Converts string '-1' to null (skipped operations have no meaningful row count) - Converts other string numbers to integers (defensive handling) - Passes through normal integers unchanged Fixes: https://github.com/elementary-data/elementary/issues/2089 Linear: CORE-267 Co-Authored-By: Yosef Arbiv --- macros/edr/dbt_artifacts/upload_run_results.sql | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/macros/edr/dbt_artifacts/upload_run_results.sql b/macros/edr/dbt_artifacts/upload_run_results.sql index 0a6fe8e70..08fa6eb58 100644 --- a/macros/edr/dbt_artifacts/upload_run_results.sql +++ b/macros/edr/dbt_artifacts/upload_run_results.sql @@ -37,10 +37,25 @@ {{ return(dbt_run_results_empty_table_query) }} {% endmacro %} +{% macro normalize_rows_affected(rows_affected) %} + {% if rows_affected is none %} + {{ return(none) }} + {% elif rows_affected is string %} + {% if rows_affected == '-1' %} + {{ return(none) }} + {% else %} + {{ return(rows_affected | int) }} + {% endif %} + {% else %} + {{ return(rows_affected) }} + {% endif %} +{% endmacro %} + {% macro flatten_run_result(run_result) %} {% set run_result_dict = elementary.get_run_result_dict(run_result) %} {% set node = elementary.safe_get_with_default(run_result_dict, 'node', {}) %} {% set config_dict = elementary.safe_get_with_default(node, 'config', {}) %} + {% set raw_rows_affected = run_result_dict.get('adapter_response', {}).get('rows_affected') %} {% set flatten_run_result_dict = { 'model_execution_id': elementary.get_node_execution_id(node), 'invocation_id': invocation_id, @@ -48,7 +63,7 @@ 'name': node.get('name'), 'message': run_result_dict.get('message'), 'generated_at': elementary.datetime_now_utc_as_string(), - 'rows_affected': run_result_dict.get('adapter_response', {}).get('rows_affected'), + 'rows_affected': elementary.normalize_rows_affected(raw_rows_affected), 'execution_time': run_result_dict.get('execution_time'), 'status': run_result_dict.get('status'), 'resource_type': node.get('resource_type'), From 65d6c82a016a9209ae9f37659b0d1e38c182191e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:07:15 +0000 Subject: [PATCH 2/4] Add unit test for normalize_rows_affected macro Tests the following cases: - None input returns None - Integer inputs pass through unchanged (123, 0, -1) - String '-1' returns None (skipped operations) - Other string numbers are converted to integers ('123', '0', '456') Co-Authored-By: Yosef Arbiv --- .../macros/test_normalize_rows_affected.sql | 4 +++ .../test_normalize_rows_affected.py | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 integration_tests/dbt_project/macros/test_normalize_rows_affected.sql create mode 100644 integration_tests/tests/test_dbt_artifacts/test_normalize_rows_affected.py diff --git a/integration_tests/dbt_project/macros/test_normalize_rows_affected.sql b/integration_tests/dbt_project/macros/test_normalize_rows_affected.sql new file mode 100644 index 000000000..24fcc91ec --- /dev/null +++ b/integration_tests/dbt_project/macros/test_normalize_rows_affected.sql @@ -0,0 +1,4 @@ +{% macro test_normalize_rows_affected(rows_affected) %} + {% set result = elementary.normalize_rows_affected(rows_affected) %} + {{ return(tojson(result)) }} +{% endmacro %} diff --git a/integration_tests/tests/test_dbt_artifacts/test_normalize_rows_affected.py b/integration_tests/tests/test_dbt_artifacts/test_normalize_rows_affected.py new file mode 100644 index 000000000..2c35b7e12 --- /dev/null +++ b/integration_tests/tests/test_dbt_artifacts/test_normalize_rows_affected.py @@ -0,0 +1,29 @@ +import json + +import pytest +from dbt_project import DbtProject + + +@pytest.mark.parametrize( + "input_value,expected_output", + [ + (None, None), + (123, 123), + (0, 0), + (-1, -1), + ("123", 123), + ("0", 0), + ("-1", None), + ("456", 456), + ], +) +def test_normalize_rows_affected(dbt_project: DbtProject, input_value, expected_output): + result = dbt_project.dbt_runner.run_operation( + "elementary_tests.test_normalize_rows_affected", + macro_args={"rows_affected": input_value}, + ) + actual_output = json.loads(result[0]) + assert actual_output == expected_output, ( + f"normalize_rows_affected({input_value!r}) returned {actual_output!r}, " + f"expected {expected_output!r}" + ) From c71e58b53a59f2cc30a2f3dd01e303eb95c14114 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:19:42 +0000 Subject: [PATCH 3/4] Fix test macro to not double-encode JSON results The log_macro_results helper already applies tojson() to the result, so the test macro should return the raw value instead of tojson(result). Co-Authored-By: Yosef Arbiv --- .../dbt_project/macros/test_normalize_rows_affected.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/dbt_project/macros/test_normalize_rows_affected.sql b/integration_tests/dbt_project/macros/test_normalize_rows_affected.sql index 24fcc91ec..33c0a4a29 100644 --- a/integration_tests/dbt_project/macros/test_normalize_rows_affected.sql +++ b/integration_tests/dbt_project/macros/test_normalize_rows_affected.sql @@ -1,4 +1,4 @@ {% macro test_normalize_rows_affected(rows_affected) %} {% set result = elementary.normalize_rows_affected(rows_affected) %} - {{ return(tojson(result)) }} + {{ return(result) }} {% endmacro %} From 2162b964e1dfe08464aa8aaa90d0faf69a93a4fe Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:30:43 +0000 Subject: [PATCH 4/4] Fix test to handle None return values from macro When the macro returns None, log_macro_results doesn't log anything, so run_operation returns an empty list. The test now handles this case by checking if the result list is empty before parsing. Co-Authored-By: Yosef Arbiv --- .../test_dbt_artifacts/test_normalize_rows_affected.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/integration_tests/tests/test_dbt_artifacts/test_normalize_rows_affected.py b/integration_tests/tests/test_dbt_artifacts/test_normalize_rows_affected.py index 2c35b7e12..9c7e2bd10 100644 --- a/integration_tests/tests/test_dbt_artifacts/test_normalize_rows_affected.py +++ b/integration_tests/tests/test_dbt_artifacts/test_normalize_rows_affected.py @@ -22,7 +22,12 @@ def test_normalize_rows_affected(dbt_project: DbtProject, input_value, expected_ "elementary_tests.test_normalize_rows_affected", macro_args={"rows_affected": input_value}, ) - actual_output = json.loads(result[0]) + # When the macro returns None, log_macro_results doesn't log anything, + # so run_operation returns an empty list + if not result: + actual_output = None + else: + actual_output = json.loads(result[0]) assert actual_output == expected_output, ( f"normalize_rows_affected({input_value!r}) returned {actual_output!r}, " f"expected {expected_output!r}"