Skip to content

default__concat produces invalid SQL for single-field input when adapters override with CONCAT() function #1948

@sdebruyn

Description

@sdebruyn

Summary

The current default__concat macro joins fields with ||, which works correctly for a single-field list (it simply returns the field). However, many adapters override default__concat to emit a SQL CONCAT(...) function call instead of the || operator. On engines like SQL Server / Microsoft Fabric T-SQL, CONCAT() requires at least two arguments — CONCAT(x) raises a syntax error.

Because the default macro propagates a single-field list to overrides, every override that uses the function form must add its own defensive branch (or break on single-field input).

Repro

When dbt_utils.generate_surrogate_key([single_field]) is called, the package generates dbt.hash(dbt.concat([field])). On Fabric this becomes HASHBYTES(... CONCAT(field) ...), which fails to compile.

Affected adapters / known workarounds

Any adapter that uses the CONCAT(...) function form needs the same fix.

Proposal

Short-circuit default__concat on length == 1 and return the field unchanged. Behavior is identical for multi-field lists; the change is purely defensive.

{% macro default__concat(fields) -%}
    {%- if fields | length == 1 -%}
        {{ fields[0] }}
    {%- else -%}
        {{ fields | join(' || ') }}
    {%- endif -%}
{%- endmacro %}

A dispatched-down override would have to repeat the same guard in every adapter, so the fix belongs in the default.

I'll open a PR with the change plus a BaseConcatSingleField test in dbt-tests-adapter so every adapter inherits coverage.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions