Repository: dbt-labs/dbt-core

Issue:
[CT-2980] Fix untyped functions in adapters/base/ (mypy warning)
### Problem
When we hit a mypy error the console is flooded with mypy warnings as well.  Clean up the warning to improve developer experience. 

### Reproduction
Simply force a mypy error (change types) and the warnings will be generated.

### Relevant Output
```
core/dbt/adapters/base/query_headers.py:29: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs  [annotation-unchecked]
core/dbt/adapters/base/impl.py:229: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs  [annotation-unchecked]
```


Discussion:
The query_headers one has been fixed. The one at core/dbt/adapters/base/impl.py:229 is harder. It uses a Protocol which is way underspecified, but it's not clear to me what it ought to be. This one might more appropriately be looked at by an adapters dev.

```
core/dbt/adapters/base/impl.py:228: error: Too many arguments for "ConnectionManagerProtocol"  [call-arg]
core/dbt/adapters/base/impl.py:235: error: "ConnectionManagerProtocol" has no attribute "set_connection_name"  [attr-defined]
core/dbt/adapters/base/impl.py:238: error: "ConnectionManagerProtocol" has no attribute "release"  [attr-defined]
core/dbt/adapters/base/impl.py:241: error: "ConnectionManagerProtocol" has no attribute "cleanup_all"  [attr-defined]
core/dbt/adapters/base/impl.py:244: error: "ConnectionManagerProtocol" has no attribute "clear_transaction"  [attr-defined]
core/dbt/adapters/base/impl.py:247: error: "ConnectionManagerProtocol" has no attribute "commit_if_has_connection"  [attr-defined]
core/dbt/adapters/base/impl.py:253: error: "ConnectionManagerProtocol" has no attribute "get_if_exists"  [attr-defined]
core/dbt/adapters/base/impl.py:261: error: "ConnectionManagerProtocol" has no attribute "query_header"  [attr-defined]
core/dbt/adapters/base/impl.py:262: error: "ConnectionManagerProtocol" has no attribute "query_header"  [attr-defined]
core/dbt/adapters/base/impl.py:267: error: "ConnectionManagerProtocol" has no attribute "query_header"  [attr-defined]
core/dbt/adapters/base/impl.py:268: error: "ConnectionManagerProtocol" has no attribute "query_header"  [attr-defined]
core/dbt/adapters/base/impl.py:290: error: "ConnectionManagerProtocol" has no attribute "execute"  [attr-defined]
core/dbt/adapters/base/impl.py:306: error: "ConnectionManagerProtocol" has no attribute "add_select_query"  [attr-defined]
core/dbt/adapters/base/impl.py:309: error: "ConnectionManagerProtocol" has no attribute "data_type_code_to_name"  [attr-defined]
core/dbt/adapters/base/impl.py:325: error: "ConnectionManagerProtocol" has no attribute "get_partitions_metadata"  [attr-defined]
core/dbt/adapters/base/impl.py:360: error: "ConnectionManagerProtocol" has no attribute "set_query_header"  [attr-defined]
core/dbt/adapters/base/impl.py:459: error: Argument 1 to "update_schemas" of "RelationsCache" has incompatible type "Set[Tuple[Optional[str], Optional[str]]]"; expected "Iterable[Tuple[Optional[str], str]]"  [arg-type]
core/dbt/adapters/base/impl.py:1053: error: "ConnectionManagerProtocol" has no attribute "exception_handler"  [attr-defined]
core/dbt/adapters/base/impl.py:1271: error: "ConnectionManagerProtocol" has no attribute "profile"  [attr-defined]
```
@mikealfare If you get a chance, could you take a look?
@gshank Is there a branch in progress that I should start from or just peel another off of `main`?
Just from main is fine.  You see the issues if you add a return value -> None to the init method of BaseAdapter

Resolving patch:
diff --git a/core/dbt/adapters/base/connections.py b/core/dbt/adapters/base/connections.py
--- a/core/dbt/adapters/base/connections.py
+++ b/core/dbt/adapters/base/connections.py
@@ -400,7 +400,7 @@ def _add_query_comment(self, sql: str) -> str:
 
     @abc.abstractmethod
     def execute(
-        self, sql: str, auto_begin: bool = False, fetch: bool = False
+        self, sql: str, auto_begin: bool = False, fetch: bool = False, limit: Optional[int] = None
     ) -> Tuple[AdapterResponse, agate.Table]:
         """Execute the given SQL.
 
@@ -408,7 +408,28 @@ def execute(
         :param bool auto_begin: If set, and dbt is not currently inside a
             transaction, automatically begin one.
         :param bool fetch: If set, fetch results.
+        :param int limit: If set, limits the result set
         :return: A tuple of the query status and results (empty if fetch=False).
         :rtype: Tuple[AdapterResponse, agate.Table]
         """
         raise dbt.exceptions.NotImplementedError("`execute` is not implemented for this adapter!")
+
+    def add_select_query(self, sql: str) -> Tuple[Connection, Any]:
+        """
+        This was added here because base.impl.BaseAdapter.get_column_schema_from_query expects it to be here.
+        That method wouldn't work unless the adapter used sql.impl.SQLAdapter, sql.connections.SQLConnectionManager
+        or defined this method on <Adapter>ConnectionManager before passing it in to <Adapter>Adapter.
+
+        See https://github.com/dbt-labs/dbt-core/issues/8396 for more information.
+        """
+        raise dbt.exceptions.NotImplementedError(
+            "`add_select_query` is not implemented for this adapter!"
+        )
+
+    @classmethod
+    def data_type_code_to_name(cls, type_code: Union[int, str]) -> str:
+        """Get the string representation of the data type from the type_code."""
+        # https://peps.python.org/pep-0249/#type-objects
+        raise dbt.exceptions.NotImplementedError(
+            "`data_type_code_to_name` is not implemented for this adapter!"
+        )
diff --git a/core/dbt/adapters/base/impl.py b/core/dbt/adapters/base/impl.py
--- a/core/dbt/adapters/base/impl.py
+++ b/core/dbt/adapters/base/impl.py
@@ -43,7 +43,7 @@
     UnexpectedNullError,
 )
 
-from dbt.adapters.protocol import AdapterConfig, ConnectionManagerProtocol
+from dbt.adapters.protocol import AdapterConfig
 from dbt.clients.agate_helper import empty_table, merge_tables, table_from_rows
 from dbt.clients.jinja import MacroGenerator
 from dbt.contracts.graph.manifest import Manifest, MacroManifest
@@ -60,7 +60,7 @@
 )
 from dbt.utils import filter_null_values, executor, cast_to_str, AttrDict
 
-from dbt.adapters.base.connections import Connection, AdapterResponse
+from dbt.adapters.base.connections import Connection, AdapterResponse, BaseConnectionManager
 from dbt.adapters.base.meta import AdapterMeta, available
 from dbt.adapters.base.relation import (
     ComponentName,
@@ -208,7 +208,7 @@ class BaseAdapter(metaclass=AdapterMeta):
 
     Relation: Type[BaseRelation] = BaseRelation
     Column: Type[BaseColumn] = BaseColumn
-    ConnectionManager: Type[ConnectionManagerProtocol]
+    ConnectionManager: Type[BaseConnectionManager]
 
     # A set of clobber config fields accepted by this adapter
     # for use in materializations
@@ -315,14 +315,21 @@ def get_column_schema_from_query(self, sql: str) -> List[BaseColumn]:
 
     @available.parse(lambda *a, **k: ("", empty_table()))
     def get_partitions_metadata(self, table: str) -> Tuple[agate.Table]:
-        """Obtain partitions metadata for a BigQuery partitioned table.
+        """
+        TODO: Can we move this to dbt-bigquery?
+        Obtain partitions metadata for a BigQuery partitioned table.
 
-        :param str table_id: a partitioned table id, in standard SQL format.
+        :param str table: a partitioned table id, in standard SQL format.
         :return: a partition metadata tuple, as described in
             https://cloud.google.com/bigquery/docs/creating-partitioned-tables#getting_partition_metadata_using_meta_tables.
         :rtype: agate.Table
         """
-        return self.connections.get_partitions_metadata(table=table)
+        if hasattr(self.connections, "get_partitions_metadata"):
+            return self.connections.get_partitions_metadata(table=table)
+        else:
+            raise NotImplementedError(
+                "`get_partitions_metadata` is not implemented for this adapter!"
+            )
 
     ###
     # Methods that should never be overridden
@@ -453,9 +460,10 @@ def _relations_cache_for_schemas(
         # it's possible that there were no relations in some schemas. We want
         # to insert the schemas we query into the cache's `.schemas` attribute
         # so we can check it later
-        cache_update: Set[Tuple[Optional[str], Optional[str]]] = set()
+        cache_update: Set[Tuple[Optional[str], str]] = set()
         for relation in cache_schemas:
-            cache_update.add((relation.database, relation.schema))
+            if relation.schema:
+                cache_update.add((relation.database, relation.schema))
         self.cache.update_schemas(cache_update)
 
     def set_relations_cache(
diff --git a/core/dbt/adapters/sql/connections.py b/core/dbt/adapters/sql/connections.py
--- a/core/dbt/adapters/sql/connections.py
+++ b/core/dbt/adapters/sql/connections.py
@@ -1,6 +1,6 @@
 import abc
 import time
-from typing import List, Optional, Tuple, Any, Iterable, Dict, Union
+from typing import List, Optional, Tuple, Any, Iterable, Dict
 
 import agate
 
@@ -131,14 +131,6 @@ def get_result_from_cursor(cls, cursor: Any, limit: Optional[int]) -> agate.Tabl
 
         return dbt.clients.agate_helper.table_from_data_flat(data, column_names)
 
-    @classmethod
-    def data_type_code_to_name(cls, type_code: Union[int, str]) -> str:
-        """Get the string representation of the data type from the type_code."""
-        # https://peps.python.org/pep-0249/#type-objects
-        raise dbt.exceptions.NotImplementedError(
-            "`data_type_code_to_name` is not implemented for this adapter!"
-        )
-
     def execute(
         self, sql: str, auto_begin: bool = False, fetch: bool = False, limit: Optional[int] = None
     ) -> Tuple[AdapterResponse, agate.Table]:


Test patch: