dbt-adapters 0.1.0a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of dbt-adapters might be problematic. Click here for more details.

Files changed (147) hide show
  1. dbt/__init__.py +0 -0
  2. dbt/adapters/__about__.py +1 -0
  3. dbt/adapters/__init__.py +7 -0
  4. dbt/adapters/base/README.md +13 -0
  5. dbt/adapters/base/__init__.py +15 -0
  6. dbt/adapters/base/column.py +166 -0
  7. dbt/adapters/base/connections.py +426 -0
  8. dbt/adapters/base/impl.py +1654 -0
  9. dbt/adapters/base/meta.py +131 -0
  10. dbt/adapters/base/plugin.py +32 -0
  11. dbt/adapters/base/query_headers.py +101 -0
  12. dbt/adapters/base/relation.py +471 -0
  13. dbt/adapters/cache.py +521 -0
  14. dbt/adapters/capability.py +52 -0
  15. dbt/adapters/clients/__init__.py +0 -0
  16. dbt/adapters/clients/jinja.py +24 -0
  17. dbt/adapters/contracts/__init__.py +0 -0
  18. dbt/adapters/contracts/connection.py +228 -0
  19. dbt/adapters/contracts/macros.py +11 -0
  20. dbt/adapters/contracts/relation.py +125 -0
  21. dbt/adapters/events/README.md +57 -0
  22. dbt/adapters/events/__init__.py +0 -0
  23. dbt/adapters/events/adapter_types.proto +517 -0
  24. dbt/adapters/events/adapter_types_pb2.py +208 -0
  25. dbt/adapters/events/base_types.py +40 -0
  26. dbt/adapters/events/logging.py +83 -0
  27. dbt/adapters/events/types.py +423 -0
  28. dbt/adapters/exceptions/__init__.py +40 -0
  29. dbt/adapters/exceptions/alias.py +24 -0
  30. dbt/adapters/exceptions/cache.py +68 -0
  31. dbt/adapters/exceptions/compilation.py +255 -0
  32. dbt/adapters/exceptions/connection.py +16 -0
  33. dbt/adapters/exceptions/database.py +51 -0
  34. dbt/adapters/factory.py +246 -0
  35. dbt/adapters/protocol.py +173 -0
  36. dbt/adapters/reference_keys.py +39 -0
  37. dbt/adapters/relation_configs/README.md +25 -0
  38. dbt/adapters/relation_configs/__init__.py +12 -0
  39. dbt/adapters/relation_configs/config_base.py +44 -0
  40. dbt/adapters/relation_configs/config_change.py +24 -0
  41. dbt/adapters/relation_configs/config_validation.py +57 -0
  42. dbt/adapters/sql/__init__.py +2 -0
  43. dbt/adapters/sql/connections.py +195 -0
  44. dbt/adapters/sql/impl.py +273 -0
  45. dbt/adapters/utils.py +69 -0
  46. dbt/include/global_project/__init__.py +4 -0
  47. dbt/include/global_project/dbt_project.yml +7 -0
  48. dbt/include/global_project/docs/overview.md +43 -0
  49. dbt/include/global_project/macros/adapters/apply_grants.sql +167 -0
  50. dbt/include/global_project/macros/adapters/columns.sql +137 -0
  51. dbt/include/global_project/macros/adapters/freshness.sql +16 -0
  52. dbt/include/global_project/macros/adapters/indexes.sql +41 -0
  53. dbt/include/global_project/macros/adapters/metadata.sql +96 -0
  54. dbt/include/global_project/macros/adapters/persist_docs.sql +33 -0
  55. dbt/include/global_project/macros/adapters/relation.sql +79 -0
  56. dbt/include/global_project/macros/adapters/schema.sql +20 -0
  57. dbt/include/global_project/macros/adapters/show.sql +22 -0
  58. dbt/include/global_project/macros/adapters/timestamps.sql +44 -0
  59. dbt/include/global_project/macros/adapters/validate_sql.sql +10 -0
  60. dbt/include/global_project/macros/etc/datetime.sql +62 -0
  61. dbt/include/global_project/macros/etc/statement.sql +52 -0
  62. dbt/include/global_project/macros/generic_test_sql/accepted_values.sql +27 -0
  63. dbt/include/global_project/macros/generic_test_sql/not_null.sql +9 -0
  64. dbt/include/global_project/macros/generic_test_sql/relationships.sql +23 -0
  65. dbt/include/global_project/macros/generic_test_sql/unique.sql +12 -0
  66. dbt/include/global_project/macros/get_custom_name/get_custom_alias.sql +36 -0
  67. dbt/include/global_project/macros/get_custom_name/get_custom_database.sql +32 -0
  68. dbt/include/global_project/macros/get_custom_name/get_custom_schema.sql +60 -0
  69. dbt/include/global_project/macros/materializations/configs.sql +21 -0
  70. dbt/include/global_project/macros/materializations/hooks.sql +35 -0
  71. dbt/include/global_project/macros/materializations/models/clone/can_clone_table.sql +7 -0
  72. dbt/include/global_project/macros/materializations/models/clone/clone.sql +67 -0
  73. dbt/include/global_project/macros/materializations/models/clone/create_or_replace_clone.sql +7 -0
  74. dbt/include/global_project/macros/materializations/models/incremental/column_helpers.sql +80 -0
  75. dbt/include/global_project/macros/materializations/models/incremental/incremental.sql +92 -0
  76. dbt/include/global_project/macros/materializations/models/incremental/is_incremental.sql +13 -0
  77. dbt/include/global_project/macros/materializations/models/incremental/merge.sql +131 -0
  78. dbt/include/global_project/macros/materializations/models/incremental/on_schema_change.sql +144 -0
  79. dbt/include/global_project/macros/materializations/models/incremental/strategies.sql +79 -0
  80. dbt/include/global_project/macros/materializations/models/materialized_view.sql +121 -0
  81. dbt/include/global_project/macros/materializations/models/table.sql +64 -0
  82. dbt/include/global_project/macros/materializations/models/view.sql +72 -0
  83. dbt/include/global_project/macros/materializations/seeds/helpers.sql +128 -0
  84. dbt/include/global_project/macros/materializations/seeds/seed.sql +60 -0
  85. dbt/include/global_project/macros/materializations/snapshots/helpers.sql +181 -0
  86. dbt/include/global_project/macros/materializations/snapshots/snapshot.sql +99 -0
  87. dbt/include/global_project/macros/materializations/snapshots/snapshot_merge.sql +25 -0
  88. dbt/include/global_project/macros/materializations/snapshots/strategies.sql +174 -0
  89. dbt/include/global_project/macros/materializations/tests/helpers.sql +14 -0
  90. dbt/include/global_project/macros/materializations/tests/test.sql +60 -0
  91. dbt/include/global_project/macros/materializations/tests/where_subquery.sql +15 -0
  92. dbt/include/global_project/macros/python_model/python.sql +103 -0
  93. dbt/include/global_project/macros/relations/column/columns_spec_ddl.sql +89 -0
  94. dbt/include/global_project/macros/relations/create.sql +23 -0
  95. dbt/include/global_project/macros/relations/create_backup.sql +17 -0
  96. dbt/include/global_project/macros/relations/create_intermediate.sql +17 -0
  97. dbt/include/global_project/macros/relations/drop.sql +41 -0
  98. dbt/include/global_project/macros/relations/drop_backup.sql +14 -0
  99. dbt/include/global_project/macros/relations/materialized_view/alter.sql +55 -0
  100. dbt/include/global_project/macros/relations/materialized_view/create.sql +10 -0
  101. dbt/include/global_project/macros/relations/materialized_view/drop.sql +14 -0
  102. dbt/include/global_project/macros/relations/materialized_view/refresh.sql +9 -0
  103. dbt/include/global_project/macros/relations/materialized_view/rename.sql +10 -0
  104. dbt/include/global_project/macros/relations/materialized_view/replace.sql +10 -0
  105. dbt/include/global_project/macros/relations/rename.sql +35 -0
  106. dbt/include/global_project/macros/relations/rename_intermediate.sql +14 -0
  107. dbt/include/global_project/macros/relations/replace.sql +50 -0
  108. dbt/include/global_project/macros/relations/schema.sql +8 -0
  109. dbt/include/global_project/macros/relations/table/create.sql +60 -0
  110. dbt/include/global_project/macros/relations/table/drop.sql +14 -0
  111. dbt/include/global_project/macros/relations/table/rename.sql +10 -0
  112. dbt/include/global_project/macros/relations/table/replace.sql +10 -0
  113. dbt/include/global_project/macros/relations/view/create.sql +27 -0
  114. dbt/include/global_project/macros/relations/view/drop.sql +14 -0
  115. dbt/include/global_project/macros/relations/view/rename.sql +10 -0
  116. dbt/include/global_project/macros/relations/view/replace.sql +66 -0
  117. dbt/include/global_project/macros/utils/any_value.sql +9 -0
  118. dbt/include/global_project/macros/utils/array_append.sql +8 -0
  119. dbt/include/global_project/macros/utils/array_concat.sql +7 -0
  120. dbt/include/global_project/macros/utils/array_construct.sql +12 -0
  121. dbt/include/global_project/macros/utils/bool_or.sql +9 -0
  122. dbt/include/global_project/macros/utils/cast_bool_to_text.sql +7 -0
  123. dbt/include/global_project/macros/utils/concat.sql +7 -0
  124. dbt/include/global_project/macros/utils/data_types.sql +129 -0
  125. dbt/include/global_project/macros/utils/date_spine.sql +75 -0
  126. dbt/include/global_project/macros/utils/date_trunc.sql +7 -0
  127. dbt/include/global_project/macros/utils/dateadd.sql +14 -0
  128. dbt/include/global_project/macros/utils/datediff.sql +14 -0
  129. dbt/include/global_project/macros/utils/escape_single_quotes.sql +8 -0
  130. dbt/include/global_project/macros/utils/except.sql +9 -0
  131. dbt/include/global_project/macros/utils/generate_series.sql +53 -0
  132. dbt/include/global_project/macros/utils/hash.sql +7 -0
  133. dbt/include/global_project/macros/utils/intersect.sql +9 -0
  134. dbt/include/global_project/macros/utils/last_day.sql +15 -0
  135. dbt/include/global_project/macros/utils/length.sql +11 -0
  136. dbt/include/global_project/macros/utils/listagg.sql +30 -0
  137. dbt/include/global_project/macros/utils/literal.sql +7 -0
  138. dbt/include/global_project/macros/utils/position.sql +11 -0
  139. dbt/include/global_project/macros/utils/replace.sql +14 -0
  140. dbt/include/global_project/macros/utils/right.sql +12 -0
  141. dbt/include/global_project/macros/utils/safe_cast.sql +9 -0
  142. dbt/include/global_project/macros/utils/split_part.sql +26 -0
  143. dbt/include/global_project/tests/generic/builtin.sql +30 -0
  144. dbt_adapters-0.1.0a1.dist-info/METADATA +81 -0
  145. dbt_adapters-0.1.0a1.dist-info/RECORD +147 -0
  146. dbt_adapters-0.1.0a1.dist-info/WHEEL +4 -0
  147. dbt_adapters-0.1.0a1.dist-info/licenses/LICENSE +201 -0
dbt/__init__.py ADDED
File without changes
@@ -0,0 +1 @@
1
+ version = "0.1.0a1"
@@ -0,0 +1,7 @@
1
+ """
2
+ This adds all subdirectories of directories on `sys.path` to this package’s `__path__` .
3
+ It effectively combines all adapters into a single namespace (dbt.adapter).
4
+ """
5
+ from pkgutil import extend_path
6
+
7
+ __path__ = extend_path(__path__, __name__)
@@ -0,0 +1,13 @@
1
+ ## Base adapters
2
+
3
+ ### impl.py
4
+
5
+ The class `SQLAdapter` in [base/imply.py](https://github.com/dbt-labs/dbt-core/blob/main/core/dbt/adapters/base/impl.py)
6
+ is a (mostly) abstract object that adapter objects inherit from.
7
+ The base class scaffolds out methods that every adapter project
8
+ usually should implement for smooth communication between dbt and database.
9
+
10
+ Some target databases require more or fewer methods--
11
+ it all depends on what the warehouse's featureset is.
12
+
13
+ Look into the class for function-level comments.
@@ -0,0 +1,15 @@
1
+ from dbt.adapters.base.meta import available
2
+ from dbt.adapters.base.column import Column
3
+ from dbt.adapters.base.connections import BaseConnectionManager
4
+ from dbt.adapters.base.impl import (
5
+ AdapterConfig,
6
+ BaseAdapter,
7
+ ConstraintSupport,
8
+ PythonJobHelper,
9
+ )
10
+ from dbt.adapters.base.plugin import AdapterPlugin
11
+ from dbt.adapters.base.relation import (
12
+ BaseRelation,
13
+ RelationType,
14
+ SchemaSearchMap,
15
+ )
@@ -0,0 +1,166 @@
1
+ from dataclasses import dataclass
2
+ import re
3
+ from typing import Any, ClassVar, Dict, Optional
4
+
5
+ from dbt_common.exceptions import DbtRuntimeError
6
+
7
+
8
+ @dataclass
9
+ class Column:
10
+ # Note: This is automatically used by contract code
11
+ # No-op conversions (INTEGER => INT) have been removed.
12
+ # Any adapter that wants to take advantage of "translate_type"
13
+ # should create a ClassVar with the appropriate conversions.
14
+ TYPE_LABELS: ClassVar[Dict[str, str]] = {
15
+ "STRING": "TEXT",
16
+ }
17
+ column: str
18
+ dtype: str
19
+ char_size: Optional[int] = None
20
+ numeric_precision: Optional[Any] = None
21
+ numeric_scale: Optional[Any] = None
22
+
23
+ @classmethod
24
+ def translate_type(cls, dtype: str) -> str:
25
+ return cls.TYPE_LABELS.get(dtype.upper(), dtype)
26
+
27
+ @classmethod
28
+ def create(cls, name, label_or_dtype: str) -> "Column":
29
+ column_type = cls.translate_type(label_or_dtype)
30
+ return cls(name, column_type)
31
+
32
+ @property
33
+ def name(self) -> str:
34
+ return self.column
35
+
36
+ @property
37
+ def quoted(self) -> str:
38
+ return '"{}"'.format(self.column)
39
+
40
+ @property
41
+ def data_type(self) -> str:
42
+ if self.is_string():
43
+ return self.string_type(self.string_size())
44
+ elif self.is_numeric():
45
+ return self.numeric_type(self.dtype, self.numeric_precision, self.numeric_scale)
46
+ else:
47
+ return self.dtype
48
+
49
+ def is_string(self) -> bool:
50
+ return self.dtype.lower() in [
51
+ "text",
52
+ "character varying",
53
+ "character",
54
+ "varchar",
55
+ ]
56
+
57
+ def is_number(self):
58
+ return any([self.is_integer(), self.is_numeric(), self.is_float()])
59
+
60
+ def is_float(self):
61
+ return self.dtype.lower() in [
62
+ # floats
63
+ "real",
64
+ "float4",
65
+ "float",
66
+ "double precision",
67
+ "float8",
68
+ "double",
69
+ ]
70
+
71
+ def is_integer(self) -> bool:
72
+ return self.dtype.lower() in [
73
+ # real types
74
+ "smallint",
75
+ "integer",
76
+ "bigint",
77
+ "smallserial",
78
+ "serial",
79
+ "bigserial",
80
+ # aliases
81
+ "int2",
82
+ "int4",
83
+ "int8",
84
+ "serial2",
85
+ "serial4",
86
+ "serial8",
87
+ ]
88
+
89
+ def is_numeric(self) -> bool:
90
+ return self.dtype.lower() in ["numeric", "decimal"]
91
+
92
+ def string_size(self) -> int:
93
+ if not self.is_string():
94
+ raise DbtRuntimeError("Called string_size() on non-string field!")
95
+
96
+ if self.dtype == "text" or self.char_size is None:
97
+ # char_size should never be None. Handle it reasonably just in case
98
+ return 256
99
+ else:
100
+ return int(self.char_size)
101
+
102
+ def can_expand_to(self, other_column: "Column") -> bool:
103
+ """returns True if this column can be expanded to the size of the
104
+ other column"""
105
+ if not self.is_string() or not other_column.is_string():
106
+ return False
107
+
108
+ return other_column.string_size() > self.string_size()
109
+
110
+ def literal(self, value: Any) -> str:
111
+ return "{}::{}".format(value, self.data_type)
112
+
113
+ @classmethod
114
+ def string_type(cls, size: int) -> str:
115
+ return "character varying({})".format(size)
116
+
117
+ @classmethod
118
+ def numeric_type(cls, dtype: str, precision: Any, scale: Any) -> str:
119
+ # This could be decimal(...), numeric(...), number(...)
120
+ # Just use whatever was fed in here -- don't try to get too clever
121
+ if precision is None or scale is None:
122
+ return dtype
123
+ else:
124
+ return "{}({},{})".format(dtype, precision, scale)
125
+
126
+ def __repr__(self) -> str:
127
+ return "<Column {} ({})>".format(self.name, self.data_type)
128
+
129
+ @classmethod
130
+ def from_description(cls, name: str, raw_data_type: str) -> "Column":
131
+ match = re.match(r"([^(]+)(\([^)]+\))?", raw_data_type)
132
+ if match is None:
133
+ raise DbtRuntimeError(f'Could not interpret data type "{raw_data_type}"')
134
+ data_type, size_info = match.groups()
135
+ char_size = None
136
+ numeric_precision = None
137
+ numeric_scale = None
138
+ if size_info is not None:
139
+ # strip out the parentheses
140
+ size_info = size_info[1:-1]
141
+ parts = size_info.split(",")
142
+ if len(parts) == 1:
143
+ try:
144
+ char_size = int(parts[0])
145
+ except ValueError:
146
+ raise DbtRuntimeError(
147
+ f'Could not interpret data_type "{raw_data_type}": '
148
+ f'could not convert "{parts[0]}" to an integer'
149
+ )
150
+ elif len(parts) == 2:
151
+ try:
152
+ numeric_precision = int(parts[0])
153
+ except ValueError:
154
+ raise DbtRuntimeError(
155
+ f'Could not interpret data_type "{raw_data_type}": '
156
+ f'could not convert "{parts[0]}" to an integer'
157
+ )
158
+ try:
159
+ numeric_scale = int(parts[1])
160
+ except ValueError:
161
+ raise DbtRuntimeError(
162
+ f'Could not interpret data_type "{raw_data_type}": '
163
+ f'could not convert "{parts[1]}" to an integer'
164
+ )
165
+
166
+ return cls(name, data_type, char_size, numeric_precision, numeric_scale)
@@ -0,0 +1,426 @@
1
+ import abc
2
+ import os
3
+ import sys
4
+ from time import sleep
5
+ import traceback
6
+ from multiprocessing.context import SpawnContext
7
+ from multiprocessing.synchronize import RLock
8
+ from threading import get_ident
9
+ from typing import (
10
+ Any,
11
+ Callable,
12
+ ContextManager,
13
+ Dict,
14
+ Hashable,
15
+ Iterable,
16
+ List,
17
+ Optional,
18
+ Tuple,
19
+ Type,
20
+ Union,
21
+ )
22
+
23
+ import agate
24
+ from dbt_common.events.contextvars import get_node_info
25
+ from dbt_common.events.functions import fire_event
26
+ from dbt_common.exceptions import DbtInternalError, NotImplementedError
27
+ from dbt_common.utils import cast_to_str
28
+
29
+ from dbt.adapters.base.query_headers import MacroQueryStringSetter
30
+ from dbt.adapters.contracts.connection import (
31
+ AdapterRequiredConfig,
32
+ AdapterResponse,
33
+ Connection,
34
+ ConnectionState,
35
+ Identifier,
36
+ LazyHandle,
37
+ )
38
+ from dbt.adapters.events.logging import AdapterLogger
39
+ from dbt.adapters.events.types import (
40
+ ConnectionClosed,
41
+ ConnectionClosedInCleanup,
42
+ ConnectionLeftOpen,
43
+ ConnectionLeftOpenInCleanup,
44
+ ConnectionReused,
45
+ NewConnection,
46
+ Rollback,
47
+ RollbackFailed,
48
+ )
49
+ from dbt.adapters.exceptions import FailedToConnectError, InvalidConnectionError
50
+
51
+
52
+ SleepTime = Union[int, float] # As taken by time.sleep.
53
+ AdapterHandle = Any # Adapter connection handle objects can be any class.
54
+
55
+
56
+ class BaseConnectionManager(metaclass=abc.ABCMeta):
57
+ """Methods to implement:
58
+ - exception_handler
59
+ - cancel_open
60
+ - open
61
+ - begin
62
+ - commit
63
+ - clear_transaction
64
+ - execute
65
+
66
+ You must also set the 'TYPE' class attribute with a class-unique constant
67
+ string.
68
+ """
69
+
70
+ TYPE: str = NotImplemented
71
+
72
+ def __init__(self, profile: AdapterRequiredConfig, mp_context: SpawnContext) -> None:
73
+ self.profile = profile
74
+ self.thread_connections: Dict[Hashable, Connection] = {}
75
+ self.lock: RLock = mp_context.RLock()
76
+ self.query_header: Optional[MacroQueryStringSetter] = None
77
+
78
+ def set_query_header(self, query_header_context: Dict[str, Any]) -> None:
79
+ self.query_header = MacroQueryStringSetter(self.profile, query_header_context)
80
+
81
+ @staticmethod
82
+ def get_thread_identifier() -> Hashable:
83
+ # note that get_ident() may be re-used, but we should never experience
84
+ # that within a single process
85
+ return os.getpid(), get_ident()
86
+
87
+ def get_thread_connection(self) -> Connection:
88
+ key = self.get_thread_identifier()
89
+ with self.lock:
90
+ if key not in self.thread_connections:
91
+ raise InvalidConnectionError(key, list(self.thread_connections))
92
+ return self.thread_connections[key]
93
+
94
+ def set_thread_connection(self, conn: Connection) -> None:
95
+ key = self.get_thread_identifier()
96
+ if key in self.thread_connections:
97
+ raise DbtInternalError("In set_thread_connection, existing connection exists for {}")
98
+ self.thread_connections[key] = conn
99
+
100
+ def get_if_exists(self) -> Optional[Connection]:
101
+ key = self.get_thread_identifier()
102
+ with self.lock:
103
+ return self.thread_connections.get(key)
104
+
105
+ def clear_thread_connection(self) -> None:
106
+ key = self.get_thread_identifier()
107
+ with self.lock:
108
+ if key in self.thread_connections:
109
+ del self.thread_connections[key]
110
+
111
+ def clear_transaction(self) -> None:
112
+ """Clear any existing transactions."""
113
+ conn = self.get_thread_connection()
114
+ if conn is not None:
115
+ if conn.transaction_open:
116
+ self._rollback(conn)
117
+ self.begin()
118
+ self.commit()
119
+
120
+ def rollback_if_open(self) -> None:
121
+ conn = self.get_if_exists()
122
+ if conn is not None and conn.handle and conn.transaction_open:
123
+ self._rollback(conn)
124
+
125
+ @abc.abstractmethod
126
+ def exception_handler(self, sql: str) -> ContextManager:
127
+ """Create a context manager that handles exceptions caused by database
128
+ interactions.
129
+
130
+ :param str sql: The SQL string that the block inside the context
131
+ manager is executing.
132
+ :return: A context manager that handles exceptions raised by the
133
+ underlying database.
134
+ """
135
+ raise NotImplementedError("`exception_handler` is not implemented for this adapter!")
136
+
137
+ def set_connection_name(self, name: Optional[str] = None) -> Connection:
138
+ """Called by 'acquire_connection' in BaseAdapter, which is called by
139
+ 'connection_named'.
140
+ Creates a connection for this thread if one doesn't already
141
+ exist, and will rename an existing connection."""
142
+
143
+ conn_name: str = "master" if name is None else name
144
+
145
+ # Get a connection for this thread
146
+ conn = self.get_if_exists()
147
+
148
+ if conn and conn.name == conn_name and conn.state == "open":
149
+ # Found a connection and nothing to do, so just return it
150
+ return conn
151
+
152
+ if conn is None:
153
+ # Create a new connection
154
+ conn = Connection(
155
+ type=Identifier(self.TYPE),
156
+ name=conn_name,
157
+ state=ConnectionState.INIT,
158
+ transaction_open=False,
159
+ handle=None,
160
+ credentials=self.profile.credentials,
161
+ )
162
+ conn.handle = LazyHandle(self.open)
163
+ # Add the connection to thread_connections for this thread
164
+ self.set_thread_connection(conn)
165
+ fire_event(
166
+ NewConnection(conn_name=conn_name, conn_type=self.TYPE, node_info=get_node_info())
167
+ )
168
+ else: # existing connection either wasn't open or didn't have the right name
169
+ if conn.state != "open":
170
+ conn.handle = LazyHandle(self.open)
171
+ if conn.name != conn_name:
172
+ orig_conn_name: str = conn.name or ""
173
+ conn.name = conn_name
174
+ fire_event(ConnectionReused(orig_conn_name=orig_conn_name, conn_name=conn_name))
175
+
176
+ return conn
177
+
178
+ @classmethod
179
+ def retry_connection(
180
+ cls,
181
+ connection: Connection,
182
+ connect: Callable[[], AdapterHandle],
183
+ logger: AdapterLogger,
184
+ retryable_exceptions: Iterable[Type[Exception]],
185
+ retry_limit: int = 1,
186
+ retry_timeout: Union[Callable[[int], SleepTime], SleepTime] = 1,
187
+ _attempts: int = 0,
188
+ ) -> Connection:
189
+ """Given a Connection, set its handle by calling connect.
190
+
191
+ The calls to connect will be retried up to retry_limit times to deal with transient
192
+ connection errors. By default, one retry will be attempted if retryable_exceptions is set.
193
+
194
+ :param Connection connection: An instance of a Connection that needs a handle to be set,
195
+ usually when attempting to open it.
196
+ :param connect: A callable that returns the appropiate connection handle for a
197
+ given adapter. This callable will be retried retry_limit times if a subclass of any
198
+ Exception in retryable_exceptions is raised by connect.
199
+ :type connect: Callable[[], AdapterHandle]
200
+ :param AdapterLogger logger: A logger to emit messages on retry attempts or errors. When
201
+ handling expected errors, we call debug, and call warning on unexpected errors or when
202
+ all retry attempts have been exhausted.
203
+ :param retryable_exceptions: An iterable of exception classes that if raised by
204
+ connect should trigger a retry.
205
+ :type retryable_exceptions: Iterable[Type[Exception]]
206
+ :param int retry_limit: How many times to retry the call to connect. If this limit
207
+ is exceeded before a successful call, a FailedToConnectError will be raised.
208
+ Must be non-negative.
209
+ :param retry_timeout: Time to wait between attempts to connect. Can also take a
210
+ Callable that takes the number of attempts so far, beginning at 0, and returns an int
211
+ or float to be passed to time.sleep.
212
+ :type retry_timeout: Union[Callable[[int], SleepTime], SleepTime] = 1
213
+ :param int _attempts: Parameter used to keep track of the number of attempts in calling the
214
+ connect function across recursive calls. Passed as an argument to retry_timeout if it
215
+ is a Callable. This parameter should not be set by the initial caller.
216
+ :raises dbt.adapters.exceptions.FailedToConnectError: Upon exhausting all retry attempts without
217
+ successfully acquiring a handle.
218
+ :return: The given connection with its appropriate state and handle attributes set
219
+ depending on whether we successfully acquired a handle or not.
220
+ """
221
+ timeout = retry_timeout(_attempts) if callable(retry_timeout) else retry_timeout
222
+ if timeout < 0:
223
+ raise FailedToConnectError(
224
+ "retry_timeout cannot be negative or return a negative time."
225
+ )
226
+
227
+ if retry_limit < 0 or retry_limit > sys.getrecursionlimit():
228
+ # This guard is not perfect others may add to the recursion limit (e.g. built-ins).
229
+ connection.handle = None
230
+ connection.state = ConnectionState.FAIL
231
+ raise FailedToConnectError("retry_limit cannot be negative")
232
+
233
+ try:
234
+ connection.handle = connect()
235
+ connection.state = ConnectionState.OPEN
236
+ return connection
237
+
238
+ except tuple(retryable_exceptions) as e:
239
+ if retry_limit <= 0:
240
+ connection.handle = None
241
+ connection.state = ConnectionState.FAIL
242
+ raise FailedToConnectError(str(e))
243
+
244
+ logger.debug(
245
+ f"Got a retryable error when attempting to open a {cls.TYPE} connection.\n"
246
+ f"{retry_limit} attempts remaining. Retrying in {timeout} seconds.\n"
247
+ f"Error:\n{e}"
248
+ )
249
+
250
+ sleep(timeout)
251
+ return cls.retry_connection(
252
+ connection=connection,
253
+ connect=connect,
254
+ logger=logger,
255
+ retry_limit=retry_limit - 1,
256
+ retry_timeout=retry_timeout,
257
+ retryable_exceptions=retryable_exceptions,
258
+ _attempts=_attempts + 1,
259
+ )
260
+
261
+ except Exception as e:
262
+ connection.handle = None
263
+ connection.state = ConnectionState.FAIL
264
+ raise FailedToConnectError(str(e))
265
+
266
+ @abc.abstractmethod
267
+ def cancel_open(self) -> Optional[List[str]]:
268
+ """Cancel all open connections on the adapter. (passable)"""
269
+ raise NotImplementedError("`cancel_open` is not implemented for this adapter!")
270
+
271
+ @classmethod
272
+ @abc.abstractmethod
273
+ def open(cls, connection: Connection) -> Connection:
274
+ """Open the given connection on the adapter and return it.
275
+
276
+ This may mutate the given connection (in particular, its state and its
277
+ handle).
278
+
279
+ This should be thread-safe, or hold the lock if necessary. The given
280
+ connection should not be in either in_use or available.
281
+ """
282
+ raise NotImplementedError("`open` is not implemented for this adapter!")
283
+
284
+ def release(self) -> None:
285
+ with self.lock:
286
+ conn = self.get_if_exists()
287
+ if conn is None:
288
+ return
289
+
290
+ try:
291
+ # always close the connection. close() calls _rollback() if there
292
+ # is an open transaction
293
+ self.close(conn)
294
+ except Exception:
295
+ # if rollback or close failed, remove our busted connection
296
+ self.clear_thread_connection()
297
+ raise
298
+
299
+ def cleanup_all(self) -> None:
300
+ with self.lock:
301
+ for connection in self.thread_connections.values():
302
+ if connection.state not in {"closed", "init"}:
303
+ fire_event(ConnectionLeftOpenInCleanup(conn_name=cast_to_str(connection.name)))
304
+ else:
305
+ fire_event(ConnectionClosedInCleanup(conn_name=cast_to_str(connection.name)))
306
+ self.close(connection)
307
+
308
+ # garbage collect these connections
309
+ self.thread_connections.clear()
310
+
311
+ @abc.abstractmethod
312
+ def begin(self) -> None:
313
+ """Begin a transaction. (passable)"""
314
+ raise NotImplementedError("`begin` is not implemented for this adapter!")
315
+
316
+ @abc.abstractmethod
317
+ def commit(self) -> None:
318
+ """Commit a transaction. (passable)"""
319
+ raise NotImplementedError("`commit` is not implemented for this adapter!")
320
+
321
+ @classmethod
322
+ def _rollback_handle(cls, connection: Connection) -> None:
323
+ """Perform the actual rollback operation."""
324
+ try:
325
+ connection.handle.rollback()
326
+ except Exception:
327
+ fire_event(
328
+ RollbackFailed(
329
+ conn_name=cast_to_str(connection.name),
330
+ exc_info=traceback.format_exc(),
331
+ node_info=get_node_info(),
332
+ )
333
+ )
334
+
335
+ @classmethod
336
+ def _close_handle(cls, connection: Connection) -> None:
337
+ """Perform the actual close operation."""
338
+ # On windows, sometimes connection handles don't have a close() attr.
339
+ if hasattr(connection.handle, "close"):
340
+ fire_event(
341
+ ConnectionClosed(conn_name=cast_to_str(connection.name), node_info=get_node_info())
342
+ )
343
+ connection.handle.close()
344
+ else:
345
+ fire_event(
346
+ ConnectionLeftOpen(
347
+ conn_name=cast_to_str(connection.name), node_info=get_node_info()
348
+ )
349
+ )
350
+
351
+ @classmethod
352
+ def _rollback(cls, connection: Connection) -> None:
353
+ """Roll back the given connection."""
354
+ if connection.transaction_open is False:
355
+ raise DbtInternalError(
356
+ f"Tried to rollback transaction on connection "
357
+ f'"{connection.name}", but it does not have one open!'
358
+ )
359
+
360
+ fire_event(Rollback(conn_name=cast_to_str(connection.name), node_info=get_node_info()))
361
+ cls._rollback_handle(connection)
362
+
363
+ connection.transaction_open = False
364
+
365
+ @classmethod
366
+ def close(cls, connection: Connection) -> Connection:
367
+ # if the connection is in closed or init, there's nothing to do
368
+ if connection.state in {ConnectionState.CLOSED, ConnectionState.INIT}:
369
+ return connection
370
+
371
+ if connection.transaction_open and connection.handle:
372
+ fire_event(Rollback(conn_name=cast_to_str(connection.name), node_info=get_node_info()))
373
+ cls._rollback_handle(connection)
374
+ connection.transaction_open = False
375
+
376
+ cls._close_handle(connection)
377
+ connection.state = ConnectionState.CLOSED
378
+
379
+ return connection
380
+
381
+ def commit_if_has_connection(self) -> None:
382
+ """If the named connection exists, commit the current transaction."""
383
+ connection = self.get_if_exists()
384
+ if connection:
385
+ self.commit()
386
+
387
+ def _add_query_comment(self, sql: str) -> str:
388
+ if self.query_header is None:
389
+ return sql
390
+ return self.query_header.add(sql)
391
+
392
+ @abc.abstractmethod
393
+ def execute(
394
+ self,
395
+ sql: str,
396
+ auto_begin: bool = False,
397
+ fetch: bool = False,
398
+ limit: Optional[int] = None,
399
+ ) -> Tuple[AdapterResponse, agate.Table]:
400
+ """Execute the given SQL.
401
+
402
+ :param str sql: The sql to execute.
403
+ :param bool auto_begin: If set, and dbt is not currently inside a
404
+ transaction, automatically begin one.
405
+ :param bool fetch: If set, fetch results.
406
+ :param int limit: If set, limits the result set
407
+ :return: A tuple of the query status and results (empty if fetch=False).
408
+ :rtype: Tuple[AdapterResponse, agate.Table]
409
+ """
410
+ raise NotImplementedError("`execute` is not implemented for this adapter!")
411
+
412
+ def add_select_query(self, sql: str) -> Tuple[Connection, Any]:
413
+ """
414
+ This was added here because base.impl.BaseAdapter.get_column_schema_from_query expects it to be here.
415
+ That method wouldn't work unless the adapter used sql.impl.SQLAdapter, sql.connections.SQLConnectionManager
416
+ or defined this method on <Adapter>ConnectionManager before passing it in to <Adapter>Adapter.
417
+
418
+ See https://github.com/dbt-labs/dbt-core/issues/8396 for more information.
419
+ """
420
+ raise NotImplementedError("`add_select_query` is not implemented for this adapter!")
421
+
422
+ @classmethod
423
+ def data_type_code_to_name(cls, type_code: Union[int, str]) -> str:
424
+ """Get the string representation of the data type from the type_code."""
425
+ # https://peps.python.org/pep-0249/#type-objects
426
+ raise NotImplementedError("`data_type_code_to_name` is not implemented for this adapter!")