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,648 @@
1
+ from collections.abc import Hashable
2
+ from dataclasses import dataclass, field
3
+ from datetime import datetime
4
+ from typing import (
5
+ Any,
6
+ Dict,
7
+ FrozenSet,
8
+ Iterator,
9
+ List,
10
+ Optional,
11
+ Set,
12
+ Tuple,
13
+ Type,
14
+ TypeVar,
15
+ Union,
16
+ )
17
+
18
+ from dbt_common.exceptions import CompilationError, DbtRuntimeError
19
+ from dbt_common.utils import deep_merge, filter_null_values
20
+
21
+ from dbt.adapters.contracts.relation import (
22
+ ComponentName,
23
+ HasQuoting,
24
+ FakeAPIObject,
25
+ Path,
26
+ Policy,
27
+ RelationConfig,
28
+ RelationType,
29
+ )
30
+ from dbt.adapters.relation_configs import (
31
+ RelationConfigBase,
32
+ RelationConfigValidationMixin,
33
+ RelationConfigValidationRule,
34
+ )
35
+ from dbt.adapters.exceptions import (
36
+ ApproximateMatchError,
37
+ MultipleDatabasesNotAllowedError,
38
+ )
39
+ from dbt.adapters.utils import classproperty
40
+
41
+
42
+ Self = TypeVar("Self", bound="BaseRelation")
43
+ SerializableIterable = Union[Tuple, FrozenSet]
44
+
45
+
46
+ @dataclass
47
+ class EventTimeFilter(FakeAPIObject):
48
+ field_name: str
49
+ start: Optional[datetime] = None
50
+ end: Optional[datetime] = None
51
+
52
+
53
+ @dataclass(frozen=True, eq=False, repr=False)
54
+ class FunctionConfig(RelationConfigBase, RelationConfigValidationMixin):
55
+ language: str
56
+ type: str
57
+ runtime_version: Optional[str] = None
58
+ entry_point: Optional[str] = None
59
+
60
+ def _validate_runtime_version(self) -> bool:
61
+ if self.language == "python":
62
+ return self.runtime_version is not None
63
+ else:
64
+ return True
65
+
66
+ def _validate_entry_point(self) -> bool:
67
+ if self.language == "python":
68
+ return self.entry_point is not None
69
+ else:
70
+ return True
71
+
72
+ @property
73
+ def validation_rules(self) -> Set[RelationConfigValidationRule]:
74
+ return {
75
+ RelationConfigValidationRule(
76
+ validation_check=self.language != "" and self.language is not None,
77
+ validation_error=DbtRuntimeError("A `language` is required for functions"),
78
+ ),
79
+ RelationConfigValidationRule(
80
+ validation_check=self.type != "" and self.type is not None,
81
+ validation_error=DbtRuntimeError("A `type` is required for functions"),
82
+ ),
83
+ RelationConfigValidationRule(
84
+ validation_check=self.language != "python" or self.runtime_version is not None,
85
+ validation_error=DbtRuntimeError(
86
+ "A `runtime_version` is required for python functions"
87
+ ),
88
+ ),
89
+ RelationConfigValidationRule(
90
+ validation_check=self.language != "python" or self.entry_point is not None,
91
+ validation_error=DbtRuntimeError(
92
+ "An `entry_point` is required for python functions"
93
+ ),
94
+ ),
95
+ }
96
+
97
+
98
+ @dataclass(frozen=True, eq=False, repr=False)
99
+ class BaseRelation(FakeAPIObject, Hashable):
100
+ path: Path
101
+ type: Optional[RelationType] = None
102
+ quote_character: str = '"'
103
+ # Python 3.11 requires that these use default_factory instead of simple default
104
+ # ValueError: mutable default <class 'dbt.contracts.relation.Policy'> for field include_policy is not allowed: use default_factory
105
+ include_policy: Policy = field(default_factory=lambda: Policy())
106
+ quote_policy: Policy = field(default_factory=lambda: Policy())
107
+ dbt_created: bool = False
108
+ limit: Optional[int] = None
109
+ event_time_filter: Optional[EventTimeFilter] = None
110
+ require_alias: bool = (
111
+ True # used to govern whether to add an alias when render_limited is called
112
+ )
113
+ catalog: Optional[str] = None
114
+
115
+ # register relation types that can be renamed for the purpose of replacing relations using stages and backups
116
+ # adding a relation type here also requires defining the associated rename macro
117
+ # e.g. adding RelationType.View in dbt-postgres requires that you define:
118
+ # include/postgres/macros/relations/view/rename.sql::postgres__get_rename_view_sql()
119
+ renameable_relations: SerializableIterable = field(default_factory=frozenset)
120
+
121
+ # register relation types that are atomically replaceable, e.g. they have "create or replace" syntax
122
+ # adding a relation type here also requires defining the associated replace macro
123
+ # e.g. adding RelationType.View in dbt-postgres requires that you define:
124
+ # include/postgres/macros/relations/view/replace.sql::postgres__get_replace_view_sql()
125
+ replaceable_relations: SerializableIterable = field(default_factory=frozenset)
126
+
127
+ def _is_exactish_match(self, field: ComponentName, value: str) -> bool:
128
+ if self.dbt_created and self.quote_policy.get_part(field) is False:
129
+ return self.path.get_lowered_part(field) == value.lower()
130
+ else:
131
+ return self.path.get_part(field) == value
132
+
133
+ @classmethod
134
+ def _get_field_named(cls, field_name):
135
+ for f, _ in cls._get_fields():
136
+ if f.name == field_name:
137
+ return f
138
+ # this should be unreachable
139
+ raise ValueError(f"BaseRelation has no {field_name} field!")
140
+
141
+ def __eq__(self, other):
142
+ if not isinstance(other, self.__class__):
143
+ return False
144
+ return self.to_dict(omit_none=True) == other.to_dict(omit_none=True)
145
+
146
+ @classmethod
147
+ def get_default_quote_policy(cls) -> Policy:
148
+ return cls._get_field_named("quote_policy").default_factory()
149
+
150
+ @classmethod
151
+ def get_default_include_policy(cls) -> Policy:
152
+ return cls._get_field_named("include_policy").default_factory()
153
+
154
+ def get(self, key, default=None):
155
+ """Override `.get` to return a metadata object so we don't break
156
+ dbt_utils.
157
+ """
158
+ if key == "metadata":
159
+ return {"type": self.__class__.__name__}
160
+ return super().get(key, default)
161
+
162
+ def matches(
163
+ self,
164
+ database: Optional[str] = None,
165
+ schema: Optional[str] = None,
166
+ identifier: Optional[str] = None,
167
+ ) -> bool:
168
+ search = filter_null_values(
169
+ {
170
+ ComponentName.Database: database,
171
+ ComponentName.Schema: schema,
172
+ ComponentName.Identifier: identifier,
173
+ }
174
+ )
175
+
176
+ if not search:
177
+ # nothing was passed in
178
+ raise DbtRuntimeError("Tried to match relation, but no search path was passed!")
179
+
180
+ exact_match = True
181
+ approximate_match = True
182
+
183
+ for k, v in search.items():
184
+ if not self._is_exactish_match(k, v):
185
+ exact_match = False
186
+ if str(self.path.get_lowered_part(k)).strip(self.quote_character) != v.lower().strip(
187
+ self.quote_character
188
+ ):
189
+ approximate_match = False
190
+
191
+ if approximate_match and not exact_match:
192
+ target = self.create(database=database, schema=schema, identifier=identifier)
193
+ raise ApproximateMatchError(target, self)
194
+
195
+ return exact_match
196
+
197
+ def replace_path(self, **kwargs):
198
+ return self.replace(path=self.path.replace(**kwargs))
199
+
200
+ def quote(
201
+ self: Self,
202
+ database: Optional[bool] = None,
203
+ schema: Optional[bool] = None,
204
+ identifier: Optional[bool] = None,
205
+ ) -> Self:
206
+ policy = filter_null_values(
207
+ {
208
+ ComponentName.Database: database,
209
+ ComponentName.Schema: schema,
210
+ ComponentName.Identifier: identifier,
211
+ }
212
+ )
213
+
214
+ new_quote_policy = self.quote_policy.replace_dict(policy)
215
+ return self.replace(quote_policy=new_quote_policy)
216
+
217
+ def include(
218
+ self: Self,
219
+ database: Optional[bool] = None,
220
+ schema: Optional[bool] = None,
221
+ identifier: Optional[bool] = None,
222
+ ) -> Self:
223
+ policy = filter_null_values(
224
+ {
225
+ ComponentName.Database: database,
226
+ ComponentName.Schema: schema,
227
+ ComponentName.Identifier: identifier,
228
+ }
229
+ )
230
+
231
+ new_include_policy = self.include_policy.replace_dict(policy)
232
+ return self.replace(include_policy=new_include_policy)
233
+
234
+ def information_schema(self, view_name=None) -> "InformationSchema":
235
+ # some of our data comes from jinja, where things can be `Undefined`.
236
+ if not isinstance(view_name, str):
237
+ view_name = None
238
+
239
+ # Kick the user-supplied schema out of the information schema relation
240
+ # Instead address this as <database>.information_schema by default
241
+ info_schema = InformationSchema.from_relation(self, view_name)
242
+ return info_schema.incorporate(path={"schema": None})
243
+
244
+ def information_schema_only(self) -> "InformationSchema":
245
+ return self.information_schema()
246
+
247
+ def without_identifier(self) -> "BaseRelation":
248
+ """Return a form of this relation that only has the database and schema
249
+ set to included. To get the appropriately-quoted form the schema out of
250
+ the result (for use as part of a query), use `.render()`. To get the
251
+ raw database or schema name, use `.database` or `.schema`.
252
+
253
+ The hash of the returned object is the result of render().
254
+ """
255
+ return self.include(identifier=False).replace_path(identifier=None)
256
+
257
+ def _render_iterator(
258
+ self,
259
+ ) -> Iterator[Tuple[Optional[ComponentName], Optional[str]]]:
260
+ for key in ComponentName: # type: ignore
261
+ path_part: Optional[str] = None
262
+ if self.include_policy.get_part(key):
263
+ path_part = self.path.get_part(key)
264
+ if path_part is not None and self.quote_policy.get_part(key):
265
+ path_part = self.quoted(path_part)
266
+ yield key, path_part
267
+
268
+ def render(self) -> str:
269
+ # if there is nothing set, this will return the empty string.
270
+ return ".".join(part for _, part in self._render_iterator() if part is not None)
271
+
272
+ def _render_subquery_alias(self, namespace: str) -> str:
273
+ """Some databases require an alias for subqueries (postgres, mysql) for all others we want to avoid adding
274
+ an alias as it has the potential to introduce issues with the query if the user also defines an alias.
275
+ """
276
+ if self.require_alias:
277
+ return f" _dbt_{namespace}_subq_{self.table}"
278
+ return ""
279
+
280
+ def _render_limited_alias(
281
+ self,
282
+ ) -> str:
283
+ return self._render_subquery_alias(namespace="limit")
284
+
285
+ def render_limited(self) -> str:
286
+ rendered = self.render()
287
+ if self.limit is None:
288
+ return rendered
289
+ elif self.limit == 0:
290
+ return f"(select * from {rendered} where false limit 0){self._render_limited_alias()}"
291
+ else:
292
+ return f"(select * from {rendered} limit {self.limit}){self._render_limited_alias()}"
293
+
294
+ def render_event_time_filtered(self, rendered: Optional[str] = None) -> str:
295
+ rendered = rendered or self.render()
296
+ if self.event_time_filter is None:
297
+ return rendered
298
+
299
+ filter = self._render_event_time_filtered(self.event_time_filter)
300
+ if not filter:
301
+ return rendered
302
+
303
+ return f"(select * from {rendered} where {filter}){self._render_subquery_alias(namespace='et_filter')}"
304
+
305
+ def _render_event_time_filtered(self, event_time_filter: EventTimeFilter) -> str:
306
+ """
307
+ Returns "" if start and end are both None
308
+ """
309
+ filter = ""
310
+ if event_time_filter.start and event_time_filter.end:
311
+ filter = f"{event_time_filter.field_name} >= '{event_time_filter.start}' and {event_time_filter.field_name} < '{event_time_filter.end}'"
312
+ elif event_time_filter.start:
313
+ filter = f"{event_time_filter.field_name} >= '{event_time_filter.start}'"
314
+ elif event_time_filter.end:
315
+ filter = f"{event_time_filter.field_name} < '{event_time_filter.end}'"
316
+
317
+ return filter
318
+
319
+ def quoted(self, identifier):
320
+ return "{quote_char}{identifier}{quote_char}".format(
321
+ quote_char=self.quote_character,
322
+ identifier=identifier,
323
+ )
324
+
325
+ @staticmethod
326
+ def add_ephemeral_prefix(name: str):
327
+ return f"__dbt__cte__{name}"
328
+
329
+ @classmethod
330
+ def create_ephemeral_from(
331
+ cls: Type[Self],
332
+ relation_config: RelationConfig,
333
+ limit: Optional[int] = None,
334
+ event_time_filter: Optional[EventTimeFilter] = None,
335
+ ) -> Self:
336
+ # Note that ephemeral models are based on the identifier, which will
337
+ # point to the model's alias if one exists and otherwise fall back to
338
+ # the filename. This is intended to give the user more control over
339
+ # the way that the CTE name is constructed
340
+ identifier = cls.add_ephemeral_prefix(relation_config.identifier)
341
+ return cls.create(
342
+ type=cls.CTE,
343
+ identifier=identifier,
344
+ limit=limit,
345
+ event_time_filter=event_time_filter,
346
+ ).quote(identifier=False)
347
+
348
+ @classmethod
349
+ def create_from(
350
+ cls: Type[Self],
351
+ quoting: HasQuoting,
352
+ relation_config: RelationConfig,
353
+ **kwargs: Any,
354
+ ) -> Self:
355
+ quote_policy = kwargs.pop("quote_policy", {})
356
+
357
+ config_quoting = relation_config.quoting_dict
358
+ config_quoting.pop("column", None)
359
+
360
+ catalog_name = (
361
+ relation_config.catalog_name
362
+ if hasattr(relation_config, "catalog_name")
363
+ else relation_config.config.get("catalog", None) # type: ignore
364
+ )
365
+
366
+ # precedence: kwargs quoting > relation config quoting > base quoting > default quoting
367
+ quote_policy = deep_merge(
368
+ cls.get_default_quote_policy().to_dict(omit_none=True),
369
+ quoting.quoting,
370
+ config_quoting,
371
+ quote_policy,
372
+ )
373
+
374
+ return cls.create(
375
+ database=relation_config.database,
376
+ schema=relation_config.schema,
377
+ identifier=relation_config.identifier,
378
+ quote_policy=quote_policy,
379
+ catalog_name=catalog_name,
380
+ **kwargs,
381
+ )
382
+
383
+ @classmethod
384
+ def create(
385
+ cls: Type[Self],
386
+ database: Optional[str] = None,
387
+ schema: Optional[str] = None,
388
+ identifier: Optional[str] = None,
389
+ type: Optional[RelationType] = None,
390
+ **kwargs,
391
+ ) -> Self:
392
+ kwargs.update(
393
+ {
394
+ "path": {
395
+ "database": database,
396
+ "schema": schema,
397
+ "identifier": identifier,
398
+ },
399
+ "type": type,
400
+ }
401
+ )
402
+ return cls.from_dict(kwargs)
403
+
404
+ @classmethod
405
+ def scd_args(cls: Type[Self], primary_key: Union[str, List[str]], updated_at) -> List[str]:
406
+ scd_args = []
407
+ if isinstance(primary_key, list):
408
+ scd_args.extend(primary_key)
409
+ else:
410
+ scd_args.append(primary_key)
411
+ scd_args.append(updated_at)
412
+ return scd_args
413
+
414
+ @property
415
+ def can_be_renamed(self) -> bool:
416
+ return self.type in self.renameable_relations
417
+
418
+ @property
419
+ def can_be_replaced(self) -> bool:
420
+ return self.type in self.replaceable_relations
421
+
422
+ def __repr__(self) -> str:
423
+ return "<{} {}>".format(self.__class__.__name__, self.render())
424
+
425
+ def __hash__(self) -> int:
426
+ return hash(self.render())
427
+
428
+ def __str__(self) -> str:
429
+ # TODO: This function seems to have more if's than it needs to. We should see if we can simplify it.
430
+ if self.is_function:
431
+ # If it's a function we skip all special rendering logic and just return the raw render
432
+ rendered = self.render()
433
+ else:
434
+ rendered = self.render() if self.limit is None else self.render_limited()
435
+
436
+ # Limited subquery is wrapped by the event time filter subquery, and not the other way around.
437
+ # This is because in the context of resolving limited refs, we care more about performance than reliably producing a sample of a certain size.
438
+ if self.event_time_filter:
439
+ rendered = self.render_event_time_filtered(rendered)
440
+
441
+ return rendered
442
+
443
+ @property
444
+ def database(self) -> Optional[str]:
445
+ return self.path.database
446
+
447
+ @property
448
+ def schema(self) -> Optional[str]:
449
+ return self.path.schema
450
+
451
+ @property
452
+ def identifier(self) -> Optional[str]:
453
+ return self.path.identifier
454
+
455
+ @property
456
+ def table(self) -> Optional[str]:
457
+ return self.path.identifier
458
+
459
+ # Here for compatibility with old Relation interface
460
+ @property
461
+ def name(self) -> Optional[str]:
462
+ return self.identifier
463
+
464
+ @property
465
+ def is_table(self) -> bool:
466
+ return self.type == RelationType.Table
467
+
468
+ @property
469
+ def is_cte(self) -> bool:
470
+ return self.type == RelationType.CTE
471
+
472
+ @property
473
+ def is_view(self) -> bool:
474
+ return self.type == RelationType.View
475
+
476
+ @property
477
+ def is_materialized_view(self) -> bool:
478
+ return self.type == RelationType.MaterializedView
479
+
480
+ @property
481
+ def is_pointer(self) -> bool:
482
+ return self.type == RelationType.PointerTable
483
+
484
+ @property
485
+ def is_function(self) -> bool:
486
+ return self.type == RelationType.Function
487
+
488
+ @classproperty
489
+ def Table(cls) -> str:
490
+ return str(RelationType.Table)
491
+
492
+ @classproperty
493
+ def CTE(cls) -> str:
494
+ return str(RelationType.CTE)
495
+
496
+ @classproperty
497
+ def View(cls) -> str:
498
+ return str(RelationType.View)
499
+
500
+ @classproperty
501
+ def External(cls) -> str:
502
+ return str(RelationType.External)
503
+
504
+ @classproperty
505
+ def MaterializedView(cls) -> str:
506
+ return str(RelationType.MaterializedView)
507
+
508
+ @classproperty
509
+ def PointerTable(cls) -> str:
510
+ return str(RelationType.PointerTable)
511
+
512
+ @classproperty
513
+ def Function(cls) -> str:
514
+ return str(RelationType.Function)
515
+
516
+ @classproperty
517
+ def get_relation_type(cls) -> Type[RelationType]:
518
+ return RelationType
519
+
520
+ def get_function_config(self, model: Dict[str, Any]) -> Optional[FunctionConfig]:
521
+ # TODO: We shouldn't have to check the model.resource_type here. We should be alble to do self.is_function instead.
522
+ # However, somehow when we get here self.type is None, and thus self.is_function is False.
523
+ if model.get("resource_type") == "function":
524
+ return FunctionConfig(
525
+ language=model.get("language", ""),
526
+ type=model.get("config", {}).get("type", ""),
527
+ runtime_version=model.get("config", {}).get("runtime_version", None),
528
+ entry_point=model.get("config", {}).get("entry_point", None),
529
+ )
530
+ else:
531
+ return None
532
+
533
+ def get_function_macro_name(self, config: FunctionConfig) -> str:
534
+ return f"{config.type}_function_{config.language}"
535
+
536
+
537
+ Info = TypeVar("Info", bound="InformationSchema")
538
+
539
+
540
+ @dataclass(frozen=True, eq=False, repr=False)
541
+ class InformationSchema(BaseRelation):
542
+ information_schema_view: Optional[str] = None
543
+
544
+ def __post_init__(self):
545
+ if not isinstance(self.information_schema_view, (type(None), str)):
546
+ raise CompilationError("Got an invalid name: {}".format(self.information_schema_view))
547
+
548
+ @classmethod
549
+ def get_path(cls, relation: BaseRelation, information_schema_view: Optional[str]) -> Path:
550
+ return Path(
551
+ database=relation.database,
552
+ schema=relation.schema,
553
+ identifier="INFORMATION_SCHEMA",
554
+ )
555
+
556
+ @classmethod
557
+ def get_include_policy(
558
+ cls,
559
+ relation,
560
+ information_schema_view: Optional[str],
561
+ ) -> Policy:
562
+ return relation.include_policy.replace(
563
+ database=relation.database is not None,
564
+ schema=False,
565
+ identifier=True,
566
+ )
567
+
568
+ @classmethod
569
+ def get_quote_policy(
570
+ cls,
571
+ relation,
572
+ information_schema_view: Optional[str],
573
+ ) -> Policy:
574
+ return relation.quote_policy.replace(
575
+ identifier=False,
576
+ )
577
+
578
+ @classmethod
579
+ def from_relation(
580
+ cls: Type[Info],
581
+ relation: BaseRelation,
582
+ information_schema_view: Optional[str],
583
+ ) -> Info:
584
+ include_policy = cls.get_include_policy(relation, information_schema_view)
585
+ quote_policy = cls.get_quote_policy(relation, information_schema_view)
586
+ path = cls.get_path(relation, information_schema_view)
587
+ return cls(
588
+ type=RelationType.View, # type: ignore
589
+ path=path,
590
+ include_policy=include_policy,
591
+ quote_policy=quote_policy,
592
+ information_schema_view=information_schema_view,
593
+ )
594
+
595
+ def _render_iterator(self):
596
+ for k, v in super()._render_iterator():
597
+ yield k, v
598
+ yield None, self.information_schema_view
599
+
600
+
601
+ class SchemaSearchMap(Dict[InformationSchema, Set[Optional[str]]]):
602
+ """A utility class to keep track of what information_schema tables to
603
+ search for what schemas. The schema values are all lowercased to avoid
604
+ duplication.
605
+ """
606
+
607
+ def add(self, relation: BaseRelation):
608
+ key = relation.information_schema_only()
609
+ if key not in self:
610
+ self[key] = set()
611
+ schema: Optional[str] = None
612
+ if relation.schema is not None:
613
+ schema = relation.schema.lower()
614
+ self[key].add(schema)
615
+
616
+ def search(self) -> Iterator[Tuple[InformationSchema, Optional[str]]]:
617
+ for information_schema, schemas in self.items():
618
+ for schema in schemas:
619
+ yield information_schema, schema
620
+
621
+ def flatten(self, allow_multiple_databases: bool = False) -> "SchemaSearchMap":
622
+ new = self.__class__()
623
+
624
+ # make sure we don't have multiple databases if allow_multiple_databases is set to False
625
+ if not allow_multiple_databases:
626
+ seen = {r.database.lower() for r in self if r.database}
627
+ if len(seen) > 1:
628
+ raise MultipleDatabasesNotAllowedError(seen)
629
+
630
+ for information_schema_name, schema in self.search():
631
+ path = {"database": information_schema_name.database, "schema": schema}
632
+ new.add(
633
+ information_schema_name.incorporate(
634
+ path=path,
635
+ quote_policy={"database": False},
636
+ include_policy={"database": False},
637
+ )
638
+ )
639
+
640
+ return new
641
+
642
+
643
+ @dataclass(frozen=True, eq=False, repr=False)
644
+ class AdapterTrackingRelationInfo(FakeAPIObject, Hashable):
645
+ adapter_name: str
646
+ base_adapter_version: str
647
+ adapter_version: str
648
+ model_adapter_details: Any