diff --git a/dbt/include/redshift/macros/utils/cast_bool_to_text.sql b/dbt/include/redshift/macros/utils/cast_bool_to_text.sql new file mode 100644 index 000000000..f88dee17b --- /dev/null +++ b/dbt/include/redshift/macros/utils/cast_bool_to_text.sql @@ -0,0 +1,6 @@ +{% macro redshift__cast_bool_to_text(field) %} + case + when {{ field }} is true then 'true' + when {{ field }} is false then 'false' + end::text +{% endmacro %} diff --git a/dbt/include/redshift/macros/utils/dateadd.sql b/dbt/include/redshift/macros/utils/dateadd.sql new file mode 100644 index 000000000..dc90f9231 --- /dev/null +++ b/dbt/include/redshift/macros/utils/dateadd.sql @@ -0,0 +1,10 @@ +{#-- redshift should use default instead of postgres --#} +{% macro redshift__dateadd(datepart, interval, from_date_or_timestamp) %} + + dateadd( + {{ datepart }}, + {{ interval }}, + {{ from_date_or_timestamp }} + ) + +{% endmacro %} diff --git a/dbt/include/redshift/macros/utils/datediff.sql b/dbt/include/redshift/macros/utils/datediff.sql new file mode 100644 index 000000000..c20513961 --- /dev/null +++ b/dbt/include/redshift/macros/utils/datediff.sql @@ -0,0 +1,10 @@ +{#-- redshift should use default instead of postgres --#} +{% macro redshift__datediff(first_date, second_date, datepart) -%} + + datediff( + {{ datepart }}, + {{ first_date }}, + {{ second_date }} + ) + +{%- endmacro %} diff --git a/dbt/include/redshift/macros/utils/last_day.sql b/dbt/include/redshift/macros/utils/last_day.sql new file mode 100644 index 000000000..be0e2253a --- /dev/null +++ b/dbt/include/redshift/macros/utils/last_day.sql @@ -0,0 +1,8 @@ +{# redshift should use default instead of postgres #} +{% macro redshift__last_day(date, datepart) %} + cast( + {{dbt.dateadd('day', '-1', + dbt.dateadd(datepart, '1', dbt.date_trunc(datepart, date)) + )}} + as date) +{% endmacro %} diff --git a/dbt/include/redshift/macros/utils/length.sql b/dbt/include/redshift/macros/utils/length.sql new file mode 100644 index 000000000..040874353 --- /dev/null +++ b/dbt/include/redshift/macros/utils/length.sql @@ -0,0 +1,7 @@ +{% macro redshift__length(expression) %} + + len( + {{ expression }} + ) + +{%- endmacro -%} diff --git a/dbt/include/redshift/macros/utils/listagg.sql b/dbt/include/redshift/macros/utils/listagg.sql new file mode 100644 index 000000000..3dc6092b4 --- /dev/null +++ b/dbt/include/redshift/macros/utils/listagg.sql @@ -0,0 +1,34 @@ +{# if there are instances of delimiter_text within your measure, you cannot include a limit_num #} +{% macro redshift__listagg(measure, delimiter_text, order_by_clause, limit_num) -%} + + {% if limit_num -%} + {% set ns = namespace() %} + {% set ns.delimiter_text_regex = delimiter_text|trim("'") %} + {% set special_chars %}\,^,$,.,|,?,*,+,(,),[,],{,}{% endset %} + {%- for char in special_chars.split(',') -%} + {% set escape_char %}\\{{ char }}{% endset %} + {% set ns.delimiter_text_regex = ns.delimiter_text_regex|replace(char,escape_char) %} + {%- endfor -%} + + {% set regex %}'([^{{ ns.delimiter_text_regex }}]+{{ ns.delimiter_text_regex }}){1,{{ limit_num - 1}}}[^{{ ns.delimiter_text_regex }}]+'{% endset %} + regexp_substr( + listagg( + {{ measure }}, + {{ delimiter_text }} + ) + {% if order_by_clause -%} + within group ({{ order_by_clause }}) + {%- endif %} + ,{{ regex }} + ) + {%- else %} + listagg( + {{ measure }}, + {{ delimiter_text }} + ) + {% if order_by_clause -%} + within group ({{ order_by_clause }}) + {%- endif %} + {%- endif %} + +{%- endmacro %} diff --git a/dbt/include/redshift/macros/utils/split_part.sql b/dbt/include/redshift/macros/utils/split_part.sql new file mode 100644 index 000000000..e594d6fa3 --- /dev/null +++ b/dbt/include/redshift/macros/utils/split_part.sql @@ -0,0 +1,9 @@ +{% macro redshift__split_part(string_text, delimiter_text, part_number) %} + + {% if part_number >= 0 %} + {{ dbt.default__split_part(string_text, delimiter_text, part_number) }} + {% else %} + {{ dbt._split_part_negative(string_text, delimiter_text, part_number) }} + {% endif %} + +{% endmacro %} diff --git a/tests/functional/adapter/test_utils.py b/tests/functional/adapter/test_utils.py new file mode 100644 index 000000000..e7a5491b2 --- /dev/null +++ b/tests/functional/adapter/test_utils.py @@ -0,0 +1,103 @@ +import pytest +from dbt.tests.adapter.utils.base_utils import BaseUtils +from dbt.tests.adapter.utils.test_any_value import BaseAnyValue +from dbt.tests.adapter.utils.test_bool_or import BaseBoolOr +from dbt.tests.adapter.utils.test_cast_bool_to_text import BaseCastBoolToText +from dbt.tests.adapter.utils.test_concat import BaseConcat +from dbt.tests.adapter.utils.test_dateadd import BaseDateAdd +from dbt.tests.adapter.utils.test_datediff import BaseDateDiff +from dbt.tests.adapter.utils.test_date_trunc import BaseDateTrunc +from dbt.tests.adapter.utils.test_escape_single_quotes import BaseEscapeSingleQuotesQuote +from dbt.tests.adapter.utils.test_escape_single_quotes import BaseEscapeSingleQuotesBackslash +from dbt.tests.adapter.utils.test_except import BaseExcept +from dbt.tests.adapter.utils.test_hash import BaseHash +from dbt.tests.adapter.utils.test_intersect import BaseIntersect +from dbt.tests.adapter.utils.test_last_day import BaseLastDay +from dbt.tests.adapter.utils.test_length import BaseLength +from dbt.tests.adapter.utils.test_listagg import BaseListagg +from dbt.tests.adapter.utils.test_position import BasePosition +from dbt.tests.adapter.utils.test_replace import BaseReplace +from dbt.tests.adapter.utils.test_right import BaseRight +from dbt.tests.adapter.utils.test_safe_cast import BaseSafeCast +from dbt.tests.adapter.utils.test_split_part import BaseSplitPart +from dbt.tests.adapter.utils.test_string_literal import BaseStringLiteral + + +class TestAnyValue(BaseAnyValue): + pass + + +class TestBoolOr(BaseBoolOr): + pass + + +class TestCastBoolToText(BaseCastBoolToText): + pass + + +class TestConcat(BaseConcat): + pass + + +class TestDateAdd(BaseDateAdd): + pass + + +class TestDateDiff(BaseDateDiff): + pass + + +class TestDateTrunc(BaseDateTrunc): + pass + + +class TestEscapeSingleQuotes(BaseEscapeSingleQuotesQuote): + pass + + +class TestExcept(BaseExcept): + pass + + +class TestHash(BaseHash): + pass + + +class TestIntersect(BaseIntersect): + pass + + +class TestLastDay(BaseLastDay): + pass + + +class TestLength(BaseLength): + pass + + +class TestListagg(BaseListagg): + pass + + +class TestPosition(BasePosition): + pass + + +class TestReplace(BaseReplace): + pass + + +class TestRight(BaseRight): + pass + + +class TestSafeCast(BaseSafeCast): + pass + + +class TestSplitPart(BaseSplitPart): + pass + + +class TestStringLiteral(BaseStringLiteral): + pass