Skip to content

Commit dd42ece

Browse files
fix: add Dremio-specific edr_dateadd and improve edr_cast_as_timestamp (#957)
* fix: add Dremio-specific edr_dateadd and improve edr_cast_as_timestamp - Convert edr_dateadd to use adapter.dispatch for adapter-specific overrides - Add dremio__edr_dateadd that fixes dbt-dremio's dateadd bugs: 1. Casts interval to string before calling .replace() (fixes integer interval) 2. Outputs TIMESTAMPADD expression directly (no scalar subquery wrapper) - Update dremio__edr_cast_as_timestamp to normalize ISO 8601 timestamps: 1. Remove 'T' date-time separator (Gandiva can't parse it) 2. Remove 'Z' UTC suffix (Gandiva rejects unknown zone) 3. Truncate sub-millisecond precision Co-Authored-By: Itamar Hartstein <haritamar@gmail.com> * fix: address CodeRabbit review - cast to string before REGEXP_REPLACE, handle uppercase ORDER BY 1 Co-Authored-By: Itamar Hartstein <haritamar@gmail.com> * fix: remove order-by-1 hack, add cast-to-string for REGEXP_REPLACE, improve comments - Remove unnecessary 'order by 1' stripping from dremio__edr_dateadd (not needed since adapter.dispatch bypasses dbt-dremio's macro entirely) - Keep three REGEXP_REPLACE calls since each has a different replacement pattern (space, truncation, removal) that can't be combined into a single call - Add concrete example in comment showing the transformation Co-Authored-By: Itamar Hartstein <haritamar@gmail.com> * style: apply sqlfmt formatting to dateadd.sql Co-Authored-By: Itamar Hartstein <haritamar@gmail.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Itamar Hartstein <haritamar@gmail.com>
1 parent ac90080 commit dd42ece

2 files changed

Lines changed: 99 additions & 1 deletion

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,86 @@
11
{% macro edr_dateadd(datepart, interval, from_date_or_timestamp) %}
2+
{{
3+
return(
4+
adapter.dispatch("edr_dateadd", "elementary")(
5+
datepart, interval, from_date_or_timestamp
6+
)
7+
)
8+
}}
9+
{% endmacro %}
10+
11+
{% macro default__edr_dateadd(datepart, interval, from_date_or_timestamp) %}
212
{% set macro = dbt.dateadd or dbt_utils.dateadd %}
313
{% if not macro %}
414
{{ exceptions.raise_compiler_error("Did not find a `dateadd` macro.") }}
515
{% endif %}
616
{{ return(macro(datepart, interval, from_date_or_timestamp)) }}
717
{% endmacro %}
18+
19+
{#
20+
Override dbt-dremio's dateadd macro which has two bugs:
21+
1. Calls interval.replace() on the interval parameter, failing when interval is an integer
22+
2. Wraps result in "select TIMESTAMPADD(...)" which creates a scalar subquery when
23+
embedded in larger SQL expressions, causing $SCALAR_QUERY errors in Dremio
24+
25+
This override outputs just TIMESTAMPADD(...) as an expression (no "select" prefix).
26+
#}
27+
{% macro dremio__edr_dateadd(datepart, interval, from_date_or_timestamp) %}
28+
{% set datepart = datepart | lower %}
29+
{% if datepart == "year" %}
30+
timestampadd(
31+
year,
32+
cast({{ interval }} as int),
33+
cast({{ from_date_or_timestamp }} as timestamp)
34+
)
35+
{% elif datepart == "quarter" %}
36+
timestampadd(
37+
quarter,
38+
cast({{ interval }} as int),
39+
cast({{ from_date_or_timestamp }} as timestamp)
40+
)
41+
{% elif datepart == "month" %}
42+
timestampadd(
43+
month,
44+
cast({{ interval }} as int),
45+
cast({{ from_date_or_timestamp }} as timestamp)
46+
)
47+
{% elif datepart == "week" %}
48+
timestampadd(
49+
week,
50+
cast({{ interval }} as int),
51+
cast({{ from_date_or_timestamp }} as timestamp)
52+
)
53+
{% elif datepart == "hour" %}
54+
timestampadd(
55+
hour,
56+
cast({{ interval }} as int),
57+
cast({{ from_date_or_timestamp }} as timestamp)
58+
)
59+
{% elif datepart == "minute" %}
60+
timestampadd(
61+
minute,
62+
cast({{ interval }} as int),
63+
cast({{ from_date_or_timestamp }} as timestamp)
64+
)
65+
{% elif datepart == "second" %}
66+
timestampadd(
67+
second,
68+
cast({{ interval }} as int),
69+
cast({{ from_date_or_timestamp }} as timestamp)
70+
)
71+
{% elif datepart == "day" %}
72+
timestampadd(
73+
day,
74+
cast({{ interval }} as int),
75+
cast({{ from_date_or_timestamp }} as timestamp)
76+
)
77+
{% else %}
78+
{{
79+
exceptions.raise_compiler_error(
80+
"dremio__edr_dateadd: unrecognized datepart '"
81+
~ datepart
82+
~ "'. Supported: year, quarter, month, week, day, hour, minute, second."
83+
)
84+
}}
85+
{% endif %}
86+
{% endmacro %}

macros/utils/data_types/cast_column.sql

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,29 @@
4242
{%- endmacro -%}
4343
-- fmt: on
4444

45+
{#
46+
Dremio's Gandiva (Arrow execution engine) cannot parse ISO 8601 timestamps:
47+
1. The 'T' date-time separator is not recognized (needs space)
48+
2. Sub-millisecond precision causes overflow
49+
3. The 'Z' UTC timezone suffix is rejected as an unknown zone
50+
This normalizes '2024-01-15T12:30:00.123456Z' to '2024-01-15 12:30:00.123'.
51+
Three separate REGEXP_REPLACE calls are needed because each has a different
52+
replacement pattern (space, truncation, removal) that can't be combined into one.
53+
#}
4554
{%- macro dremio__edr_cast_as_timestamp(timestamp_field) -%}
4655
cast(
4756
regexp_replace(
48-
{{ timestamp_field }}, '(\.\d{3})\d+', '$1'
57+
regexp_replace(
58+
regexp_replace(
59+
cast({{ timestamp_field }} as {{ elementary.edr_type_string() }}),
60+
'(\d)T(\d)',
61+
'$1 $2'
62+
),
63+
'(\.\d{3})\d+',
64+
'$1'
65+
),
66+
'Z$',
67+
''
4968
) as {{ elementary.edr_type_timestamp() }}
5069
)
5170
{%- endmacro -%}

0 commit comments

Comments
 (0)