diff --git a/.changes/unreleased/Features-20220913-084836.yaml b/.changes/unreleased/Features-20220913-084836.yaml new file mode 100644 index 000000000..0a4bd275d --- /dev/null +++ b/.changes/unreleased/Features-20220913-084836.yaml @@ -0,0 +1,7 @@ +kind: Features +body: Array macros +time: 2022-09-13T08:48:36.255365-06:00 +custom: + Author: graciegoheen dbeatty10 + Issue: "307" + PR: "308" diff --git a/dbt/include/bigquery/macros/utils/array_append.sql b/dbt/include/bigquery/macros/utils/array_append.sql new file mode 100644 index 000000000..78bd5cc43 --- /dev/null +++ b/dbt/include/bigquery/macros/utils/array_append.sql @@ -0,0 +1,3 @@ +{% macro bigquery__array_append(array, new_element) -%} + {{ array_concat(array, array_construct([new_element])) }} +{%- endmacro %} diff --git a/dbt/include/bigquery/macros/utils/array_concat.sql b/dbt/include/bigquery/macros/utils/array_concat.sql new file mode 100644 index 000000000..eff8f524a --- /dev/null +++ b/dbt/include/bigquery/macros/utils/array_concat.sql @@ -0,0 +1,3 @@ +{% macro bigquery__array_concat(array_1, array_2) -%} + array_concat({{ array_1 }}, {{ array_2 }}) +{%- endmacro %} diff --git a/dbt/include/bigquery/macros/utils/array_construct.sql b/dbt/include/bigquery/macros/utils/array_construct.sql new file mode 100644 index 000000000..270b1f785 --- /dev/null +++ b/dbt/include/bigquery/macros/utils/array_construct.sql @@ -0,0 +1,7 @@ +{% macro bigquery__array_construct(inputs, data_type) -%} + {% if inputs|length > 0 %} + [ {{ inputs|join(' , ') }} ] + {% else %} + ARRAY<{{data_type}}>[] + {% endif %} +{%- endmacro %} diff --git a/tests/functional/adapter/utils/fixture_array_append.py b/tests/functional/adapter/utils/fixture_array_append.py new file mode 100644 index 000000000..0558d66e1 --- /dev/null +++ b/tests/functional/adapter/utils/fixture_array_append.py @@ -0,0 +1,13 @@ +# array_append + +# EXCEPT can't be used with ARRAYs in BigQuery, so convert to a string +models__array_append_expected_sql = """ +select 1 as id, {{ array_to_string(array_construct([1,2,3,4])) }} as array_col union all +select 2 as id, {{ array_to_string(array_construct([4])) }} as array_col +""" + + +models__array_append_actual_sql = """ +select 1 as id, {{ array_to_string(array_append(array_construct([1,2,3]), 4)) }} as array_col union all +select 2 as id, {{ array_to_string(array_append(array_construct([]), 4)) }} as array_col +""" diff --git a/tests/functional/adapter/utils/fixture_array_concat.py b/tests/functional/adapter/utils/fixture_array_concat.py new file mode 100644 index 000000000..51af8bf12 --- /dev/null +++ b/tests/functional/adapter/utils/fixture_array_concat.py @@ -0,0 +1,15 @@ +# array_concat + +# EXCEPT can't be used with ARRAYs in BigQuery, so convert to a string +models__array_concat_expected_sql = """ +select 1 as id, {{ array_to_string(array_construct([1,2,3,4,5,6])) }} as array_col union all +select 2 as id, {{ array_to_string(array_construct([2])) }} as array_col union all +select 3 as id, {{ array_to_string(array_construct([3])) }} as array_col +""" + + +models__array_concat_actual_sql = """ +select 1 as id, {{ array_to_string(array_concat(array_construct([1,2,3]), array_construct([4,5,6]))) }} as array_col union all +select 2 as id, {{ array_to_string(array_concat(array_construct([]), array_construct([2]))) }} as array_col union all +select 3 as id, {{ array_to_string(array_concat(array_construct([3]), array_construct([]))) }} as array_col +""" diff --git a/tests/functional/adapter/utils/fixture_array_construct.py b/tests/functional/adapter/utils/fixture_array_construct.py new file mode 100644 index 000000000..13d0bb2f3 --- /dev/null +++ b/tests/functional/adapter/utils/fixture_array_construct.py @@ -0,0 +1,20 @@ +# array_construct + +# EXCEPT can't be used with ARRAYs in BigQuery, so convert to a string +models__array_construct_expected_sql = """ +select 1 as id, {{ array_to_string(array_construct([1,2,3])) }} as array_col union all +select 2 as id, {{ array_to_string(array_construct([])) }} as array_col +""" + + +models__array_construct_actual_sql = """ +select 1 as id, {{ array_to_string(array_construct([1,2,3])) }} as array_col union all +select 2 as id, {{ array_to_string(array_construct([])) }} as array_col +""" + + +macros__array_to_string_sql = """ +{% macro array_to_string(array) %} + (select string_agg(cast(element as string), ',') from unnest({{ array }}) element) +{% endmacro %} +""" diff --git a/tests/functional/adapter/utils/test_utils.py b/tests/functional/adapter/utils/test_utils.py index ae1aadcbc..5f5c5d83b 100644 --- a/tests/functional/adapter/utils/test_utils.py +++ b/tests/functional/adapter/utils/test_utils.py @@ -1,5 +1,8 @@ import pytest -from dbt.tests.adapter.utils.base_utils import BaseUtils + +from dbt.tests.adapter.utils.test_array_append import BaseArrayAppend +from dbt.tests.adapter.utils.test_array_concat import BaseArrayConcat +from dbt.tests.adapter.utils.test_array_construct import BaseArrayConstruct 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 @@ -22,12 +25,70 @@ 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 +from tests.functional.adapter.utils.fixture_array_append import ( + models__array_append_actual_sql, + models__array_append_expected_sql, +) +from tests.functional.adapter.utils.fixture_array_concat import ( + models__array_concat_actual_sql, + models__array_concat_expected_sql, +) +from tests.functional.adapter.utils.fixture_array_construct import ( + models__array_construct_actual_sql, + models__array_construct_expected_sql, + macros__array_to_string_sql, +) class TestAnyValue(BaseAnyValue): pass +class TestArrayAppend(BaseArrayAppend): + @pytest.fixture(scope="class") + def models(self): + return { + "actual.sql": models__array_append_actual_sql, + "expected.sql": models__array_append_expected_sql, + } + + @pytest.fixture(scope="class") + def macros(self): + return { + "array_to_string.sql": macros__array_to_string_sql, + } + + +class TestArrayConcat(BaseArrayConcat): + @pytest.fixture(scope="class") + def models(self): + return { + "actual.sql": models__array_concat_actual_sql, + "expected.sql": models__array_concat_expected_sql, + } + + @pytest.fixture(scope="class") + def macros(self): + return { + "array_to_string.sql": macros__array_to_string_sql, + } + + +class TestArrayConstruct(BaseArrayConstruct): + @pytest.fixture(scope="class") + def models(self): + return { + "actual.sql": models__array_construct_actual_sql, + "expected.sql": models__array_construct_expected_sql, + } + + @pytest.fixture(scope="class") + def macros(self): + return { + "array_to_string.sql": macros__array_to_string_sql, + } + + class TestBoolOr(BaseBoolOr): pass