Repository: firebolt-db/dbt-firebolt (language: python)

Issue:
upgrade to support dbt-core v1.6.0
## Background <!-- markdownlint-disable-line MD041 -->

Minor version `v1.6` is targeted for final release on July 27, 2023. As a maintainer of a dbt adapter, we strongly encourage you to release a corresponding minor version increment to ensure users of your adapter can make use of this new minor version.

## How to upgrade

https://github.com/dbt-labs/dbt-core/discussions/7958 is an open discussion with more detailed information. If you have questions, please put them there!

The above linked guide has more information, but below is a high-level checklist of work that would enable a successful 1.6.0 release of your adapter:

```[tasklist]
### Tasks
- [ ] SUPPORT: materialized views
- [ ] SUPPORT: new `clone` command
- [ ] BEHIND THE SCENES: Drop support for Python 3.7 (if you haven't already)
- [ ] BEHIND THE SCENES: new arg for `adapter.execute()`
- [ ] BEHIND THE SCENES: ensure support for revamped `dbt debug``
- [ ] BEHIND THE SCENES: Add support for new/modified relevant tests
```

## the next minor release: `1.7.0`

FYI, `dbt-core==1.7.0` is expected to be released on October 12, 2023 in time for [Coalesce](https://coalesce.getdbt.com/), the annual analytics engineering conference!


Discussion / hints:

Fix patch:
diff --git a/.changes/unreleased/Added-20240201-162000.yaml b/.changes/unreleased/Added-20240201-162000.yaml
new file mode 100644
index 000000000..8af56b6a6
--- /dev/null
+++ b/.changes/unreleased/Added-20240201-162000.yaml
@@ -0,0 +1,3 @@
+kind: Added
+body: Stubs for materialised view
+time: 2024-02-01T16:20:00.854246Z
diff --git a/.changes/unreleased/Added-20240201-162011.yaml b/.changes/unreleased/Added-20240201-162011.yaml
new file mode 100644
index 000000000..62fcc25c5
--- /dev/null
+++ b/.changes/unreleased/Added-20240201-162011.yaml
@@ -0,0 +1,3 @@
+kind: Added
+body: Stubs for dbt clone
+time: 2024-02-01T16:20:11.889706Z
diff --git a/.changes/unreleased/Added-20240201-162027.yaml b/.changes/unreleased/Added-20240201-162027.yaml
new file mode 100644
index 000000000..1e8c41050
--- /dev/null
+++ b/.changes/unreleased/Added-20240201-162027.yaml
@@ -0,0 +1,3 @@
+kind: Added
+body: Additional tests
+time: 2024-02-01T16:20:27.876911Z
diff --git a/dbt/include/firebolt/macros/materializations/clone.sql b/dbt/include/firebolt/macros/materializations/clone.sql
new file mode 100644
index 000000000..d639b745e
--- /dev/null
+++ b/dbt/include/firebolt/macros/materializations/clone.sql
@@ -0,0 +1,3 @@
+{% macro firebolt__can_clone_table() %}
+    {{ return(False) }}
+{% endmacro %}
diff --git a/dbt/include/firebolt/macros/materializations/materialized_view.sql b/dbt/include/firebolt/macros/materializations/materialized_view.sql
new file mode 100644
index 000000000..d37b3f713
--- /dev/null
+++ b/dbt/include/firebolt/macros/materializations/materialized_view.sql
@@ -0,0 +1,48 @@
+{#
+    All these macros are stubbed with compiler errors because Firebolt does not
+    support materialized views.
+#}
+{% materialization materialized_view, adapter='firebolt' %}
+
+    {{ exceptions.raise_compiler_error("Firebolt does not support materialized views") }}
+
+{% endmaterialization %}
+
+{% macro firebolt__get_alter_materialized_view_as_sql(
+    relation,
+    configuration_changes,
+    sql,
+    existing_relation,
+    backup_relation,
+    intermediate_relation
+) %}
+
+    {{ exceptions.raise_compiler_error("Firebolt does not support materialized views") }}
+{% endmacro %}
+
+
+{% macro firebolt__get_create_materialized_view_as_sql(relation, sql) %}
+
+    {{ exceptions.raise_compiler_error("Firebolt does not support materialized views") }}
+
+{% endmacro %}
+
+
+{% macro firebolt__get_replace_materialized_view_as_sql(relation, sql, existing_relation, backup_relation, intermediate_relation) %}
+    {{ exceptions.raise_compiler_error("Firebolt does not support materialized views") }}
+{% endmacro %}
+
+
+{% macro firebolt__get_materialized_view_configuration_changes(existing_relation, new_config) %}
+    {{ exceptions.raise_compiler_error("Firebolt does not support materialized views") }}
+{% endmacro %}
+
+
+{% macro firebolt__refresh_materialized_view(relation) -%}
+    {{ exceptions.raise_compiler_error("Firebolt does not support materialized views") }}
+{% endmacro %}
+
+
+{% macro firebolt__drop_materialized_view(relation) -%}
+    {{ exceptions.raise_compiler_error("Firebolt does not support materialized views") }}
+{%- endmacro %}
diff --git a/dbt/include/firebolt/macros/utils/bool_or.sql b/dbt/include/firebolt/macros/utils/bool_or.sql
index c6194b2ab..18af5e7a7 100644
--- a/dbt/include/firebolt/macros/utils/bool_or.sql
+++ b/dbt/include/firebolt/macros/utils/bool_or.sql
@@ -1,5 +1,5 @@
 {% macro firebolt__bool_or(expression) -%}
 
-    ANY({{ expression }})
+    MAX({{ expression }})
 
 {%- endmacro %}
diff --git a/setup.cfg b/setup.cfg
index d8c74280c..87f9f7f13 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -22,7 +22,7 @@ project_urls =
 [options]
 packages = find_namespace:
 install_requires =
-    dbt-core~=1.5
+    dbt-core~=1.6
     firebolt-sdk>=1.1.0
     pydantic>=0.23
 python_requires = >=3.8
@@ -37,7 +37,7 @@ include = dbt, dbt.*
 [options.extras_require]
 dev =
     allure-pytest==2.*
-    dbt-tests-adapter~=1.5
+    dbt-tests-adapter~=1.6
     mypy==1.4.1
     pre-commit==2.15.0
     pytest==7.*


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index cd7b63bb1..f5cb8419f 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -88,3 +88,9 @@ def profiles_yml(profiles_root, dbt_profile_data):
 @pytest.fixture(scope='class')
 def profile_user(dbt_profile_target):
     return dbt_profile_target.get('user', dbt_profile_target.get('client_id'))
+
+
+@pytest.fixture(scope='class')
+def unique_schema(request, prefix) -> str:
+    """Firebolt does not have concept of schemas so we return 'public' here."""
+    return 'public'
diff --git a/tests/functional/adapter/test_clone.py b/tests/functional/adapter/test_clone.py
new file mode 100644
index 000000000..959d9ac59
--- /dev/null
+++ b/tests/functional/adapter/test_clone.py
@@ -0,0 +1,8 @@
+from dbt.tests.adapter.dbt_clone.test_dbt_clone import BaseCloneNotPossible
+from pytest import mark
+
+
+# Test clone works by copying tables to a new schema
+@mark.skip("Can't test this before schemas are implemented")
+class TestFireboltCloneNotPossible(BaseCloneNotPossible):
+    pass
diff --git a/tests/functional/adapter/test_constraints.py b/tests/functional/adapter/test_constraints.py
index ea6c4a21d..b0ce281c0 100644
--- a/tests/functional/adapter/test_constraints.py
+++ b/tests/functional/adapter/test_constraints.py
@@ -79,6 +79,7 @@ class TestIncrementalConstraintsColumnsEqualFirebolt(
     pass
 
 
+@mark.skip(reason='Firebolt does not support foreign key')
 class TestConstraintsRuntimeDdlEnforcementFirebolt(
     BaseConstraintsRuntimeDdlEnforcement
 ):
@@ -92,6 +93,7 @@ class TestConstraintsRollbackFirebolt(BaseConstraintsRollback):
     pass
 
 
+@mark.skip(reason='Firebolt does not support foreign key')
 class TestIncrementalConstraintsRuntimeDdlEnforcementFirebolt(
     BaseIncrementalConstraintsRuntimeDdlEnforcement
 ):
diff --git a/tests/functional/adapter/test_dbt_debug.py b/tests/functional/adapter/test_dbt_debug.py
new file mode 100644
index 000000000..c9e045dc4
--- /dev/null
+++ b/tests/functional/adapter/test_dbt_debug.py
@@ -0,0 +1,42 @@
+import re
+
+from dbt.tests.adapter.dbt_debug.test_dbt_debug import BaseDebug
+from dbt.tests.util import run_dbt, run_dbt_and_capture
+
+
+class TestDebugFirebolt(BaseDebug):
+    def test_ok(self, project):
+        run_dbt(['debug'])
+        assert 'ERROR' not in self.capsys.readouterr().out
+
+    def test_connection_flag(self, project):
+        """
+        Testing that the --connection flag works as expected,
+        including that output is not lost
+        """
+        _, out = run_dbt_and_capture(['debug', '--connection'])
+        assert 'Skipping steps before connection verification' in out
+
+        _, out = run_dbt_and_capture(
+            ['debug', '--connection', '--target', 'NONE'], expect_pass=False
+        )
+        assert '1 check failed' in out
+        assert "The profile 'test' does not have a target named 'NONE'." in out
+
+        _, out = run_dbt_and_capture(
+            ['debug', '--connection', '--profiles-dir', 'NONE'], expect_pass=False
+        )
+        assert '1 check failed' in out
+        assert 'dbt looked for a profiles.yml file in NONE' in out
+
+    def test_nopass(self, project):
+        run_dbt(['debug', '--target', 'nopass'], expect_pass=False)
+        self.assertGotValue(re.compile(r'\s+profiles\.yml file'), 'ERROR invalid')
+
+    def test_wronguser(self, project):
+        run_dbt(['debug', '--target', 'wronguser'], expect_pass=False)
+        self.assertGotValue(re.compile(r'\s+Connection test'), 'ERROR')
+
+    def test_empty_target(self, project):
+        run_dbt(['debug', '--target', 'none_target'], expect_pass=False)
+        self.assertGotValue(re.compile(r"\s+output 'none_target'"), 'misconfigured')
diff --git a/tests/functional/adapter/test_materialized_views.py b/tests/functional/adapter/test_materialized_views.py
new file mode 100644
index 000000000..c1834f137
--- /dev/null
+++ b/tests/functional/adapter/test_materialized_views.py
@@ -0,0 +1,49 @@
+from dbt.tests.adapter.basic.files import (
+    base_table_sql,
+    model_base,
+    schema_base_yml,
+    seeds_base_csv,
+)
+from dbt.tests.util import check_result_nodes_by_name, run_dbt
+from pytest import fixture
+
+config_materialized_view = """
+  {{ config(materialized='materialized_view') }}
+"""
+materialized_view_sql = config_materialized_view + model_base
+
+
+class TestMaterializedViews:
+    @fixture(scope='class')
+    def models(self):
+        return {
+            'view_model.sql': materialized_view_sql,
+            'table_model.sql': base_table_sql,
+            'schema.yml': schema_base_yml,
+        }
+
+    @fixture(scope='class')
+    def seeds(self):
+        return {
+            'base.csv': seeds_base_csv,
+        }
+
+    @fixture(scope='class')
+    def project_config_update(self):
+        return {'name': 'materialized_views'}
+
+    def test_singular_tests(self, project):
+        run_dbt(['seed'], expect_pass=True)
+        # test command
+        results = run_dbt(['run'], expect_pass=False)
+        assert len(results) == 2
+
+        # We have the right result nodes
+        check_result_nodes_by_name(results, ['table_model', 'view_model'])
+
+        # Check result status
+        for result in results:
+            if result.node.name == 'table_model':
+                assert result.status == 'success'
+            if result.node.name == 'view_model':
+                assert result.status == 'error'
diff --git a/tests/functional/adapter/utils/test_utils.py b/tests/functional/adapter/utils/test_utils.py
index 317f7b45f..8cc130f10 100644
--- a/tests/functional/adapter/utils/test_utils.py
+++ b/tests/functional/adapter/utils/test_utils.py
@@ -16,6 +16,10 @@
     models__test_dateadd_yml,
 )
 from dbt.tests.adapter.utils.fixture_datediff import models__test_datediff_yml
+from dbt.tests.adapter.utils.fixture_equals import (
+    MODELS__EQUAL_VALUES_SQL,
+    MODELS__NOT_EQUAL_VALUES_SQL,
+)
 from dbt.tests.adapter.utils.fixture_hash import (
     models__test_hash_sql,
     models__test_hash_yml,
@@ -65,6 +69,7 @@
 from dbt.tests.adapter.utils.test_date_trunc import BaseDateTrunc
 from dbt.tests.adapter.utils.test_dateadd import BaseDateAdd
 from dbt.tests.adapter.utils.test_datediff import BaseDateDiff
+from dbt.tests.adapter.utils.test_equals import BaseEquals
 from dbt.tests.adapter.utils.test_escape_single_quotes import (
     BaseEscapeSingleQuotesBackslash,
 )
@@ -74,12 +79,17 @@
 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_null_compare import (
+    BaseMixedNullCompare,
+    BaseNullCompare,
+)
 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
+from dbt.tests.adapter.utils.test_validate_sql import BaseValidateSqlMethod
 from pytest import mark
 
 schema_actual_table_yml = """
@@ -481,3 +491,38 @@ def models(self):
             'expected.yml': schema_expected_table_yml,
             'expected.sql': models__array_construct_expected_sql,
         }
+
+
+schema_seed_equals_yml = """
+version: 2
+seeds:
+  - name: data_equals
+    config:
+      column_types:
+        key_name: INT
+        x: INT NULL
+        y: INT NULL
+        expected: TEXT
+"""
+
+
+class TestFireboltEquals(BaseEquals):
+    @pytest.fixture(scope='class')
+    def models(self):
+        return {
+            'equal_values.sql': MODELS__EQUAL_VALUES_SQL,
+            'not_equal_values.sql': MODELS__NOT_EQUAL_VALUES_SQL,
+            'seeds.yml': schema_seed_equals_yml,
+        }
+
+
+class TestFireboltNullCompare(BaseNullCompare):
+    pass
+
+
+class TestFireboltMixedNullCompare(BaseMixedNullCompare):
+    pass
+
+
+class TestFireboltValidateSqlMethod(BaseValidateSqlMethod):
+    pass