dbt-adapters 1.22.2__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.
Files changed (173) hide show
  1. dbt/adapters/__about__.py +1 -0
  2. dbt/adapters/__init__.py +8 -0
  3. dbt/adapters/base/README.md +13 -0
  4. dbt/adapters/base/__init__.py +16 -0
  5. dbt/adapters/base/column.py +173 -0
  6. dbt/adapters/base/connections.py +429 -0
  7. dbt/adapters/base/impl.py +2036 -0
  8. dbt/adapters/base/meta.py +150 -0
  9. dbt/adapters/base/plugin.py +32 -0
  10. dbt/adapters/base/query_headers.py +106 -0
  11. dbt/adapters/base/relation.py +648 -0
  12. dbt/adapters/cache.py +521 -0
  13. dbt/adapters/capability.py +63 -0
  14. dbt/adapters/catalogs/__init__.py +14 -0
  15. dbt/adapters/catalogs/_client.py +54 -0
  16. dbt/adapters/catalogs/_constants.py +1 -0
  17. dbt/adapters/catalogs/_exceptions.py +39 -0
  18. dbt/adapters/catalogs/_integration.py +113 -0
  19. dbt/adapters/clients/__init__.py +0 -0
  20. dbt/adapters/clients/jinja.py +24 -0
  21. dbt/adapters/contracts/__init__.py +0 -0
  22. dbt/adapters/contracts/connection.py +229 -0
  23. dbt/adapters/contracts/macros.py +11 -0
  24. dbt/adapters/contracts/relation.py +160 -0
  25. dbt/adapters/events/README.md +51 -0
  26. dbt/adapters/events/__init__.py +0 -0
  27. dbt/adapters/events/adapter_types_pb2.py +2 -0
  28. dbt/adapters/events/base_types.py +36 -0
  29. dbt/adapters/events/logging.py +83 -0
  30. dbt/adapters/events/types.py +436 -0
  31. dbt/adapters/exceptions/__init__.py +40 -0
  32. dbt/adapters/exceptions/alias.py +24 -0
  33. dbt/adapters/exceptions/cache.py +68 -0
  34. dbt/adapters/exceptions/compilation.py +269 -0
  35. dbt/adapters/exceptions/connection.py +16 -0
  36. dbt/adapters/exceptions/database.py +51 -0
  37. dbt/adapters/factory.py +264 -0
  38. dbt/adapters/protocol.py +150 -0
  39. dbt/adapters/py.typed +0 -0
  40. dbt/adapters/record/__init__.py +2 -0
  41. dbt/adapters/record/base.py +291 -0
  42. dbt/adapters/record/cursor/cursor.py +69 -0
  43. dbt/adapters/record/cursor/description.py +37 -0
  44. dbt/adapters/record/cursor/execute.py +39 -0
  45. dbt/adapters/record/cursor/fetchall.py +69 -0
  46. dbt/adapters/record/cursor/fetchmany.py +23 -0
  47. dbt/adapters/record/cursor/fetchone.py +23 -0
  48. dbt/adapters/record/cursor/rowcount.py +23 -0
  49. dbt/adapters/record/handle.py +55 -0
  50. dbt/adapters/record/serialization.py +115 -0
  51. dbt/adapters/reference_keys.py +39 -0
  52. dbt/adapters/relation_configs/README.md +25 -0
  53. dbt/adapters/relation_configs/__init__.py +12 -0
  54. dbt/adapters/relation_configs/config_base.py +46 -0
  55. dbt/adapters/relation_configs/config_change.py +26 -0
  56. dbt/adapters/relation_configs/config_validation.py +57 -0
  57. dbt/adapters/sql/__init__.py +2 -0
  58. dbt/adapters/sql/connections.py +263 -0
  59. dbt/adapters/sql/impl.py +286 -0
  60. dbt/adapters/utils.py +69 -0
  61. dbt/include/__init__.py +3 -0
  62. dbt/include/global_project/__init__.py +4 -0
  63. dbt/include/global_project/dbt_project.yml +7 -0
  64. dbt/include/global_project/docs/overview.md +43 -0
  65. dbt/include/global_project/macros/adapters/apply_grants.sql +167 -0
  66. dbt/include/global_project/macros/adapters/columns.sql +144 -0
  67. dbt/include/global_project/macros/adapters/freshness.sql +32 -0
  68. dbt/include/global_project/macros/adapters/indexes.sql +41 -0
  69. dbt/include/global_project/macros/adapters/metadata.sql +105 -0
  70. dbt/include/global_project/macros/adapters/persist_docs.sql +33 -0
  71. dbt/include/global_project/macros/adapters/relation.sql +84 -0
  72. dbt/include/global_project/macros/adapters/schema.sql +20 -0
  73. dbt/include/global_project/macros/adapters/show.sql +26 -0
  74. dbt/include/global_project/macros/adapters/timestamps.sql +52 -0
  75. dbt/include/global_project/macros/adapters/validate_sql.sql +10 -0
  76. dbt/include/global_project/macros/etc/datetime.sql +62 -0
  77. dbt/include/global_project/macros/etc/statement.sql +52 -0
  78. dbt/include/global_project/macros/generic_test_sql/accepted_values.sql +27 -0
  79. dbt/include/global_project/macros/generic_test_sql/not_null.sql +9 -0
  80. dbt/include/global_project/macros/generic_test_sql/relationships.sql +23 -0
  81. dbt/include/global_project/macros/generic_test_sql/unique.sql +12 -0
  82. dbt/include/global_project/macros/get_custom_name/get_custom_alias.sql +36 -0
  83. dbt/include/global_project/macros/get_custom_name/get_custom_database.sql +32 -0
  84. dbt/include/global_project/macros/get_custom_name/get_custom_schema.sql +60 -0
  85. dbt/include/global_project/macros/materializations/configs.sql +21 -0
  86. dbt/include/global_project/macros/materializations/functions/aggregate.sql +65 -0
  87. dbt/include/global_project/macros/materializations/functions/function.sql +20 -0
  88. dbt/include/global_project/macros/materializations/functions/helpers.sql +20 -0
  89. dbt/include/global_project/macros/materializations/functions/scalar.sql +69 -0
  90. dbt/include/global_project/macros/materializations/hooks.sql +35 -0
  91. dbt/include/global_project/macros/materializations/models/clone/can_clone_table.sql +7 -0
  92. dbt/include/global_project/macros/materializations/models/clone/clone.sql +67 -0
  93. dbt/include/global_project/macros/materializations/models/clone/create_or_replace_clone.sql +7 -0
  94. dbt/include/global_project/macros/materializations/models/incremental/column_helpers.sql +80 -0
  95. dbt/include/global_project/macros/materializations/models/incremental/incremental.sql +99 -0
  96. dbt/include/global_project/macros/materializations/models/incremental/is_incremental.sql +13 -0
  97. dbt/include/global_project/macros/materializations/models/incremental/merge.sql +120 -0
  98. dbt/include/global_project/macros/materializations/models/incremental/on_schema_change.sql +159 -0
  99. dbt/include/global_project/macros/materializations/models/incremental/strategies.sql +92 -0
  100. dbt/include/global_project/macros/materializations/models/materialized_view.sql +121 -0
  101. dbt/include/global_project/macros/materializations/models/table.sql +64 -0
  102. dbt/include/global_project/macros/materializations/models/view.sql +72 -0
  103. dbt/include/global_project/macros/materializations/seeds/helpers.sql +128 -0
  104. dbt/include/global_project/macros/materializations/seeds/seed.sql +60 -0
  105. dbt/include/global_project/macros/materializations/snapshots/helpers.sql +345 -0
  106. dbt/include/global_project/macros/materializations/snapshots/snapshot.sql +109 -0
  107. dbt/include/global_project/macros/materializations/snapshots/snapshot_merge.sql +34 -0
  108. dbt/include/global_project/macros/materializations/snapshots/strategies.sql +184 -0
  109. dbt/include/global_project/macros/materializations/tests/helpers.sql +44 -0
  110. dbt/include/global_project/macros/materializations/tests/test.sql +66 -0
  111. dbt/include/global_project/macros/materializations/tests/unit.sql +40 -0
  112. dbt/include/global_project/macros/materializations/tests/where_subquery.sql +15 -0
  113. dbt/include/global_project/macros/python_model/python.sql +114 -0
  114. dbt/include/global_project/macros/relations/column/columns_spec_ddl.sql +89 -0
  115. dbt/include/global_project/macros/relations/create.sql +23 -0
  116. dbt/include/global_project/macros/relations/create_backup.sql +17 -0
  117. dbt/include/global_project/macros/relations/create_intermediate.sql +17 -0
  118. dbt/include/global_project/macros/relations/drop.sql +41 -0
  119. dbt/include/global_project/macros/relations/drop_backup.sql +14 -0
  120. dbt/include/global_project/macros/relations/materialized_view/alter.sql +55 -0
  121. dbt/include/global_project/macros/relations/materialized_view/create.sql +10 -0
  122. dbt/include/global_project/macros/relations/materialized_view/drop.sql +14 -0
  123. dbt/include/global_project/macros/relations/materialized_view/refresh.sql +9 -0
  124. dbt/include/global_project/macros/relations/materialized_view/rename.sql +10 -0
  125. dbt/include/global_project/macros/relations/materialized_view/replace.sql +10 -0
  126. dbt/include/global_project/macros/relations/rename.sql +35 -0
  127. dbt/include/global_project/macros/relations/rename_intermediate.sql +14 -0
  128. dbt/include/global_project/macros/relations/replace.sql +50 -0
  129. dbt/include/global_project/macros/relations/schema.sql +8 -0
  130. dbt/include/global_project/macros/relations/table/create.sql +60 -0
  131. dbt/include/global_project/macros/relations/table/drop.sql +14 -0
  132. dbt/include/global_project/macros/relations/table/rename.sql +10 -0
  133. dbt/include/global_project/macros/relations/table/replace.sql +10 -0
  134. dbt/include/global_project/macros/relations/view/create.sql +27 -0
  135. dbt/include/global_project/macros/relations/view/drop.sql +14 -0
  136. dbt/include/global_project/macros/relations/view/rename.sql +10 -0
  137. dbt/include/global_project/macros/relations/view/replace.sql +66 -0
  138. dbt/include/global_project/macros/unit_test_sql/get_fixture_sql.sql +107 -0
  139. dbt/include/global_project/macros/utils/any_value.sql +9 -0
  140. dbt/include/global_project/macros/utils/array_append.sql +8 -0
  141. dbt/include/global_project/macros/utils/array_concat.sql +7 -0
  142. dbt/include/global_project/macros/utils/array_construct.sql +12 -0
  143. dbt/include/global_project/macros/utils/bool_or.sql +9 -0
  144. dbt/include/global_project/macros/utils/cast.sql +7 -0
  145. dbt/include/global_project/macros/utils/cast_bool_to_text.sql +7 -0
  146. dbt/include/global_project/macros/utils/concat.sql +7 -0
  147. dbt/include/global_project/macros/utils/data_types.sql +129 -0
  148. dbt/include/global_project/macros/utils/date.sql +10 -0
  149. dbt/include/global_project/macros/utils/date_spine.sql +75 -0
  150. dbt/include/global_project/macros/utils/date_trunc.sql +7 -0
  151. dbt/include/global_project/macros/utils/dateadd.sql +14 -0
  152. dbt/include/global_project/macros/utils/datediff.sql +14 -0
  153. dbt/include/global_project/macros/utils/equals.sql +14 -0
  154. dbt/include/global_project/macros/utils/escape_single_quotes.sql +8 -0
  155. dbt/include/global_project/macros/utils/except.sql +9 -0
  156. dbt/include/global_project/macros/utils/generate_series.sql +53 -0
  157. dbt/include/global_project/macros/utils/hash.sql +7 -0
  158. dbt/include/global_project/macros/utils/intersect.sql +9 -0
  159. dbt/include/global_project/macros/utils/last_day.sql +15 -0
  160. dbt/include/global_project/macros/utils/length.sql +11 -0
  161. dbt/include/global_project/macros/utils/listagg.sql +30 -0
  162. dbt/include/global_project/macros/utils/literal.sql +7 -0
  163. dbt/include/global_project/macros/utils/position.sql +11 -0
  164. dbt/include/global_project/macros/utils/replace.sql +14 -0
  165. dbt/include/global_project/macros/utils/right.sql +12 -0
  166. dbt/include/global_project/macros/utils/safe_cast.sql +9 -0
  167. dbt/include/global_project/macros/utils/split_part.sql +26 -0
  168. dbt/include/global_project/tests/generic/builtin.sql +30 -0
  169. dbt/include/py.typed +0 -0
  170. dbt_adapters-1.22.2.dist-info/METADATA +124 -0
  171. dbt_adapters-1.22.2.dist-info/RECORD +173 -0
  172. dbt_adapters-1.22.2.dist-info/WHEEL +4 -0
  173. dbt_adapters-1.22.2.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,23 @@
1
+ import dataclasses
2
+ from typing import Any
3
+
4
+ from dbt_common.record import Record, Recorder
5
+
6
+
7
+ @dataclasses.dataclass
8
+ class CursorFetchOneParams:
9
+ connection_name: str
10
+
11
+
12
+ @dataclasses.dataclass
13
+ class CursorFetchOneResult:
14
+ result: Any
15
+
16
+
17
+ @Recorder.register_record_type
18
+ class CursorFetchOneRecord(Record):
19
+ """Implements record/replay support for the cursor.fetchone() method."""
20
+
21
+ params_cls = CursorFetchOneParams
22
+ result_cls = CursorFetchOneResult
23
+ group = "Database"
@@ -0,0 +1,23 @@
1
+ import dataclasses
2
+ from typing import Optional
3
+
4
+ from dbt_common.record import Record, Recorder
5
+
6
+
7
+ @dataclasses.dataclass
8
+ class CursorGetRowCountParams:
9
+ connection_name: str
10
+
11
+
12
+ @dataclasses.dataclass
13
+ class CursorGetRowCountResult:
14
+ rowcount: Optional[int]
15
+
16
+
17
+ @Recorder.register_record_type
18
+ class CursorGetRowCountRecord(Record):
19
+ """Implements record/replay support for the cursor.rowcount property."""
20
+
21
+ params_cls = CursorGetRowCountParams
22
+ result_cls = CursorGetRowCountResult
23
+ group = "Database"
@@ -0,0 +1,55 @@
1
+ from typing import Any
2
+
3
+ from dbt_common.events.base_types import BaseEvent
4
+ from dbt_common.events.functions import fire_event
5
+ from dbt_common.events.types import RecordReplayIssue
6
+
7
+ from dbt.adapters.contracts.connection import Connection
8
+ from dbt.adapters.record.cursor.cursor import RecordReplayCursor
9
+
10
+
11
+ class RecordReplayHandle:
12
+ """A proxy object used for record/replay modes. What adapters call a
13
+ 'handle' is typically a native database connection, but should not be
14
+ confused with the Connection protocol, which is a dbt-adapters concept.
15
+
16
+ Currently, the only function of the handle proxy is to provide a record/replay
17
+ aware cursor object when cursor() is called."""
18
+
19
+ def __init__(self, native_handle: Any, connection: Connection) -> None:
20
+ self.native_handle = native_handle
21
+ self.connection = connection
22
+
23
+ def cursor(self) -> Any:
24
+ # The native handle could be None if we are in replay mode, because no
25
+ # actual database access should be performed in that mode.
26
+ cursor = None if self.native_handle is None else self.native_handle.cursor()
27
+ return RecordReplayCursor(cursor, self.connection)
28
+
29
+ def commit(self):
30
+ self.native_handle.commit()
31
+
32
+ def rollback(self):
33
+ self.native_handle.rollback()
34
+
35
+ def close(self):
36
+ self.native_handle.close()
37
+
38
+ def get_backend_pid(self):
39
+ return self.native_handle.get_backend_pid()
40
+
41
+ @property
42
+ def closed(self):
43
+ return self.native_handle.closed
44
+
45
+ def _fire_event(self, evt: BaseEvent) -> None:
46
+ """Wraps fire_event for easier test mocking."""
47
+ fire_event(evt)
48
+
49
+ def __getattr__(self, name: str) -> Any:
50
+ self._fire_event(
51
+ RecordReplayIssue(
52
+ msg=f"Unexpected attribute '{name}' accessed on {self.__class__.__name__}"
53
+ )
54
+ )
55
+ return getattr(self.native_handle, name)
@@ -0,0 +1,115 @@
1
+ import dataclasses
2
+ from datetime import datetime, date
3
+ from decimal import Decimal
4
+ from typing import Any, Dict, TYPE_CHECKING, List, Union, Optional
5
+
6
+ from dbt_common.record import get_record_row_limit_from_env
7
+
8
+ RECORDER_ROW_LIMIT: Optional[int] = get_record_row_limit_from_env()
9
+
10
+ if TYPE_CHECKING:
11
+ from agate import Table
12
+ from dbt.adapters.base.relation import BaseRelation
13
+ from dbt.adapters.base.column import Column as BaseColumn
14
+
15
+
16
+ def _column_filter(val: Any) -> Any:
17
+ return (
18
+ float(val)
19
+ if isinstance(val, Decimal)
20
+ else (
21
+ str(val)
22
+ if isinstance(val, datetime)
23
+ else str(val) if isinstance(val, date) else str(val)
24
+ )
25
+ )
26
+
27
+
28
+ def serialize_agate_table(table: "Table") -> Dict[str, Any]:
29
+ rows = []
30
+
31
+ if RECORDER_ROW_LIMIT and len(table.rows) > RECORDER_ROW_LIMIT:
32
+ rows = [
33
+ [
34
+ f"Recording Error: Agate table contains {len(table.rows)} rows, maximum is {RECORDER_ROW_LIMIT} rows."
35
+ ]
36
+ ]
37
+ else:
38
+ for row in table.rows:
39
+ row = list(map(_column_filter, row))
40
+ rows.append(row)
41
+
42
+ return {
43
+ "column_names": table.column_names,
44
+ "column_types": [t.__class__.__name__ for t in table.column_types],
45
+ "rows": rows,
46
+ }
47
+
48
+
49
+ def serialize_bindings(bindings: Any) -> Union[None, List[Any], str]:
50
+ if bindings is None:
51
+ return None
52
+ elif isinstance(bindings, list):
53
+ return list(map(_column_filter, bindings))
54
+ else:
55
+ return "bindings"
56
+
57
+
58
+ def serialize_base_relation(relation: "BaseRelation") -> Dict[str, Any]:
59
+ """Serialize a BaseRelation object for recording."""
60
+ return relation.to_dict(omit_none=True)
61
+
62
+
63
+ def serialize_base_relation_list(relations: List["BaseRelation"]) -> List[Dict[str, Any]]:
64
+ """Serialize a list of BaseRelation objects for recording."""
65
+ if RECORDER_ROW_LIMIT and len(relations) > RECORDER_ROW_LIMIT:
66
+ return [
67
+ {
68
+ "error": f"Recording Error: List of BaseRelation objects contains {len(relations)} objects, maximum is {RECORDER_ROW_LIMIT} objects."
69
+ }
70
+ ]
71
+ else:
72
+ return [serialize_base_relation(relation) for relation in relations]
73
+
74
+
75
+ def deserialize_base_relation(relation_dict: Dict[str, Any]) -> "BaseRelation":
76
+ """Deserialize a BaseRelation object from a dictionary."""
77
+ from dbt.adapters.base.relation import BaseRelation
78
+
79
+ return BaseRelation.from_dict(relation_dict)
80
+
81
+
82
+ def deserialize_base_relation_list(relations_data: List[Dict[str, Any]]) -> List["BaseRelation"]:
83
+ """Deserialize a list of BaseRelation objects from dictionaries."""
84
+ return [deserialize_base_relation(relation_dict) for relation_dict in relations_data]
85
+
86
+
87
+ def serialize_base_column_list(columns: List["BaseColumn"]) -> List[Dict[str, Any]]:
88
+ if RECORDER_ROW_LIMIT and len(columns) > RECORDER_ROW_LIMIT:
89
+ return [
90
+ {
91
+ "error": f"Recording Error: List of BaseColumn objects contains {len(columns)} objects, maximum is {RECORDER_ROW_LIMIT} objects."
92
+ }
93
+ ]
94
+ else:
95
+ return [serialize_base_column(column) for column in columns]
96
+
97
+
98
+ def serialize_base_column(column: "BaseColumn") -> Dict[str, Any]:
99
+ column_dict = dataclasses.asdict(column)
100
+ return column_dict
101
+
102
+
103
+ def deserialize_base_column_list(columns_data: List[Dict[str, Any]]) -> List["BaseColumn"]:
104
+ return [deserialize_base_column(column_dict) for column_dict in columns_data]
105
+
106
+
107
+ def deserialize_base_column(column_dict: Dict[str, Any]) -> "BaseColumn":
108
+ # Only include fields that are present in the base column class
109
+ params_dict = {
110
+ field.name: column_dict[field.name]
111
+ for field in dataclasses.fields(BaseColumn)
112
+ if field.name in column_dict
113
+ }
114
+
115
+ return BaseColumn(**params_dict)
@@ -0,0 +1,39 @@
1
+ # this module exists to resolve circular imports with the events module
2
+ from collections import namedtuple
3
+ from typing import Any, Optional
4
+
5
+
6
+ _ReferenceKey = namedtuple("_ReferenceKey", "database schema identifier")
7
+
8
+
9
+ def lowercase(value: Optional[str]) -> Optional[str]:
10
+ if value is None:
11
+ return None
12
+ else:
13
+ return value.lower()
14
+
15
+
16
+ # For backwards compatibility. New code should use _make_ref_key
17
+ def _make_key(relation: Any) -> _ReferenceKey:
18
+ return _make_ref_key(relation)
19
+
20
+
21
+ def _make_ref_key(relation: Any) -> _ReferenceKey:
22
+ """
23
+ Make _ReferenceKeys with lowercase values for the cache,
24
+ so we don't have to keep track of quoting
25
+ """
26
+ # databases and schemas can both be None
27
+ return _ReferenceKey(
28
+ lowercase(relation.database),
29
+ lowercase(relation.schema),
30
+ lowercase(relation.identifier),
31
+ )
32
+
33
+
34
+ def _make_ref_key_dict(relation: Any):
35
+ return {
36
+ "database": relation.database,
37
+ "schema": relation.schema,
38
+ "identifier": relation.identifier,
39
+ }
@@ -0,0 +1,25 @@
1
+ # RelationConfig
2
+ This package serves as an initial abstraction for managing the inspection of existing relations and determining
3
+ changes on those relations. It arose from the materialized view work and is currently only supporting
4
+ materialized views for Postgres and Redshift as well as dynamic tables for Snowflake. There are three main
5
+ classes in this package.
6
+
7
+ ## RelationConfigBase
8
+ This is a very small class that only has a `from_dict()` method and a default `NotImplementedError()`. At some
9
+ point this could be replaced by a more robust framework, like `mashumaro` or `pydantic`.
10
+
11
+ ## RelationConfigChange
12
+ This class inherits from `RelationConfigBase` ; however, this can be thought of as a separate class. The subclassing
13
+ merely points to the idea that both classes would likely inherit from the same class in a `mashumaro` or
14
+ `pydantic` implementation. This class is much more restricted in attribution. It should really only
15
+ ever need an `action` and a `context`. This can be though of as being analogous to a web request. You need to
16
+ know what you're doing (`action`: 'create' = GET, 'drop' = DELETE, etc.) and the information (`context`) needed
17
+ to make the change. In our scenarios, the context tends to be an instance of `RelationConfigBase` corresponding
18
+ to the new state.
19
+
20
+ ## RelationConfigValidationMixin
21
+ This mixin provides optional validation mechanics that can be applied to either `RelationConfigBase` or
22
+ `RelationConfigChange` subclasses. A validation rule is a combination of a `validation_check`, something
23
+ that should evaluate to `True`, and an optional `validation_error`, an instance of `DbtRuntimeError`
24
+ that should be raised in the event the `validation_check` fails. While optional, it's recommended that
25
+ the `validation_error` be provided for clearer transparency to the end user.
@@ -0,0 +1,12 @@
1
+ from dbt.adapters.relation_configs.config_base import (
2
+ RelationConfigBase,
3
+ RelationResults,
4
+ )
5
+ from dbt.adapters.relation_configs.config_change import (
6
+ RelationConfigChange,
7
+ RelationConfigChangeAction,
8
+ )
9
+ from dbt.adapters.relation_configs.config_validation import (
10
+ RelationConfigValidationMixin,
11
+ RelationConfigValidationRule,
12
+ )
@@ -0,0 +1,46 @@
1
+ from dataclasses import dataclass
2
+ from typing import Dict, Union, TYPE_CHECKING
3
+
4
+ from dbt_common.utils import filter_null_values
5
+
6
+ if TYPE_CHECKING:
7
+ import agate
8
+
9
+
10
+ """
11
+ This is what relation metadata from the database looks like. It's a dictionary because there will be
12
+ multiple grains of data for a single object. For example, a materialized view in Postgres has base level information,
13
+ like name. But it also can have multiple indexes, which needs to be a separate query. It might look like this:
14
+
15
+ {
16
+ "base": agate.Row({"table_name": "table_abc", "query": "select * from table_def"})
17
+ "indexes": agate.Table("rows": [
18
+ agate.Row({"name": "index_a", "columns": ["column_a"], "type": "hash", "unique": False}),
19
+ agate.Row({"name": "index_b", "columns": ["time_dim_a"], "type": "btree", "unique": False}),
20
+ ])
21
+ }
22
+ """
23
+ RelationResults = Dict[str, Union["agate.Row", "agate.Table"]]
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class RelationConfigBase:
28
+ @classmethod
29
+ def from_dict(cls, kwargs_dict) -> "RelationConfigBase":
30
+ """
31
+ This assumes the subclass of `RelationConfigBase` is flat, in the sense that no attribute is
32
+ itself another subclass of `RelationConfigBase`. If that's not the case, this should be overriden
33
+ to manually manage that complexity.
34
+
35
+ Args:
36
+ kwargs_dict: the dict representation of this instance
37
+
38
+ Returns: the `RelationConfigBase` representation associated with the provided dict
39
+ """
40
+ return cls(**filter_null_values(kwargs_dict))
41
+
42
+ @classmethod
43
+ def _not_implemented_error(cls) -> NotImplementedError:
44
+ return NotImplementedError(
45
+ "This relation type has not been fully configured for this adapter."
46
+ )
@@ -0,0 +1,26 @@
1
+ from abc import ABC, abstractmethod
2
+ from dataclasses import dataclass
3
+ from typing import Hashable
4
+
5
+ from dbt_common.dataclass_schema import StrEnum
6
+
7
+ from dbt.adapters.relation_configs.config_base import RelationConfigBase
8
+
9
+
10
+ class RelationConfigChangeAction(StrEnum):
11
+ alter = "alter"
12
+ create = "create"
13
+ drop = "drop"
14
+
15
+
16
+ @dataclass(frozen=True, eq=True, unsafe_hash=True)
17
+ class RelationConfigChange(RelationConfigBase, ABC):
18
+ action: RelationConfigChangeAction
19
+ context: (
20
+ Hashable # this is usually a RelationConfig, e.g. IndexConfig, but shouldn't be limited
21
+ )
22
+
23
+ @property
24
+ @abstractmethod
25
+ def requires_full_refresh(self) -> bool:
26
+ raise self._not_implemented_error()
@@ -0,0 +1,57 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional, Set
3
+
4
+ from dbt_common.exceptions import DbtRuntimeError
5
+
6
+
7
+ @dataclass(frozen=True, eq=True, unsafe_hash=True)
8
+ class RelationConfigValidationRule:
9
+ validation_check: bool
10
+ validation_error: Optional[DbtRuntimeError]
11
+
12
+ @property
13
+ def default_error(self):
14
+ return DbtRuntimeError(
15
+ "There was a validation error in preparing this relation config."
16
+ "No additional context was provided by this adapter."
17
+ )
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class RelationConfigValidationMixin:
22
+ def __post_init__(self):
23
+ self.run_validation_rules()
24
+
25
+ @property
26
+ def validation_rules(self) -> Set[RelationConfigValidationRule]:
27
+ """
28
+ A set of validation rules to run against the object upon creation.
29
+
30
+ A validation rule is a combination of a validation check (bool) and an optional error message.
31
+
32
+ This defaults to no validation rules if not implemented. It's recommended to override this with values,
33
+ but that may not always be necessary.
34
+
35
+ Returns: a set of validation rules
36
+ """
37
+ return set()
38
+
39
+ def run_validation_rules(self):
40
+ for validation_rule in self.validation_rules:
41
+ try:
42
+ assert validation_rule.validation_check
43
+ except AssertionError:
44
+ if validation_rule.validation_error:
45
+ raise validation_rule.validation_error
46
+ else:
47
+ raise validation_rule.default_error
48
+ self.run_child_validation_rules()
49
+
50
+ def run_child_validation_rules(self):
51
+ for attr_value in vars(self).values():
52
+ if hasattr(attr_value, "validation_rules"):
53
+ attr_value.run_validation_rules()
54
+ if isinstance(attr_value, set):
55
+ for member in attr_value:
56
+ if hasattr(member, "validation_rules"):
57
+ member.run_validation_rules()
@@ -0,0 +1,2 @@
1
+ from dbt.adapters.sql.connections import SQLConnectionManager
2
+ from dbt.adapters.sql.impl import SQLAdapter