mirror of
https://github.com/dbt-labs/dbt-core
synced 2025-12-21 14:31:27 +00:00
Compare commits
2 Commits
enable-pos
...
jerco/util
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8952617f61 | ||
|
|
ce88aba97d |
7
.changes/unreleased/Features-20220518-114604.yaml
Normal file
7
.changes/unreleased/Features-20220518-114604.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kind: Features
|
||||||
|
body: Move cross-db macros from dbt-utils into dbt-core global project
|
||||||
|
time: 2022-05-18T11:46:04.557104+02:00
|
||||||
|
custom:
|
||||||
|
Author: jtcohen6
|
||||||
|
Issue: "4813"
|
||||||
|
PR: "5265"
|
||||||
14
core/dbt/include/global_project/macros/utils/dateadd.sql
Normal file
14
core/dbt/include/global_project/macros/utils/dateadd.sql
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{% macro dateadd(datepart, interval, from_date_or_timestamp) %}
|
||||||
|
{{ return(adapter.dispatch('dateadd', 'dbt')(datepart, interval, from_date_or_timestamp)) }}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
|
{% macro default__dateadd(datepart, interval, from_date_or_timestamp) %}
|
||||||
|
|
||||||
|
dateadd(
|
||||||
|
{{ datepart }},
|
||||||
|
{{ interval }},
|
||||||
|
{{ from_date_or_timestamp }}
|
||||||
|
)
|
||||||
|
|
||||||
|
{% endmacro %}
|
||||||
14
core/dbt/include/global_project/macros/utils/datediff.sql
Normal file
14
core/dbt/include/global_project/macros/utils/datediff.sql
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{% macro datediff(first_date, second_date, datepart) %}
|
||||||
|
{{ return(adapter.dispatch('datediff', 'dbt')(first_date, second_date, datepart)) }}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
|
{% macro default__datediff(first_date, second_date, datepart) -%}
|
||||||
|
|
||||||
|
datediff(
|
||||||
|
{{ datepart }},
|
||||||
|
{{ first_date }},
|
||||||
|
{{ second_date }}
|
||||||
|
)
|
||||||
|
|
||||||
|
{%- endmacro %}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{% macro postgres__dateadd(datepart, interval, from_date_or_timestamp) %}
|
||||||
|
|
||||||
|
{{ from_date_or_timestamp }} + ((interval '1 {{ datepart }}') * ({{ interval }}))
|
||||||
|
|
||||||
|
{% endmacro %}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{% macro postgres__datediff(first_date, second_date, datepart) -%}
|
||||||
|
|
||||||
|
{% if datepart == 'year' %}
|
||||||
|
(date_part('year', ({{second_date}})::date) - date_part('year', ({{first_date}})::date))
|
||||||
|
{% elif datepart == 'quarter' %}
|
||||||
|
({{ datediff(first_date, second_date, 'year') }} * 4 + date_part('quarter', ({{second_date}})::date) - date_part('quarter', ({{first_date}})::date))
|
||||||
|
{% elif datepart == 'month' %}
|
||||||
|
({{ datediff(first_date, second_date, 'year') }} * 12 + date_part('month', ({{second_date}})::date) - date_part('month', ({{first_date}})::date))
|
||||||
|
{% elif datepart == 'day' %}
|
||||||
|
(({{second_date}})::date - ({{first_date}})::date)
|
||||||
|
{% elif datepart == 'week' %}
|
||||||
|
({{ datediff(first_date, second_date, 'day') }} / 7 + case
|
||||||
|
when date_part('dow', ({{first_date}})::timestamp) <= date_part('dow', ({{second_date}})::timestamp) then
|
||||||
|
case when {{first_date}} <= {{second_date}} then 0 else -1 end
|
||||||
|
else
|
||||||
|
case when {{first_date}} <= {{second_date}} then 1 else 0 end
|
||||||
|
end)
|
||||||
|
{% elif datepart == 'hour' %}
|
||||||
|
({{ datediff(first_date, second_date, 'day') }} * 24 + date_part('hour', ({{second_date}})::timestamp) - date_part('hour', ({{first_date}})::timestamp))
|
||||||
|
{% elif datepart == 'minute' %}
|
||||||
|
({{ datediff(first_date, second_date, 'hour') }} * 60 + date_part('minute', ({{second_date}})::timestamp) - date_part('minute', ({{first_date}})::timestamp))
|
||||||
|
{% elif datepart == 'second' %}
|
||||||
|
({{ datediff(first_date, second_date, 'minute') }} * 60 + floor(date_part('second', ({{second_date}})::timestamp)) - floor(date_part('second', ({{first_date}})::timestamp)))
|
||||||
|
{% elif datepart == 'millisecond' %}
|
||||||
|
({{ datediff(first_date, second_date, 'minute') }} * 60000 + floor(date_part('millisecond', ({{second_date}})::timestamp)) - floor(date_part('millisecond', ({{first_date}})::timestamp)))
|
||||||
|
{% elif datepart == 'microsecond' %}
|
||||||
|
({{ datediff(first_date, second_date, 'minute') }} * 60000000 + floor(date_part('microsecond', ({{second_date}})::timestamp)) - floor(date_part('microsecond', ({{first_date}})::timestamp)))
|
||||||
|
{% else %}
|
||||||
|
{{ exceptions.raise_compiler_error("Unsupported datepart for macro datediff in postgres: {!r}".format(datepart)) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{%- endmacro %}
|
||||||
34
tests/adapter/dbt/tests/adapter/utils/base_utils.py
Normal file
34
tests/adapter/dbt/tests/adapter/utils/base_utils.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
from dbt.tests.util import run_dbt
|
||||||
|
|
||||||
|
macros__test_assert_equal_sql = """
|
||||||
|
{% test assert_equal(model, actual, expected) %}
|
||||||
|
select * from {{ model }} where {{ actual }} != {{ expected }}
|
||||||
|
|
||||||
|
{% endtest %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class BaseUtils:
|
||||||
|
# setup
|
||||||
|
@pytest.fixture(scope="class")
|
||||||
|
def macros(self):
|
||||||
|
return {"test_assert_equal.sql": macros__test_assert_equal_sql}
|
||||||
|
|
||||||
|
# make it possible to dynamically update the macro call with a namespace
|
||||||
|
# (e.g.) 'dateadd', 'dbt.dateadd', 'dbt_utils.dateadd'
|
||||||
|
def macro_namespace(self):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def interpolate_macro_namespace(self, model_sql, macro_name):
|
||||||
|
macro_namespace = self.macro_namespace()
|
||||||
|
return (
|
||||||
|
model_sql.replace(f"{macro_name}(", f"{macro_namespace}.{macro_name}(")
|
||||||
|
if macro_namespace
|
||||||
|
else model_sql
|
||||||
|
)
|
||||||
|
|
||||||
|
# actual test sequence
|
||||||
|
def test_build_assert_equal(self, project):
|
||||||
|
run_dbt(["build"]) # seed, model, test
|
||||||
40
tests/adapter/dbt/tests/adapter/utils/fixture_dateadd.py
Normal file
40
tests/adapter/dbt/tests/adapter/utils/fixture_dateadd.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# dateadd
|
||||||
|
|
||||||
|
seeds__data_dateadd_csv = """from_time,interval_length,datepart,result
|
||||||
|
2018-01-01 01:00:00,1,day,2018-01-02 01:00:00
|
||||||
|
2018-01-01 01:00:00,1,month,2018-02-01 01:00:00
|
||||||
|
2018-01-01 01:00:00,1,year,2019-01-01 01:00:00
|
||||||
|
2018-01-01 01:00:00,1,hour,2018-01-01 02:00:00
|
||||||
|
,1,day,
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
models__test_dateadd_sql = """
|
||||||
|
with data as (
|
||||||
|
|
||||||
|
select * from {{ ref('data_dateadd') }}
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
select
|
||||||
|
case
|
||||||
|
when datepart = 'hour' then cast({{ dateadd('hour', 'interval_length', 'from_time') }} as {{ api.Column.translate_type('timestamp') }})
|
||||||
|
when datepart = 'day' then cast({{ dateadd('day', 'interval_length', 'from_time') }} as {{ api.Column.translate_type('timestamp') }})
|
||||||
|
when datepart = 'month' then cast({{ dateadd('month', 'interval_length', 'from_time') }} as {{ api.Column.translate_type('timestamp') }})
|
||||||
|
when datepart = 'year' then cast({{ dateadd('year', 'interval_length', 'from_time') }} as {{ api.Column.translate_type('timestamp') }})
|
||||||
|
else null
|
||||||
|
end as actual,
|
||||||
|
result as expected
|
||||||
|
|
||||||
|
from data
|
||||||
|
"""
|
||||||
|
|
||||||
|
models__test_dateadd_yml = """
|
||||||
|
version: 2
|
||||||
|
models:
|
||||||
|
- name: test_dateadd
|
||||||
|
tests:
|
||||||
|
- assert_equal:
|
||||||
|
actual: actual
|
||||||
|
expected: expected
|
||||||
|
"""
|
||||||
66
tests/adapter/dbt/tests/adapter/utils/fixture_datediff.py
Normal file
66
tests/adapter/dbt/tests/adapter/utils/fixture_datediff.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
# datediff
|
||||||
|
|
||||||
|
seeds__data_datediff_csv = """first_date,second_date,datepart,result
|
||||||
|
2018-01-01 01:00:00,2018-01-02 01:00:00,day,1
|
||||||
|
2018-01-01 01:00:00,2018-02-01 01:00:00,month,1
|
||||||
|
2018-01-01 01:00:00,2019-01-01 01:00:00,year,1
|
||||||
|
2018-01-01 01:00:00,2018-01-01 02:00:00,hour,1
|
||||||
|
2018-01-01 01:00:00,2018-01-01 02:01:00,minute,61
|
||||||
|
2018-01-01 01:00:00,2018-01-01 02:00:01,second,3601
|
||||||
|
2019-12-31 00:00:00,2019-12-27 00:00:00,week,-1
|
||||||
|
2019-12-31 00:00:00,2019-12-30 00:00:00,week,0
|
||||||
|
2019-12-31 00:00:00,2020-01-02 00:00:00,week,0
|
||||||
|
2019-12-31 00:00:00,2020-01-06 02:00:00,week,1
|
||||||
|
,2018-01-01 02:00:00,hour,
|
||||||
|
2018-01-01 02:00:00,,hour,
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
models__test_datediff_sql = """
|
||||||
|
with data as (
|
||||||
|
|
||||||
|
select * from {{ ref('data_datediff') }}
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
select
|
||||||
|
|
||||||
|
case
|
||||||
|
when datepart = 'second' then {{ datediff('first_date', 'second_date', 'second') }}
|
||||||
|
when datepart = 'minute' then {{ datediff('first_date', 'second_date', 'minute') }}
|
||||||
|
when datepart = 'hour' then {{ datediff('first_date', 'second_date', 'hour') }}
|
||||||
|
when datepart = 'day' then {{ datediff('first_date', 'second_date', 'day') }}
|
||||||
|
when datepart = 'week' then {{ datediff('first_date', 'second_date', 'week') }}
|
||||||
|
when datepart = 'month' then {{ datediff('first_date', 'second_date', 'month') }}
|
||||||
|
when datepart = 'year' then {{ datediff('first_date', 'second_date', 'year') }}
|
||||||
|
else null
|
||||||
|
end as actual,
|
||||||
|
result as expected
|
||||||
|
|
||||||
|
from data
|
||||||
|
|
||||||
|
-- Also test correct casting of literal values.
|
||||||
|
|
||||||
|
union all select {{ datediff("'1999-12-31 23:59:59.999999'", "'2000-01-01 00:00:00.000000'", "microsecond") }} as actual, 1 as expected
|
||||||
|
union all select {{ datediff("'1999-12-31 23:59:59.999999'", "'2000-01-01 00:00:00.000000'", "millisecond") }} as actual, 1 as expected
|
||||||
|
union all select {{ datediff("'1999-12-31 23:59:59.999999'", "'2000-01-01 00:00:00.000000'", "second") }} as actual, 1 as expected
|
||||||
|
union all select {{ datediff("'1999-12-31 23:59:59.999999'", "'2000-01-01 00:00:00.000000'", "minute") }} as actual, 1 as expected
|
||||||
|
union all select {{ datediff("'1999-12-31 23:59:59.999999'", "'2000-01-01 00:00:00.000000'", "hour") }} as actual, 1 as expected
|
||||||
|
union all select {{ datediff("'1999-12-31 23:59:59.999999'", "'2000-01-01 00:00:00.000000'", "day") }} as actual, 1 as expected
|
||||||
|
union all select {{ datediff("'1999-12-31 23:59:59.999999'", "'2000-01-03 00:00:00.000000'", "week") }} as actual, 1 as expected
|
||||||
|
union all select {{ datediff("'1999-12-31 23:59:59.999999'", "'2000-01-01 00:00:00.000000'", "month") }} as actual, 1 as expected
|
||||||
|
union all select {{ datediff("'1999-12-31 23:59:59.999999'", "'2000-01-01 00:00:00.000000'", "quarter") }} as actual, 1 as expected
|
||||||
|
union all select {{ datediff("'1999-12-31 23:59:59.999999'", "'2000-01-01 00:00:00.000000'", "year") }} as actual, 1 as expected
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
models__test_datediff_yml = """
|
||||||
|
version: 2
|
||||||
|
models:
|
||||||
|
- name: test_datediff
|
||||||
|
tests:
|
||||||
|
- assert_equal:
|
||||||
|
actual: actual
|
||||||
|
expected: expected
|
||||||
|
"""
|
||||||
45
tests/adapter/dbt/tests/adapter/utils/test_dateadd.py
Normal file
45
tests/adapter/dbt/tests/adapter/utils/test_dateadd.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import pytest
|
||||||
|
from dbt.tests.adapter.utils.base_utils import BaseUtils
|
||||||
|
from dbt.tests.adapter.utils.fixture_dateadd import (
|
||||||
|
seeds__data_dateadd_csv,
|
||||||
|
models__test_dateadd_sql,
|
||||||
|
models__test_dateadd_yml,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseDateAdd(BaseUtils):
|
||||||
|
@pytest.fixture(scope="class")
|
||||||
|
def project_config_update(self):
|
||||||
|
return {
|
||||||
|
"name": "test",
|
||||||
|
# this is only needed for BigQuery, right?
|
||||||
|
# no harm having it here until/unless there's an adapter that doesn't support the 'timestamp' type
|
||||||
|
"seeds": {
|
||||||
|
"test": {
|
||||||
|
"data_dateadd": {
|
||||||
|
"+column_types": {
|
||||||
|
"from_time": "timestamp",
|
||||||
|
"result": "timestamp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
@pytest.fixture(scope="class")
|
||||||
|
def seeds(self):
|
||||||
|
return {"data_dateadd.csv": seeds__data_dateadd_csv}
|
||||||
|
|
||||||
|
@pytest.fixture(scope="class")
|
||||||
|
def models(self):
|
||||||
|
macro_namespace = self.macro_namespace()
|
||||||
|
return {
|
||||||
|
"test_dateadd.yml": models__test_dateadd_yml,
|
||||||
|
"test_dateadd.sql": self.interpolate_macro_namespace(
|
||||||
|
models__test_dateadd_sql, "dateadd"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestDateAdd(BaseDateAdd):
|
||||||
|
pass
|
||||||
24
tests/adapter/dbt/tests/adapter/utils/test_datediff.py
Normal file
24
tests/adapter/dbt/tests/adapter/utils/test_datediff.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import pytest
|
||||||
|
from dbt.tests.adapter.utils.base_utils import BaseUtils
|
||||||
|
from dbt.tests.adapter.utils.fixture_datediff import (
|
||||||
|
seeds__data_datediff_csv,
|
||||||
|
models__test_datediff_sql,
|
||||||
|
models__test_datediff_yml,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseDateDiff(BaseUtils):
|
||||||
|
@pytest.fixture(scope="class")
|
||||||
|
def seeds(self):
|
||||||
|
return {"data_datediff.csv": seeds__data_datediff_csv}
|
||||||
|
|
||||||
|
@pytest.fixture(scope="class")
|
||||||
|
def models(self):
|
||||||
|
return {
|
||||||
|
"test_datediff.yml": models__test_datediff_yml,
|
||||||
|
"test_datediff.sql": self.interpolate_macro_namespace(models__test_datediff_sql, "datediff"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestDateDiff(BaseDateDiff):
|
||||||
|
pass
|
||||||
Reference in New Issue
Block a user