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

Issue:
upgrade to support dbt-core v1.5.0
## Background

The latest version of dbt Core,`dbt-core==1.5.0rc1`, was published on April 13, 2023 ([PyPI](https://pypi.org/project/dbt-core/1.5.0rc1/) | [Github](https://github.com/dbt-labs/dbt-core/releases/tag/v1.5.0rc1)).

## How to upgrade

https://github.com/dbt-labs/dbt-core/discussions/7213 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.5.0 release of your adapter. 

- [x] Add support Python 3.11 (if you haven't already)
- [x] Add support for relevant tests (there's a lot of new ones!)
- [x] Add support model contracts
- [ ] ~~Add support for materialized views~~ (this likely will be bumped to 1.6.0)

## the next minor release: `1.6.0`

FYI, `dbt-core==1.6.0` is expected to be released at the end of July, with a release cut at least two weeks prior.


Discussion / hints:

Fix patch:
diff --git a/.changes/unreleased/Added-20240117-110352.yaml b/.changes/unreleased/Added-20240117-110352.yaml
new file mode 100644
index 000000000..c61951e36
--- /dev/null
+++ b/.changes/unreleased/Added-20240117-110352.yaml
@@ -0,0 +1,3 @@
+kind: Added
+body: constraints support and enforcement
+time: 2024-01-17T11:03:52.13375Z
diff --git a/README.md b/README.md
index d1711c209..ab5899e01 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,19 @@ The table below shows which dbt and Firebolt features are supported by the adapt
 | Join indexes                 | :x: (syntax supported, but not effective) |
 
 
+## Constraints support
+
+More on constraints in [Platform constraint support](https://docs.getdbt.com/docs/collaborate/govern/model-contracts#platform-constraint-support)
+
+
+| Constraint type | Support | Platform enforcement |
+|-----------------|---------|----------------------|
+| not_null        | :white_check_mark: Supported | :white_check_mark: Enforced |
+| primary_key     | :x: Not Supported | :x: Not enforced |
+| foreign_key     | :x: Not Supported | :x: Not enforced |
+| unique          | :white_check_mark: Supported | :x: Not enforced |
+| check           | :x: Not supported | :x: Not enforced |
+
 
 ## Using dbt-firebolt
 
diff --git a/dbt/adapters/firebolt/connections.py b/dbt/adapters/firebolt/connections.py
index 9b5aaeb04..80045c5bb 100644
--- a/dbt/adapters/firebolt/connections.py
+++ b/dbt/adapters/firebolt/connections.py
@@ -1,6 +1,7 @@
 from contextlib import contextmanager
 from dataclasses import dataclass
-from typing import Generator, Optional, Tuple
+from datetime import datetime
+from typing import Generator, Optional, Tuple, Union
 
 import dbt.exceptions
 from dbt.adapters.base import Credentials
@@ -15,6 +16,7 @@
 from dbt.exceptions import DbtRuntimeError
 from firebolt.client import DEFAULT_API_URL
 from firebolt.client.auth import Auth, ClientCredentials, UsernamePassword
+from firebolt.db import ARRAY, DECIMAL
 from firebolt.db import connect as sdk_connect
 from firebolt.db.connection import Connection as SDKConnection
 from firebolt.db.cursor import Cursor
@@ -183,6 +185,35 @@ def cancel(self, connection: Connection) -> None:
             '`cancel` is not implemented for this adapter!'
         )
 
+    @classmethod
+    def data_type_code_to_name(  # type: ignore[override] # FIR-29423
+        cls, type_code: Union[type, ARRAY, DECIMAL]
+    ) -> str:
+        """
+        Convert a Firebolt data type code to a string representing the data type.
+        type_code: code type retrieved from the cursor description
+        """
+        if isinstance(type_code, ARRAY):
+            return f'array[{cls.data_type_code_to_name(type_code.subtype)}]'
+        elif isinstance(type_code, DECIMAL):
+            return str(type_code)
+        elif isinstance(type_code, type):
+            if issubclass(type_code, str):
+                return 'text'
+            elif issubclass(type_code, bool):
+                # This has to be before int, as bool is a subclass of int
+                return 'boolean'
+            elif issubclass(type_code, int):
+                return 'integer'
+            elif issubclass(type_code, float):
+                return 'float'
+            elif issubclass(type_code, datetime):
+                return 'timestamp'
+            elif issubclass(type_code, bytes):
+                return 'bytea'
+            else:
+                return 'text'
+
 
 def _determine_auth(credentials: FireboltCredentials) -> Auth:
     if credentials.client_id and credentials.client_secret:
diff --git a/dbt/adapters/firebolt/impl.py b/dbt/adapters/firebolt/impl.py
index 824d19648..5ff5bcb06 100644
--- a/dbt/adapters/firebolt/impl.py
+++ b/dbt/adapters/firebolt/impl.py
@@ -6,9 +6,10 @@
 
 import agate
 from dbt.adapters.base import available
-from dbt.adapters.base.impl import AdapterConfig
+from dbt.adapters.base.impl import AdapterConfig, ConstraintSupport
 from dbt.adapters.base.relation import BaseRelation
 from dbt.adapters.sql import SQLAdapter
+from dbt.contracts.graph.nodes import ConstraintType
 from dbt.dataclass_schema import ValidationError, dbtClassMixin
 from dbt.exceptions import (
     CompilationError,
@@ -105,6 +106,14 @@ class FireboltAdapter(SQLAdapter):
     ConnectionManager = FireboltConnectionManager
     Column = FireboltColumn
 
+    CONSTRAINT_SUPPORT = {
+        ConstraintType.check: ConstraintSupport.NOT_SUPPORTED,
+        ConstraintType.not_null: ConstraintSupport.ENFORCED,
+        ConstraintType.unique: ConstraintSupport.NOT_ENFORCED,
+        ConstraintType.primary_key: ConstraintSupport.NOT_SUPPORTED,
+        ConstraintType.foreign_key: ConstraintSupport.NOT_SUPPORTED,
+    }
+
     @classmethod
     def is_cancelable(cls) -> bool:
         return False
diff --git a/dbt/include/firebolt/macros/adapters.sql b/dbt/include/firebolt/macros/adapters.sql
index 246d94027..c201a7214 100644
--- a/dbt/include/firebolt/macros/adapters.sql
+++ b/dbt/include/firebolt/macros/adapters.sql
@@ -194,7 +194,7 @@
       temporary (bool): Unused, included so macro signature matches
         that of dbt's default macro
       relation (dbt relation/dict)
-      select_sql (string): The SQL query that will be used to generate 
+      select_sql (string): The SQL query that will be used to generate
         the internal query of the CTAS
       language (string): sql or python models. Firebolt only supports sql.
   #}
@@ -212,6 +212,15 @@
   {%- set partitions = config.get('partition_by') %}
 
   CREATE {{ table_type }} TABLE IF NOT EXISTS {{ relation }}
+  {%- set contract_config = config.get('contract') -%}
+  {%- if contract_config.enforced -%}
+    {{ get_assert_columns_equivalent(select_sql) }}
+    {{ get_table_columns_and_constraints() }} ;
+    insert into {{ relation }} (
+      {{ adapter.dispatch('get_column_names', 'dbt')() }}
+    )
+    {%- set select_sql = get_select_subquery(select_sql) %}
+  {% endif %}
   {%- if primary_index %}
   PRIMARY INDEX
     {% if primary_index is iterable and primary_index is not string %}
@@ -228,9 +237,13 @@
       {{ partitions }}
     {%- endif -%}
   {%- endif  %}
-  AS (
+  {%- if not contract_config.enforced %}
+    AS (
+  {%- endif -%}
     {{ select_sql }}
-  )
+  {%- if not contract_config.enforced -%}
+    )
+  {%- endif -%}
 {% endmacro %}
 
 
@@ -239,11 +252,17 @@
   Return SQL string to create view.
     Args:
       relation (dict): dbt relation
-      select_sql (string): The SQL query that will be used to generate 
+      select_sql (string): The SQL query that will be used to generate
         the internal query of the CTAS
   #}
 
-    CREATE OR REPLACE VIEW {{ relation.identifier }} AS (
+    CREATE OR REPLACE VIEW {{ relation.identifier }}
+
+    {%- set contract_config = config.get('contract') -%}
+    {%- if contract_config.enforced -%}
+      {{ get_assert_columns_equivalent(select_sql) }}
+    {%- endif %}
+    AS (
       {{ select_sql }}
     )
 {% endmacro %}
diff --git a/setup.cfg b/setup.cfg
index 5b712dc38..d13e72f46 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -23,7 +23,7 @@ project_urls =
 [options]
 packages = find_namespace:
 install_requires =
-    dbt-core~=1.4
+    dbt-core~=1.5
     firebolt-sdk>=1.1.0
 python_requires = >=3.7
 include_package_data = True
@@ -37,7 +37,7 @@ include = dbt, dbt.*
 [options.extras_require]
 dev =
     allure-pytest==2.*
-    dbt-tests-adapter~=1.4
+    dbt-tests-adapter~=1.5
     mypy==0.910
     pre-commit==2.15.0
     pytest==7.*


Test patch:
diff --git a/.github/workflows/integration-tests-v1.yml b/.github/workflows/integration-tests-v1.yml
index de9a16713..81ba55cbc 100644
--- a/.github/workflows/integration-tests-v1.yml
+++ b/.github/workflows/integration-tests-v1.yml
@@ -34,10 +34,10 @@ jobs:
       - name: Check out code
         uses: actions/checkout@v2
 
-      - name: Set up Python 3.7
+      - name: Set up Python 3.9
         uses: actions/setup-python@v2
         with:
-          python-version: 3.7
+          python-version: 3.9
 
       - name: Install dependencies
         run: |
diff --git a/.github/workflows/integration-tests-v2.yml b/.github/workflows/integration-tests-v2.yml
index 626c1cd59..193ec0b4b 100644
--- a/.github/workflows/integration-tests-v2.yml
+++ b/.github/workflows/integration-tests-v2.yml
@@ -34,10 +34,10 @@ jobs:
       - name: Check out code
         uses: actions/checkout@v2
 
-      - name: Set up Python 3.7
+      - name: Set up Python 3.9
         uses: actions/setup-python@v2
         with:
-          python-version: 3.7
+          python-version: 3.9
 
       - name: Install dependencies
         run: |
diff --git a/tests/conftest.py b/tests/conftest.py
index e6ff64fd0..c856c8c76 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -13,7 +13,7 @@
 def dbt_profile_target():
     profile = {
         'type': 'firebolt',
-        'threads': 2,
+        'threads': 1,
         'api_endpoint': os.getenv('API_ENDPOINT'),
         'account_name': os.getenv('ACCOUNT_NAME'),
         'database': os.getenv('DATABASE_NAME'),
diff --git a/tests/functional/adapter/hooks/data/seed_model.sql b/tests/functional/adapter/hooks/data/seed_model.sql
new file mode 100644
index 000000000..fb77cf8a0
--- /dev/null
+++ b/tests/functional/adapter/hooks/data/seed_model.sql
@@ -0,0 +1,17 @@
+
+drop table if exists {schema}.on_model_hook;
+
+create table {schema}.on_model_hook (
+    test_state       TEXT, -- start|end
+    target_dbname    TEXT,
+    target_host      TEXT,
+    target_name      TEXT,
+    target_schema    TEXT,
+    target_type      TEXT,
+    target_user      TEXT,
+    target_pass      TEXT,
+    target_threads   INTEGER,
+    run_started_at   TEXT,
+    invocation_id    TEXT,
+    thread_id        TEXT NULL
+);
diff --git a/tests/functional/adapter/hooks/data/seed_run.sql b/tests/functional/adapter/hooks/data/seed_run.sql
new file mode 100644
index 000000000..d9565e8e1
--- /dev/null
+++ b/tests/functional/adapter/hooks/data/seed_run.sql
@@ -0,0 +1,17 @@
+
+drop table if exists {schema}.on_run_hook;
+
+create table {schema}.on_run_hook (
+    test_state       TEXT, -- start|end
+    target_dbname    TEXT,
+    target_host      TEXT,
+    target_name      TEXT,
+    target_schema    TEXT,
+    target_type      TEXT,
+    target_user      TEXT,
+    target_pass      TEXT,
+    target_threads   INTEGER,
+    run_started_at   TEXT,
+    invocation_id    TEXT,
+    thread_id        TEXT NULL
+);
diff --git a/tests/functional/adapter/hooks/test_model_hooks.py b/tests/functional/adapter/hooks/test_model_hooks.py
new file mode 100644
index 000000000..bbefe146f
--- /dev/null
+++ b/tests/functional/adapter/hooks/test_model_hooks.py
@@ -0,0 +1,226 @@
+from dbt.tests.adapter.hooks.test_model_hooks import (
+    TestDuplicateHooksInConfigs as DuplicateHooksInConfigs,
+)
+from dbt.tests.adapter.hooks.test_model_hooks import TestHookRefs as HookRefs
+from dbt.tests.adapter.hooks.test_model_hooks import (
+    TestHooksRefsOnSeeds as HooksRefsOnSeeds,
+)
+from dbt.tests.adapter.hooks.test_model_hooks import (
+    TestPrePostModelHooks as PrePostModelHooks,
+)
+from dbt.tests.adapter.hooks.test_model_hooks import (
+    TestPrePostModelHooksInConfig as PrePostModelHooksInConfig,
+)
+from dbt.tests.adapter.hooks.test_model_hooks import (
+    TestPrePostModelHooksInConfigKwargs as PrePostModelHooksInConfigKwargs,
+)
+from dbt.tests.adapter.hooks.test_model_hooks import (
+    TestPrePostModelHooksInConfigWithCount as PrePostModelHooksInConfigWithCount,
+)
+from dbt.tests.adapter.hooks.test_model_hooks import (
+    TestPrePostModelHooksOnSeeds as PrePostModelHooksOnSeeds,
+)
+from dbt.tests.adapter.hooks.test_model_hooks import (
+    TestPrePostModelHooksOnSeedsPlusPrefixed as PrePostModelHooksOnSeedsPlusPrefixed,
+)
+
+# isort: off
+from dbt.tests.adapter.hooks.test_model_hooks import (
+    TestPrePostModelHooksOnSeedsPlusPrefixedWhitespace as PrePostModelHooksOnSeedsPlusPrefixedWhitespace,  # noqa: E501
+)
+
+# isort: on
+from dbt.tests.adapter.hooks.test_model_hooks import (
+    TestPrePostModelHooksOnSnapshots as PrePostModelHooksOnSnapshots,
+)
+from dbt.tests.adapter.hooks.test_model_hooks import (
+    TestPrePostModelHooksUnderscores as PrePostModelHooksUnderscores,
+)
+from dbt.tests.adapter.hooks.test_model_hooks import (
+    TestPrePostSnapshotHooksInConfigKwargs as PrePostSnapshotHooksInConfigKwargs,
+)
+from pytest import fixture
+
+
+@fixture(scope='module')
+def unique_schema():
+    return 'public'
+
+
+class FireboltCheckerMixin:
+    def check_hooks(self, state, project, host, count=1):
+        ctxs = self.get_ctx_vars(state, count=count, project=project)
+        for ctx in ctxs:
+            # Removing some of the checks from the original test
+            # because they don't apply to the current implementation
+            assert ctx['test_state'] == state
+            assert ctx['target_name'] == 'default'
+            assert ctx['target_type'] == 'firebolt'
+            assert ctx['target_schema'] == project.test_schema
+
+            assert (
+                ctx['run_started_at'] is not None and len(ctx['run_started_at']) > 0
+            ), 'run_started_at was not set'
+            assert (
+                ctx['invocation_id'] is not None and len(ctx['invocation_id']) > 0
+            ), 'invocation_id was not set'
+
+
+# Override to check existing column instead, since we can't add a new column
+properties__seed_models = """
+version: 2
+seeds:
+- name: example_seed
+  columns:
+  - name: c
+    tests:
+    - accepted_values:
+        values: [0]
+"""
+
+properties__test_snapshot_models = """
+version: 2
+snapshots:
+- name: example_snapshot
+  columns:
+  - name: c
+    tests:
+    - accepted_values:
+        values: [0]
+"""
+
+
+class FireboltProjectUpdateMixin:
+    # Override because we don't support ALTER TABLE
+    @fixture(scope='class')
+    def project_config_update(self):
+        return {
+            'seed-paths': ['seeds'],
+            'models': {},
+            'seeds': {
+                'post-hook': [
+                    'update {{ this }} set c = 0',
+                    # call any macro to track dependency:
+                    # https://github.com/dbt-labs/dbt-core/issues/6806
+                    'select null::{{ dbt.type_int() }} as id',
+                ],
+                'quote_columns': False,
+            },
+        }
+
+    @fixture(scope='class')
+    def models(self):
+        return {'schema.yml': properties__seed_models}
+
+
+class TestPrePostModelHooksFirebolt(FireboltCheckerMixin, PrePostModelHooks):
+    pass
+
+
+class TestPrePostModelHooksUnderscoresFirebolt(
+    FireboltCheckerMixin, PrePostModelHooksUnderscores
+):
+    pass
+
+
+class TestHookRefsFirebolt(FireboltCheckerMixin, HookRefs):
+    pass
+
+
+class TestPrePostModelHooksOnSeedsFirebolt(
+    FireboltProjectUpdateMixin, FireboltCheckerMixin, PrePostModelHooksOnSeeds
+):
+    pass
+
+
+class TestHooksRefsOnSeedsFirebolt(FireboltCheckerMixin, HooksRefsOnSeeds):
+    pass
+
+
+class TestPrePostModelHooksOnSeedsPlusPrefixedFirebolt(
+    FireboltProjectUpdateMixin,
+    FireboltCheckerMixin,
+    PrePostModelHooksOnSeedsPlusPrefixed,
+):
+    pass
+
+
+class TestPrePostModelHooksOnSeedsPlusPrefixedWhitespaceFirebolt(
+    FireboltProjectUpdateMixin,
+    FireboltCheckerMixin,
+    PrePostModelHooksOnSeedsPlusPrefixedWhitespace,
+):
+    pass
+
+
+class TestPrePostModelHooksOnSnapshotsFirebolt(
+    FireboltCheckerMixin, PrePostModelHooksOnSnapshots
+):
+    # Override because we don't support ALTER TABLE
+    @fixture(scope='class')
+    def project_config_update(self):
+        return {
+            'seed-paths': ['seeds'],
+            'snapshot-paths': ['test-snapshots'],
+            'models': {},
+            'snapshots': {
+                'post-hook': [
+                    'update {{ this }} set c = 0',
+                ]
+            },
+            'seeds': {
+                'quote_columns': False,
+            },
+        }
+
+    @fixture(scope='class')
+    def models(self):
+        return {'schema.yml': properties__test_snapshot_models}
+
+
+class TestPrePostModelHooksInConfigFirebolt(
+    FireboltCheckerMixin, PrePostModelHooksInConfig
+):
+    pass
+
+
+class TestPrePostModelHooksInConfigWithCountFirebolt(
+    FireboltCheckerMixin, PrePostModelHooksInConfigWithCount
+):
+    pass
+
+
+class TestPrePostModelHooksInConfigKwargsFirebolt(
+    FireboltCheckerMixin, PrePostModelHooksInConfigKwargs
+):
+    pass
+
+
+class TestPrePostSnapshotHooksInConfigKwargsFirebolt(
+    FireboltCheckerMixin, PrePostSnapshotHooksInConfigKwargs
+):
+    @fixture(scope='class')
+    def project_config_update(self):
+        return {
+            'seed-paths': ['seeds'],
+            'snapshot-paths': ['test-kwargs-snapshots'],
+            'models': {},
+            'snapshots': {
+                'post-hook': [
+                    'update {{ this }} set c = 0',
+                ]
+            },
+            'seeds': {
+                'quote_columns': False,
+            },
+        }
+
+    @fixture(scope='class')
+    def models(self):
+        return {'schema.yml': properties__test_snapshot_models}
+
+
+class TestDuplicateHooksInConfigsFirebolt(
+    FireboltCheckerMixin, DuplicateHooksInConfigs
+):
+    pass
diff --git a/tests/functional/adapter/test_column_types.py b/tests/functional/adapter/test_column_types.py
new file mode 100644
index 000000000..2aae60ea1
--- /dev/null
+++ b/tests/functional/adapter/test_column_types.py
@@ -0,0 +1,25 @@
+from dbt.tests.adapter.column_types.fixtures import schema_yml
+from dbt.tests.adapter.column_types.test_column_types import BaseColumnTypes
+from pytest import fixture
+
+# Override since there is no smallint in Firebolt
+model_sql = """
+select
+    1::integer as smallint_col,
+    2::integer as int_col,
+    3::bigint as bigint_col,
+    4.0::real as real_col,
+    5.0::double precision as double_col,
+    6.0::numeric as numeric_col,
+    '7'::text as text_col,
+    '8'::varchar(20) as varchar_col
+"""
+
+
+class TestFireboltColumnTypes(BaseColumnTypes):
+    @fixture(scope='class')
+    def models(self):
+        return {'model.sql': model_sql, 'schema.yml': schema_yml}
+
+    def test_run_and_test(self, project):
+        self.run_and_test()
diff --git a/tests/functional/adapter/test_constraints.py b/tests/functional/adapter/test_constraints.py
new file mode 100644
index 000000000..ea6c4a21d
--- /dev/null
+++ b/tests/functional/adapter/test_constraints.py
@@ -0,0 +1,117 @@
+from dbt.tests.adapter.constraints.test_constraints import (
+    BaseConstraintQuotedColumn,
+    BaseConstraintsRollback,
+    BaseConstraintsRuntimeDdlEnforcement,
+    BaseIncrementalConstraintsColumnsEqual,
+    BaseIncrementalConstraintsRollback,
+    BaseIncrementalConstraintsRuntimeDdlEnforcement,
+    BaseModelConstraintsRuntimeEnforcement,
+    BaseTableConstraintsColumnsEqual,
+    BaseViewConstraintsColumnsEqual,
+)
+from pytest import fixture, mark
+
+_expected_sql = """
+create dimension table if not exists <model_identifier> (
+    id integer not null unique,
+    color text,
+    date_day text
+) ;
+insert into <model_identifier> (
+    id ,
+    color ,
+    date_day
+)
+    select
+       id,
+       color,
+       date_day
+       from
+    (
+        -- depends_on: <foreign_key_model_identifier>
+        select
+            'blue' as color,
+            1 as id,
+            '2019-01-01' as date_day
+    ) as model_subq
+"""
+
+
+class FireboltTestTypesMixin:
+    # Overriding as some of the data types are not supported by firebolt
+    @fixture
+    def data_types(self, schema_int_type, int_type, string_type):
+        # sql_column_value, schema_data_type, error_data_type
+        return [
+            ['1', schema_int_type, int_type],
+            ["'1'", string_type, string_type],
+            ['true', 'bool', 'boolean'],
+            ["'2013-11-03 00:00:00-07'::timestamptz", 'timestamptz', 'timestamp'],
+            ["ARRAY['a','b','c']", 'text[]', 'array'],
+            ['ARRAY[1,2,3]', 'int[]', 'array'],
+            ["'1'::numeric", 'numeric', 'Decimal'],
+        ]
+
+    @fixture
+    def string_type(self):
+        return 'text'
+
+    @fixture
+    def int_type(self):
+        return 'integer'
+
+
+class TestTableConstraintsColumnsEqualFirebolt(
+    FireboltTestTypesMixin, BaseTableConstraintsColumnsEqual
+):
+    pass
+
+
+class TestViewConstraintsColumnsEqualFirebolt(
+    FireboltTestTypesMixin, BaseViewConstraintsColumnsEqual
+):
+    pass
+
+
+class TestIncrementalConstraintsColumnsEqualFirebolt(
+    FireboltTestTypesMixin, BaseIncrementalConstraintsColumnsEqual
+):
+    pass
+
+
+class TestConstraintsRuntimeDdlEnforcementFirebolt(
+    BaseConstraintsRuntimeDdlEnforcement
+):
+    @fixture(scope='class')
+    def expected_sql(self):
+        return _expected_sql
+
+
+@mark.skip(reason='Firebolt does not support commit/rollback')
+class TestConstraintsRollbackFirebolt(BaseConstraintsRollback):
+    pass
+
+
+class TestIncrementalConstraintsRuntimeDdlEnforcementFirebolt(
+    BaseIncrementalConstraintsRuntimeDdlEnforcement
+):
+    @fixture(scope='class')
+    def expected_sql(self):
+        return _expected_sql
+
+
+@mark.skip(reason='Firebolt does not support commit/rollback')
+class TestIncrementalConstraintsRollbackFirebolt(BaseIncrementalConstraintsRollback):
+    pass
+
+
+@mark.skip(reason='Firebolt does not support constraint clause in DDL')
+class TestModelConstraintsRuntimeEnforcementFirebolt(
+    BaseModelConstraintsRuntimeEnforcement
+):
+    pass
+
+
+@mark.skip(reason='Firebolt does not support check constraint')
+class TestConstraintQuotedColumnFirebolt(BaseConstraintQuotedColumn):
+    pass
diff --git a/tests/functional/adapter/test_query_comment.py b/tests/functional/adapter/test_query_comment.py
index ecd1db2d3..f8181ba7e 100644
--- a/tests/functional/adapter/test_query_comment.py
+++ b/tests/functional/adapter/test_query_comment.py
@@ -11,31 +11,21 @@
 from dbt.version import __version__ as dbt_version
 
 
-class FireboltTestFixMixin:
-    """
-    Fixing Base class test_comments method to actually
-    assert that the logs contain the expected string.
-    """
-
-    def test_comments(self, project):
-        logs = self.run_assert_comments()
-        self.matches_comment(logs)
-
-
-class TestQueryCommentsFirebolt(FireboltTestFixMixin, BaseQueryComments):
-    def matches_comment(self, logs) -> bool:
+class TestQueryCommentsFirebolt(BaseQueryComments):
+    def test_matches_comment(self, project) -> bool:
+        logs = self.run_get_json()
         assert '\\n/* dbt\\nrules! */' in logs
 
 
-class TestMacroQueryCommentsFirebolt(FireboltTestFixMixin, BaseMacroQueryComments):
-    def matches_comment(self, logs) -> bool:
+class TestMacroQueryCommentsFirebolt(BaseMacroQueryComments):
+    def test_matches_comment(self, project) -> bool:
+        logs = self.run_get_json()
         assert '\\n/* dbt macros\\nare pretty cool */' in logs
 
 
-class TestMacroArgsQueryCommentsFirebolt(
-    FireboltTestFixMixin, BaseMacroArgsQueryComments
-):
-    def matches_comment(self, logs) -> bool:
+class TestMacroArgsQueryCommentsFirebolt(BaseMacroArgsQueryComments):
+    def test_matches_comment(self, project) -> bool:
+        logs = self.run_get_json()
         expected_dct = {
             'app': 'dbt++',
             'dbt_version': dbt_version,
@@ -52,9 +42,9 @@ class TestMacroInvalidQueryCommentsFirebolt(BaseMacroInvalidQueryComments):
     pass
 
 
-class TestNullQueryCommentsFirebolt(FireboltTestFixMixin, BaseNullQueryComments):
+class TestNullQueryCommentsFirebolt(BaseNullQueryComments):
     pass
 
 
-class TestEmptyQueryCommentsFirebolt(FireboltTestFixMixin, BaseEmptyQueryComments):
+class TestEmptyQueryCommentsFirebolt(BaseEmptyQueryComments):
     pass
diff --git a/tests/functional/adapter/test_simple_copy.py b/tests/functional/adapter/test_simple_copy.py
new file mode 100644
index 000000000..08bd47c26
--- /dev/null
+++ b/tests/functional/adapter/test_simple_copy.py
@@ -0,0 +1,15 @@
+from dbt.tests.adapter.simple_copy.test_simple_copy import (
+    EmptyModelsArentRunBase,
+    SimpleCopyBase,
+)
+from pytest import mark
+
+
+class TestSimpleCopyBase(SimpleCopyBase):
+    @mark.skip(reason="Firebolt doesn't support materialized views")
+    def test_simple_copy_with_materialized_views(self, project):
+        super().test_simple_copy_with_materialized_views(project)
+
+
+class TestEmptyModelsArentRun(EmptyModelsArentRunBase):
+    pass
diff --git a/tests/functional/adapter/utils/test_utils.py b/tests/functional/adapter/utils/test_utils.py
index a76129492..317f7b45f 100644
--- a/tests/functional/adapter/utils/test_utils.py
+++ b/tests/functional/adapter/utils/test_utils.py
@@ -376,6 +376,7 @@ def models(self):
 """
 
 
+@mark.xfail(reason='susbstr(text, bigint) is not supported, waiting on FIR-28289')
 class TestRight(BaseRight):
     @pytest.fixture(scope='class')
     def models(self):
diff --git a/tests/unit/test_firebolt_adapter.py b/tests/unit/test_firebolt_adapter.py
index 680cedb95..1d2fcac61 100644
--- a/tests/unit/test_firebolt_adapter.py
+++ b/tests/unit/test_firebolt_adapter.py
@@ -1,7 +1,9 @@
+from datetime import datetime
 from unittest.mock import MagicMock, patch
 
 from dbt.contracts.connection import Connection
 from firebolt.client.auth import ClientCredentials, UsernamePassword
+from firebolt.db import ARRAY, DECIMAL
 from firebolt.db.connection import Connection as SDKConnection
 from firebolt.utils.exception import InterfaceError
 from pytest import fixture, mark
@@ -72,6 +74,26 @@ def test_resolve_columns(adapter, column, expected):
     assert adapter.resolve_special_columns(column) == expected
 
 
+@mark.parametrize(
+    'input_type, expected_output',
+    [
+        (str, 'text'),
+        (int, 'integer'),
+        (float, 'float'),
+        (bool, 'boolean'),
+        (datetime, 'timestamp'),
+        (bytes, 'bytea'),
+        (DECIMAL(1, 23), 'Decimal(1, 23)'),
+        (ARRAY(int), 'array[integer]'),
+        (dict, 'text'),  # Test for a type that is not handled and defaults to 'text'
+    ],
+)
+def test_data_type_code_to_name(input_type, expected_output):
+    assert (
+        FireboltConnectionManager.data_type_code_to_name(input_type) == expected_output
+    )
+
+
 @mark.parametrize(
     'profile,expected',
     [