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.
- dbt/__init__.py +0 -0
- dbt/adapters/__about__.py +1 -0
- dbt/adapters/__init__.py +7 -0
- dbt/adapters/base/README.md +13 -0
- dbt/adapters/base/__init__.py +15 -0
- dbt/adapters/base/column.py +166 -0
- dbt/adapters/base/connections.py +426 -0
- dbt/adapters/base/impl.py +1654 -0
- dbt/adapters/base/meta.py +131 -0
- dbt/adapters/base/plugin.py +32 -0
- dbt/adapters/base/query_headers.py +101 -0
- dbt/adapters/base/relation.py +471 -0
- dbt/adapters/cache.py +521 -0
- dbt/adapters/capability.py +52 -0
- dbt/adapters/clients/__init__.py +0 -0
- dbt/adapters/clients/jinja.py +24 -0
- dbt/adapters/contracts/__init__.py +0 -0
- dbt/adapters/contracts/connection.py +228 -0
- dbt/adapters/contracts/macros.py +11 -0
- dbt/adapters/contracts/relation.py +125 -0
- dbt/adapters/events/README.md +57 -0
- dbt/adapters/events/__init__.py +0 -0
- dbt/adapters/events/adapter_types.proto +517 -0
- dbt/adapters/events/adapter_types_pb2.py +208 -0
- dbt/adapters/events/base_types.py +40 -0
- dbt/adapters/events/logging.py +83 -0
- dbt/adapters/events/types.py +423 -0
- dbt/adapters/exceptions/__init__.py +40 -0
- dbt/adapters/exceptions/alias.py +24 -0
- dbt/adapters/exceptions/cache.py +68 -0
- dbt/adapters/exceptions/compilation.py +255 -0
- dbt/adapters/exceptions/connection.py +16 -0
- dbt/adapters/exceptions/database.py +51 -0
- dbt/adapters/factory.py +246 -0
- dbt/adapters/protocol.py +173 -0
- dbt/adapters/reference_keys.py +39 -0
- dbt/adapters/relation_configs/README.md +25 -0
- dbt/adapters/relation_configs/__init__.py +12 -0
- dbt/adapters/relation_configs/config_base.py +44 -0
- dbt/adapters/relation_configs/config_change.py +24 -0
- dbt/adapters/relation_configs/config_validation.py +57 -0
- dbt/adapters/sql/__init__.py +2 -0
- dbt/adapters/sql/connections.py +195 -0
- dbt/adapters/sql/impl.py +273 -0
- dbt/adapters/utils.py +69 -0
- dbt/include/global_project/__init__.py +4 -0
- dbt/include/global_project/dbt_project.yml +7 -0
- dbt/include/global_project/docs/overview.md +43 -0
- dbt/include/global_project/macros/adapters/apply_grants.sql +167 -0
- dbt/include/global_project/macros/adapters/columns.sql +137 -0
- dbt/include/global_project/macros/adapters/freshness.sql +16 -0
- dbt/include/global_project/macros/adapters/indexes.sql +41 -0
- dbt/include/global_project/macros/adapters/metadata.sql +96 -0
- dbt/include/global_project/macros/adapters/persist_docs.sql +33 -0
- dbt/include/global_project/macros/adapters/relation.sql +79 -0
- dbt/include/global_project/macros/adapters/schema.sql +20 -0
- dbt/include/global_project/macros/adapters/show.sql +22 -0
- dbt/include/global_project/macros/adapters/timestamps.sql +44 -0
- dbt/include/global_project/macros/adapters/validate_sql.sql +10 -0
- dbt/include/global_project/macros/etc/datetime.sql +62 -0
- dbt/include/global_project/macros/etc/statement.sql +52 -0
- dbt/include/global_project/macros/generic_test_sql/accepted_values.sql +27 -0
- dbt/include/global_project/macros/generic_test_sql/not_null.sql +9 -0
- dbt/include/global_project/macros/generic_test_sql/relationships.sql +23 -0
- dbt/include/global_project/macros/generic_test_sql/unique.sql +12 -0
- dbt/include/global_project/macros/get_custom_name/get_custom_alias.sql +36 -0
- dbt/include/global_project/macros/get_custom_name/get_custom_database.sql +32 -0
- dbt/include/global_project/macros/get_custom_name/get_custom_schema.sql +60 -0
- dbt/include/global_project/macros/materializations/configs.sql +21 -0
- dbt/include/global_project/macros/materializations/hooks.sql +35 -0
- dbt/include/global_project/macros/materializations/models/clone/can_clone_table.sql +7 -0
- dbt/include/global_project/macros/materializations/models/clone/clone.sql +67 -0
- dbt/include/global_project/macros/materializations/models/clone/create_or_replace_clone.sql +7 -0
- dbt/include/global_project/macros/materializations/models/incremental/column_helpers.sql +80 -0
- dbt/include/global_project/macros/materializations/models/incremental/incremental.sql +92 -0
- dbt/include/global_project/macros/materializations/models/incremental/is_incremental.sql +13 -0
- dbt/include/global_project/macros/materializations/models/incremental/merge.sql +131 -0
- dbt/include/global_project/macros/materializations/models/incremental/on_schema_change.sql +144 -0
- dbt/include/global_project/macros/materializations/models/incremental/strategies.sql +79 -0
- dbt/include/global_project/macros/materializations/models/materialized_view.sql +121 -0
- dbt/include/global_project/macros/materializations/models/table.sql +64 -0
- dbt/include/global_project/macros/materializations/models/view.sql +72 -0
- dbt/include/global_project/macros/materializations/seeds/helpers.sql +128 -0
- dbt/include/global_project/macros/materializations/seeds/seed.sql +60 -0
- dbt/include/global_project/macros/materializations/snapshots/helpers.sql +181 -0
- dbt/include/global_project/macros/materializations/snapshots/snapshot.sql +99 -0
- dbt/include/global_project/macros/materializations/snapshots/snapshot_merge.sql +25 -0
- dbt/include/global_project/macros/materializations/snapshots/strategies.sql +174 -0
- dbt/include/global_project/macros/materializations/tests/helpers.sql +14 -0
- dbt/include/global_project/macros/materializations/tests/test.sql +60 -0
- dbt/include/global_project/macros/materializations/tests/where_subquery.sql +15 -0
- dbt/include/global_project/macros/python_model/python.sql +103 -0
- dbt/include/global_project/macros/relations/column/columns_spec_ddl.sql +89 -0
- dbt/include/global_project/macros/relations/create.sql +23 -0
- dbt/include/global_project/macros/relations/create_backup.sql +17 -0
- dbt/include/global_project/macros/relations/create_intermediate.sql +17 -0
- dbt/include/global_project/macros/relations/drop.sql +41 -0
- dbt/include/global_project/macros/relations/drop_backup.sql +14 -0
- dbt/include/global_project/macros/relations/materialized_view/alter.sql +55 -0
- dbt/include/global_project/macros/relations/materialized_view/create.sql +10 -0
- dbt/include/global_project/macros/relations/materialized_view/drop.sql +14 -0
- dbt/include/global_project/macros/relations/materialized_view/refresh.sql +9 -0
- dbt/include/global_project/macros/relations/materialized_view/rename.sql +10 -0
- dbt/include/global_project/macros/relations/materialized_view/replace.sql +10 -0
- dbt/include/global_project/macros/relations/rename.sql +35 -0
- dbt/include/global_project/macros/relations/rename_intermediate.sql +14 -0
- dbt/include/global_project/macros/relations/replace.sql +50 -0
- dbt/include/global_project/macros/relations/schema.sql +8 -0
- dbt/include/global_project/macros/relations/table/create.sql +60 -0
- dbt/include/global_project/macros/relations/table/drop.sql +14 -0
- dbt/include/global_project/macros/relations/table/rename.sql +10 -0
- dbt/include/global_project/macros/relations/table/replace.sql +10 -0
- dbt/include/global_project/macros/relations/view/create.sql +27 -0
- dbt/include/global_project/macros/relations/view/drop.sql +14 -0
- dbt/include/global_project/macros/relations/view/rename.sql +10 -0
- dbt/include/global_project/macros/relations/view/replace.sql +66 -0
- dbt/include/global_project/macros/utils/any_value.sql +9 -0
- dbt/include/global_project/macros/utils/array_append.sql +8 -0
- dbt/include/global_project/macros/utils/array_concat.sql +7 -0
- dbt/include/global_project/macros/utils/array_construct.sql +12 -0
- dbt/include/global_project/macros/utils/bool_or.sql +9 -0
- dbt/include/global_project/macros/utils/cast_bool_to_text.sql +7 -0
- dbt/include/global_project/macros/utils/concat.sql +7 -0
- dbt/include/global_project/macros/utils/data_types.sql +129 -0
- dbt/include/global_project/macros/utils/date_spine.sql +75 -0
- dbt/include/global_project/macros/utils/date_trunc.sql +7 -0
- dbt/include/global_project/macros/utils/dateadd.sql +14 -0
- dbt/include/global_project/macros/utils/datediff.sql +14 -0
- dbt/include/global_project/macros/utils/escape_single_quotes.sql +8 -0
- dbt/include/global_project/macros/utils/except.sql +9 -0
- dbt/include/global_project/macros/utils/generate_series.sql +53 -0
- dbt/include/global_project/macros/utils/hash.sql +7 -0
- dbt/include/global_project/macros/utils/intersect.sql +9 -0
- dbt/include/global_project/macros/utils/last_day.sql +15 -0
- dbt/include/global_project/macros/utils/length.sql +11 -0
- dbt/include/global_project/macros/utils/listagg.sql +30 -0
- dbt/include/global_project/macros/utils/literal.sql +7 -0
- dbt/include/global_project/macros/utils/position.sql +11 -0
- dbt/include/global_project/macros/utils/replace.sql +14 -0
- dbt/include/global_project/macros/utils/right.sql +12 -0
- dbt/include/global_project/macros/utils/safe_cast.sql +9 -0
- dbt/include/global_project/macros/utils/split_part.sql +26 -0
- dbt/include/global_project/tests/generic/builtin.sql +30 -0
- dbt_adapters-0.1.0a1.dist-info/METADATA +81 -0
- dbt_adapters-0.1.0a1.dist-info/RECORD +147 -0
- dbt_adapters-0.1.0a1.dist-info/WHEEL +4 -0
- dbt_adapters-0.1.0a1.dist-info/licenses/LICENSE +201 -0
dbt/adapters/sql/impl.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
from typing import Any, List, Optional, Tuple, Type
|
|
2
|
+
|
|
3
|
+
import agate
|
|
4
|
+
from dbt_common.events.functions import fire_event
|
|
5
|
+
|
|
6
|
+
from dbt.adapters.base import BaseAdapter, BaseRelation, available
|
|
7
|
+
from dbt.adapters.cache import _make_ref_key_dict
|
|
8
|
+
from dbt.adapters.contracts.connection import AdapterResponse, Connection
|
|
9
|
+
from dbt.adapters.events.types import ColTypeChange, SchemaCreation, SchemaDrop
|
|
10
|
+
from dbt.adapters.exceptions import RelationTypeNullError
|
|
11
|
+
from dbt.adapters.sql.connections import SQLConnectionManager
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
LIST_RELATIONS_MACRO_NAME = "list_relations_without_caching"
|
|
15
|
+
GET_COLUMNS_IN_RELATION_MACRO_NAME = "get_columns_in_relation"
|
|
16
|
+
LIST_SCHEMAS_MACRO_NAME = "list_schemas"
|
|
17
|
+
CHECK_SCHEMA_EXISTS_MACRO_NAME = "check_schema_exists"
|
|
18
|
+
CREATE_SCHEMA_MACRO_NAME = "create_schema"
|
|
19
|
+
DROP_SCHEMA_MACRO_NAME = "drop_schema"
|
|
20
|
+
RENAME_RELATION_MACRO_NAME = "rename_relation"
|
|
21
|
+
TRUNCATE_RELATION_MACRO_NAME = "truncate_relation"
|
|
22
|
+
DROP_RELATION_MACRO_NAME = "drop_relation"
|
|
23
|
+
ALTER_COLUMN_TYPE_MACRO_NAME = "alter_column_type"
|
|
24
|
+
VALIDATE_SQL_MACRO_NAME = "validate_sql"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class SQLAdapter(BaseAdapter):
|
|
28
|
+
"""The default adapter with the common agate conversions and some SQL
|
|
29
|
+
methods was implemented. This adapter has a different much shorter list of
|
|
30
|
+
methods to implement, but some more macros that must be implemented.
|
|
31
|
+
|
|
32
|
+
To implement a macro, implement "${adapter_type}__${macro_name}". in the
|
|
33
|
+
adapter's internal project.
|
|
34
|
+
|
|
35
|
+
Methods to implement:
|
|
36
|
+
- date_function
|
|
37
|
+
|
|
38
|
+
Macros to implement:
|
|
39
|
+
- get_catalog
|
|
40
|
+
- list_relations_without_caching
|
|
41
|
+
- get_columns_in_relation
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
ConnectionManager: Type[SQLConnectionManager]
|
|
45
|
+
connections: SQLConnectionManager
|
|
46
|
+
|
|
47
|
+
@available.parse(lambda *a, **k: (None, None))
|
|
48
|
+
def add_query(
|
|
49
|
+
self,
|
|
50
|
+
sql: str,
|
|
51
|
+
auto_begin: bool = True,
|
|
52
|
+
bindings: Optional[Any] = None,
|
|
53
|
+
abridge_sql_log: bool = False,
|
|
54
|
+
) -> Tuple[Connection, Any]:
|
|
55
|
+
"""Add a query to the current transaction. A thin wrapper around
|
|
56
|
+
ConnectionManager.add_query.
|
|
57
|
+
|
|
58
|
+
:param sql: The SQL query to add
|
|
59
|
+
:param auto_begin: If set and there is no transaction in progress,
|
|
60
|
+
begin a new one.
|
|
61
|
+
:param bindings: An optional list of bindings for the query.
|
|
62
|
+
:param abridge_sql_log: If set, limit the raw sql logged to 512
|
|
63
|
+
characters
|
|
64
|
+
"""
|
|
65
|
+
return self.connections.add_query(sql, auto_begin, bindings, abridge_sql_log)
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def convert_text_type(cls, agate_table: agate.Table, col_idx: int) -> str:
|
|
69
|
+
return "text"
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def convert_number_type(cls, agate_table: agate.Table, col_idx: int) -> str:
|
|
73
|
+
# TODO CT-211
|
|
74
|
+
decimals = agate_table.aggregate(agate.MaxPrecision(col_idx)) # type: ignore[attr-defined]
|
|
75
|
+
return "float8" if decimals else "integer"
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def convert_integer_type(cls, agate_table: agate.Table, col_idx: int) -> str:
|
|
79
|
+
return "integer"
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def convert_boolean_type(cls, agate_table: agate.Table, col_idx: int) -> str:
|
|
83
|
+
return "boolean"
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def convert_datetime_type(cls, agate_table: agate.Table, col_idx: int) -> str:
|
|
87
|
+
return "timestamp without time zone"
|
|
88
|
+
|
|
89
|
+
@classmethod
|
|
90
|
+
def convert_date_type(cls, agate_table: agate.Table, col_idx: int) -> str:
|
|
91
|
+
return "date"
|
|
92
|
+
|
|
93
|
+
@classmethod
|
|
94
|
+
def convert_time_type(cls, agate_table: agate.Table, col_idx: int) -> str:
|
|
95
|
+
return "time"
|
|
96
|
+
|
|
97
|
+
@classmethod
|
|
98
|
+
def is_cancelable(cls) -> bool:
|
|
99
|
+
return True
|
|
100
|
+
|
|
101
|
+
def expand_column_types(self, goal, current):
|
|
102
|
+
reference_columns = {c.name: c for c in self.get_columns_in_relation(goal)}
|
|
103
|
+
|
|
104
|
+
target_columns = {c.name: c for c in self.get_columns_in_relation(current)}
|
|
105
|
+
|
|
106
|
+
for column_name, reference_column in reference_columns.items():
|
|
107
|
+
target_column = target_columns.get(column_name)
|
|
108
|
+
|
|
109
|
+
if target_column is not None and target_column.can_expand_to(reference_column):
|
|
110
|
+
col_string_size = reference_column.string_size()
|
|
111
|
+
new_type = self.Column.string_type(col_string_size)
|
|
112
|
+
fire_event(
|
|
113
|
+
ColTypeChange(
|
|
114
|
+
orig_type=target_column.data_type,
|
|
115
|
+
new_type=new_type,
|
|
116
|
+
table=_make_ref_key_dict(current),
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
self.alter_column_type(current, column_name, new_type)
|
|
121
|
+
|
|
122
|
+
def alter_column_type(self, relation, column_name, new_column_type) -> None:
|
|
123
|
+
"""
|
|
124
|
+
1. Create a new column (w/ temp name and correct type)
|
|
125
|
+
2. Copy data over to it
|
|
126
|
+
3. Drop the existing column (cascade!)
|
|
127
|
+
4. Rename the new column to existing column
|
|
128
|
+
"""
|
|
129
|
+
kwargs = {
|
|
130
|
+
"relation": relation,
|
|
131
|
+
"column_name": column_name,
|
|
132
|
+
"new_column_type": new_column_type,
|
|
133
|
+
}
|
|
134
|
+
self.execute_macro(ALTER_COLUMN_TYPE_MACRO_NAME, kwargs=kwargs)
|
|
135
|
+
|
|
136
|
+
def drop_relation(self, relation):
|
|
137
|
+
if relation.type is None:
|
|
138
|
+
raise RelationTypeNullError(relation)
|
|
139
|
+
|
|
140
|
+
self.cache_dropped(relation)
|
|
141
|
+
self.execute_macro(DROP_RELATION_MACRO_NAME, kwargs={"relation": relation})
|
|
142
|
+
|
|
143
|
+
def truncate_relation(self, relation):
|
|
144
|
+
self.execute_macro(TRUNCATE_RELATION_MACRO_NAME, kwargs={"relation": relation})
|
|
145
|
+
|
|
146
|
+
def rename_relation(self, from_relation, to_relation):
|
|
147
|
+
self.cache_renamed(from_relation, to_relation)
|
|
148
|
+
|
|
149
|
+
kwargs = {"from_relation": from_relation, "to_relation": to_relation}
|
|
150
|
+
self.execute_macro(RENAME_RELATION_MACRO_NAME, kwargs=kwargs)
|
|
151
|
+
|
|
152
|
+
def get_columns_in_relation(self, relation):
|
|
153
|
+
return self.execute_macro(
|
|
154
|
+
GET_COLUMNS_IN_RELATION_MACRO_NAME, kwargs={"relation": relation}
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
def create_schema(self, relation: BaseRelation) -> None:
|
|
158
|
+
relation = relation.without_identifier()
|
|
159
|
+
fire_event(SchemaCreation(relation=_make_ref_key_dict(relation)))
|
|
160
|
+
kwargs = {
|
|
161
|
+
"relation": relation,
|
|
162
|
+
}
|
|
163
|
+
self.execute_macro(CREATE_SCHEMA_MACRO_NAME, kwargs=kwargs)
|
|
164
|
+
self.commit_if_has_connection()
|
|
165
|
+
# we can't update the cache here, as if the schema already existed we
|
|
166
|
+
# don't want to (incorrectly) say that it's empty
|
|
167
|
+
|
|
168
|
+
def drop_schema(self, relation: BaseRelation) -> None:
|
|
169
|
+
relation = relation.without_identifier()
|
|
170
|
+
fire_event(SchemaDrop(relation=_make_ref_key_dict(relation)))
|
|
171
|
+
kwargs = {
|
|
172
|
+
"relation": relation,
|
|
173
|
+
}
|
|
174
|
+
self.execute_macro(DROP_SCHEMA_MACRO_NAME, kwargs=kwargs)
|
|
175
|
+
self.commit_if_has_connection()
|
|
176
|
+
# we can update the cache here
|
|
177
|
+
self.cache.drop_schema(relation.database, relation.schema)
|
|
178
|
+
|
|
179
|
+
def list_relations_without_caching(
|
|
180
|
+
self,
|
|
181
|
+
schema_relation: BaseRelation,
|
|
182
|
+
) -> List[BaseRelation]:
|
|
183
|
+
kwargs = {"schema_relation": schema_relation}
|
|
184
|
+
results = self.execute_macro(LIST_RELATIONS_MACRO_NAME, kwargs=kwargs)
|
|
185
|
+
|
|
186
|
+
relations = []
|
|
187
|
+
quote_policy = {"database": True, "schema": True, "identifier": True}
|
|
188
|
+
for _database, name, _schema, _type in results:
|
|
189
|
+
try:
|
|
190
|
+
_type = self.Relation.get_relation_type(_type)
|
|
191
|
+
except ValueError:
|
|
192
|
+
_type = self.Relation.External
|
|
193
|
+
relations.append(
|
|
194
|
+
self.Relation.create(
|
|
195
|
+
database=_database,
|
|
196
|
+
schema=_schema,
|
|
197
|
+
identifier=name,
|
|
198
|
+
quote_policy=quote_policy,
|
|
199
|
+
type=_type,
|
|
200
|
+
)
|
|
201
|
+
)
|
|
202
|
+
return relations
|
|
203
|
+
|
|
204
|
+
@classmethod
|
|
205
|
+
def quote(self, identifier):
|
|
206
|
+
return '"{}"'.format(identifier)
|
|
207
|
+
|
|
208
|
+
def list_schemas(self, database: str) -> List[str]:
|
|
209
|
+
results = self.execute_macro(LIST_SCHEMAS_MACRO_NAME, kwargs={"database": database})
|
|
210
|
+
|
|
211
|
+
return [row[0] for row in results]
|
|
212
|
+
|
|
213
|
+
def check_schema_exists(self, database: str, schema: str) -> bool:
|
|
214
|
+
information_schema = self.Relation.create(
|
|
215
|
+
database=database,
|
|
216
|
+
schema=schema,
|
|
217
|
+
identifier="INFORMATION_SCHEMA",
|
|
218
|
+
quote_policy=self.config.quoting,
|
|
219
|
+
).information_schema()
|
|
220
|
+
|
|
221
|
+
kwargs = {"information_schema": information_schema, "schema": schema}
|
|
222
|
+
results = self.execute_macro(CHECK_SCHEMA_EXISTS_MACRO_NAME, kwargs=kwargs)
|
|
223
|
+
return results[0][0] > 0
|
|
224
|
+
|
|
225
|
+
def validate_sql(self, sql: str) -> AdapterResponse:
|
|
226
|
+
"""Submit the given SQL to the engine for validation, but not execution.
|
|
227
|
+
|
|
228
|
+
By default we simply prefix the query with the explain keyword and allow the
|
|
229
|
+
exceptions thrown by the underlying engine on invalid SQL inputs to bubble up
|
|
230
|
+
to the exception handler. For adjustments to the explain statement - such as
|
|
231
|
+
for adapters that have different mechanisms for hinting at query validation
|
|
232
|
+
or dry-run - callers may be able to override the validate_sql_query macro with
|
|
233
|
+
the addition of an <adapter>__validate_sql implementation.
|
|
234
|
+
|
|
235
|
+
:param sql str: The sql to validate
|
|
236
|
+
"""
|
|
237
|
+
kwargs = {
|
|
238
|
+
"sql": sql,
|
|
239
|
+
}
|
|
240
|
+
result = self.execute_macro(VALIDATE_SQL_MACRO_NAME, kwargs=kwargs)
|
|
241
|
+
# The statement macro always returns an AdapterResponse in the output AttrDict's
|
|
242
|
+
# `response` property, and we preserve the full payload in case we want to
|
|
243
|
+
# return fetched output for engines where explain plans are emitted as columnar
|
|
244
|
+
# results. Any macro override that deviates from this behavior may encounter an
|
|
245
|
+
# assertion error in the runtime.
|
|
246
|
+
adapter_response = result.response # type: ignore[attr-defined]
|
|
247
|
+
assert isinstance(adapter_response, AdapterResponse), (
|
|
248
|
+
f"Expected AdapterResponse from validate_sql macro execution, "
|
|
249
|
+
f"got {type(adapter_response)}."
|
|
250
|
+
)
|
|
251
|
+
return adapter_response
|
|
252
|
+
|
|
253
|
+
# This is for use in the test suite
|
|
254
|
+
def run_sql_for_tests(self, sql, fetch, conn):
|
|
255
|
+
cursor = conn.handle.cursor()
|
|
256
|
+
try:
|
|
257
|
+
cursor.execute(sql)
|
|
258
|
+
if hasattr(conn.handle, "commit"):
|
|
259
|
+
conn.handle.commit()
|
|
260
|
+
if fetch == "one":
|
|
261
|
+
return cursor.fetchone()
|
|
262
|
+
elif fetch == "all":
|
|
263
|
+
return cursor.fetchall()
|
|
264
|
+
else:
|
|
265
|
+
return
|
|
266
|
+
except BaseException as e:
|
|
267
|
+
if conn.handle and not getattr(conn.handle, "closed", True):
|
|
268
|
+
conn.handle.rollback()
|
|
269
|
+
print(sql)
|
|
270
|
+
print(e)
|
|
271
|
+
raise
|
|
272
|
+
finally:
|
|
273
|
+
conn.transaction_open = False
|
dbt/adapters/utils.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from typing import Mapping, Sequence, Any, Dict, List
|
|
2
|
+
|
|
3
|
+
from dbt.adapters.exceptions import DuplicateAliasError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Translator:
|
|
7
|
+
def __init__(self, aliases: Mapping[str, str], recursive: bool = False) -> None:
|
|
8
|
+
self.aliases = aliases
|
|
9
|
+
self.recursive = recursive
|
|
10
|
+
|
|
11
|
+
def translate_mapping(self, kwargs: Mapping[str, Any]) -> Dict[str, Any]:
|
|
12
|
+
result: Dict[str, Any] = {}
|
|
13
|
+
|
|
14
|
+
for key, value in kwargs.items():
|
|
15
|
+
canonical_key = self.aliases.get(key, key)
|
|
16
|
+
if canonical_key in result:
|
|
17
|
+
raise DuplicateAliasError(kwargs, self.aliases, canonical_key)
|
|
18
|
+
result[canonical_key] = self.translate_value(value)
|
|
19
|
+
return result
|
|
20
|
+
|
|
21
|
+
def translate_sequence(self, value: Sequence[Any]) -> List[Any]:
|
|
22
|
+
return [self.translate_value(v) for v in value]
|
|
23
|
+
|
|
24
|
+
def translate_value(self, value: Any) -> Any:
|
|
25
|
+
if self.recursive:
|
|
26
|
+
if isinstance(value, Mapping):
|
|
27
|
+
return self.translate_mapping(value)
|
|
28
|
+
elif isinstance(value, (list, tuple)):
|
|
29
|
+
return self.translate_sequence(value)
|
|
30
|
+
return value
|
|
31
|
+
|
|
32
|
+
def translate(self, value: Mapping[str, Any]) -> Dict[str, Any]:
|
|
33
|
+
try:
|
|
34
|
+
return self.translate_mapping(value)
|
|
35
|
+
except RuntimeError as exc:
|
|
36
|
+
if "maximum recursion depth exceeded" in str(exc):
|
|
37
|
+
raise RecursionError("Cycle detected in a value passed to translate!")
|
|
38
|
+
raise
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def translate_aliases(
|
|
42
|
+
kwargs: Dict[str, Any],
|
|
43
|
+
aliases: Dict[str, str],
|
|
44
|
+
recurse: bool = False,
|
|
45
|
+
) -> Dict[str, Any]:
|
|
46
|
+
"""Given a dict of keyword arguments and a dict mapping aliases to their
|
|
47
|
+
canonical values, canonicalize the keys in the kwargs dict.
|
|
48
|
+
|
|
49
|
+
If recurse is True, perform this operation recursively.
|
|
50
|
+
|
|
51
|
+
:returns: A dict containing all the values in kwargs referenced by their
|
|
52
|
+
canonical key.
|
|
53
|
+
:raises: `AliasError`, if a canonical key is defined more than once.
|
|
54
|
+
"""
|
|
55
|
+
translator = Translator(aliases, recurse)
|
|
56
|
+
return translator.translate(kwargs)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# some types need to make constants available to the jinja context as
|
|
60
|
+
# attributes, and regular properties only work with objects. maybe this should
|
|
61
|
+
# be handled by the RelationProxy?
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class classproperty(object):
|
|
65
|
+
def __init__(self, func) -> None:
|
|
66
|
+
self.func = func
|
|
67
|
+
|
|
68
|
+
def __get__(self, obj, objtype):
|
|
69
|
+
return self.func(objtype)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
|
|
2
|
+
{% docs __overview__ %}
|
|
3
|
+
|
|
4
|
+
### Welcome!
|
|
5
|
+
|
|
6
|
+
Welcome to the auto-generated documentation for your dbt project!
|
|
7
|
+
|
|
8
|
+
### Navigation
|
|
9
|
+
|
|
10
|
+
You can use the `Project` and `Database` navigation tabs on the left side of the window to explore the models
|
|
11
|
+
in your project.
|
|
12
|
+
|
|
13
|
+
#### Project Tab
|
|
14
|
+
The `Project` tab mirrors the directory structure of your dbt project. In this tab, you can see all of the
|
|
15
|
+
models defined in your dbt project, as well as models imported from dbt packages.
|
|
16
|
+
|
|
17
|
+
#### Database Tab
|
|
18
|
+
The `Database` tab also exposes your models, but in a format that looks more like a database explorer. This view
|
|
19
|
+
shows relations (tables and views) grouped into database schemas. Note that ephemeral models are _not_ shown
|
|
20
|
+
in this interface, as they do not exist in the database.
|
|
21
|
+
|
|
22
|
+
### Graph Exploration
|
|
23
|
+
You can click the blue icon on the bottom-right corner of the page to view the lineage graph of your models.
|
|
24
|
+
|
|
25
|
+
On model pages, you'll see the immediate parents and children of the model you're exploring. By clicking the `Expand`
|
|
26
|
+
button at the top-right of this lineage pane, you'll be able to see all of the models that are used to build,
|
|
27
|
+
or are built from, the model you're exploring.
|
|
28
|
+
|
|
29
|
+
Once expanded, you'll be able to use the `--select` and `--exclude` model selection syntax to filter the
|
|
30
|
+
models in the graph. For more information on model selection, check out the [dbt docs](https://docs.getdbt.com/docs/model-selection-syntax).
|
|
31
|
+
|
|
32
|
+
Note that you can also right-click on models to interactively filter and explore the graph.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
### More information
|
|
37
|
+
|
|
38
|
+
- [What is dbt](https://docs.getdbt.com/docs/introduction)?
|
|
39
|
+
- Read the [dbt viewpoint](https://docs.getdbt.com/docs/viewpoint)
|
|
40
|
+
- [Installation](https://docs.getdbt.com/docs/installation)
|
|
41
|
+
- Join the [dbt Community](https://www.getdbt.com/community/) for questions and discussion
|
|
42
|
+
|
|
43
|
+
{% enddocs %}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
{# ------- BOOLEAN MACROS --------- #}
|
|
2
|
+
|
|
3
|
+
{#
|
|
4
|
+
-- COPY GRANTS
|
|
5
|
+
-- When a relational object (view or table) is replaced in this database,
|
|
6
|
+
-- do previous grants carry over to the new object? This may depend on:
|
|
7
|
+
-- whether we use alter-rename-swap versus CREATE OR REPLACE
|
|
8
|
+
-- user-supplied configuration (e.g. copy_grants on Snowflake)
|
|
9
|
+
-- By default, play it safe, assume TRUE: that grants ARE copied over.
|
|
10
|
+
-- This means dbt will first "show" current grants and then calculate diffs.
|
|
11
|
+
-- It may require an additional query than is strictly necessary,
|
|
12
|
+
-- but better safe than sorry.
|
|
13
|
+
#}
|
|
14
|
+
|
|
15
|
+
{% macro copy_grants() %}
|
|
16
|
+
{{ return(adapter.dispatch('copy_grants', 'dbt')()) }}
|
|
17
|
+
{% endmacro %}
|
|
18
|
+
|
|
19
|
+
{% macro default__copy_grants() %}
|
|
20
|
+
{{ return(True) }}
|
|
21
|
+
{% endmacro %}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
{#
|
|
25
|
+
-- SUPPORT MULTIPLE GRANTEES PER DCL STATEMENT
|
|
26
|
+
-- Does this database support 'grant {privilege} to {grantee_1}, {grantee_2}, ...'
|
|
27
|
+
-- Or must these be separate statements:
|
|
28
|
+
-- `grant {privilege} to {grantee_1}`;
|
|
29
|
+
-- `grant {privilege} to {grantee_2}`;
|
|
30
|
+
-- By default, pick the former, because it's what we prefer when available.
|
|
31
|
+
#}
|
|
32
|
+
|
|
33
|
+
{% macro support_multiple_grantees_per_dcl_statement() %}
|
|
34
|
+
{{ return(adapter.dispatch('support_multiple_grantees_per_dcl_statement', 'dbt')()) }}
|
|
35
|
+
{% endmacro %}
|
|
36
|
+
|
|
37
|
+
{%- macro default__support_multiple_grantees_per_dcl_statement() -%}
|
|
38
|
+
{{ return(True) }}
|
|
39
|
+
{%- endmacro -%}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
{% macro should_revoke(existing_relation, full_refresh_mode=True) %}
|
|
43
|
+
|
|
44
|
+
{% if not existing_relation %}
|
|
45
|
+
{#-- The table doesn't already exist, so no grants to copy over --#}
|
|
46
|
+
{{ return(False) }}
|
|
47
|
+
{% elif full_refresh_mode %}
|
|
48
|
+
{#-- The object is being REPLACED -- whether grants are copied over depends on the value of user config --#}
|
|
49
|
+
{{ return(copy_grants()) }}
|
|
50
|
+
{% else %}
|
|
51
|
+
{#-- The table is being merged/upserted/inserted -- grants will be carried over --#}
|
|
52
|
+
{{ return(True) }}
|
|
53
|
+
{% endif %}
|
|
54
|
+
|
|
55
|
+
{% endmacro %}
|
|
56
|
+
|
|
57
|
+
{# ------- DCL STATEMENT TEMPLATES --------- #}
|
|
58
|
+
|
|
59
|
+
{% macro get_show_grant_sql(relation) %}
|
|
60
|
+
{{ return(adapter.dispatch("get_show_grant_sql", "dbt")(relation)) }}
|
|
61
|
+
{% endmacro %}
|
|
62
|
+
|
|
63
|
+
{% macro default__get_show_grant_sql(relation) %}
|
|
64
|
+
show grants on {{ relation }}
|
|
65
|
+
{% endmacro %}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
{% macro get_grant_sql(relation, privilege, grantees) %}
|
|
69
|
+
{{ return(adapter.dispatch('get_grant_sql', 'dbt')(relation, privilege, grantees)) }}
|
|
70
|
+
{% endmacro %}
|
|
71
|
+
|
|
72
|
+
{%- macro default__get_grant_sql(relation, privilege, grantees) -%}
|
|
73
|
+
grant {{ privilege }} on {{ relation }} to {{ grantees | join(', ') }}
|
|
74
|
+
{%- endmacro -%}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
{% macro get_revoke_sql(relation, privilege, grantees) %}
|
|
78
|
+
{{ return(adapter.dispatch('get_revoke_sql', 'dbt')(relation, privilege, grantees)) }}
|
|
79
|
+
{% endmacro %}
|
|
80
|
+
|
|
81
|
+
{%- macro default__get_revoke_sql(relation, privilege, grantees) -%}
|
|
82
|
+
revoke {{ privilege }} on {{ relation }} from {{ grantees | join(', ') }}
|
|
83
|
+
{%- endmacro -%}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
{# ------- RUNTIME APPLICATION --------- #}
|
|
87
|
+
|
|
88
|
+
{% macro get_dcl_statement_list(relation, grant_config, get_dcl_macro) %}
|
|
89
|
+
{{ return(adapter.dispatch('get_dcl_statement_list', 'dbt')(relation, grant_config, get_dcl_macro)) }}
|
|
90
|
+
{% endmacro %}
|
|
91
|
+
|
|
92
|
+
{%- macro default__get_dcl_statement_list(relation, grant_config, get_dcl_macro) -%}
|
|
93
|
+
{#
|
|
94
|
+
-- Unpack grant_config into specific privileges and the set of users who need them granted/revoked.
|
|
95
|
+
-- Depending on whether this database supports multiple grantees per statement, pass in the list of
|
|
96
|
+
-- all grantees per privilege, or (if not) template one statement per privilege-grantee pair.
|
|
97
|
+
-- `get_dcl_macro` will be either `get_grant_sql` or `get_revoke_sql`
|
|
98
|
+
#}
|
|
99
|
+
{%- set dcl_statements = [] -%}
|
|
100
|
+
{%- for privilege, grantees in grant_config.items() %}
|
|
101
|
+
{%- if support_multiple_grantees_per_dcl_statement() and grantees -%}
|
|
102
|
+
{%- set dcl = get_dcl_macro(relation, privilege, grantees) -%}
|
|
103
|
+
{%- do dcl_statements.append(dcl) -%}
|
|
104
|
+
{%- else -%}
|
|
105
|
+
{%- for grantee in grantees -%}
|
|
106
|
+
{% set dcl = get_dcl_macro(relation, privilege, [grantee]) %}
|
|
107
|
+
{%- do dcl_statements.append(dcl) -%}
|
|
108
|
+
{% endfor -%}
|
|
109
|
+
{%- endif -%}
|
|
110
|
+
{%- endfor -%}
|
|
111
|
+
{{ return(dcl_statements) }}
|
|
112
|
+
{%- endmacro %}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
{% macro call_dcl_statements(dcl_statement_list) %}
|
|
116
|
+
{{ return(adapter.dispatch("call_dcl_statements", "dbt")(dcl_statement_list)) }}
|
|
117
|
+
{% endmacro %}
|
|
118
|
+
|
|
119
|
+
{% macro default__call_dcl_statements(dcl_statement_list) %}
|
|
120
|
+
{#
|
|
121
|
+
-- By default, supply all grant + revoke statements in a single semicolon-separated block,
|
|
122
|
+
-- so that they're all processed together.
|
|
123
|
+
|
|
124
|
+
-- Some databases do not support this. Those adapters will need to override this macro
|
|
125
|
+
-- to run each statement individually.
|
|
126
|
+
#}
|
|
127
|
+
{% call statement('grants') %}
|
|
128
|
+
{% for dcl_statement in dcl_statement_list %}
|
|
129
|
+
{{ dcl_statement }};
|
|
130
|
+
{% endfor %}
|
|
131
|
+
{% endcall %}
|
|
132
|
+
{% endmacro %}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
{% macro apply_grants(relation, grant_config, should_revoke) %}
|
|
136
|
+
{{ return(adapter.dispatch("apply_grants", "dbt")(relation, grant_config, should_revoke)) }}
|
|
137
|
+
{% endmacro %}
|
|
138
|
+
|
|
139
|
+
{% macro default__apply_grants(relation, grant_config, should_revoke=True) %}
|
|
140
|
+
{#-- If grant_config is {} or None, this is a no-op --#}
|
|
141
|
+
{% if grant_config %}
|
|
142
|
+
{% if should_revoke %}
|
|
143
|
+
{#-- We think previous grants may have carried over --#}
|
|
144
|
+
{#-- Show current grants and calculate diffs --#}
|
|
145
|
+
{% set current_grants_table = run_query(get_show_grant_sql(relation)) %}
|
|
146
|
+
{% set current_grants_dict = adapter.standardize_grants_dict(current_grants_table) %}
|
|
147
|
+
{% set needs_granting = diff_of_two_dicts(grant_config, current_grants_dict) %}
|
|
148
|
+
{% set needs_revoking = diff_of_two_dicts(current_grants_dict, grant_config) %}
|
|
149
|
+
{% if not (needs_granting or needs_revoking) %}
|
|
150
|
+
{{ log('On ' ~ relation ~': All grants are in place, no revocation or granting needed.')}}
|
|
151
|
+
{% endif %}
|
|
152
|
+
{% else %}
|
|
153
|
+
{#-- We don't think there's any chance of previous grants having carried over. --#}
|
|
154
|
+
{#-- Jump straight to granting what the user has configured. --#}
|
|
155
|
+
{% set needs_revoking = {} %}
|
|
156
|
+
{% set needs_granting = grant_config %}
|
|
157
|
+
{% endif %}
|
|
158
|
+
{% if needs_granting or needs_revoking %}
|
|
159
|
+
{% set revoke_statement_list = get_dcl_statement_list(relation, needs_revoking, get_revoke_sql) %}
|
|
160
|
+
{% set grant_statement_list = get_dcl_statement_list(relation, needs_granting, get_grant_sql) %}
|
|
161
|
+
{% set dcl_statement_list = revoke_statement_list + grant_statement_list %}
|
|
162
|
+
{% if dcl_statement_list %}
|
|
163
|
+
{{ call_dcl_statements(dcl_statement_list) }}
|
|
164
|
+
{% endif %}
|
|
165
|
+
{% endif %}
|
|
166
|
+
{% endif %}
|
|
167
|
+
{% endmacro %}
|