From 6f4820a26e0e6ee136bc26fd9a7c9f8d48d6ec9f Mon Sep 17 00:00:00 2001 From: Sam Debruyn Date: Sun, 17 May 2026 16:16:53 +0200 Subject: [PATCH 1/9] Add Microsoft Fabric Data Warehouse support Fabric uses T-SQL with BIT booleans and has several SQL dialect differences from PostgreSQL/Snowflake/BigQuery. This commit makes the package cross-platform compatible with Fabric DW by: - Adding fabric__recursive_dag (loop-based, no recursive CTEs) - Adding fabric__get_dbtreplace_directory_pattern (no regexp_replace) - Adding fabric__type_string_dpe (varchar(8000)) - Adding quote_identifier/bool_literal dispatch macros - Replacing boolean expressions in SELECT with CASE WHEN - Replacing bare booleans in WHERE with explicit comparisons - Replacing GROUP BY ordinals with column names - Replacing || with dbt.concat() - Replacing 'where false' with 'where 1=0' - Replacing cast(True/False as ...) with cast(1/0 as ...) - Adding 'fabric' to target.type conditionals - Guarding ORDER BY in CTEs (invalid in T-SQL without TOP) Relates to #229 Co-Authored-By: Claude Opus 4.6 --- dbt_project.yml | 20 +-- macros/cross_db_shim/fabric_shims.sql | 20 +++ macros/cross_db_shim/type_string.sql | 4 + macros/get_directory_pattern.sql | 18 +-- macros/is_not_empty_string.sql | 4 + macros/recursive_dag.sql | 119 ++++++++++++++++++ macros/unpack/get_column_values.sql | 2 +- macros/unpack/get_node_values.sql | 6 +- macros/unpack/get_relationship_values.sql | 8 +- macros/unpack/get_source_values.sql | 14 +-- models/marts/core/int_all_graph_resources.sql | 16 ++- .../marts/core/int_direct_relationships.sql | 5 +- .../marts/dag/fct_direct_join_to_source.sql | 22 ++-- models/marts/dag/fct_duplicate_sources.sql | 14 ++- .../marts/dag/fct_hard_coded_references.sql | 2 +- ...ts_or_intermediate_dependent_on_source.sql | 4 +- models/marts/dag/fct_model_fanout.sql | 20 +-- .../marts/dag/fct_multiple_sources_joined.sql | 14 ++- .../fct_rejoining_of_upstream_concepts.sql | 18 +-- models/marts/dag/fct_root_models.sql | 4 +- models/marts/dag/fct_source_fanout.sql | 14 ++- ...ing_dependent_on_marts_or_intermediate.sql | 4 +- .../dag/fct_staging_dependent_on_staging.sql | 4 +- models/marts/dag/fct_too_many_joins.sql | 4 +- models/marts/dag/fct_unused_sources.sql | 6 +- .../fct_documentation_coverage.sql | 6 +- .../documentation/fct_undocumented_models.sql | 4 +- .../fct_undocumented_source_tables.sql | 4 +- .../fct_undocumented_sources.sql | 4 +- ..._exposures_dependent_on_private_models.sql | 4 +- .../fct_public_models_without_contract.sql | 6 +- .../fct_undocumented_public_models.sql | 16 +-- .../fct_chained_views_dependencies.sql | 6 +- .../fct_exposure_parents_materializations.sql | 2 +- .../marts/structure/fct_model_directories.sql | 10 +- .../fct_model_naming_conventions.sql | 16 +-- .../structure/fct_source_directories.sql | 6 +- .../marts/structure/fct_test_directories.sql | 4 +- .../tests/fct_missing_primary_key_tests.sql | 2 +- .../tests/fct_sources_without_freshness.sql | 4 +- .../intermediate/int_model_test_summary.sql | 28 ++--- .../base/base_exposure_relationships.sql | 4 +- .../graph/base/base_metric_relationships.sql | 4 +- .../staging/graph/base/base_node_columns.sql | 4 +- .../graph/base/base_node_relationships.sql | 4 +- .../graph/base/base_source_columns.sql | 4 +- models/staging/graph/stg_exposures.sql | 4 +- models/staging/graph/stg_metrics.sql | 4 +- models/staging/graph/stg_nodes.sql | 12 +- models/staging/graph/stg_sources.sql | 12 +- supported_adapters.env | 2 +- 51 files changed, 352 insertions(+), 190 deletions(-) create mode 100644 macros/cross_db_shim/fabric_shims.sql diff --git a/dbt_project.yml b/dbt_project.yml index d83989e3..9261ff15 100644 --- a/dbt_project.yml +++ b/dbt_project.yml @@ -26,17 +26,17 @@ dispatch: models: dbt_project_evaluator: - +materialized: "{{ 'table' if target.type in ['duckdb'] else 'view' }}" + +materialized: "{{ 'table' if target.type in ['duckdb', 'fabric'] else 'view' }}" marts: core: int_all_graph_resources: +materialized: table int_direct_relationships: - # required for BigQuery and Redshift for performance/memory reasons - +materialized: "{{ 'table' if target.type in ['bigquery', 'redshift', 'databricks'] else 'view' }}" + # required for BigQuery, Redshift, Databricks, and Fabric for performance/memory reasons + +materialized: "{{ 'table' if target.type in ['bigquery', 'redshift', 'databricks', 'fabric'] else 'view' }}" int_all_dag_relationships: - # required for BigQuery, Redshift, and Databricks for performance/memory reasons - +materialized: "{{ 'table' if target.type in ['bigquery', 'redshift', 'databricks', 'clickhouse'] else 'view' }}" + # required for BigQuery, Redshift, Databricks, Clickhouse, and Fabric for performance/memory reasons + +materialized: "{{ 'table' if target.type in ['bigquery', 'redshift', 'databricks', 'clickhouse', 'fabric'] else 'view' }}" dag: +materialized: table staging: @@ -45,11 +45,11 @@ models: +materialized: table variables: stg_naming_convention_folders: - # required for Redshift because listagg runs only on tables - +materialized: "{{ 'table' if target.type == 'redshift' else 'view' }}" + # required for Redshift and Fabric because listagg runs only on tables + +materialized: "{{ 'table' if target.type in ['redshift', 'fabric'] else 'view' }}" stg_naming_convention_prefixes: - # required for Redshift because listagg runs only on tables - +materialized: "{{ 'table' if target.type == 'redshift' else 'view' }}" + # required for Redshift and Fabric because listagg runs only on tables + +materialized: "{{ 'table' if target.type in ['redshift', 'fabric'] else 'view' }}" vars: @@ -89,7 +89,7 @@ vars: # -- Execution variables -- insert_batch_size: "{{ 500 if target.type in ['athena', 'bigquery'] else 10000 }}" - max_depth_dag: "{{ 9 if target.type in ['bigquery', 'spark', 'databricks'] else 4 if target.type in ['athena', 'trino', 'clickhouse'] else -1 }}" + max_depth_dag: "{{ 9 if target.type in ['bigquery', 'spark', 'databricks', 'fabric'] else 4 if target.type in ['athena', 'trino', 'clickhouse'] else -1 }}" # -- Code complexity variables -- comment_chars: ["--"] diff --git a/macros/cross_db_shim/fabric_shims.sql b/macros/cross_db_shim/fabric_shims.sql new file mode 100644 index 00000000..dd01ed67 --- /dev/null +++ b/macros/cross_db_shim/fabric_shims.sql @@ -0,0 +1,20 @@ +{% macro fabric__escape_single_quotes(expression) -%} + {{ expression | replace("'","''") }} +{%- endmacro %} + +{% macro quote_identifier(name) %} + {{ return(adapter.dispatch('quote_identifier', 'dbt_project_evaluator')(name)) }} +{% endmacro %} + +{% macro default__quote_identifier(name) %}{{ name }}{% endmacro %} + +{% macro fabric__quote_identifier(name) %}[{{ name }}]{% endmacro %} + +{# Convert a Python boolean to a SQL boolean literal appropriate for the target adapter #} +{% macro bool_literal(value) %} + {{ return(adapter.dispatch('bool_literal', 'dbt_project_evaluator')(value)) }} +{% endmacro %} + +{% macro default__bool_literal(value) %}{{ value | trim }}{% endmacro %} + +{% macro fabric__bool_literal(value) %}{% if value %}1{% else %}0{% endif %}{% endmacro %} diff --git a/macros/cross_db_shim/type_string.sql b/macros/cross_db_shim/type_string.sql index a93db6db..1f8d418a 100644 --- a/macros/cross_db_shim/type_string.sql +++ b/macros/cross_db_shim/type_string.sql @@ -9,3 +9,7 @@ {%- macro redshift__type_string_dpe() -%} {{ return(api.Column.string_type(600)) }} {%- endmacro -%} + +{%- macro fabric__type_string_dpe() -%} + {{ return("varchar(8000)") }} +{%- endmacro -%} diff --git a/macros/get_directory_pattern.sql b/macros/get_directory_pattern.sql index e16caefb..41b9f049 100644 --- a/macros/get_directory_pattern.sql +++ b/macros/get_directory_pattern.sql @@ -24,11 +24,15 @@ {% macro get_dbtreplace_directory_pattern() %} {% if execute %} - {%- set on_mac_or_linux = dbt_project_evaluator.is_os_mac_or_linux() -%} - {%- if on_mac_or_linux -%} - {{ dbt.replace("file_path", "regexp_replace(file_path,'.*/','')", "''") }} - {% else %} - {{ dbt.replace("file_path", "regexp_replace(file_path,'.*\\\\\\\\','')", "''") }} - {% endif %} + {%- if target.type == 'fabric' -%} + left(file_path, len(file_path) - charindex('/', reverse(file_path))) + {%- else -%} + {%- set on_mac_or_linux = dbt_project_evaluator.is_os_mac_or_linux() -%} + {%- if on_mac_or_linux -%} + {{ dbt.replace("file_path", "regexp_replace(file_path,'.*/','')", "''") }} + {% else %} + {{ dbt.replace("file_path", "regexp_replace(file_path,'.*\\\\\\\\','')", "''") }} + {% endif %} + {%- endif -%} {% endif %} -{% endmacro %} \ No newline at end of file +{% endmacro %} \ No newline at end of file diff --git a/macros/is_not_empty_string.sql b/macros/is_not_empty_string.sql index 52b57505..e7819d3d 100644 --- a/macros/is_not_empty_string.sql +++ b/macros/is_not_empty_string.sql @@ -10,4 +10,8 @@ {{ false }} {% endif %} +{% endmacro %} + +{% macro fabric__is_not_empty_string(str) %} + {% if str %}1{% else %}0{% endif %} {% endmacro %} \ No newline at end of file diff --git a/macros/recursive_dag.sql b/macros/recursive_dag.sql index 360990ee..39beef47 100644 --- a/macros/recursive_dag.sql +++ b/macros/recursive_dag.sql @@ -257,6 +257,125 @@ with direct_relationships as ( {% endmacro %} +{% macro fabric__recursive_dag() %} + +{% set max_depth = var('max_depth_dag') | int %} +{% if max_depth < 2 or max_depth < var('chained_views_threshold') | int %} + {% do exceptions.raise_compiler_error( + 'Variable max_depth_dag must be at least 2 and must be greater or equal to than chained_views_threshold.' + ) %} +{% endif %} + +with direct_relationships as ( + select + * + from {{ ref('int_direct_relationships') }} + where resource_type <> 'test' +) + +, get_distinct as ( + select distinct + resource_id as parent_id, + resource_id as child_id, + resource_name, + materialized as child_materialized, + is_public as child_is_public, + access as child_access, + is_excluded as child_is_excluded + + from direct_relationships +) + +, cte_0 as ( + select + parent_id, + child_id, + child_materialized, + child_is_public, + child_access, + child_is_excluded, + 0 as distance, + cast({{ dbt.array_construct(['resource_name']) }} as varchar(max)) as path, + cast(null as {{ dbt.type_boolean() }}) as is_dependent_on_chain_of_views + from get_distinct +) + +{% for i in range(1, max_depth) %} +{% set prev_cte_path %}cte_{{ i - 1 }}.path{% endset %} +, cte_{{ i }} as ( + select + cte_{{ i - 1 }}.parent_id as parent_id, + direct_relationships.resource_id as child_id, + direct_relationships.materialized as child_materialized, + direct_relationships.is_public as child_is_public, + direct_relationships.access as child_access, + direct_relationships.is_excluded as child_is_excluded, + cte_{{ i - 1 }}.distance+1 as distance, + cast({{ dbt.array_append(prev_cte_path, 'direct_relationships.resource_name') }} as varchar(max)) as path, + case + when + cte_{{ i - 1 }}.child_materialized in ('view', 'ephemeral') + and coalesce(cte_{{ i - 1 }}.is_dependent_on_chain_of_views, cast(1 as bit)) = cast(1 as bit) + then cast(1 as bit) + else cast(0 as bit) + end as is_dependent_on_chain_of_views + + from direct_relationships + inner join cte_{{ i - 1 }} + on cte_{{ i - 1 }}.child_id = direct_relationships.direct_parent_id +) +{% endfor %} + +, all_relationships_unioned as ( + {% for i in range(max_depth) %} + select * from cte_{{ i }} + {% if not loop.last %}union all{% endif %} + {% endfor %} +) + +, resource_info as ( + select * from {{ ref('int_all_graph_resources') }} +) + +, all_relationships as ( + select + parent.resource_id as parent_id, + parent.resource_name as parent, + parent.resource_type as parent_resource_type, + parent.model_type as parent_model_type, + parent.materialized as parent_materialized, + parent.is_public as parent_is_public, + parent.access as parent_access, + parent.source_name as parent_source_name, + parent.file_path as parent_file_path, + parent.directory_path as parent_directory_path, + parent.file_name as parent_file_name, + parent.is_excluded as parent_is_excluded, + child.resource_id as child_id, + child.resource_name as child, + child.resource_type as child_resource_type, + child.model_type as child_model_type, + child.materialized as child_materialized, + child.is_public as child_is_public, + child.access as child_access, + child.source_name as child_source_name, + child.file_path as child_file_path, + child.directory_path as child_directory_path, + child.file_name as child_file_name, + child.is_excluded as child_is_excluded, + cast(all_relationships_unioned.distance as {{ dbt.type_int() }}) as distance, + all_relationships_unioned.path, + case when all_relationships_unioned.is_dependent_on_chain_of_views = cast(1 as bit) then cast(1 as bit) else cast(0 as bit) end as is_dependent_on_chain_of_views + from all_relationships_unioned + left join resource_info as parent + on all_relationships_unioned.parent_id = parent.resource_id + left join resource_info as child + on all_relationships_unioned.child_id = child.resource_id +) + +{% endmacro %} + + {% macro clickhouse__recursive_dag() %} {{ return(bigquery__recursive_dag()) }} {% endmacro %} diff --git a/macros/unpack/get_column_values.sql b/macros/unpack/get_column_values.sql index 0163fa85..0e152156 100644 --- a/macros/unpack/get_column_values.sql +++ b/macros/unpack/get_column_values.sql @@ -25,7 +25,7 @@ wrap_string_with_quotes(dbt.escape_single_quotes(column.description | replace("\\","\\\\"))), wrap_string_with_quotes(dbt.escape_single_quotes(column.data_type)), wrap_string_with_quotes(dbt.escape_single_quotes(tojson(column.constraints))), - column.constraints | selectattr('type', 'equalto', 'not_null') | list | length > 0, + "cast(" ~ dbt_project_evaluator.bool_literal(column.constraints | selectattr('type', 'equalto', 'not_null') | list | length > 0) | trim ~ " as " ~ dbt.type_boolean() ~ ")", column.constraints | length, wrap_string_with_quotes(dbt.escape_single_quotes(column.quote)) ] diff --git a/macros/unpack/get_node_values.sql b/macros/unpack/get_node_values.sql index 1ef899ea..26cc93ad 100644 --- a/macros/unpack/get_node_values.sql +++ b/macros/unpack/get_node_values.sql @@ -23,7 +23,7 @@ wrap_string_with_quotes(node.name), wrap_string_with_quotes(node.resource_type), wrap_string_with_quotes(node.original_file_path | replace("\\","\\\\")), - "cast(" ~ node.config.enabled | trim ~ " as " ~ dbt.type_boolean() ~ ")", + "cast(" ~ dbt_project_evaluator.bool_literal(node.config.enabled) | trim ~ " as " ~ dbt.type_boolean() ~ ")", wrap_string_with_quotes(node.config.materialized), wrap_string_with_quotes(node.config.on_schema_change), wrap_string_with_quotes(node.group), @@ -31,7 +31,7 @@ wrap_string_with_quotes(node.latest_version), wrap_string_with_quotes(node.version), wrap_string_with_quotes(node.deprecation_date), - "cast(" ~ contract | trim ~ " as " ~ dbt.type_boolean() ~ ")", + "cast(" ~ dbt_project_evaluator.bool_literal(contract) | trim ~ " as " ~ dbt.type_boolean() ~ ")", node.columns.values() | list | length, node.columns.values() | list | selectattr('description') | list | length, wrap_string_with_quotes(node.database), @@ -46,7 +46,7 @@ sql_complexity, wrap_string_with_quotes(node.get('depends_on',{}).get('macros',[]) | tojson), "cast(" ~ dbt_project_evaluator.is_not_empty_string(node.test_metadata) | trim ~ " as " ~ dbt.type_boolean() ~ ")", - "cast(" ~ exclude_node ~ " as " ~ dbt.type_boolean() ~ ")", + "cast(" ~ dbt_project_evaluator.bool_literal(exclude_node) | trim ~ " as " ~ dbt.type_boolean() ~ ")", ] %} diff --git a/macros/unpack/get_relationship_values.sql b/macros/unpack/get_relationship_values.sql index 78b02dc3..1587e494 100644 --- a/macros/unpack/get_relationship_values.sql +++ b/macros/unpack/get_relationship_values.sql @@ -21,12 +21,12 @@ {%- if node.get('depends_on',{}).get('nodes',[]) |length == 0 -%} - {%- set values_line = + {%- set values_line = [ "cast('" ~ node.unique_id ~ "' as " ~ dbt_project_evaluator.type_string_dpe() ~ ")", "cast(NULL as " ~ dbt_project_evaluator.type_string_dpe() ~ ")", - "FALSE", - ] + "cast(" ~ dbt_project_evaluator.bool_literal(false) | trim ~ " as " ~ dbt.type_boolean() ~ ")", + ] %} {%- do values.append(values_line) -%} @@ -42,7 +42,7 @@ [ "cast('" ~ node.unique_id ~ "' as " ~ dbt_project_evaluator.type_string_dpe() ~ ")", "cast('" ~ parent ~ "' as " ~ dbt_project_evaluator.type_string_dpe() ~ ")", - "" ~ is_primary ~ "" if node.unique_id.split('.')[0] == 'test' else "FALSE" + "cast(" ~ dbt_project_evaluator.bool_literal(is_primary if node.unique_id.split('.')[0] == 'test' else false) | trim ~ " as " ~ dbt.type_boolean() ~ ")" ] %} diff --git a/macros/unpack/get_source_values.sql b/macros/unpack/get_source_values.sql index cf860538..da5423df 100644 --- a/macros/unpack/get_source_values.sql +++ b/macros/unpack/get_source_values.sql @@ -22,15 +22,15 @@ wrap_string_with_quotes(node.source_name), "cast(" ~ dbt_project_evaluator.is_not_empty_string(node.source_description) | trim ~ " as " ~ dbt.type_boolean() ~ ")", "cast(" ~ dbt_project_evaluator.is_not_empty_string(node.description) | trim ~ " as " ~ dbt.type_boolean() ~ ")", - "cast(" ~ node.config.enabled ~ " as " ~ dbt.type_boolean() ~ ")", + "cast(" ~ dbt_project_evaluator.bool_literal(node.config.enabled) | trim ~ " as " ~ dbt.type_boolean() ~ ")", wrap_string_with_quotes(node.loaded_at_field | replace("'", "_")), - "cast(" ~ ( - ((node.config.freshness != None) and (dbt_project_evaluator.is_not_empty_string(node.config.freshness.warn_after.count) - or dbt_project_evaluator.is_not_empty_string(node.config.freshness.error_after.count))) - or ((node.freshness != None) and (dbt_project_evaluator.is_not_empty_string(node.freshness.warn_after.count) + "cast(" ~ dbt_project_evaluator.bool_literal( + ((node.config.freshness != None) and (dbt_project_evaluator.is_not_empty_string(node.config.freshness.warn_after.count) + or dbt_project_evaluator.is_not_empty_string(node.config.freshness.error_after.count))) + or ((node.freshness != None) and (dbt_project_evaluator.is_not_empty_string(node.freshness.warn_after.count) or dbt_project_evaluator.is_not_empty_string(node.freshness.error_after.count))) - ) | trim ~ " as boolean)", + ) | trim ~ " as " ~ dbt.type_boolean() ~ ")", wrap_string_with_quotes(node.database), wrap_string_with_quotes(node.schema), @@ -38,7 +38,7 @@ wrap_string_with_quotes(node.loader), wrap_string_with_quotes(node.identifier), wrap_string_with_quotes(node.meta | tojson), - "cast(" ~ exclude_source ~ " as " ~ dbt.type_boolean() ~ ")", + "cast(" ~ dbt_project_evaluator.bool_literal(exclude_source) | trim ~ " as " ~ dbt.type_boolean() ~ ")", ] %} diff --git a/models/marts/core/int_all_graph_resources.sql b/models/marts/core/int_all_graph_resources.sql index 1d3534c4..37fc698c 100644 --- a/models/marts/core/int_all_graph_resources.sql +++ b/models/marts/core/int_all_graph_resources.sql @@ -47,12 +47,16 @@ unioned_with_calc as ( end as resource_name, case when resource_type = 'source' then null - else {{ dbt.split_part('name', "'_'", 1) }}||'_' + else {{ dbt.concat([dbt.split_part('name', "'_'", 1), "'_'"]) }} end as prefix, {{ get_dbtreplace_directory_pattern() }} as directory_path, + {% if target.type == 'fabric' %} + right(file_path, charindex('/', reverse(file_path)) - 1) as file_name + {% else %} regexp_replace(file_path,'.*{{ get_regexp_directory_pattern() }}','') as file_name + {% endif %} from unioned - where coalesce(is_enabled, True) = True and package_name != 'dbt_project_evaluator' + where coalesce(is_enabled, cast(1 as {{ dbt.type_boolean() }})) = cast(1 as {{ dbt.type_boolean() }}) and package_name != 'dbt_project_evaluator' ), joined as ( @@ -78,19 +82,19 @@ joined as ( {{ dbt.position(dbt.concat([quoted_directory_pattern, 'naming_convention_folders.folder_name_value', quoted_directory_pattern]),'unioned_with_calc.directory_path') }} as position_folder, nullif(unioned_with_calc.column_name, '') as column_name, {% for test in test_macro_set %} - unioned_with_calc.macro_dependencies like '%macro.{{ test }}%' and unioned_with_calc.resource_type = 'test' as is_{{ test.split('.')[1] }}, + case when unioned_with_calc.macro_dependencies like '%macro.{{ test }}%' and unioned_with_calc.resource_type = 'test' then cast(1 as {{ dbt.type_boolean() }}) else cast(0 as {{ dbt.type_boolean() }}) end as is_{{ test.split('.')[1] }}, {% endfor %} unioned_with_calc.is_enabled, unioned_with_calc.materialized, unioned_with_calc.on_schema_change, - unioned_with_calc.database, - unioned_with_calc.schema, + unioned_with_calc.{{ dbt_project_evaluator.quote_identifier('database') }}, + unioned_with_calc.{{ dbt_project_evaluator.quote_identifier('schema') }}, unioned_with_calc.package_name, unioned_with_calc.alias, unioned_with_calc.is_described, unioned_with_calc.model_group, unioned_with_calc.access, - unioned_with_calc.access = 'public' as is_public, + case when unioned_with_calc.access = 'public' then cast(1 as {{ dbt.type_boolean() }}) else cast(0 as {{ dbt.type_boolean() }}) end as is_public, unioned_with_calc.latest_version, unioned_with_calc.version, unioned_with_calc.deprecation_date, diff --git a/models/marts/core/int_direct_relationships.sql b/models/marts/core/int_direct_relationships.sql index 92256879..6ac0dd98 100644 --- a/models/marts/core/int_direct_relationships.sql +++ b/models/marts/core/int_direct_relationships.sql @@ -64,10 +64,7 @@ direct_relationships as ( when all_graph_resources.resource_type in ('model', 'snapshot', 'test') then models.direct_parent_id else null end as direct_parent_id, - ( - all_graph_resources.resource_type = 'test' - and models.is_primary_relationship - ) as is_primary_test_relationship + case when all_graph_resources.resource_type = 'test' and models.is_primary_relationship = cast(1 as {{ dbt.type_boolean() }}) then cast(1 as {{ dbt.type_boolean() }}) else cast(0 as {{ dbt.type_boolean() }}) end as is_primary_test_relationship from all_graph_resources left join direct_model_relationships as models on all_graph_resources.resource_id = models.resource_id diff --git a/models/marts/dag/fct_direct_join_to_source.sql b/models/marts/dag/fct_direct_join_to_source.sql index 41c71c04..7a0f29ac 100644 --- a/models/marts/dag/fct_direct_join_to_source.sql +++ b/models/marts/dag/fct_direct_join_to_source.sql @@ -6,27 +6,27 @@ with direct_model_relationships as ( from {{ ref('int_all_dag_relationships') }} where child_resource_type = 'model' and distance = 1 - and not parent_is_excluded - and not child_is_excluded + and parent_is_excluded = cast(0 as {{ dbt.type_boolean() }}) + and child_is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), model_and_source_joined as ( select child, - case + case when ( - sum(case when parent_resource_type = 'model' then 1 else 0 end) > 0 + sum(case when parent_resource_type = 'model' then 1 else 0 end) > 0 and sum(case when parent_resource_type = 'source' then 1 else 0 end) > 0 - ) - then true - else false + ) + then cast(1 as {{ dbt.type_boolean() }}) + else cast(0 as {{ dbt.type_boolean() }}) end as keep_row from direct_model_relationships - group by 1 + group by child ), final as ( - select + select direct_model_relationships.parent, direct_model_relationships.parent_resource_type, direct_model_relationships.child, @@ -35,10 +35,10 @@ final as ( from direct_model_relationships inner join model_and_source_joined on direct_model_relationships.child = model_and_source_joined.child - where model_and_source_joined.keep_row - order by direct_model_relationships.child + where model_and_source_joined.keep_row = cast(1 as {{ dbt.type_boolean() }}) ) select * from final +order by child {{ filter_exceptions() }} \ No newline at end of file diff --git a/models/marts/dag/fct_duplicate_sources.sql b/models/marts/dag/fct_duplicate_sources.sql index 94b45293..7e2ff180 100644 --- a/models/marts/dag/fct_duplicate_sources.sql +++ b/models/marts/dag/fct_duplicate_sources.sql @@ -3,23 +3,25 @@ with sources as ( resource_name, case -- if you're using databricks but not the unity catalog, database will be null - when database is NULL then {{ dbt.concat(["schema", "'.'", "identifier"]) }} - else {{ dbt.concat(["database", "'.'", "schema", "'.'", "identifier"]) }} + when {{ dbt_project_evaluator.quote_identifier('database') }} is NULL then {{ dbt.concat([dbt_project_evaluator.quote_identifier('schema'), "'.'", "identifier"]) }} + else {{ dbt.concat([dbt_project_evaluator.quote_identifier('database'), "'.'", dbt_project_evaluator.quote_identifier('schema'), "'.'", "identifier"]) }} end as source_db_location from {{ ref('int_all_graph_resources') }} where resource_type = 'source' - and not is_excluded + and is_excluded = cast(0 as {{ dbt.type_boolean() }}) + {% if target.type not in ['fabric'] %} -- we order the CTE so that listagg returns values correctly sorted for some warehouses order by 1, 2 + {% endif %} ), source_duplicates as ( select source_db_location, {{ dbt.listagg( - measure = 'resource_name', - delimiter_text = "', '", - order_by_clause = 'order by resource_name' if target.type in ['snowflake','redshift','duckdb','trino']) + measure = 'resource_name', + delimiter_text = "', '", + order_by_clause = 'order by resource_name' if target.type in ['snowflake','redshift','duckdb','trino','fabric']) }} as source_names from sources group by source_db_location diff --git a/models/marts/dag/fct_hard_coded_references.sql b/models/marts/dag/fct_hard_coded_references.sql index b0d1f157..0502c2c6 100644 --- a/models/marts/dag/fct_hard_coded_references.sql +++ b/models/marts/dag/fct_hard_coded_references.sql @@ -3,7 +3,7 @@ with models as ( select * from {{ ref('int_all_graph_resources') }} where resource_type = 'model' - and not is_excluded + and is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), final as ( diff --git a/models/marts/dag/fct_marts_or_intermediate_dependent_on_source.sql b/models/marts/dag/fct_marts_or_intermediate_dependent_on_source.sql index e8ef303e..16a68928 100644 --- a/models/marts/dag/fct_marts_or_intermediate_dependent_on_source.sql +++ b/models/marts/dag/fct_marts_or_intermediate_dependent_on_source.sql @@ -4,8 +4,8 @@ with direct_relationships as ( * from {{ ref('int_all_dag_relationships') }} where distance = 1 - and not parent_is_excluded - and not child_is_excluded + and parent_is_excluded = cast(0 as {{ dbt.type_boolean() }}) + and child_is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), final as ( select diff --git a/models/marts/dag/fct_model_fanout.sql b/models/marts/dag/fct_model_fanout.sql index 5e7f317a..72a6198e 100644 --- a/models/marts/dag/fct_model_fanout.sql +++ b/models/marts/dag/fct_model_fanout.sql @@ -1,9 +1,9 @@ with all_dag_relationships as ( - select + select * from {{ ref('int_all_dag_relationships') }} - where not parent_is_excluded - and not child_is_excluded + where parent_is_excluded = cast(0 as {{ dbt.type_boolean() }}) + and child_is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), -- find all models without children @@ -12,7 +12,7 @@ models_without_children as ( parent from all_dag_relationships where parent_resource_type = 'model' - group by 1 + group by parent having max(distance) = 0 ), @@ -27,9 +27,11 @@ model_fanout as ( inner join models_without_children on all_dag_relationships.child = models_without_children.parent where all_dag_relationships.distance = 1 and all_dag_relationships.child_resource_type = 'model' - group by 1, 2, 3 + group by all_dag_relationships.parent, all_dag_relationships.parent_model_type, all_dag_relationships.child + {% if target.type not in ['fabric'] %} -- we order the CTE so that listagg returns values correctly sorted for some warehouses order by 1, 2, 3 + {% endif %} ), model_fanout_agg as ( @@ -37,12 +39,12 @@ model_fanout_agg as ( parent, parent_model_type, {{ dbt.listagg( - measure = 'child', - delimiter_text = "', '", - order_by_clause = 'order by child' if target.type in ['snowflake','redshift','duckdb','trino']) + measure = 'child', + delimiter_text = "', '", + order_by_clause = 'order by child' if target.type in ['snowflake','redshift','duckdb','trino','fabric']) }} as leaf_children from model_fanout - group by 1, 2 + group by parent, parent_model_type having count(*) >= {{ var('models_fanout_threshold') }} ) diff --git a/models/marts/dag/fct_multiple_sources_joined.sql b/models/marts/dag/fct_multiple_sources_joined.sql index 175d07c7..e540405c 100644 --- a/models/marts/dag/fct_multiple_sources_joined.sql +++ b/models/marts/dag/fct_multiple_sources_joined.sql @@ -6,22 +6,24 @@ with direct_source_relationships as ( from {{ ref('int_all_dag_relationships') }} where distance = 1 and parent_resource_type = 'source' - and not parent_is_excluded - and not child_is_excluded + and parent_is_excluded = cast(0 as {{ dbt.type_boolean() }}) + and child_is_excluded = cast(0 as {{ dbt.type_boolean() }}) + {% if target.type not in ['fabric'] %} -- we order the CTE so that listagg returns values correctly sorted for some warehouses order by 1, 2 + {% endif %} ), multiple_sources_joined as ( select child, {{ dbt.listagg( - measure='parent', - delimiter_text="', '", - order_by_clause='order by parent' if target.type in ['snowflake','redshift','duckdb','trino']) + measure='parent', + delimiter_text="', '", + order_by_clause='order by parent' if target.type in ['snowflake','redshift','duckdb','trino','fabric']) }} as source_parents from direct_source_relationships - group by 1 + group by child having count(*) > 1 ) diff --git a/models/marts/dag/fct_rejoining_of_upstream_concepts.sql b/models/marts/dag/fct_rejoining_of_upstream_concepts.sql index 65a12875..7ecc1ddb 100644 --- a/models/marts/dag/fct_rejoining_of_upstream_concepts.sql +++ b/models/marts/dag/fct_rejoining_of_upstream_concepts.sql @@ -4,8 +4,8 @@ with all_relationships as ( from {{ ref('int_all_dag_relationships') }} where parent_resource_type not in ('exposure', 'metric') and child_resource_type not in ('exposure', 'metric') - and not parent_is_excluded - and not child_is_excluded + and parent_is_excluded = cast(0 as {{ dbt.type_boolean() }}) + and child_is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), -- all parent/child relationships where the parent is BOTH the direct parent of the child and the second level parent of the child @@ -14,8 +14,8 @@ rejoined as ( parent, child from all_relationships - group by 1, 2 - having (sum(case when distance = 1 then 1 else 0 end) >= 1 + group by parent, child + having (sum(case when distance = 1 then 1 else 0 end) >= 1 and sum(case when distance = 2 then 1 else 0 end) >= 1) ), @@ -25,7 +25,7 @@ single_use_resources as ( parent from all_relationships where distance = 1 - group by 1 + group by parent having count(*) = 1 ), @@ -50,9 +50,9 @@ triad_relationships as ( final as ( select triad_relationships.*, - case - when single_use_resources.parent is not null then true - else false + case + when single_use_resources.parent is not null then cast(1 as {{ dbt.type_boolean() }}) + else cast(0 as {{ dbt.type_boolean() }}) end as is_loop_independent from triad_relationships left join single_use_resources @@ -61,7 +61,7 @@ final as ( final_filtered as ( select * from final - where is_loop_independent + where is_loop_independent = cast(1 as {{ dbt.type_boolean() }}) ) select * from final_filtered diff --git a/models/marts/dag/fct_root_models.sql b/models/marts/dag/fct_root_models.sql index c0a63016..0c91a414 100644 --- a/models/marts/dag/fct_root_models.sql +++ b/models/marts/dag/fct_root_models.sql @@ -16,7 +16,7 @@ with model_relationships as ( where child_resource_type = 'model' -- only filter out excluded children nodes -- filtering parents could result in incorrectly flagging nodes that depend on excluded nodes - and not child_is_excluded + and child_is_excluded = cast(0 as {{ dbt.type_boolean() }}) -- exclude required time spine {% if metric_flow_time_spine_names %} and child not in ('{{ metric_flow_time_spine_names }}') @@ -27,7 +27,7 @@ final as ( select child from model_relationships - group by 1 + group by child having max(distance) = 0 ) diff --git a/models/marts/dag/fct_source_fanout.sql b/models/marts/dag/fct_source_fanout.sql index e3f6fa35..d477a881 100644 --- a/models/marts/dag/fct_source_fanout.sql +++ b/models/marts/dag/fct_source_fanout.sql @@ -6,22 +6,24 @@ with direct_source_relationships as ( where distance = 1 and parent_resource_type = 'source' and child_resource_type = 'model' - and not parent_is_excluded - and not child_is_excluded + and parent_is_excluded = cast(0 as {{ dbt.type_boolean() }}) + and child_is_excluded = cast(0 as {{ dbt.type_boolean() }}) + {% if target.type not in ['fabric'] %} -- we order the CTE so that listagg returns values correctly sorted for some warehouses order by child + {% endif %} ), source_fanout as ( select parent, {{ dbt.listagg( - measure='child', - delimiter_text="', '", - order_by_clause='order by child' if target.type in ['snowflake','redshift','duckdb','trino']) + measure='child', + delimiter_text="', '", + order_by_clause='order by child' if target.type in ['snowflake','redshift','duckdb','trino','fabric']) }} as model_children from direct_source_relationships - group by 1 + group by parent having count(*) > 1 ) diff --git a/models/marts/dag/fct_staging_dependent_on_marts_or_intermediate.sql b/models/marts/dag/fct_staging_dependent_on_marts_or_intermediate.sql index 0aa7fb3d..0af0fdf7 100644 --- a/models/marts/dag/fct_staging_dependent_on_marts_or_intermediate.sql +++ b/models/marts/dag/fct_staging_dependent_on_marts_or_intermediate.sql @@ -7,8 +7,8 @@ with direct_model_relationships as ( where distance = 1 and parent_resource_type = 'model' and child_resource_type = 'model' - and not parent_is_excluded - and not child_is_excluded + and parent_is_excluded = cast(0 as {{ dbt.type_boolean() }}) + and child_is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), final as ( select diff --git a/models/marts/dag/fct_staging_dependent_on_staging.sql b/models/marts/dag/fct_staging_dependent_on_staging.sql index 57982d4e..b77e7d4d 100644 --- a/models/marts/dag/fct_staging_dependent_on_staging.sql +++ b/models/marts/dag/fct_staging_dependent_on_staging.sql @@ -5,8 +5,8 @@ with direct_model_relationships as ( from {{ ref('int_all_dag_relationships') }} where parent_resource_type in ('model', 'snapshot') and child_resource_type in ('model', 'snapshot') - and not parent_is_excluded - and not child_is_excluded + and parent_is_excluded = cast(0 as {{ dbt.type_boolean() }}) + and child_is_excluded = cast(0 as {{ dbt.type_boolean() }}) and distance = 1 ), diff --git a/models/marts/dag/fct_too_many_joins.sql b/models/marts/dag/fct_too_many_joins.sql index 6ce72618..b6acef35 100644 --- a/models/marts/dag/fct_too_many_joins.sql +++ b/models/marts/dag/fct_too_many_joins.sql @@ -2,7 +2,7 @@ with all_dag_relationships as ( select * from {{ ref('int_all_dag_relationships') }} - where not child_is_excluded + where child_is_excluded = cast(0 as {{ dbt.type_boolean() }}) and child_resource_type = 'model' ), @@ -13,7 +13,7 @@ final as ( cast(count(distinct parent) as {{ dbt.type_int() }}) as join_count from all_dag_relationships where distance = 1 - group by 1, 2 + group by child, child_file_path having count(distinct parent) >= {{ var('too_many_joins_threshold') }} ) diff --git a/models/marts/dag/fct_unused_sources.sql b/models/marts/dag/fct_unused_sources.sql index 84259c9e..ba2cb0cd 100644 --- a/models/marts/dag/fct_unused_sources.sql +++ b/models/marts/dag/fct_unused_sources.sql @@ -5,15 +5,15 @@ with source_relationships as ( * from {{ ref('int_all_dag_relationships') }} where parent_resource_type = 'source' - and not parent_is_excluded - and not child_is_excluded + and parent_is_excluded = cast(0 as {{ dbt.type_boolean() }}) + and child_is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), final as ( select parent from source_relationships - group by 1 + group by parent having max(distance) = 0 ) diff --git a/models/marts/documentation/fct_documentation_coverage.sql b/models/marts/documentation/fct_documentation_coverage.sql index eeae0ccb..ae2423df 100644 --- a/models/marts/documentation/fct_documentation_coverage.sql +++ b/models/marts/documentation/fct_documentation_coverage.sql @@ -3,16 +3,16 @@ with models as ( select * from {{ ref('int_all_graph_resources') }} where resource_type = 'model' - and not is_excluded + and is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), conversion as ( select resource_id, - case when is_described then 1 else 0 end as is_described_model, + case when is_described = cast(1 as {{ dbt.type_boolean() }}) then 1 else 0 end as is_described_model, {% for model_type in var('model_types') %} case when model_type = '{{ model_type }}' then 1.0 else NULL end as is_{{ model_type }}_model, - case when is_described and model_type = '{{ model_type }}' then 1.0 else 0 end as is_described_{{ model_type }}_model{% if not loop.last %},{% endif %} + case when is_described = cast(1 as {{ dbt.type_boolean() }}) and model_type = '{{ model_type }}' then 1.0 else 0 end as is_described_{{ model_type }}_model{% if not loop.last %},{% endif %} {% endfor %} from models diff --git a/models/marts/documentation/fct_undocumented_models.sql b/models/marts/documentation/fct_undocumented_models.sql index 19799ca9..a09b5e9c 100644 --- a/models/marts/documentation/fct_undocumented_models.sql +++ b/models/marts/documentation/fct_undocumented_models.sql @@ -2,7 +2,7 @@ with all_resources as ( select * from {{ ref('int_all_graph_resources') }} - where not is_excluded + where is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), @@ -13,7 +13,7 @@ final as ( model_type from all_resources - where not is_described and resource_type = 'model' + where is_described = cast(0 as {{ dbt.type_boolean() }}) and resource_type = 'model' ) diff --git a/models/marts/documentation/fct_undocumented_source_tables.sql b/models/marts/documentation/fct_undocumented_source_tables.sql index 1bdb570b..3e42f1e4 100644 --- a/models/marts/documentation/fct_undocumented_source_tables.sql +++ b/models/marts/documentation/fct_undocumented_source_tables.sql @@ -2,7 +2,7 @@ with all_resources as ( select * from {{ ref('int_all_graph_resources') }} - where not is_excluded + where is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), @@ -12,7 +12,7 @@ final as ( resource_name from all_resources - where not is_described and resource_type = 'source' + where is_described = cast(0 as {{ dbt.type_boolean() }}) and resource_type = 'source' ) diff --git a/models/marts/documentation/fct_undocumented_sources.sql b/models/marts/documentation/fct_undocumented_sources.sql index 64207a09..acc9cb67 100644 --- a/models/marts/documentation/fct_undocumented_sources.sql +++ b/models/marts/documentation/fct_undocumented_sources.sql @@ -2,7 +2,7 @@ with all_resources as ( select * from {{ ref('int_all_graph_resources') }} - where not is_excluded + where is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), @@ -12,7 +12,7 @@ final as ( source_name from all_resources - where not is_source_described and resource_type = 'source' + where is_source_described = cast(0 as {{ dbt.type_boolean() }}) and resource_type = 'source' ) diff --git a/models/marts/governance/fct_exposures_dependent_on_private_models.sql b/models/marts/governance/fct_exposures_dependent_on_private_models.sql index 356777e6..21555656 100644 --- a/models/marts/governance/fct_exposures_dependent_on_private_models.sql +++ b/models/marts/governance/fct_exposures_dependent_on_private_models.sql @@ -7,9 +7,9 @@ direct_exposure_relationships as ( and child_resource_type = 'exposure' and not ( parent_resource_type = 'model' - and parent_is_public + and parent_is_public = cast(1 as {{ dbt.type_boolean() }}) ) - and not parent_is_excluded + and parent_is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), final as ( diff --git a/models/marts/governance/fct_public_models_without_contract.sql b/models/marts/governance/fct_public_models_without_contract.sql index 09bac2b0..ba62ca45 100644 --- a/models/marts/governance/fct_public_models_without_contract.sql +++ b/models/marts/governance/fct_public_models_without_contract.sql @@ -2,7 +2,7 @@ with all_resources as ( select * from {{ ref('int_all_graph_resources') }} - where not is_excluded + where is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), final as ( @@ -14,8 +14,8 @@ final as ( from all_resources where - is_public - and not is_contract_enforced + is_public = cast(1 as {{ dbt.type_boolean() }}) + and is_contract_enforced = cast(0 as {{ dbt.type_boolean() }}) ) select * from final diff --git a/models/marts/governance/fct_undocumented_public_models.sql b/models/marts/governance/fct_undocumented_public_models.sql index c6527001..754f2531 100644 --- a/models/marts/governance/fct_undocumented_public_models.sql +++ b/models/marts/governance/fct_undocumented_public_models.sql @@ -2,23 +2,23 @@ with all_resources as ( select * from {{ ref('int_all_graph_resources') }} - where not is_excluded + where is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), final as ( - select + select resource_name, - access, - is_described, + access, + is_described, total_defined_columns, total_described_columns - + from all_resources - where - is_public + where + is_public = cast(1 as {{ dbt.type_boolean() }}) and ( -- no model level description - not is_described + is_described = cast(0 as {{ dbt.type_boolean() }}) -- not all columns defined have descriptions or total_described_columns < total_defined_columns -- no columns defined at all diff --git a/models/marts/performance/fct_chained_views_dependencies.sql b/models/marts/performance/fct_chained_views_dependencies.sql index 93b8dc7e..4ff7d228 100644 --- a/models/marts/performance/fct_chained_views_dependencies.sql +++ b/models/marts/performance/fct_chained_views_dependencies.sql @@ -3,8 +3,8 @@ with all_relationships as ( * from {{ ref('int_all_dag_relationships') }} where distance <> 0 - and not parent_is_excluded - and not child_is_excluded + and parent_is_excluded = cast(0 as {{ dbt.type_boolean() }}) + and child_is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), final as ( @@ -14,7 +14,7 @@ final as ( distance, path from all_relationships - where is_dependent_on_chain_of_views + where is_dependent_on_chain_of_views = cast(1 as {{ dbt.type_boolean() }}) and child_resource_type = 'model' and distance > {{ var('chained_views_threshold') }} ) diff --git a/models/marts/performance/fct_exposure_parents_materializations.sql b/models/marts/performance/fct_exposure_parents_materializations.sql index bbb801d4..585c6ab1 100644 --- a/models/marts/performance/fct_exposure_parents_materializations.sql +++ b/models/marts/performance/fct_exposure_parents_materializations.sql @@ -14,7 +14,7 @@ direct_exposure_relationships as ( ) ) -- no test on child_is_excluded because exposures are never excluded - and not parent_is_excluded + and parent_is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), final as ( diff --git a/models/marts/structure/fct_model_directories.sql b/models/marts/structure/fct_model_directories.sql index 082fde7c..08260fa9 100644 --- a/models/marts/structure/fct_model_directories.sql +++ b/models/marts/structure/fct_model_directories.sql @@ -5,7 +5,7 @@ with all_graph_resources as ( select * from {{ ref('int_all_graph_resources') }} - where not is_excluded + where is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), folders as ( @@ -14,7 +14,7 @@ folders as ( all_dag_relationships as ( select * from {{ ref('int_all_dag_relationships') }} - where not child_is_excluded + where child_is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), staging_models as ( @@ -39,9 +39,9 @@ inappropriate_subdirectories_staging as ( child_resource_type as resource_type, child_model_type as model_type, child_file_path as current_file_path, - 'models{{ directory_pattern }}' || '{{ var("staging_folder_name") }}' || '{{ directory_pattern }}' || parent_source_name || '{{ directory_pattern }}' || child_file_name as change_file_path_to + {{ dbt.concat(["'models" ~ directory_pattern ~ "'", "'" ~ var("staging_folder_name") ~ "'", "'" ~ directory_pattern ~ "'", 'parent_source_name', "'" ~ directory_pattern ~ "'", 'child_file_name']) }} as change_file_path_to from staging_models - where child_directory_path not like '%' || parent_source_name || '%' + where child_directory_path not like {{ dbt.concat(["'%'", 'parent_source_name', "'%'"]) }} ), -- find all non-staging models that are NOT nested closest to their appropriate folder @@ -51,7 +51,7 @@ innappropriate_subdirectories_non_staging_models as ( all_graph_resources.resource_type, all_graph_resources.model_type, all_graph_resources.file_path as current_file_path, - 'models' || '{{ directory_pattern }}...{{ directory_pattern }}' || folders.folder_name_value || '{{ directory_pattern }}...{{ directory_pattern }}' || all_graph_resources.file_name as change_file_path_to + {{ dbt.concat(["'models'", "'" ~ directory_pattern ~ "..." ~ directory_pattern ~ "'", 'folders.folder_name_value', "'" ~ directory_pattern ~ "..." ~ directory_pattern ~ "'", 'all_graph_resources.file_name']) }} as change_file_path_to from all_graph_resources left join folders on folders.model_type = all_graph_resources.model_type diff --git a/models/marts/structure/fct_model_naming_conventions.sql b/models/marts/structure/fct_model_naming_conventions.sql index 4837a384..189dcfd7 100644 --- a/models/marts/structure/fct_model_naming_conventions.sql +++ b/models/marts/structure/fct_model_naming_conventions.sql @@ -12,7 +12,7 @@ with all_graph_resources as ( select * from {{ ref('int_all_graph_resources') }} - where not is_excluded + where is_excluded = cast(0 as {{ dbt.type_boolean() }}) -- exclude required time spine {% if metric_flow_time_spine_names %} and resource_name not in ('{{ metric_flow_time_spine_names }}') @@ -21,17 +21,19 @@ with all_graph_resources as ( naming_convention_prefixes as ( select * from {{ ref('stg_naming_convention_prefixes') }} + {% if target.type not in ['fabric'] %} -- we order the CTE so that listagg returns values correctly sorted for some warehouses order by prefix_value -), + {% endif %} +), appropriate_prefixes as ( - select - model_type, + select + model_type, {{ dbt.listagg( - measure='prefix_value', - delimiter_text="', '", - order_by_clause='order by prefix_value' if target.type in ['snowflake','redshift','duckdb','trino']) + measure='prefix_value', + delimiter_text="', '", + order_by_clause='order by prefix_value' if target.type in ['snowflake','redshift','duckdb','trino','fabric']) }} as appropriate_prefixes from naming_convention_prefixes group by model_type diff --git a/models/marts/structure/fct_source_directories.sql b/models/marts/structure/fct_source_directories.sql index 41372eb3..4cce90eb 100644 --- a/models/marts/structure/fct_source_directories.sql +++ b/models/marts/structure/fct_source_directories.sql @@ -6,7 +6,7 @@ with all_graph_resources as ( select * from {{ ref('int_all_graph_resources') }} - where not is_excluded + where is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), -- find all sources that are definied in a .yml file NOT in their subdirectory @@ -15,10 +15,10 @@ inappropriate_subdirectories_sources as ( resource_name, resource_type, file_path as current_file_path, - 'models{{ directory_pattern }}' || '{{ var("staging_folder_name") }}' || '{{ directory_pattern }}' || source_name || '{{ directory_pattern }}' || file_name as change_file_path_to + {{ dbt.concat(["'models" ~ directory_pattern ~ "'", "'" ~ var("staging_folder_name") ~ "'", "'" ~ directory_pattern ~ "'", 'source_name', "'" ~ directory_pattern ~ "'", 'file_name']) }} as change_file_path_to from all_graph_resources where resource_type = 'source' - and directory_path not like '%' || source_name || '%' + and directory_path not like {{ dbt.concat(["'%'", 'source_name', "'%'"]) }} ) select * from inappropriate_subdirectories_sources diff --git a/models/marts/structure/fct_test_directories.sql b/models/marts/structure/fct_test_directories.sql index d0c56969..24d35b5c 100644 --- a/models/marts/structure/fct_test_directories.sql +++ b/models/marts/structure/fct_test_directories.sql @@ -4,7 +4,7 @@ with resources as ( select * from {{ ref('int_all_graph_resources') }} - where not is_excluded + where is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), @@ -51,7 +51,7 @@ test_file_paths as ( from resources where resource_type = 'test' - and is_generic_test + and is_generic_test = cast(1 as {{ dbt.type_boolean() }}) ), diff --git a/models/marts/tests/fct_missing_primary_key_tests.sql b/models/marts/tests/fct_missing_primary_key_tests.sql index be0430cf..1ce6b92b 100644 --- a/models/marts/tests/fct_missing_primary_key_tests.sql +++ b/models/marts/tests/fct_missing_primary_key_tests.sql @@ -19,7 +19,7 @@ final as ( number_of_tests_on_model, number_of_constraints_on_model from tests - where not(is_primary_key_tested) + where is_primary_key_tested = cast(0 as {{ dbt.type_boolean() }}) ) diff --git a/models/marts/tests/fct_sources_without_freshness.sql b/models/marts/tests/fct_sources_without_freshness.sql index 464db3d3..495b3b51 100644 --- a/models/marts/tests/fct_sources_without_freshness.sql +++ b/models/marts/tests/fct_sources_without_freshness.sql @@ -2,7 +2,7 @@ with all_resources as ( select * from {{ ref('int_all_graph_resources') }} - where not is_excluded + where is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), @@ -12,7 +12,7 @@ final as ( resource_name from all_resources - where not is_freshness_enabled and resource_type = 'source' + where is_freshness_enabled = cast(0 as {{ dbt.type_boolean() }}) and resource_type = 'source' ) diff --git a/models/marts/tests/intermediate/int_model_test_summary.sql b/models/marts/tests/intermediate/int_model_test_summary.sql index db968929..4dedbe92 100644 --- a/models/marts/tests/intermediate/int_model_test_summary.sql +++ b/models/marts/tests/intermediate/int_model_test_summary.sql @@ -2,7 +2,7 @@ with all_graph_resources as ( select * from {{ ref('int_all_graph_resources') }} - where not is_excluded + where is_excluded = cast(0 as {{ dbt.type_boolean() }}) ), relationships as ( @@ -15,16 +15,16 @@ count_column_tests as ( relationships.direct_parent_id, all_graph_resources.column_name, sum(case - when all_graph_resources.is_test_unique + when all_graph_resources.is_test_unique = cast(1 as {{ dbt.type_boolean() }}) then 1 else 0 end ) as test_unique_count, {%- for test_set in var('primary_key_test_macros') %} {%- set outer_loop = loop -%} - count(distinct case when - {%- for test in test_set %} - all_graph_resources.is_{{ test.split('.')[1] }} {%- if not loop.last %} or {% endif %} + count(distinct case when + {%- for test in test_set %} + all_graph_resources.is_{{ test.split('.')[1] }} = cast(1 as {{ dbt.type_boolean() }}) {%- if not loop.last %} or {% endif %} {%- endfor %} then relationships.resource_id else null end ) as primary_key_method_{{ outer_loop.index }}_count, @@ -34,8 +34,8 @@ count_column_tests as ( left join relationships on all_graph_resources.resource_id = relationships.resource_id where all_graph_resources.resource_type = 'test' - and relationships.is_primary_test_relationship - group by 1,2 + and relationships.is_primary_test_relationship = cast(1 as {{ dbt.type_boolean() }}) + group by relationships.direct_parent_id, all_graph_resources.column_name ), count_column_constraints as ( @@ -44,7 +44,7 @@ count_column_constraints as ( node_unique_id as direct_parent_id, name as column_name, case - when has_not_null_constraint + when has_not_null_constraint = cast(1 as {{ dbt.type_boolean() }}) then 1 else 0 end as constraint_not_null_count, @@ -70,7 +70,7 @@ agg_test_relationships as ( select direct_parent_id, - cast(sum(case + case when cast(sum(case when ( {%- for test_set in var('primary_key_test_macros') %} {%- set compare_value = test_set | length %} @@ -78,14 +78,14 @@ agg_test_relationships as ( or {%- endfor %} primary_key_mixed_method_count >= 2 - ) then 1 - else 0 + ) then 1 + else 0 end - ) >= 1 as {{ dbt.type_boolean() }}) as is_primary_key_tested, + ) as {{ dbt.type_int() }}) >= 1 then cast(1 as {{ dbt.type_boolean() }}) else cast(0 as {{ dbt.type_boolean() }}) end as is_primary_key_tested, cast(sum(tests_count) as {{ dbt.type_int()}}) as number_of_tests_on_model, cast(sum(constraints_count) as {{ dbt.type_int()}}) as number_of_constraints_on_model from combine_column_counts - group by 1 + group by direct_parent_id ), @@ -94,7 +94,7 @@ final as ( all_graph_resources.resource_name, all_graph_resources.resource_type, all_graph_resources.model_type, - cast(coalesce(agg_test_relationships.is_primary_key_tested, FALSE) as {{ dbt.type_boolean()}}) as is_primary_key_tested, + cast(coalesce(agg_test_relationships.is_primary_key_tested, cast(0 as {{ dbt.type_boolean() }})) as {{ dbt.type_boolean()}}) as is_primary_key_tested, cast(coalesce(agg_test_relationships.number_of_tests_on_model, 0) as {{ dbt.type_int()}}) as number_of_tests_on_model, cast(coalesce(agg_test_relationships.number_of_constraints_on_model, 0) as {{ dbt.type_int()}}) as number_of_constraints_on_model from all_graph_resources diff --git a/models/staging/graph/base/base_exposure_relationships.sql b/models/staging/graph/base/base_exposure_relationships.sql index 87f9f40c..9ec72563 100644 --- a/models/staging/graph/base/base_exposure_relationships.sql +++ b/models/staging/graph/base/base_exposure_relationships.sql @@ -17,7 +17,7 @@ with dummy_cte as ( select cast(null as {{ dbt_project_evaluator.type_string_dpe()}}) as resource_id, cast(null as {{ dbt_project_evaluator.type_string_dpe()}}) as direct_parent_id, - cast(True as {{ dbt.type_boolean() }}) as is_primary_relationship + cast(1 as {{ dbt.type_boolean() }}) as is_primary_relationship from dummy_cte -where false +where 1=0 diff --git a/models/staging/graph/base/base_metric_relationships.sql b/models/staging/graph/base/base_metric_relationships.sql index 084daced..0a157315 100644 --- a/models/staging/graph/base/base_metric_relationships.sql +++ b/models/staging/graph/base/base_metric_relationships.sql @@ -17,7 +17,7 @@ with dummy_cte as ( select cast(null as {{ dbt_project_evaluator.type_string_dpe()}}) as resource_id, cast(null as {{ dbt_project_evaluator.type_string_dpe()}}) as direct_parent_id, - cast(True as {{ dbt.type_boolean() }}) as is_primary_relationship + cast(1 as {{ dbt.type_boolean() }}) as is_primary_relationship from dummy_cte -where false +where 1=0 diff --git a/models/staging/graph/base/base_node_columns.sql b/models/staging/graph/base/base_node_columns.sql index b9c33c92..c0921a34 100644 --- a/models/staging/graph/base/base_node_columns.sql +++ b/models/staging/graph/base/base_node_columns.sql @@ -19,9 +19,9 @@ select cast(null as {{ dbt_project_evaluator.type_large_string()}}) as description, cast(null as {{ dbt_project_evaluator.type_string_dpe()}}) as data_type, cast(null as {{ dbt_project_evaluator.type_string_dpe()}}) as constraints, - cast(True as {{ dbt.type_boolean() }}) as has_not_null_constraint, + cast(1 as {{ dbt.type_boolean() }}) as has_not_null_constraint, cast(0 as {{ dbt.type_int() }}) as constraints_count, cast(null as {{ dbt_project_evaluator.type_string_dpe()}}) as quote from dummy_cte -where false +where 1=0 diff --git a/models/staging/graph/base/base_node_relationships.sql b/models/staging/graph/base/base_node_relationships.sql index 47b66464..458210cb 100644 --- a/models/staging/graph/base/base_node_relationships.sql +++ b/models/staging/graph/base/base_node_relationships.sql @@ -16,7 +16,7 @@ with dummy_cte as ( select cast(null as {{ dbt_project_evaluator.type_string_dpe()}}) as resource_id, cast(null as {{ dbt_project_evaluator.type_string_dpe()}}) as direct_parent_id, - cast(True as {{ dbt.type_boolean() }}) as is_primary_relationship + cast(1 as {{ dbt.type_boolean() }}) as is_primary_relationship from dummy_cte -where false +where 1=0 diff --git a/models/staging/graph/base/base_source_columns.sql b/models/staging/graph/base/base_source_columns.sql index f4dcc954..fc7c24be 100644 --- a/models/staging/graph/base/base_source_columns.sql +++ b/models/staging/graph/base/base_source_columns.sql @@ -19,9 +19,9 @@ select cast(null as {{ dbt_project_evaluator.type_large_string()}}) as description, cast(null as {{ dbt_project_evaluator.type_string_dpe()}}) as data_type, cast(null as {{ dbt_project_evaluator.type_string_dpe()}}) as constraints, - cast(True as boolean) as has_not_null_constraint, + cast(1 as {{ dbt.type_boolean() }}) as has_not_null_constraint, cast(0 as {{ dbt.type_int() }}) as constraints_count, cast(null as {{ dbt_project_evaluator.type_string_dpe()}}) as quote from dummy_cte -where false \ No newline at end of file +where 1=0 \ No newline at end of file diff --git a/models/staging/graph/stg_exposures.sql b/models/staging/graph/stg_exposures.sql index 6d16f7b7..b1b3ec60 100644 --- a/models/staging/graph/stg_exposures.sql +++ b/models/staging/graph/stg_exposures.sql @@ -19,7 +19,7 @@ select cast(null as {{ dbt_project_evaluator.type_string_dpe() }} ) as name, cast(null as {{ dbt_project_evaluator.type_string_dpe() }} ) as resource_type, cast(null as {{ dbt_project_evaluator.type_string_dpe() }} ) as file_path, - cast(True as {{ dbt.type_boolean() }}) as is_described, + cast(1 as {{ dbt.type_boolean() }}) as is_described, cast(null as {{ dbt_project_evaluator.type_string_dpe() }} ) as exposure_type, cast(null as {{ dbt_project_evaluator.type_string_dpe() }} ) as maturity, cast(null as {{ dbt_project_evaluator.type_string_dpe() }} ) as package_name, @@ -29,4 +29,4 @@ select cast(null as {{ dbt_project_evaluator.type_string_dpe() }} ) as meta from dummy_cte -where false +where 1=0 diff --git a/models/staging/graph/stg_metrics.sql b/models/staging/graph/stg_metrics.sql index b8c008b3..eac97b56 100644 --- a/models/staging/graph/stg_metrics.sql +++ b/models/staging/graph/stg_metrics.sql @@ -19,7 +19,7 @@ select cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as name, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as resource_type, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as file_path, - cast(True as {{ dbt.type_boolean() }}) as is_described, + cast(1 as {{ dbt.type_boolean() }}) as is_described, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as metric_type, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as label, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as package_name, @@ -34,4 +34,4 @@ select cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as meta from dummy_cte -where false +where 1=0 diff --git a/models/staging/graph/stg_nodes.sql b/models/staging/graph/stg_nodes.sql index 5c792b41..988cdf42 100644 --- a/models/staging/graph/stg_nodes.sql +++ b/models/staging/graph/stg_nodes.sql @@ -20,7 +20,7 @@ select cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as name, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as resource_type, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as file_path, - cast(True as {{ dbt.type_boolean() }}) as is_enabled, + cast(1 as {{ dbt.type_boolean() }}) as is_enabled, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as materialized, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as on_schema_change, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as model_group, @@ -28,22 +28,22 @@ select cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as latest_version, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as version, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as deprecation_date, - cast(True as {{ dbt.type_boolean() }}) as is_contract_enforced, + cast(1 as {{ dbt.type_boolean() }}) as is_contract_enforced, cast(0 as {{ dbt.type_int() }}) as total_defined_columns, cast(0 as {{ dbt.type_int() }}) as total_described_columns, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as database, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as schema, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as package_name, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as alias, - cast(True as {{ dbt.type_boolean() }}) as is_described, + cast(1 as {{ dbt.type_boolean() }}) as is_described, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as column_name, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as meta, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as hard_coded_references, cast(null as {{ dbt.type_int() }}) as number_lines, cast(null as {{ dbt.type_float() }}) as sql_complexity, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as macro_dependencies, - cast(True as {{ dbt.type_boolean() }}) as is_generic_test, - cast(True as {{ dbt.type_boolean() }}) as is_excluded + cast(1 as {{ dbt.type_boolean() }}) as is_generic_test, + cast(1 as {{ dbt.type_boolean() }}) as is_excluded from dummy_cte -where false +where 1=0 diff --git a/models/staging/graph/stg_sources.sql b/models/staging/graph/stg_sources.sql index cb2fa921..40e6d8b3 100644 --- a/models/staging/graph/stg_sources.sql +++ b/models/staging/graph/stg_sources.sql @@ -22,18 +22,18 @@ select cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as alias, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as resource_type, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as source_name, - cast(True as {{ dbt.type_boolean() }}) as is_source_described, - cast(True as {{ dbt.type_boolean() }}) as is_described, - cast(True as {{ dbt.type_boolean() }}) as is_enabled, + cast(1 as {{ dbt.type_boolean() }}) as is_source_described, + cast(1 as {{ dbt.type_boolean() }}) as is_described, + cast(1 as {{ dbt.type_boolean() }}) as is_enabled, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as loaded_at_field, - cast(True as {{ dbt.type_boolean() }}) as is_freshness_enabled, + cast(1 as {{ dbt.type_boolean() }}) as is_freshness_enabled, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as database, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as schema, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as package_name, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as loader, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as identifier, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as meta, - cast(True as {{ dbt.type_boolean() }}) as is_excluded + cast(1 as {{ dbt.type_boolean() }}) as is_excluded from dummy_cte -where false +where 1=0 diff --git a/supported_adapters.env b/supported_adapters.env index 83de7964..a9742aa2 100644 --- a/supported_adapters.env +++ b/supported_adapters.env @@ -1 +1 @@ -SUPPORTED_ADAPTERS=snowflake,bigquery,redshift,databricks \ No newline at end of file +SUPPORTED_ADAPTERS=snowflake,bigquery,redshift,databricks,fabric \ No newline at end of file From 95fffdeaab87a32e12fdd95adc5f8d5bc45a03f9 Mon Sep 17 00:00:00 2001 From: Sam Debruyn Date: Sun, 17 May 2026 16:25:07 +0200 Subject: [PATCH 2/9] Fix reserved keyword quoting in staging model column definitions The column aliases `as database` and `as schema` in stg_nodes.sql and stg_sources.sql cause T-SQL syntax errors since these are reserved keywords. Use quote_identifier dispatch macro for the aliases too. Co-Authored-By: Claude Opus 4.6 --- models/staging/graph/stg_nodes.sql | 4 ++-- models/staging/graph/stg_sources.sql | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/models/staging/graph/stg_nodes.sql b/models/staging/graph/stg_nodes.sql index 988cdf42..c776ee2d 100644 --- a/models/staging/graph/stg_nodes.sql +++ b/models/staging/graph/stg_nodes.sql @@ -31,8 +31,8 @@ select cast(1 as {{ dbt.type_boolean() }}) as is_contract_enforced, cast(0 as {{ dbt.type_int() }}) as total_defined_columns, cast(0 as {{ dbt.type_int() }}) as total_described_columns, - cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as database, - cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as schema, + cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as {{ dbt_project_evaluator.quote_identifier('database') }}, + cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as {{ dbt_project_evaluator.quote_identifier('schema') }}, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as package_name, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as alias, cast(1 as {{ dbt.type_boolean() }}) as is_described, diff --git a/models/staging/graph/stg_sources.sql b/models/staging/graph/stg_sources.sql index 40e6d8b3..f360381c 100644 --- a/models/staging/graph/stg_sources.sql +++ b/models/staging/graph/stg_sources.sql @@ -27,8 +27,8 @@ select cast(1 as {{ dbt.type_boolean() }}) as is_enabled, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as loaded_at_field, cast(1 as {{ dbt.type_boolean() }}) as is_freshness_enabled, - cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as database, - cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as schema, + cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as {{ dbt_project_evaluator.quote_identifier('database') }}, + cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as {{ dbt_project_evaluator.quote_identifier('schema') }}, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as package_name, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as loader, cast(null as {{ dbt_project_evaluator.type_string_dpe() }}) as identifier, From a28b39bc3237b7c0c5785a3decf876497e04cf73 Mon Sep 17 00:00:00 2001 From: Sam Debruyn Date: Sun, 17 May 2026 16:29:48 +0200 Subject: [PATCH 3/9] Fix missed bare boolean in fct_test_directories is_primary_test_relationship was still used as a bare boolean in WHERE. Co-Authored-By: Claude Opus 4.6 --- models/marts/structure/fct_test_directories.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/marts/structure/fct_test_directories.sql b/models/marts/structure/fct_test_directories.sql index 24d35b5c..9c4d028f 100644 --- a/models/marts/structure/fct_test_directories.sql +++ b/models/marts/structure/fct_test_directories.sql @@ -22,7 +22,7 @@ models_per_test as ( direct_parent_id as parent_model_id from relationships where resource_type = 'test' - and is_primary_test_relationship + and is_primary_test_relationship = cast(1 as {{ dbt.type_boolean() }}) ), From e6588252a9ba605943ebcf7b464da4b7b9fd941b Mon Sep 17 00:00:00 2001 From: Sam Debruyn Date: Sun, 17 May 2026 16:38:30 +0200 Subject: [PATCH 4/9] Move quote_identifier and bool_literal to own files Generic dispatch macros belong in their own files (like type_string.sql), not in fabric_shims.sql which is only for Fabric-specific overrides. Co-Authored-By: Claude Opus 4.6 --- macros/cross_db_shim/bool_literal.sql | 8 ++++++++ macros/cross_db_shim/fabric_shims.sql | 17 ----------------- macros/cross_db_shim/quote_identifier.sql | 7 +++++++ 3 files changed, 15 insertions(+), 17 deletions(-) create mode 100644 macros/cross_db_shim/bool_literal.sql create mode 100644 macros/cross_db_shim/quote_identifier.sql diff --git a/macros/cross_db_shim/bool_literal.sql b/macros/cross_db_shim/bool_literal.sql new file mode 100644 index 00000000..75bf47dd --- /dev/null +++ b/macros/cross_db_shim/bool_literal.sql @@ -0,0 +1,8 @@ +{# Convert a Python boolean to a SQL boolean literal appropriate for the target adapter #} +{% macro bool_literal(value) %} + {{ return(adapter.dispatch('bool_literal', 'dbt_project_evaluator')(value)) }} +{% endmacro %} + +{% macro default__bool_literal(value) %}{{ value | trim }}{% endmacro %} + +{% macro fabric__bool_literal(value) %}{% if value %}1{% else %}0{% endif %}{% endmacro %} diff --git a/macros/cross_db_shim/fabric_shims.sql b/macros/cross_db_shim/fabric_shims.sql index dd01ed67..d1f65d78 100644 --- a/macros/cross_db_shim/fabric_shims.sql +++ b/macros/cross_db_shim/fabric_shims.sql @@ -1,20 +1,3 @@ {% macro fabric__escape_single_quotes(expression) -%} {{ expression | replace("'","''") }} {%- endmacro %} - -{% macro quote_identifier(name) %} - {{ return(adapter.dispatch('quote_identifier', 'dbt_project_evaluator')(name)) }} -{% endmacro %} - -{% macro default__quote_identifier(name) %}{{ name }}{% endmacro %} - -{% macro fabric__quote_identifier(name) %}[{{ name }}]{% endmacro %} - -{# Convert a Python boolean to a SQL boolean literal appropriate for the target adapter #} -{% macro bool_literal(value) %} - {{ return(adapter.dispatch('bool_literal', 'dbt_project_evaluator')(value)) }} -{% endmacro %} - -{% macro default__bool_literal(value) %}{{ value | trim }}{% endmacro %} - -{% macro fabric__bool_literal(value) %}{% if value %}1{% else %}0{% endif %}{% endmacro %} diff --git a/macros/cross_db_shim/quote_identifier.sql b/macros/cross_db_shim/quote_identifier.sql new file mode 100644 index 00000000..7dbc07ba --- /dev/null +++ b/macros/cross_db_shim/quote_identifier.sql @@ -0,0 +1,7 @@ +{% macro quote_identifier(name) %} + {{ return(adapter.dispatch('quote_identifier', 'dbt_project_evaluator')(name)) }} +{% endmacro %} + +{% macro default__quote_identifier(name) %}{{ name }}{% endmacro %} + +{% macro fabric__quote_identifier(name) %}[{{ name }}]{% endmacro %} From a6fa86ab7b1408bd00f532c7200285a75eaef663 Mon Sep 17 00:00:00 2001 From: Sam Debruyn Date: Sun, 17 May 2026 16:40:45 +0200 Subject: [PATCH 5/9] Remove fabric_shims.sql, move override to spark_shims.sql Adapter-specific overrides of the same macro belong in one file. fabric__escape_single_quotes joins spark__escape_single_quotes. Co-Authored-By: Claude Opus 4.6 --- macros/cross_db_shim/fabric_shims.sql | 3 --- macros/cross_db_shim/spark_shims.sql | 6 +++++- 2 files changed, 5 insertions(+), 4 deletions(-) delete mode 100644 macros/cross_db_shim/fabric_shims.sql diff --git a/macros/cross_db_shim/fabric_shims.sql b/macros/cross_db_shim/fabric_shims.sql deleted file mode 100644 index d1f65d78..00000000 --- a/macros/cross_db_shim/fabric_shims.sql +++ /dev/null @@ -1,3 +0,0 @@ -{% macro fabric__escape_single_quotes(expression) -%} - {{ expression | replace("'","''") }} -{%- endmacro %} diff --git a/macros/cross_db_shim/spark_shims.sql b/macros/cross_db_shim/spark_shims.sql index 252812c8..1d434faf 100644 --- a/macros/cross_db_shim/spark_shims.sql +++ b/macros/cross_db_shim/spark_shims.sql @@ -1,3 +1,7 @@ {% macro spark__escape_single_quotes(expression) -%} - {{ expression | replace("'","\\'") }} + {{ expression | replace("'","\\'") }} +{%- endmacro %} + +{% macro fabric__escape_single_quotes(expression) -%} + {{ expression | replace("'","''") }} {%- endmacro %} \ No newline at end of file From 57ffc9eefa3023c36b82c8c45dee589faf2ea107 Mon Sep 17 00:00:00 2001 From: Sam Debruyn Date: Sun, 17 May 2026 16:41:38 +0200 Subject: [PATCH 6/9] Rename spark_shims.sql to escape_single_quotes.sql File names should reflect the macro concept, not the adapter. Co-Authored-By: Claude Opus 4.6 --- .../cross_db_shim/{spark_shims.sql => escape_single_quotes.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename macros/cross_db_shim/{spark_shims.sql => escape_single_quotes.sql} (100%) diff --git a/macros/cross_db_shim/spark_shims.sql b/macros/cross_db_shim/escape_single_quotes.sql similarity index 100% rename from macros/cross_db_shim/spark_shims.sql rename to macros/cross_db_shim/escape_single_quotes.sql From cd776bf4ceb090886819942955e34f3deb703031 Mon Sep 17 00:00:00 2001 From: Sam Debruyn Date: Sun, 17 May 2026 16:54:19 +0200 Subject: [PATCH 7/9] Handle backslash path separators in Fabric file_path extraction The Fabric-specific charindex/left/right logic only handled forward slashes. Windows users running dbt against Fabric would get incorrect directory_path and file_name values. Now dispatches on is_os_mac_or_linux like the non-Fabric branches already do. Co-Authored-By: Claude Opus 4.6 --- macros/get_directory_pattern.sql | 7 ++++++- models/marts/core/int_all_graph_resources.sql | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/macros/get_directory_pattern.sql b/macros/get_directory_pattern.sql index 41b9f049..6f4062eb 100644 --- a/macros/get_directory_pattern.sql +++ b/macros/get_directory_pattern.sql @@ -25,7 +25,12 @@ {% macro get_dbtreplace_directory_pattern() %} {% if execute %} {%- if target.type == 'fabric' -%} - left(file_path, len(file_path) - charindex('/', reverse(file_path))) + {%- set on_mac_or_linux = dbt_project_evaluator.is_os_mac_or_linux() -%} + {%- if on_mac_or_linux -%} + left(file_path, len(file_path) - charindex('/', reverse(file_path))) + {%- else -%} + left(file_path, len(file_path) - charindex('\', reverse(file_path))) + {%- endif -%} {%- else -%} {%- set on_mac_or_linux = dbt_project_evaluator.is_os_mac_or_linux() -%} {%- if on_mac_or_linux -%} diff --git a/models/marts/core/int_all_graph_resources.sql b/models/marts/core/int_all_graph_resources.sql index 37fc698c..c15d7919 100644 --- a/models/marts/core/int_all_graph_resources.sql +++ b/models/marts/core/int_all_graph_resources.sql @@ -51,7 +51,12 @@ unioned_with_calc as ( end as prefix, {{ get_dbtreplace_directory_pattern() }} as directory_path, {% if target.type == 'fabric' %} + {%- set on_mac_or_linux = dbt_project_evaluator.is_os_mac_or_linux() -%} + {%- if on_mac_or_linux -%} right(file_path, charindex('/', reverse(file_path)) - 1) as file_name + {%- else -%} + right(file_path, charindex('\', reverse(file_path)) - 1) as file_name + {%- endif -%} {% else %} regexp_replace(file_path,'.*{{ get_regexp_directory_pattern() }}','') as file_name {% endif %} From ef2a357ed9b692c21f039ccabf14aa8e2d02c4bd Mon Sep 17 00:00:00 2001 From: Benoit Perigaud <8754100+b-per@users.noreply.github.com> Date: Thu, 21 May 2026 10:01:48 +0200 Subject: [PATCH 8/9] fix: move order by after filter_exceptions() to fix cross-adapter syntax error filter_exceptions() emits a WHERE clause; ORDER BY must come after WHERE. --- models/marts/dag/fct_direct_join_to_source.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/marts/dag/fct_direct_join_to_source.sql b/models/marts/dag/fct_direct_join_to_source.sql index 7a0f29ac..013f5745 100644 --- a/models/marts/dag/fct_direct_join_to_source.sql +++ b/models/marts/dag/fct_direct_join_to_source.sql @@ -39,6 +39,6 @@ final as ( ) select * from final -order by child -{{ filter_exceptions() }} \ No newline at end of file +{{ filter_exceptions() }} +order by child \ No newline at end of file From 3117153b4f91671a63cd35ae9dff292520f4ca03 Mon Sep 17 00:00:00 2001 From: Benoit Perigaud <8754100+b-per@users.noreply.github.com> Date: Thu, 21 May 2026 10:50:18 +0200 Subject: [PATCH 9/9] docs: add Fabric adapter support; refactor get_dbtreplace_directory_pattern to dispatch - Add Microsoft Fabric Data Warehouse and Fabric Spark to supported adapter lists in README and docs/index.md - Update Additional setup headings to include Fabric - Update Limitations section and max_depth_dag docs to include Fabric (no recursive CTE support, uses loop-based approach, defaults to 9) - Refactor get_dbtreplace_directory_pattern from if target.type guard to adapter.dispatch pattern, consistent with recursive_dag.sql --- README.md | 4 ++- docs/customization/overriding-variables.md | 4 +-- docs/index.md | 8 ++++-- macros/get_directory_pattern.sql | 32 +++++++++++++--------- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 42d11f5b..081aae87 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ Currently, the following adapters are supported: - AWS Athena (tested manually) - Greenplum (tested manually) - ClickHouse (tested manually) +- Microsoft Fabric Data Warehouse (tested manually) +- Microsoft Fabric Spark (tested manually) ## Using This Package @@ -31,7 +33,7 @@ Currently, the following adapters are supported: Check [dbt Hub](https://hub.getdbt.com/dbt-labs/dbt_project_evaluator/latest/) for the latest installation instructions, or [read the docs](https://docs.getdbt.com/docs/package-management) for more information on installing packages. -### Additional setup for Databricks/Spark/DuckDB/Redshift/ClickHouse +### Additional setup for Databricks/Spark/DuckDB/Redshift/ClickHouse/Fabric In your `dbt_project.yml`, add the following config: diff --git a/docs/customization/overriding-variables.md b/docs/customization/overriding-variables.md index dee1611a..9363b341 100644 --- a/docs/customization/overriding-variables.md +++ b/docs/customization/overriding-variables.md @@ -103,14 +103,14 @@ vars: | variable | description | default | | ----------- | ----------- | ----------- | -| `max_depth_dag` | limits the maximum distance between nodes calculated in `int_all_dag_relationships` | 9 for bigquery and spark, -1 for other adatpters | +| `max_depth_dag` | limits the maximum distance between nodes calculated in `int_all_dag_relationships` | 9 for bigquery, spark, and fabric, -1 for other adapters | | `insert_batch_size` | number of records inserted per batch when unpacking the graph into models | 10000 | **Note on max_depth_dag** The default behavior for limiting the relationships calculated in the `int_all_dag_relationships` model differs depending on your adapter. -- For Bigquery & Spark/Databricks the maximum distance between two nodes in your DAG, calculated in `int_all_dag_relationships`, is set by the `max_depth_dag` variable, which is defaulted to 9. So by default, `int_all_dag_relationships` contains a row for every path less than or equal to 9 nodes in length between two nodes in your DAG. This is because these adapters do not currently support recursive SQL, and queries often fail on more than 9 recursive joins. +- For BigQuery, Spark/Databricks, and Microsoft Fabric Data Warehouse the maximum distance between two nodes in your DAG, calculated in `int_all_dag_relationships`, is set by the `max_depth_dag` variable, which is defaulted to 9. So by default, `int_all_dag_relationships` contains a row for every path less than or equal to 9 nodes in length between two nodes in your DAG. This is because these adapters do not currently support recursive SQL, and queries often fail on more than 9 recursive joins. - For all other adapters `int_all_dag_relationships` by default contains a row for every single path between two nodes in your DAG. If you experience long runtimes for the `int_all_dag_relationships` model, you may consider limiting the length of your generated DAG paths. To do this, set `max_depth_dag: {{ whatever limit you want to enforce }}`. The value of `max_depth_dag` must be greater than 2 for all DAG tests to work, and greater than `chained_views_threshold` to ensure your performance tests to work. By default, the value of this variable for these adapters is -1, which the package interprets as "no limit". ```yaml title="dbt_project.yml" diff --git a/docs/index.md b/docs/index.md index 7e2cb243..a3348e18 100644 --- a/docs/index.md +++ b/docs/index.md @@ -25,6 +25,8 @@ Currently, the following adapters are supported: - AWS Athena (tested manually) - Greenplum (tested manually) - ClickHouse (tested manually) +- Microsoft Fabric Data Warehouse (tested manually) +- Microsoft Fabric Spark (tested manually) ## Using This Package @@ -32,7 +34,7 @@ Currently, the following adapters are supported: Check [dbt Hub](https://hub.getdbt.com/dbt-labs/dbt_project_evaluator/latest/) for the latest installation instructions, or [read the docs](https://docs.getdbt.com/docs/package-management) for more information on installing packages. -### Additional setup for Databricks/Spark/DuckDB/Redshift +### Additional setup for Databricks/Spark/DuckDB/Redshift/Fabric In your `dbt_project.yml`, add the following config: @@ -64,8 +66,8 @@ Each test warning indicates the presence of a type of misalignment. To troublesh ## Limitations -### BigQuery and Databricks +### BigQuery, Databricks, and Microsoft Fabric Data Warehouse -BigQuery current support for recursive CTEs is limited and Databricks SQL doesn't support recursive CTEs. +BigQuery has limited support for recursive CTEs, while Databricks SQL and Microsoft Fabric Data Warehouse do not support them. For those Data Warehouses, the model `int_all_dag_relationships` needs to be created by looping CTEs instead. The number of loops is configured with `max_depth_dag` and defaulted to 9. This means that dependencies between models of more than 9 levels of separation won't show in the model `int_all_dag_relationships` but tests on the DAG will still be correct. With a number of loops higher than 9 BigQuery sometimes raises an error saying the query is too complex. diff --git a/macros/get_directory_pattern.sql b/macros/get_directory_pattern.sql index 6f4062eb..41877de0 100644 --- a/macros/get_directory_pattern.sql +++ b/macros/get_directory_pattern.sql @@ -23,21 +23,27 @@ {% endmacro %} {% macro get_dbtreplace_directory_pattern() %} + {{ return(adapter.dispatch('get_dbtreplace_directory_pattern', 'dbt_project_evaluator')()) }} +{% endmacro %} + +{% macro default__get_dbtreplace_directory_pattern() %} + {% if execute %} + {%- set on_mac_or_linux = dbt_project_evaluator.is_os_mac_or_linux() -%} + {%- if on_mac_or_linux -%} + {{ dbt.replace("file_path", "regexp_replace(file_path,'.*/','')", "''") }} + {% else %} + {{ dbt.replace("file_path", "regexp_replace(file_path,'.*\\\\\\\\','')", "''") }} + {% endif %} + {% endif %} +{% endmacro %} + +{% macro fabric__get_dbtreplace_directory_pattern() %} {% if execute %} - {%- if target.type == 'fabric' -%} - {%- set on_mac_or_linux = dbt_project_evaluator.is_os_mac_or_linux() -%} - {%- if on_mac_or_linux -%} - left(file_path, len(file_path) - charindex('/', reverse(file_path))) - {%- else -%} - left(file_path, len(file_path) - charindex('\', reverse(file_path))) - {%- endif -%} + {%- set on_mac_or_linux = dbt_project_evaluator.is_os_mac_or_linux() -%} + {%- if on_mac_or_linux -%} + left(file_path, len(file_path) - charindex('/', reverse(file_path))) {%- else -%} - {%- set on_mac_or_linux = dbt_project_evaluator.is_os_mac_or_linux() -%} - {%- if on_mac_or_linux -%} - {{ dbt.replace("file_path", "regexp_replace(file_path,'.*/','')", "''") }} - {% else %} - {{ dbt.replace("file_path", "regexp_replace(file_path,'.*\\\\\\\\','')", "''") }} - {% endif %} + left(file_path, len(file_path) - charindex('\', reverse(file_path))) {%- endif -%} {% endif %} {% endmacro %} \ No newline at end of file