SQLAlchemy 2.1.0rc1__cp315-cp315-win32.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 (276) hide show
  1. sqlalchemy/__init__.py +299 -0
  2. sqlalchemy/connectors/__init__.py +18 -0
  3. sqlalchemy/connectors/aioodbc.py +171 -0
  4. sqlalchemy/connectors/asyncio.py +476 -0
  5. sqlalchemy/connectors/pyodbc.py +248 -0
  6. sqlalchemy/dialects/__init__.py +62 -0
  7. sqlalchemy/dialects/_typing.py +29 -0
  8. sqlalchemy/dialects/mssql/__init__.py +88 -0
  9. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  10. sqlalchemy/dialects/mssql/base.py +4833 -0
  11. sqlalchemy/dialects/mssql/information_schema.py +345 -0
  12. sqlalchemy/dialects/mssql/json.py +140 -0
  13. sqlalchemy/dialects/mssql/mssqlpython.py +242 -0
  14. sqlalchemy/dialects/mssql/provision.py +196 -0
  15. sqlalchemy/dialects/mssql/pymssql.py +130 -0
  16. sqlalchemy/dialects/mssql/pyodbc.py +697 -0
  17. sqlalchemy/dialects/mysql/__init__.py +106 -0
  18. sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
  19. sqlalchemy/dialects/mysql/aiomysql.py +260 -0
  20. sqlalchemy/dialects/mysql/asyncmy.py +241 -0
  21. sqlalchemy/dialects/mysql/base.py +3896 -0
  22. sqlalchemy/dialects/mysql/cymysql.py +107 -0
  23. sqlalchemy/dialects/mysql/dml.py +279 -0
  24. sqlalchemy/dialects/mysql/enumerated.py +277 -0
  25. sqlalchemy/dialects/mysql/expression.py +146 -0
  26. sqlalchemy/dialects/mysql/json.py +92 -0
  27. sqlalchemy/dialects/mysql/mariadb.py +67 -0
  28. sqlalchemy/dialects/mysql/mariadbconnector.py +314 -0
  29. sqlalchemy/dialects/mysql/mysqlconnector.py +291 -0
  30. sqlalchemy/dialects/mysql/mysqldb.py +318 -0
  31. sqlalchemy/dialects/mysql/provision.py +153 -0
  32. sqlalchemy/dialects/mysql/pymysql.py +188 -0
  33. sqlalchemy/dialects/mysql/pyodbc.py +157 -0
  34. sqlalchemy/dialects/mysql/reflection.py +724 -0
  35. sqlalchemy/dialects/mysql/reserved_words.py +570 -0
  36. sqlalchemy/dialects/mysql/types.py +845 -0
  37. sqlalchemy/dialects/oracle/__init__.py +85 -0
  38. sqlalchemy/dialects/oracle/base.py +3847 -0
  39. sqlalchemy/dialects/oracle/cx_oracle.py +1736 -0
  40. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  41. sqlalchemy/dialects/oracle/json.py +157 -0
  42. sqlalchemy/dialects/oracle/oracledb.py +898 -0
  43. sqlalchemy/dialects/oracle/provision.py +288 -0
  44. sqlalchemy/dialects/oracle/types.py +367 -0
  45. sqlalchemy/dialects/oracle/vector.py +366 -0
  46. sqlalchemy/dialects/postgresql/__init__.py +170 -0
  47. sqlalchemy/dialects/postgresql/_psycopg_common.py +232 -0
  48. sqlalchemy/dialects/postgresql/array.py +534 -0
  49. sqlalchemy/dialects/postgresql/asyncpg.py +1318 -0
  50. sqlalchemy/dialects/postgresql/base.py +5935 -0
  51. sqlalchemy/dialects/postgresql/bitstring.py +327 -0
  52. sqlalchemy/dialects/postgresql/dml.py +360 -0
  53. sqlalchemy/dialects/postgresql/ext.py +599 -0
  54. sqlalchemy/dialects/postgresql/hstore.py +422 -0
  55. sqlalchemy/dialects/postgresql/json.py +411 -0
  56. sqlalchemy/dialects/postgresql/named_types.py +535 -0
  57. sqlalchemy/dialects/postgresql/operators.py +129 -0
  58. sqlalchemy/dialects/postgresql/pg8000.py +655 -0
  59. sqlalchemy/dialects/postgresql/pg_catalog.py +345 -0
  60. sqlalchemy/dialects/postgresql/provision.py +202 -0
  61. sqlalchemy/dialects/postgresql/psycopg.py +800 -0
  62. sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
  63. sqlalchemy/dialects/postgresql/psycopg2cffi.py +62 -0
  64. sqlalchemy/dialects/postgresql/ranges.py +1002 -0
  65. sqlalchemy/dialects/postgresql/types.py +388 -0
  66. sqlalchemy/dialects/sqlite/__init__.py +59 -0
  67. sqlalchemy/dialects/sqlite/aiosqlite.py +375 -0
  68. sqlalchemy/dialects/sqlite/base.py +3103 -0
  69. sqlalchemy/dialects/sqlite/dml.py +314 -0
  70. sqlalchemy/dialects/sqlite/json.py +134 -0
  71. sqlalchemy/dialects/sqlite/provision.py +237 -0
  72. sqlalchemy/dialects/sqlite/pysqlcipher.py +166 -0
  73. sqlalchemy/dialects/sqlite/pysqlite.py +959 -0
  74. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  75. sqlalchemy/engine/__init__.py +62 -0
  76. sqlalchemy/engine/_processors_cy.cp315-win32.pyd +0 -0
  77. sqlalchemy/engine/_processors_cy.py +92 -0
  78. sqlalchemy/engine/_result_cy.cp315-win32.pyd +0 -0
  79. sqlalchemy/engine/_result_cy.py +711 -0
  80. sqlalchemy/engine/_row_cy.cp315-win32.pyd +0 -0
  81. sqlalchemy/engine/_row_cy.py +232 -0
  82. sqlalchemy/engine/_util_cy.cp315-win32.pyd +0 -0
  83. sqlalchemy/engine/_util_cy.py +136 -0
  84. sqlalchemy/engine/base.py +3357 -0
  85. sqlalchemy/engine/characteristics.py +155 -0
  86. sqlalchemy/engine/create.py +877 -0
  87. sqlalchemy/engine/cursor.py +2425 -0
  88. sqlalchemy/engine/default.py +2627 -0
  89. sqlalchemy/engine/events.py +965 -0
  90. sqlalchemy/engine/interfaces.py +3636 -0
  91. sqlalchemy/engine/mock.py +133 -0
  92. sqlalchemy/engine/processors.py +83 -0
  93. sqlalchemy/engine/reflection.py +2141 -0
  94. sqlalchemy/engine/result.py +2012 -0
  95. sqlalchemy/engine/row.py +397 -0
  96. sqlalchemy/engine/strategies.py +16 -0
  97. sqlalchemy/engine/url.py +922 -0
  98. sqlalchemy/engine/util.py +164 -0
  99. sqlalchemy/event/__init__.py +26 -0
  100. sqlalchemy/event/api.py +220 -0
  101. sqlalchemy/event/attr.py +675 -0
  102. sqlalchemy/event/base.py +473 -0
  103. sqlalchemy/event/legacy.py +259 -0
  104. sqlalchemy/event/registry.py +391 -0
  105. sqlalchemy/events.py +17 -0
  106. sqlalchemy/exc.py +939 -0
  107. sqlalchemy/ext/__init__.py +10 -0
  108. sqlalchemy/ext/associationproxy.py +2073 -0
  109. sqlalchemy/ext/asyncio/__init__.py +29 -0
  110. sqlalchemy/ext/asyncio/base.py +281 -0
  111. sqlalchemy/ext/asyncio/engine.py +1487 -0
  112. sqlalchemy/ext/asyncio/exc.py +21 -0
  113. sqlalchemy/ext/asyncio/result.py +994 -0
  114. sqlalchemy/ext/asyncio/scoping.py +1679 -0
  115. sqlalchemy/ext/asyncio/session.py +2006 -0
  116. sqlalchemy/ext/automap.py +1702 -0
  117. sqlalchemy/ext/baked.py +558 -0
  118. sqlalchemy/ext/compiler.py +601 -0
  119. sqlalchemy/ext/declarative/__init__.py +65 -0
  120. sqlalchemy/ext/declarative/extensions.py +561 -0
  121. sqlalchemy/ext/horizontal_shard.py +481 -0
  122. sqlalchemy/ext/hybrid.py +1877 -0
  123. sqlalchemy/ext/indexable.py +364 -0
  124. sqlalchemy/ext/instrumentation.py +450 -0
  125. sqlalchemy/ext/mutable.py +1081 -0
  126. sqlalchemy/ext/orderinglist.py +440 -0
  127. sqlalchemy/ext/serializer.py +184 -0
  128. sqlalchemy/future/__init__.py +17 -0
  129. sqlalchemy/future/engine.py +15 -0
  130. sqlalchemy/inspection.py +188 -0
  131. sqlalchemy/log.py +279 -0
  132. sqlalchemy/orm/__init__.py +176 -0
  133. sqlalchemy/orm/_orm_constructors.py +2694 -0
  134. sqlalchemy/orm/_typing.py +180 -0
  135. sqlalchemy/orm/attributes.py +2868 -0
  136. sqlalchemy/orm/base.py +991 -0
  137. sqlalchemy/orm/bulk_persistence.py +2168 -0
  138. sqlalchemy/orm/clsregistry.py +630 -0
  139. sqlalchemy/orm/collections.py +1569 -0
  140. sqlalchemy/orm/context.py +3475 -0
  141. sqlalchemy/orm/decl_api.py +2283 -0
  142. sqlalchemy/orm/decl_base.py +2320 -0
  143. sqlalchemy/orm/dependency.py +1306 -0
  144. sqlalchemy/orm/descriptor_props.py +1183 -0
  145. sqlalchemy/orm/dynamic.py +306 -0
  146. sqlalchemy/orm/evaluator.py +378 -0
  147. sqlalchemy/orm/events.py +3387 -0
  148. sqlalchemy/orm/exc.py +237 -0
  149. sqlalchemy/orm/identity.py +302 -0
  150. sqlalchemy/orm/instrumentation.py +749 -0
  151. sqlalchemy/orm/interfaces.py +1595 -0
  152. sqlalchemy/orm/loading.py +1712 -0
  153. sqlalchemy/orm/mapped_collection.py +557 -0
  154. sqlalchemy/orm/mapper.py +4465 -0
  155. sqlalchemy/orm/path_registry.py +907 -0
  156. sqlalchemy/orm/persistence.py +1790 -0
  157. sqlalchemy/orm/properties.py +972 -0
  158. sqlalchemy/orm/query.py +3528 -0
  159. sqlalchemy/orm/relationships.py +3608 -0
  160. sqlalchemy/orm/scoping.py +2233 -0
  161. sqlalchemy/orm/session.py +5468 -0
  162. sqlalchemy/orm/state.py +1175 -0
  163. sqlalchemy/orm/state_changes.py +196 -0
  164. sqlalchemy/orm/strategies.py +3552 -0
  165. sqlalchemy/orm/strategy_options.py +2648 -0
  166. sqlalchemy/orm/sync.py +164 -0
  167. sqlalchemy/orm/unitofwork.py +797 -0
  168. sqlalchemy/orm/util.py +2461 -0
  169. sqlalchemy/orm/writeonly.py +701 -0
  170. sqlalchemy/pool/__init__.py +41 -0
  171. sqlalchemy/pool/base.py +1540 -0
  172. sqlalchemy/pool/events.py +375 -0
  173. sqlalchemy/pool/impl.py +583 -0
  174. sqlalchemy/py.typed +0 -0
  175. sqlalchemy/schema.py +75 -0
  176. sqlalchemy/sql/__init__.py +156 -0
  177. sqlalchemy/sql/_annotated_cols.py +402 -0
  178. sqlalchemy/sql/_cache_key_cy.cp315-win32.pyd +0 -0
  179. sqlalchemy/sql/_cache_key_cy.py +363 -0
  180. sqlalchemy/sql/_dml_constructors.py +132 -0
  181. sqlalchemy/sql/_elements_constructors.py +2190 -0
  182. sqlalchemy/sql/_orm_types.py +19 -0
  183. sqlalchemy/sql/_selectable_constructors.py +840 -0
  184. sqlalchemy/sql/_typing.py +500 -0
  185. sqlalchemy/sql/_util_cy.cp315-win32.pyd +0 -0
  186. sqlalchemy/sql/_util_cy.pxd +11 -0
  187. sqlalchemy/sql/_util_cy.py +127 -0
  188. sqlalchemy/sql/annotation.py +590 -0
  189. sqlalchemy/sql/base.py +2702 -0
  190. sqlalchemy/sql/cache_key.py +915 -0
  191. sqlalchemy/sql/coercions.py +1373 -0
  192. sqlalchemy/sql/compiler.py +8453 -0
  193. sqlalchemy/sql/crud.py +1816 -0
  194. sqlalchemy/sql/ddl.py +1962 -0
  195. sqlalchemy/sql/default_comparator.py +660 -0
  196. sqlalchemy/sql/dml.py +2018 -0
  197. sqlalchemy/sql/elements.py +6057 -0
  198. sqlalchemy/sql/events.py +458 -0
  199. sqlalchemy/sql/expression.py +171 -0
  200. sqlalchemy/sql/functions.py +2380 -0
  201. sqlalchemy/sql/lambdas.py +1442 -0
  202. sqlalchemy/sql/naming.py +204 -0
  203. sqlalchemy/sql/operators.py +2909 -0
  204. sqlalchemy/sql/roles.py +332 -0
  205. sqlalchemy/sql/schema.py +7075 -0
  206. sqlalchemy/sql/selectable.py +7634 -0
  207. sqlalchemy/sql/sqltypes.py +4130 -0
  208. sqlalchemy/sql/traversals.py +1041 -0
  209. sqlalchemy/sql/type_api.py +2450 -0
  210. sqlalchemy/sql/util.py +1496 -0
  211. sqlalchemy/sql/visitors.py +1153 -0
  212. sqlalchemy/testing/__init__.py +97 -0
  213. sqlalchemy/testing/assertions.py +1007 -0
  214. sqlalchemy/testing/assertsql.py +519 -0
  215. sqlalchemy/testing/asyncio.py +128 -0
  216. sqlalchemy/testing/cancellation.py +237 -0
  217. sqlalchemy/testing/config.py +440 -0
  218. sqlalchemy/testing/engines.py +482 -0
  219. sqlalchemy/testing/entities.py +117 -0
  220. sqlalchemy/testing/exclusions.py +501 -0
  221. sqlalchemy/testing/fixtures/__init__.py +30 -0
  222. sqlalchemy/testing/fixtures/base.py +426 -0
  223. sqlalchemy/testing/fixtures/mypy.py +247 -0
  224. sqlalchemy/testing/fixtures/orm.py +227 -0
  225. sqlalchemy/testing/fixtures/sql.py +538 -0
  226. sqlalchemy/testing/pickleable.py +155 -0
  227. sqlalchemy/testing/plugin/__init__.py +6 -0
  228. sqlalchemy/testing/plugin/bootstrap.py +50 -0
  229. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  230. sqlalchemy/testing/plugin/pytestplugin.py +896 -0
  231. sqlalchemy/testing/profiles_file.py +350 -0
  232. sqlalchemy/testing/profiling.py +294 -0
  233. sqlalchemy/testing/provision.py +633 -0
  234. sqlalchemy/testing/requirements.py +1971 -0
  235. sqlalchemy/testing/schema.py +198 -0
  236. sqlalchemy/testing/suite/__init__.py +19 -0
  237. sqlalchemy/testing/suite/test_cte.py +237 -0
  238. sqlalchemy/testing/suite/test_ddl.py +420 -0
  239. sqlalchemy/testing/suite/test_dialect.py +776 -0
  240. sqlalchemy/testing/suite/test_insert.py +630 -0
  241. sqlalchemy/testing/suite/test_reflection.py +3815 -0
  242. sqlalchemy/testing/suite/test_results.py +660 -0
  243. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  244. sqlalchemy/testing/suite/test_select.py +2112 -0
  245. sqlalchemy/testing/suite/test_sequence.py +317 -0
  246. sqlalchemy/testing/suite/test_table_via_select.py +686 -0
  247. sqlalchemy/testing/suite/test_types.py +2271 -0
  248. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  249. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  250. sqlalchemy/testing/util.py +575 -0
  251. sqlalchemy/testing/warnings.py +52 -0
  252. sqlalchemy/types.py +75 -0
  253. sqlalchemy/util/__init__.py +165 -0
  254. sqlalchemy/util/_collections.py +688 -0
  255. sqlalchemy/util/_collections_cy.cp315-win32.pyd +0 -0
  256. sqlalchemy/util/_collections_cy.pxd +8 -0
  257. sqlalchemy/util/_collections_cy.py +516 -0
  258. sqlalchemy/util/_has_cython.py +48 -0
  259. sqlalchemy/util/_immutabledict_cy.cp315-win32.pyd +0 -0
  260. sqlalchemy/util/_immutabledict_cy.py +240 -0
  261. sqlalchemy/util/compat.py +298 -0
  262. sqlalchemy/util/concurrency.py +272 -0
  263. sqlalchemy/util/cython.py +95 -0
  264. sqlalchemy/util/deprecations.py +401 -0
  265. sqlalchemy/util/langhelpers.py +2797 -0
  266. sqlalchemy/util/preloaded.py +153 -0
  267. sqlalchemy/util/queue.py +304 -0
  268. sqlalchemy/util/tool_support.py +202 -0
  269. sqlalchemy/util/topological.py +120 -0
  270. sqlalchemy/util/typing.py +709 -0
  271. sqlalchemy-2.1.0rc1.dist-info/METADATA +270 -0
  272. sqlalchemy-2.1.0rc1.dist-info/RECORD +276 -0
  273. sqlalchemy-2.1.0rc1.dist-info/WHEEL +5 -0
  274. sqlalchemy-2.1.0rc1.dist-info/licenses/AUTHORS +30 -0
  275. sqlalchemy-2.1.0rc1.dist-info/licenses/LICENSE +19 -0
  276. sqlalchemy-2.1.0rc1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2141 @@
1
+ # engine/reflection.py
2
+ # Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3
+ # <see AUTHORS file>
4
+ #
5
+ # This module is part of SQLAlchemy and is released under
6
+ # the MIT License: https://www.opensource.org/licenses/mit-license.php
7
+
8
+ """Provides an abstraction for obtaining database schema information.
9
+
10
+ Usage Notes:
11
+
12
+ Here are some general conventions when accessing the low level inspector
13
+ methods such as get_table_names, get_columns, etc.
14
+
15
+ 1. Inspector methods return lists of dicts in most cases for the following
16
+ reasons:
17
+
18
+ * They're both standard types that can be serialized.
19
+ * Using a dict instead of a tuple allows easy expansion of attributes.
20
+ * Using a list for the outer structure maintains order and is easy to work
21
+ with (e.g. list comprehension [d['name'] for d in cols]).
22
+
23
+ 2. Records that contain a name, such as the column name in a column record
24
+ use the key 'name'. So for most return values, each record will have a
25
+ 'name' attribute..
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import contextlib
31
+ from dataclasses import dataclass
32
+ from enum import auto
33
+ from enum import Flag
34
+ from enum import unique
35
+ from typing import Any
36
+ from typing import Callable
37
+ from typing import Collection
38
+ from typing import Dict
39
+ from typing import final
40
+ from typing import Generator
41
+ from typing import Iterable
42
+ from typing import List
43
+ from typing import Optional
44
+ from typing import Sequence
45
+ from typing import Set
46
+ from typing import Tuple
47
+ from typing import TYPE_CHECKING
48
+ from typing import TypeVar
49
+ from typing import Union
50
+
51
+ from .base import Connection
52
+ from .base import Engine
53
+ from .. import exc
54
+ from .. import inspection
55
+ from .. import sql
56
+ from .. import util
57
+ from ..sql import operators
58
+ from ..sql import schema as sa_schema
59
+ from ..sql.cache_key import _ad_hoc_cache_key_from_args
60
+ from ..sql.elements import quoted_name
61
+ from ..sql.elements import TextClause
62
+ from ..sql.type_api import TypeEngine
63
+ from ..sql.visitors import InternalTraversal
64
+ from ..util import topological
65
+
66
+ if TYPE_CHECKING:
67
+ from .interfaces import Dialect
68
+ from .interfaces import ReflectedCheckConstraint
69
+ from .interfaces import ReflectedColumn
70
+ from .interfaces import ReflectedForeignKeyConstraint
71
+ from .interfaces import ReflectedIndex
72
+ from .interfaces import ReflectedPrimaryKeyConstraint
73
+ from .interfaces import ReflectedTableComment
74
+ from .interfaces import ReflectedUniqueConstraint
75
+ from .interfaces import TableKey
76
+
77
+ _R = TypeVar("_R")
78
+
79
+
80
+ @util.decorator
81
+ def cache(
82
+ fn: Callable[..., _R],
83
+ self: Dialect,
84
+ con: Connection,
85
+ *args: Any,
86
+ **kw: Any,
87
+ ) -> _R:
88
+ info_cache = kw.get("info_cache", None)
89
+ if info_cache is None:
90
+ return fn(self, con, *args, **kw)
91
+ exclude = {"info_cache", "unreflectable"}
92
+ key = (
93
+ fn.__name__,
94
+ tuple(
95
+ (str(a), a.quote) if isinstance(a, quoted_name) else a
96
+ for a in args
97
+ if isinstance(a, str)
98
+ ),
99
+ tuple(
100
+ (k, (str(v), v.quote) if isinstance(v, quoted_name) else v)
101
+ for k, v in kw.items()
102
+ if k not in exclude
103
+ ),
104
+ )
105
+ ret: _R = info_cache.get(key)
106
+ if ret is None:
107
+ ret = fn(self, con, *args, **kw)
108
+ info_cache[key] = ret
109
+ return ret
110
+
111
+
112
+ def flexi_cache(
113
+ *traverse_args: Tuple[str, InternalTraversal]
114
+ ) -> Callable[[Callable[..., _R]], Callable[..., _R]]:
115
+ @util.decorator
116
+ def go(
117
+ fn: Callable[..., _R],
118
+ self: Dialect,
119
+ con: Connection,
120
+ *args: Any,
121
+ **kw: Any,
122
+ ) -> _R:
123
+ info_cache = kw.get("info_cache", None)
124
+ if info_cache is None:
125
+ return fn(self, con, *args, **kw)
126
+ key = _ad_hoc_cache_key_from_args((fn.__name__,), traverse_args, args)
127
+ ret: _R = info_cache.get(key)
128
+ if ret is None:
129
+ ret = fn(self, con, *args, **kw)
130
+ info_cache[key] = ret
131
+ return ret
132
+
133
+ return go
134
+
135
+
136
+ @unique
137
+ class ObjectKind(Flag):
138
+ """Enumerator that indicates which kind of object to return when calling
139
+ the ``get_multi`` methods.
140
+
141
+ This is a Flag enum, so custom combinations can be passed. For example,
142
+ to reflect tables and plain views ``ObjectKind.TABLE | ObjectKind.VIEW``
143
+ may be used.
144
+
145
+ .. note::
146
+ Not all dialect may support all kind of object. If a dialect does
147
+ not support a particular object an empty dict is returned.
148
+ In case a dialect supports an object, but the requested method
149
+ is not applicable for the specified kind the default value
150
+ will be returned for each reflected object. For example reflecting
151
+ check constraints of view return a dict with all the views with
152
+ empty lists as values.
153
+ """
154
+
155
+ TABLE = auto()
156
+ "Reflect table objects"
157
+ VIEW = auto()
158
+ "Reflect plain view objects"
159
+ MATERIALIZED_VIEW = auto()
160
+ "Reflect materialized view object"
161
+
162
+ ANY_VIEW = VIEW | MATERIALIZED_VIEW
163
+ "Reflect any kind of view objects"
164
+ ANY = TABLE | VIEW | MATERIALIZED_VIEW
165
+ "Reflect all type of objects"
166
+
167
+
168
+ @unique
169
+ class ObjectScope(Flag):
170
+ """Enumerator that indicates which scope to use when calling
171
+ the ``get_multi`` methods.
172
+ """
173
+
174
+ DEFAULT = auto()
175
+ "Include default scope"
176
+ TEMPORARY = auto()
177
+ "Include only temp scope"
178
+ ANY = DEFAULT | TEMPORARY
179
+ "Include both default and temp scope"
180
+
181
+
182
+ @inspection._self_inspects
183
+ class Inspector(inspection.Inspectable["Inspector"]):
184
+ """Performs database schema inspection.
185
+
186
+ The Inspector acts as a proxy to the reflection methods of the
187
+ :class:`~sqlalchemy.engine.interfaces.Dialect`, providing a
188
+ consistent interface as well as caching support for previously
189
+ fetched metadata.
190
+
191
+ The caching behavior is dialect-specific: as a general rule single table
192
+ reflection methods should cache their result, while multi-table reflection
193
+ methods should not, but behavior may vary.
194
+ Following DDL operations on the database, it is recommended to either
195
+ create a new :class:`_reflection.Inspector` instance or to clear the cache
196
+ of existing instances using the :meth:`.Inspector.clear_cache` method.
197
+
198
+ A :class:`_reflection.Inspector` object is usually created via the
199
+ :func:`_sa.inspect` function, which may be passed an
200
+ :class:`_engine.Engine`
201
+ or a :class:`_engine.Connection`::
202
+
203
+ from sqlalchemy import inspect, create_engine
204
+
205
+ engine = create_engine("...")
206
+ insp = inspect(engine)
207
+
208
+ Where above, the :class:`~sqlalchemy.engine.interfaces.Dialect` associated
209
+ with the engine may opt to return an :class:`_reflection.Inspector`
210
+ subclass that
211
+ provides additional methods specific to the dialect's target database.
212
+
213
+ """
214
+
215
+ bind: Union[Engine, Connection]
216
+ engine: Engine
217
+ _op_context_requires_connect: bool
218
+ dialect: Dialect
219
+ info_cache: Dict[Any, Any]
220
+
221
+ @util.deprecated(
222
+ "1.4",
223
+ "The __init__() method on :class:`_reflection.Inspector` "
224
+ "is deprecated and "
225
+ "will be removed in a future release. Please use the "
226
+ ":func:`.sqlalchemy.inspect` "
227
+ "function on an :class:`_engine.Engine` or "
228
+ ":class:`_engine.Connection` "
229
+ "in order to "
230
+ "acquire an :class:`_reflection.Inspector`.",
231
+ )
232
+ def __init__(self, bind: Union[Engine, Connection]):
233
+ """Initialize a new :class:`_reflection.Inspector`.
234
+
235
+ :param bind: a :class:`~sqlalchemy.engine.Connection`,
236
+ which is typically an instance of
237
+ :class:`~sqlalchemy.engine.Engine` or
238
+ :class:`~sqlalchemy.engine.Connection`.
239
+
240
+ For a dialect-specific instance of :class:`_reflection.Inspector`, see
241
+ :meth:`_reflection.Inspector.from_engine`
242
+
243
+ """
244
+ self._init_legacy(bind)
245
+
246
+ @classmethod
247
+ def _construct(
248
+ cls, init: Callable[..., Any], bind: Union[Engine, Connection]
249
+ ) -> Inspector:
250
+ if hasattr(bind.dialect, "inspector"):
251
+ cls = bind.dialect.inspector
252
+
253
+ self = cls.__new__(cls)
254
+ init(self, bind)
255
+ return self
256
+
257
+ def _init_legacy(self, bind: Union[Engine, Connection]) -> None:
258
+ if hasattr(bind, "exec_driver_sql"):
259
+ self._init_connection(bind) # type: ignore[arg-type]
260
+ else:
261
+ self._init_engine(bind)
262
+
263
+ def _init_engine(self, engine: Engine) -> None:
264
+ self.bind = self.engine = engine
265
+ engine.connect().close()
266
+ self._op_context_requires_connect = True
267
+ self.dialect = self.engine.dialect
268
+ self.info_cache = {}
269
+
270
+ def _init_connection(self, connection: Connection) -> None:
271
+ self.bind = connection
272
+ self.engine = connection.engine
273
+ self._op_context_requires_connect = False
274
+ self.dialect = self.engine.dialect
275
+ self.info_cache = {}
276
+
277
+ def clear_cache(self) -> None:
278
+ """Reset the cache for this :class:`.Inspector`.
279
+
280
+ Inspection methods that have data cached will emit SQL queries
281
+ when next called to get new data.
282
+
283
+ .. versionadded:: 2.0
284
+
285
+ """
286
+ self.info_cache.clear()
287
+
288
+ @classmethod
289
+ @util.deprecated(
290
+ "1.4",
291
+ "The from_engine() method on :class:`_reflection.Inspector` "
292
+ "is deprecated and "
293
+ "will be removed in a future release. Please use the "
294
+ ":func:`.sqlalchemy.inspect` "
295
+ "function on an :class:`_engine.Engine` or "
296
+ ":class:`_engine.Connection` "
297
+ "in order to "
298
+ "acquire an :class:`_reflection.Inspector`.",
299
+ )
300
+ def from_engine(cls, bind: Engine) -> Inspector:
301
+ """Construct a new dialect-specific Inspector object from the given
302
+ engine or connection.
303
+
304
+ :param bind: a :class:`~sqlalchemy.engine.Connection`
305
+ or :class:`~sqlalchemy.engine.Engine`.
306
+
307
+ This method differs from direct a direct constructor call of
308
+ :class:`_reflection.Inspector` in that the
309
+ :class:`~sqlalchemy.engine.interfaces.Dialect` is given a chance to
310
+ provide a dialect-specific :class:`_reflection.Inspector` instance,
311
+ which may
312
+ provide additional methods.
313
+
314
+ See the example at :class:`_reflection.Inspector`.
315
+
316
+ """
317
+ return cls._construct(cls._init_legacy, bind)
318
+
319
+ @inspection._inspects(Engine)
320
+ def _engine_insp(bind: Engine) -> Inspector: # type: ignore[misc]
321
+ return Inspector._construct(Inspector._init_engine, bind)
322
+
323
+ @inspection._inspects(Connection)
324
+ def _connection_insp(bind: Connection) -> Inspector: # type: ignore[misc]
325
+ return Inspector._construct(Inspector._init_connection, bind)
326
+
327
+ @contextlib.contextmanager
328
+ def _operation_context(self) -> Generator[Connection, None, None]:
329
+ """Return a context that optimizes for multiple operations on a single
330
+ transaction.
331
+
332
+ This essentially allows connect()/close() to be called if we detected
333
+ that we're against an :class:`_engine.Engine` and not a
334
+ :class:`_engine.Connection`.
335
+
336
+ """
337
+ conn: Connection
338
+ if self._op_context_requires_connect:
339
+ conn = self.bind.connect() # type: ignore[union-attr]
340
+ else:
341
+ conn = self.bind # type: ignore[assignment]
342
+ try:
343
+ yield conn
344
+ finally:
345
+ if self._op_context_requires_connect:
346
+ conn.close()
347
+
348
+ @contextlib.contextmanager
349
+ def _inspection_context(self) -> Generator[Inspector, None, None]:
350
+ """Return an :class:`_reflection.Inspector`
351
+ from this one that will run all
352
+ operations on a single connection.
353
+
354
+ """
355
+
356
+ with self._operation_context() as conn:
357
+ sub_insp = self._construct(self.__class__._init_connection, conn)
358
+ sub_insp.info_cache = self.info_cache
359
+ yield sub_insp
360
+
361
+ @property
362
+ def default_schema_name(self) -> Optional[str]:
363
+ """Return the default schema name presented by the dialect
364
+ for the current engine's database user.
365
+
366
+ E.g. this is typically ``public`` for PostgreSQL and ``dbo``
367
+ for SQL Server.
368
+
369
+ """
370
+ return self.dialect.default_schema_name
371
+
372
+ def get_schema_names(self, **kw: Any) -> List[str]:
373
+ r"""Return all schema names.
374
+
375
+ :param \**kw: Additional keyword argument to pass to the dialect
376
+ specific implementation. See the documentation of the dialect
377
+ in use for more information.
378
+ """
379
+
380
+ with self._operation_context() as conn:
381
+ return self.dialect.get_schema_names(
382
+ conn, info_cache=self.info_cache, **kw
383
+ )
384
+
385
+ def get_table_names(
386
+ self, schema: Optional[str] = None, **kw: Any
387
+ ) -> List[str]:
388
+ r"""Return all table names within a particular schema.
389
+
390
+ The names are expected to be real tables only, not views.
391
+ Views are instead returned using the
392
+ :meth:`_reflection.Inspector.get_view_names` and/or
393
+ :meth:`_reflection.Inspector.get_materialized_view_names`
394
+ methods.
395
+
396
+ :param schema: Schema name. If ``schema`` is left at ``None``, the
397
+ database's default schema is
398
+ used, else the named schema is searched. If the database does not
399
+ support named schemas, behavior is undefined if ``schema`` is not
400
+ passed as ``None``. For special quoting, use :class:`.quoted_name`.
401
+ :param \**kw: Additional keyword argument to pass to the dialect
402
+ specific implementation. See the documentation of the dialect
403
+ in use for more information.
404
+
405
+ .. seealso::
406
+
407
+ :meth:`_reflection.Inspector.get_sorted_table_and_fkc_names`
408
+
409
+ :attr:`_schema.MetaData.sorted_tables`
410
+
411
+ """
412
+
413
+ with self._operation_context() as conn:
414
+ return self.dialect.get_table_names(
415
+ conn, schema, info_cache=self.info_cache, **kw
416
+ )
417
+
418
+ def has_table(
419
+ self, table_name: str, schema: Optional[str] = None, **kw: Any
420
+ ) -> bool:
421
+ r"""Return True if the backend has a table, view, or temporary
422
+ table of the given name.
423
+
424
+ :param table_name: name of the table to check
425
+ :param schema: schema name to query, if not the default schema.
426
+ :param \**kw: Additional keyword argument to pass to the dialect
427
+ specific implementation. See the documentation of the dialect
428
+ in use for more information.
429
+
430
+ .. versionadded:: 1.4 - the :meth:`.Inspector.has_table` method
431
+ replaces the :meth:`_engine.Engine.has_table` method.
432
+
433
+ .. versionchanged:: 2.0:: :meth:`.Inspector.has_table` now formally
434
+ supports checking for additional table-like objects:
435
+
436
+ * any type of views (plain or materialized)
437
+ * temporary tables of any kind
438
+
439
+ Previously, these two checks were not formally specified and
440
+ different dialects would vary in their behavior. The dialect
441
+ testing suite now includes tests for all of these object types
442
+ and should be supported by all SQLAlchemy-included dialects.
443
+ Support among third party dialects may be lagging, however.
444
+
445
+ """
446
+ with self._operation_context() as conn:
447
+ return self.dialect.has_table(
448
+ conn, table_name, schema, info_cache=self.info_cache, **kw
449
+ )
450
+
451
+ def has_multi_table(
452
+ self,
453
+ table_names: Sequence[str],
454
+ schema: Optional[str] = None,
455
+ **kw: Any,
456
+ ) -> Dict[TableKey, bool]:
457
+ r"""Return a dict indicating for each table name whether the backend
458
+ has a table, view, or temporary table with the given name.
459
+
460
+ :param table_names: sequence of table names to check.
461
+ :param schema: schema name to query, if not the default schema.
462
+ :param \**kw: Additional keyword argument to pass to the dialect
463
+ specific implementation. See the documentation of the dialect
464
+ in use for more information.
465
+
466
+ .. versionadded:: 2.1
467
+
468
+ .. seealso:: :meth:`Inspector.has_table`
469
+
470
+ """
471
+ with self._operation_context() as conn:
472
+ return dict(
473
+ self.dialect.has_multi_table(
474
+ conn, table_names, schema, info_cache=self.info_cache, **kw
475
+ )
476
+ )
477
+
478
+ def has_sequence(
479
+ self, sequence_name: str, schema: Optional[str] = None, **kw: Any
480
+ ) -> bool:
481
+ r"""Return True if the backend has a sequence with the given name.
482
+
483
+ :param sequence_name: name of the sequence to check
484
+ :param schema: schema name to query, if not the default schema.
485
+ :param \**kw: Additional keyword argument to pass to the dialect
486
+ specific implementation. See the documentation of the dialect
487
+ in use for more information.
488
+
489
+ .. versionadded:: 1.4
490
+
491
+ """
492
+ with self._operation_context() as conn:
493
+ return self.dialect.has_sequence(
494
+ conn, sequence_name, schema, info_cache=self.info_cache, **kw
495
+ )
496
+
497
+ def has_index(
498
+ self,
499
+ table_name: str,
500
+ index_name: str,
501
+ schema: Optional[str] = None,
502
+ **kw: Any,
503
+ ) -> bool:
504
+ r"""Check the existence of a particular index name in the database.
505
+
506
+ :param table_name: the name of the table the index belongs to
507
+ :param index_name: the name of the index to check
508
+ :param schema: schema name to query, if not the default schema.
509
+ :param \**kw: Additional keyword argument to pass to the dialect
510
+ specific implementation. See the documentation of the dialect
511
+ in use for more information.
512
+
513
+ .. versionadded:: 2.0
514
+
515
+ """
516
+ with self._operation_context() as conn:
517
+ return self.dialect.has_index(
518
+ conn,
519
+ table_name,
520
+ index_name,
521
+ schema,
522
+ info_cache=self.info_cache,
523
+ **kw,
524
+ )
525
+
526
+ def has_schema(self, schema_name: str, **kw: Any) -> bool:
527
+ r"""Return True if the backend has a schema with the given name.
528
+
529
+ :param schema_name: name of the schema to check
530
+ :param \**kw: Additional keyword argument to pass to the dialect
531
+ specific implementation. See the documentation of the dialect
532
+ in use for more information.
533
+
534
+ .. versionadded:: 2.0
535
+
536
+ """
537
+ with self._operation_context() as conn:
538
+ return self.dialect.has_schema(
539
+ conn, schema_name, info_cache=self.info_cache, **kw
540
+ )
541
+
542
+ def get_sorted_table_and_fkc_names(
543
+ self,
544
+ schema: Optional[str] = None,
545
+ **kw: Any,
546
+ ) -> List[Tuple[Optional[str], List[Tuple[str, Optional[str]]]]]:
547
+ r"""Return dependency-sorted table and foreign key constraint names in
548
+ referred to within a particular schema.
549
+
550
+ This will yield 2-tuples of
551
+ ``(tablename, [(tname, fkname), (tname, fkname), ...])``
552
+ consisting of table names in CREATE order grouped with the foreign key
553
+ constraint names that are not detected as belonging to a cycle.
554
+ The final element
555
+ will be ``(None, [(tname, fkname), (tname, fkname), ..])``
556
+ which will consist of remaining
557
+ foreign key constraint names that would require a separate CREATE
558
+ step after-the-fact, based on dependencies between tables.
559
+
560
+ :param schema: schema name to query, if not the default schema.
561
+ :param \**kw: Additional keyword argument to pass to the dialect
562
+ specific implementation. See the documentation of the dialect
563
+ in use for more information.
564
+
565
+ .. seealso::
566
+
567
+ :meth:`_reflection.Inspector.get_table_names`
568
+
569
+ :func:`.sort_tables_and_constraints` - similar method which works
570
+ with an already-given :class:`_schema.MetaData`.
571
+
572
+ """
573
+
574
+ return [
575
+ (
576
+ table_key[1] if table_key else None,
577
+ [(tname, fks) for (_, tname), fks in fk_collection],
578
+ )
579
+ for (
580
+ table_key,
581
+ fk_collection,
582
+ ) in self.sort_tables_on_foreign_key_dependency(
583
+ consider_schemas=(schema,)
584
+ )
585
+ ]
586
+
587
+ def sort_tables_on_foreign_key_dependency(
588
+ self,
589
+ consider_schemas: Collection[Optional[str]] = (None,),
590
+ **kw: Any,
591
+ ) -> List[
592
+ Tuple[
593
+ Optional[Tuple[Optional[str], str]],
594
+ List[Tuple[Tuple[Optional[str], str], Optional[str]]],
595
+ ]
596
+ ]:
597
+ r"""Return dependency-sorted table and foreign key constraint names
598
+ referred to within multiple schemas.
599
+
600
+ This method may be compared to
601
+ :meth:`.Inspector.get_sorted_table_and_fkc_names`, which
602
+ works on one schema at a time; here, the method is a generalization
603
+ that will consider multiple schemas at once including that it will
604
+ resolve for cross-schema foreign keys.
605
+
606
+ .. versionadded:: 2.0
607
+
608
+ """
609
+ SchemaTab = Tuple[Optional[str], str]
610
+
611
+ tuples: Set[Tuple[SchemaTab, SchemaTab]] = set()
612
+ remaining_fkcs: Set[Tuple[SchemaTab, Optional[str]]] = set()
613
+ fknames_for_table: Dict[SchemaTab, Set[Optional[str]]] = {}
614
+ tnames: List[SchemaTab] = []
615
+
616
+ for schname in consider_schemas:
617
+ schema_fkeys = self.get_multi_foreign_keys(schname, **kw)
618
+ tnames.extend(schema_fkeys)
619
+ for (_, tname), fkeys in schema_fkeys.items():
620
+ fknames_for_table[(schname, tname)] = {
621
+ fk["name"] for fk in fkeys
622
+ }
623
+ for fkey in fkeys:
624
+ if (
625
+ tname != fkey["referred_table"]
626
+ or schname != fkey["referred_schema"]
627
+ ):
628
+ tuples.add(
629
+ (
630
+ (
631
+ fkey["referred_schema"],
632
+ fkey["referred_table"],
633
+ ),
634
+ (schname, tname),
635
+ )
636
+ )
637
+ try:
638
+ candidate_sort = list(topological.sort(tuples, tnames))
639
+ except exc.CircularDependencyError as err:
640
+ edge: Tuple[SchemaTab, SchemaTab]
641
+ for edge in err.edges:
642
+ tuples.remove(edge)
643
+ remaining_fkcs.update(
644
+ (edge[1], fkc) for fkc in fknames_for_table[edge[1]]
645
+ )
646
+
647
+ candidate_sort = list(topological.sort(tuples, tnames))
648
+ ret: List[
649
+ Tuple[Optional[SchemaTab], List[Tuple[SchemaTab, Optional[str]]]]
650
+ ]
651
+ ret = [
652
+ (
653
+ (schname, tname),
654
+ [
655
+ ((schname, tname), fk)
656
+ for fk in fknames_for_table[(schname, tname)].difference(
657
+ name for _, name in remaining_fkcs
658
+ )
659
+ ],
660
+ )
661
+ for (schname, tname) in candidate_sort
662
+ ]
663
+ return ret + [(None, list(remaining_fkcs))]
664
+
665
+ def get_temp_table_names(self, **kw: Any) -> List[str]:
666
+ r"""Return a list of temporary table names for the current bind.
667
+
668
+ This method is unsupported by most dialects; currently
669
+ only Oracle Database, PostgreSQL and SQLite implements it.
670
+
671
+ :param \**kw: Additional keyword argument to pass to the dialect
672
+ specific implementation. See the documentation of the dialect
673
+ in use for more information.
674
+
675
+ """
676
+
677
+ with self._operation_context() as conn:
678
+ return self.dialect.get_temp_table_names(
679
+ conn, info_cache=self.info_cache, **kw
680
+ )
681
+
682
+ def get_temp_view_names(self, **kw: Any) -> List[str]:
683
+ r"""Return a list of temporary view names for the current bind.
684
+
685
+ This method is unsupported by most dialects; currently
686
+ only PostgreSQL and SQLite implements it.
687
+
688
+ :param \**kw: Additional keyword argument to pass to the dialect
689
+ specific implementation. See the documentation of the dialect
690
+ in use for more information.
691
+
692
+ """
693
+ with self._operation_context() as conn:
694
+ return self.dialect.get_temp_view_names(
695
+ conn, info_cache=self.info_cache, **kw
696
+ )
697
+
698
+ def get_table_options(
699
+ self, table_name: str, schema: Optional[str] = None, **kw: Any
700
+ ) -> Dict[str, Any]:
701
+ r"""Return a dictionary of options specified when the table of the
702
+ given name was created.
703
+
704
+ This currently includes some options that apply to MySQL and Oracle
705
+ Database tables.
706
+
707
+ :param table_name: string name of the table. For special quoting,
708
+ use :class:`.quoted_name`.
709
+
710
+ :param schema: string schema name; if omitted, uses the default schema
711
+ of the database connection. For special quoting,
712
+ use :class:`.quoted_name`.
713
+
714
+ :param \**kw: Additional keyword argument to pass to the dialect
715
+ specific implementation. See the documentation of the dialect
716
+ in use for more information.
717
+
718
+ :return: a dict with the table options. The returned keys depend on the
719
+ dialect in use. Each one is prefixed with the dialect name.
720
+
721
+ .. seealso:: :meth:`Inspector.get_multi_table_options`
722
+
723
+ """
724
+ with self._operation_context() as conn:
725
+ return self.dialect.get_table_options(
726
+ conn, table_name, schema, info_cache=self.info_cache, **kw
727
+ )
728
+
729
+ def get_multi_table_options(
730
+ self,
731
+ schema: Optional[str] = None,
732
+ filter_names: Optional[Sequence[str]] = None,
733
+ kind: ObjectKind = ObjectKind.TABLE,
734
+ scope: ObjectScope = ObjectScope.DEFAULT,
735
+ **kw: Any,
736
+ ) -> Dict[TableKey, Dict[str, Any]]:
737
+ r"""Return a dictionary of options specified when the tables in the
738
+ given schema were created.
739
+
740
+ The tables can be filtered by passing the names to use to
741
+ ``filter_names``.
742
+
743
+ This currently includes some options that apply to MySQL and Oracle
744
+ tables.
745
+
746
+ :param schema: string schema name; if omitted, uses the default schema
747
+ of the database connection. For special quoting,
748
+ use :class:`.quoted_name`.
749
+
750
+ :param filter_names: optionally return information only for the
751
+ objects listed here.
752
+
753
+ :param kind: a :class:`.ObjectKind` that specifies the type of objects
754
+ to reflect. Defaults to ``ObjectKind.TABLE``.
755
+
756
+ :param scope: a :class:`.ObjectScope` that specifies if options of
757
+ default, temporary or any tables should be reflected.
758
+ Defaults to ``ObjectScope.DEFAULT``.
759
+
760
+ :param \**kw: Additional keyword argument to pass to the dialect
761
+ specific implementation. See the documentation of the dialect
762
+ in use for more information.
763
+
764
+ :return: a dictionary where the keys are two-tuple schema,table-name
765
+ and the values are dictionaries with the table options.
766
+ The returned keys in each dict depend on the
767
+ dialect in use. Each one is prefixed with the dialect name.
768
+ The schema is ``None`` if no schema is provided.
769
+
770
+ .. versionadded:: 2.0
771
+
772
+ .. seealso:: :meth:`Inspector.get_table_options`
773
+ """
774
+ with self._operation_context() as conn:
775
+ res = self.dialect.get_multi_table_options(
776
+ conn,
777
+ schema=schema,
778
+ filter_names=filter_names,
779
+ kind=kind,
780
+ scope=scope,
781
+ info_cache=self.info_cache,
782
+ **kw,
783
+ )
784
+ return dict(res)
785
+
786
+ def get_view_names(
787
+ self, schema: Optional[str] = None, **kw: Any
788
+ ) -> List[str]:
789
+ r"""Return all non-materialized view names in `schema`.
790
+
791
+ :param schema: Optional, retrieve names from a non-default schema.
792
+ For special quoting, use :class:`.quoted_name`.
793
+ :param \**kw: Additional keyword argument to pass to the dialect
794
+ specific implementation. See the documentation of the dialect
795
+ in use for more information.
796
+
797
+
798
+ .. versionchanged:: 2.0 For those dialects that previously included
799
+ the names of materialized views in this list (currently PostgreSQL),
800
+ this method no longer returns the names of materialized views.
801
+ the :meth:`.Inspector.get_materialized_view_names` method should
802
+ be used instead.
803
+
804
+ .. seealso::
805
+
806
+ :meth:`.Inspector.get_materialized_view_names`
807
+
808
+ """
809
+
810
+ with self._operation_context() as conn:
811
+ return self.dialect.get_view_names(
812
+ conn, schema, info_cache=self.info_cache, **kw
813
+ )
814
+
815
+ def get_materialized_view_names(
816
+ self, schema: Optional[str] = None, **kw: Any
817
+ ) -> List[str]:
818
+ r"""Return all materialized view names in `schema`.
819
+
820
+ :param schema: Optional, retrieve names from a non-default schema.
821
+ For special quoting, use :class:`.quoted_name`.
822
+ :param \**kw: Additional keyword argument to pass to the dialect
823
+ specific implementation. See the documentation of the dialect
824
+ in use for more information.
825
+
826
+ .. versionadded:: 2.0
827
+
828
+ .. seealso::
829
+
830
+ :meth:`.Inspector.get_view_names`
831
+
832
+ """
833
+
834
+ with self._operation_context() as conn:
835
+ return self.dialect.get_materialized_view_names(
836
+ conn, schema, info_cache=self.info_cache, **kw
837
+ )
838
+
839
+ def get_sequence_names(
840
+ self, schema: Optional[str] = None, **kw: Any
841
+ ) -> List[str]:
842
+ r"""Return all sequence names in `schema`.
843
+
844
+ :param schema: Optional, retrieve names from a non-default schema.
845
+ For special quoting, use :class:`.quoted_name`.
846
+ :param \**kw: Additional keyword argument to pass to the dialect
847
+ specific implementation. See the documentation of the dialect
848
+ in use for more information.
849
+
850
+ """
851
+
852
+ with self._operation_context() as conn:
853
+ return self.dialect.get_sequence_names(
854
+ conn, schema, info_cache=self.info_cache, **kw
855
+ )
856
+
857
+ def get_view_definition(
858
+ self, view_name: str, schema: Optional[str] = None, **kw: Any
859
+ ) -> str:
860
+ r"""Return definition for the plain or materialized view called
861
+ ``view_name``.
862
+
863
+ :param view_name: Name of the view.
864
+ :param schema: Optional, retrieve names from a non-default schema.
865
+ For special quoting, use :class:`.quoted_name`.
866
+ :param \**kw: Additional keyword argument to pass to the dialect
867
+ specific implementation. See the documentation of the dialect
868
+ in use for more information.
869
+
870
+ """
871
+
872
+ with self._operation_context() as conn:
873
+ return self.dialect.get_view_definition(
874
+ conn, view_name, schema, info_cache=self.info_cache, **kw
875
+ )
876
+
877
+ def get_columns(
878
+ self, table_name: str, schema: Optional[str] = None, **kw: Any
879
+ ) -> List[ReflectedColumn]:
880
+ r"""Return information about columns in ``table_name``.
881
+
882
+ Given a string ``table_name`` and an optional string ``schema``,
883
+ return column information as a list of :class:`.ReflectedColumn`.
884
+
885
+ :param table_name: string name of the table. For special quoting,
886
+ use :class:`.quoted_name`.
887
+
888
+ :param schema: string schema name; if omitted, uses the default schema
889
+ of the database connection. For special quoting,
890
+ use :class:`.quoted_name`.
891
+
892
+ :param \**kw: Additional keyword argument to pass to the dialect
893
+ specific implementation. See the documentation of the dialect
894
+ in use for more information.
895
+
896
+ :return: list of dictionaries, each representing the definition of
897
+ a database column.
898
+
899
+ .. seealso:: :meth:`Inspector.get_multi_columns`.
900
+
901
+ """
902
+
903
+ with self._operation_context() as conn:
904
+ col_defs = self.dialect.get_columns(
905
+ conn, table_name, schema, info_cache=self.info_cache, **kw
906
+ )
907
+ if col_defs:
908
+ self._instantiate_types([col_defs])
909
+ return col_defs
910
+
911
+ def _instantiate_types(
912
+ self, data: Iterable[List[ReflectedColumn]]
913
+ ) -> None:
914
+ # make this easy and only return instances for coltype
915
+ for col_defs in data:
916
+ for col_def in col_defs:
917
+ coltype = col_def["type"]
918
+ if not isinstance(coltype, TypeEngine):
919
+ col_def["type"] = coltype()
920
+
921
+ def get_multi_columns(
922
+ self,
923
+ schema: Optional[str] = None,
924
+ filter_names: Optional[Sequence[str]] = None,
925
+ kind: ObjectKind = ObjectKind.TABLE,
926
+ scope: ObjectScope = ObjectScope.DEFAULT,
927
+ **kw: Any,
928
+ ) -> Dict[TableKey, List[ReflectedColumn]]:
929
+ r"""Return information about columns in all objects in the given
930
+ schema.
931
+
932
+ The objects can be filtered by passing the names to use to
933
+ ``filter_names``.
934
+
935
+ For each table the value is a list of :class:`.ReflectedColumn`.
936
+
937
+ :param schema: string schema name; if omitted, uses the default schema
938
+ of the database connection. For special quoting,
939
+ use :class:`.quoted_name`.
940
+
941
+ :param filter_names: optionally return information only for the
942
+ objects listed here.
943
+
944
+ :param kind: a :class:`.ObjectKind` that specifies the type of objects
945
+ to reflect. Defaults to ``ObjectKind.TABLE``.
946
+
947
+ :param scope: a :class:`.ObjectScope` that specifies if columns of
948
+ default, temporary or any tables should be reflected.
949
+ Defaults to ``ObjectScope.DEFAULT``.
950
+
951
+ :param \**kw: Additional keyword argument to pass to the dialect
952
+ specific implementation. See the documentation of the dialect
953
+ in use for more information.
954
+
955
+ :return: a dictionary where the keys are two-tuple schema,table-name
956
+ and the values are list of dictionaries, each representing the
957
+ definition of a database column.
958
+ The schema is ``None`` if no schema is provided.
959
+
960
+ .. versionadded:: 2.0
961
+
962
+ .. seealso:: :meth:`Inspector.get_columns`
963
+ """
964
+
965
+ with self._operation_context() as conn:
966
+ table_col_defs = dict(
967
+ self.dialect.get_multi_columns(
968
+ conn,
969
+ schema=schema,
970
+ filter_names=filter_names,
971
+ kind=kind,
972
+ scope=scope,
973
+ info_cache=self.info_cache,
974
+ **kw,
975
+ )
976
+ )
977
+ self._instantiate_types(table_col_defs.values())
978
+ return table_col_defs
979
+
980
+ def get_pk_constraint(
981
+ self, table_name: str, schema: Optional[str] = None, **kw: Any
982
+ ) -> ReflectedPrimaryKeyConstraint:
983
+ r"""Return information about primary key constraint in ``table_name``.
984
+
985
+ Given a string ``table_name``, and an optional string `schema`, return
986
+ primary key information as a :class:`.ReflectedPrimaryKeyConstraint`.
987
+
988
+ :param table_name: string name of the table. For special quoting,
989
+ use :class:`.quoted_name`.
990
+
991
+ :param schema: string schema name; if omitted, uses the default schema
992
+ of the database connection. For special quoting,
993
+ use :class:`.quoted_name`.
994
+
995
+ :param \**kw: Additional keyword argument to pass to the dialect
996
+ specific implementation. See the documentation of the dialect
997
+ in use for more information.
998
+
999
+ :return: a dictionary representing the definition of
1000
+ a primary key constraint.
1001
+
1002
+ .. seealso:: :meth:`Inspector.get_multi_pk_constraint`
1003
+ """
1004
+ with self._operation_context() as conn:
1005
+ return self.dialect.get_pk_constraint(
1006
+ conn, table_name, schema, info_cache=self.info_cache, **kw
1007
+ )
1008
+
1009
+ def get_multi_pk_constraint(
1010
+ self,
1011
+ schema: Optional[str] = None,
1012
+ filter_names: Optional[Sequence[str]] = None,
1013
+ kind: ObjectKind = ObjectKind.TABLE,
1014
+ scope: ObjectScope = ObjectScope.DEFAULT,
1015
+ **kw: Any,
1016
+ ) -> Dict[TableKey, ReflectedPrimaryKeyConstraint]:
1017
+ r"""Return information about primary key constraints in
1018
+ all tables in the given schema.
1019
+
1020
+ The tables can be filtered by passing the names to use to
1021
+ ``filter_names``.
1022
+
1023
+ For each table the value is a :class:`.ReflectedPrimaryKeyConstraint`.
1024
+
1025
+ :param schema: string schema name; if omitted, uses the default schema
1026
+ of the database connection. For special quoting,
1027
+ use :class:`.quoted_name`.
1028
+
1029
+ :param filter_names: optionally return information only for the
1030
+ objects listed here.
1031
+
1032
+ :param kind: a :class:`.ObjectKind` that specifies the type of objects
1033
+ to reflect. Defaults to ``ObjectKind.TABLE``.
1034
+
1035
+ :param scope: a :class:`.ObjectScope` that specifies if primary keys of
1036
+ default, temporary or any tables should be reflected.
1037
+ Defaults to ``ObjectScope.DEFAULT``.
1038
+
1039
+ :param \**kw: Additional keyword argument to pass to the dialect
1040
+ specific implementation. See the documentation of the dialect
1041
+ in use for more information.
1042
+
1043
+ :return: a dictionary where the keys are two-tuple schema,table-name
1044
+ and the values are dictionaries, each representing the
1045
+ definition of a primary key constraint.
1046
+ The schema is ``None`` if no schema is provided.
1047
+
1048
+ .. versionadded:: 2.0
1049
+
1050
+ .. seealso:: :meth:`Inspector.get_pk_constraint`
1051
+ """
1052
+ with self._operation_context() as conn:
1053
+ return dict(
1054
+ self.dialect.get_multi_pk_constraint(
1055
+ conn,
1056
+ schema=schema,
1057
+ filter_names=filter_names,
1058
+ kind=kind,
1059
+ scope=scope,
1060
+ info_cache=self.info_cache,
1061
+ **kw,
1062
+ )
1063
+ )
1064
+
1065
+ def get_foreign_keys(
1066
+ self, table_name: str, schema: Optional[str] = None, **kw: Any
1067
+ ) -> List[ReflectedForeignKeyConstraint]:
1068
+ r"""Return information about foreign_keys in ``table_name``.
1069
+
1070
+ Given a string ``table_name``, and an optional string `schema`, return
1071
+ foreign key information as a list of
1072
+ :class:`.ReflectedForeignKeyConstraint`.
1073
+
1074
+ :param table_name: string name of the table. For special quoting,
1075
+ use :class:`.quoted_name`.
1076
+
1077
+ :param schema: string schema name; if omitted, uses the default schema
1078
+ of the database connection. For special quoting,
1079
+ use :class:`.quoted_name`.
1080
+
1081
+ :param \**kw: Additional keyword argument to pass to the dialect
1082
+ specific implementation. See the documentation of the dialect
1083
+ in use for more information.
1084
+
1085
+ :return: a list of dictionaries, each representing the
1086
+ a foreign key definition.
1087
+
1088
+ .. seealso:: :meth:`Inspector.get_multi_foreign_keys`
1089
+ """
1090
+
1091
+ with self._operation_context() as conn:
1092
+ return self.dialect.get_foreign_keys(
1093
+ conn, table_name, schema, info_cache=self.info_cache, **kw
1094
+ )
1095
+
1096
+ def get_multi_foreign_keys(
1097
+ self,
1098
+ schema: Optional[str] = None,
1099
+ filter_names: Optional[Sequence[str]] = None,
1100
+ kind: ObjectKind = ObjectKind.TABLE,
1101
+ scope: ObjectScope = ObjectScope.DEFAULT,
1102
+ **kw: Any,
1103
+ ) -> Dict[TableKey, List[ReflectedForeignKeyConstraint]]:
1104
+ r"""Return information about foreign_keys in all tables
1105
+ in the given schema.
1106
+
1107
+ The tables can be filtered by passing the names to use to
1108
+ ``filter_names``.
1109
+
1110
+ For each table the value is a list of
1111
+ :class:`.ReflectedForeignKeyConstraint`.
1112
+
1113
+ :param schema: string schema name; if omitted, uses the default schema
1114
+ of the database connection. For special quoting,
1115
+ use :class:`.quoted_name`.
1116
+
1117
+ :param filter_names: optionally return information only for the
1118
+ objects listed here.
1119
+
1120
+ :param kind: a :class:`.ObjectKind` that specifies the type of objects
1121
+ to reflect. Defaults to ``ObjectKind.TABLE``.
1122
+
1123
+ :param scope: a :class:`.ObjectScope` that specifies if foreign keys of
1124
+ default, temporary or any tables should be reflected.
1125
+ Defaults to ``ObjectScope.DEFAULT``.
1126
+
1127
+ :param \**kw: Additional keyword argument to pass to the dialect
1128
+ specific implementation. See the documentation of the dialect
1129
+ in use for more information.
1130
+
1131
+ :return: a dictionary where the keys are two-tuple schema,table-name
1132
+ and the values are list of dictionaries, each representing
1133
+ a foreign key definition.
1134
+ The schema is ``None`` if no schema is provided.
1135
+
1136
+ .. versionadded:: 2.0
1137
+
1138
+ .. seealso:: :meth:`Inspector.get_foreign_keys`
1139
+ """
1140
+
1141
+ with self._operation_context() as conn:
1142
+ return dict(
1143
+ self.dialect.get_multi_foreign_keys(
1144
+ conn,
1145
+ schema=schema,
1146
+ filter_names=filter_names,
1147
+ kind=kind,
1148
+ scope=scope,
1149
+ info_cache=self.info_cache,
1150
+ **kw,
1151
+ )
1152
+ )
1153
+
1154
+ def get_indexes(
1155
+ self, table_name: str, schema: Optional[str] = None, **kw: Any
1156
+ ) -> List[ReflectedIndex]:
1157
+ r"""Return information about indexes in ``table_name``.
1158
+
1159
+ Given a string ``table_name`` and an optional string `schema`, return
1160
+ index information as a list of :class:`.ReflectedIndex`.
1161
+
1162
+ :param table_name: string name of the table. For special quoting,
1163
+ use :class:`.quoted_name`.
1164
+
1165
+ :param schema: string schema name; if omitted, uses the default schema
1166
+ of the database connection. For special quoting,
1167
+ use :class:`.quoted_name`.
1168
+
1169
+ :param \**kw: Additional keyword argument to pass to the dialect
1170
+ specific implementation. See the documentation of the dialect
1171
+ in use for more information.
1172
+
1173
+ :return: a list of dictionaries, each representing the
1174
+ definition of an index.
1175
+
1176
+ .. seealso:: :meth:`Inspector.get_multi_indexes`
1177
+ """
1178
+
1179
+ with self._operation_context() as conn:
1180
+ return self.dialect.get_indexes(
1181
+ conn, table_name, schema, info_cache=self.info_cache, **kw
1182
+ )
1183
+
1184
+ def get_multi_indexes(
1185
+ self,
1186
+ schema: Optional[str] = None,
1187
+ filter_names: Optional[Sequence[str]] = None,
1188
+ kind: ObjectKind = ObjectKind.TABLE,
1189
+ scope: ObjectScope = ObjectScope.DEFAULT,
1190
+ **kw: Any,
1191
+ ) -> Dict[TableKey, List[ReflectedIndex]]:
1192
+ r"""Return information about indexes in in all objects
1193
+ in the given schema.
1194
+
1195
+ The objects can be filtered by passing the names to use to
1196
+ ``filter_names``.
1197
+
1198
+ For each table the value is a list of :class:`.ReflectedIndex`.
1199
+
1200
+ :param schema: string schema name; if omitted, uses the default schema
1201
+ of the database connection. For special quoting,
1202
+ use :class:`.quoted_name`.
1203
+
1204
+ :param filter_names: optionally return information only for the
1205
+ objects listed here.
1206
+
1207
+ :param kind: a :class:`.ObjectKind` that specifies the type of objects
1208
+ to reflect. Defaults to ``ObjectKind.TABLE``.
1209
+
1210
+ :param scope: a :class:`.ObjectScope` that specifies if indexes of
1211
+ default, temporary or any tables should be reflected.
1212
+ Defaults to ``ObjectScope.DEFAULT``.
1213
+
1214
+ :param \**kw: Additional keyword argument to pass to the dialect
1215
+ specific implementation. See the documentation of the dialect
1216
+ in use for more information.
1217
+
1218
+ :return: a dictionary where the keys are two-tuple schema,table-name
1219
+ and the values are list of dictionaries, each representing the
1220
+ definition of an index.
1221
+ The schema is ``None`` if no schema is provided.
1222
+
1223
+ .. versionadded:: 2.0
1224
+
1225
+ .. seealso:: :meth:`Inspector.get_indexes`
1226
+ """
1227
+
1228
+ with self._operation_context() as conn:
1229
+ return dict(
1230
+ self.dialect.get_multi_indexes(
1231
+ conn,
1232
+ schema=schema,
1233
+ filter_names=filter_names,
1234
+ kind=kind,
1235
+ scope=scope,
1236
+ info_cache=self.info_cache,
1237
+ **kw,
1238
+ )
1239
+ )
1240
+
1241
+ def get_unique_constraints(
1242
+ self, table_name: str, schema: Optional[str] = None, **kw: Any
1243
+ ) -> List[ReflectedUniqueConstraint]:
1244
+ r"""Return information about unique constraints in ``table_name``.
1245
+
1246
+ Given a string ``table_name`` and an optional string `schema`, return
1247
+ unique constraint information as a list of
1248
+ :class:`.ReflectedUniqueConstraint`.
1249
+
1250
+ :param table_name: string name of the table. For special quoting,
1251
+ use :class:`.quoted_name`.
1252
+
1253
+ :param schema: string schema name; if omitted, uses the default schema
1254
+ of the database connection. For special quoting,
1255
+ use :class:`.quoted_name`.
1256
+
1257
+ :param \**kw: Additional keyword argument to pass to the dialect
1258
+ specific implementation. See the documentation of the dialect
1259
+ in use for more information.
1260
+
1261
+ :return: a list of dictionaries, each representing the
1262
+ definition of an unique constraint.
1263
+
1264
+ .. seealso:: :meth:`Inspector.get_multi_unique_constraints`
1265
+ """
1266
+
1267
+ with self._operation_context() as conn:
1268
+ return self.dialect.get_unique_constraints(
1269
+ conn, table_name, schema, info_cache=self.info_cache, **kw
1270
+ )
1271
+
1272
+ def get_multi_unique_constraints(
1273
+ self,
1274
+ schema: Optional[str] = None,
1275
+ filter_names: Optional[Sequence[str]] = None,
1276
+ kind: ObjectKind = ObjectKind.TABLE,
1277
+ scope: ObjectScope = ObjectScope.DEFAULT,
1278
+ **kw: Any,
1279
+ ) -> Dict[TableKey, List[ReflectedUniqueConstraint]]:
1280
+ r"""Return information about unique constraints in all tables
1281
+ in the given schema.
1282
+
1283
+ The tables can be filtered by passing the names to use to
1284
+ ``filter_names``.
1285
+
1286
+ For each table the value is a list of
1287
+ :class:`.ReflectedUniqueConstraint`.
1288
+
1289
+ :param schema: string schema name; if omitted, uses the default schema
1290
+ of the database connection. For special quoting,
1291
+ use :class:`.quoted_name`.
1292
+
1293
+ :param filter_names: optionally return information only for the
1294
+ objects listed here.
1295
+
1296
+ :param kind: a :class:`.ObjectKind` that specifies the type of objects
1297
+ to reflect. Defaults to ``ObjectKind.TABLE``.
1298
+
1299
+ :param scope: a :class:`.ObjectScope` that specifies if constraints of
1300
+ default, temporary or any tables should be reflected.
1301
+ Defaults to ``ObjectScope.DEFAULT``.
1302
+
1303
+ :param \**kw: Additional keyword argument to pass to the dialect
1304
+ specific implementation. See the documentation of the dialect
1305
+ in use for more information.
1306
+
1307
+ :return: a dictionary where the keys are two-tuple schema,table-name
1308
+ and the values are list of dictionaries, each representing the
1309
+ definition of an unique constraint.
1310
+ The schema is ``None`` if no schema is provided.
1311
+
1312
+ .. versionadded:: 2.0
1313
+
1314
+ .. seealso:: :meth:`Inspector.get_unique_constraints`
1315
+ """
1316
+
1317
+ with self._operation_context() as conn:
1318
+ return dict(
1319
+ self.dialect.get_multi_unique_constraints(
1320
+ conn,
1321
+ schema=schema,
1322
+ filter_names=filter_names,
1323
+ kind=kind,
1324
+ scope=scope,
1325
+ info_cache=self.info_cache,
1326
+ **kw,
1327
+ )
1328
+ )
1329
+
1330
+ def get_table_comment(
1331
+ self, table_name: str, schema: Optional[str] = None, **kw: Any
1332
+ ) -> ReflectedTableComment:
1333
+ r"""Return information about the table comment for ``table_name``.
1334
+
1335
+ Given a string ``table_name`` and an optional string ``schema``,
1336
+ return table comment information as a :class:`.ReflectedTableComment`.
1337
+
1338
+ Raises ``NotImplementedError`` for a dialect that does not support
1339
+ comments.
1340
+
1341
+ :param table_name: string name of the table. For special quoting,
1342
+ use :class:`.quoted_name`.
1343
+
1344
+ :param schema: string schema name; if omitted, uses the default schema
1345
+ of the database connection. For special quoting,
1346
+ use :class:`.quoted_name`.
1347
+
1348
+ :param \**kw: Additional keyword argument to pass to the dialect
1349
+ specific implementation. See the documentation of the dialect
1350
+ in use for more information.
1351
+
1352
+ :return: a dictionary, with the table comment.
1353
+
1354
+ .. seealso:: :meth:`Inspector.get_multi_table_comment`
1355
+ """
1356
+
1357
+ with self._operation_context() as conn:
1358
+ return self.dialect.get_table_comment(
1359
+ conn, table_name, schema, info_cache=self.info_cache, **kw
1360
+ )
1361
+
1362
+ def get_multi_table_comment(
1363
+ self,
1364
+ schema: Optional[str] = None,
1365
+ filter_names: Optional[Sequence[str]] = None,
1366
+ kind: ObjectKind = ObjectKind.TABLE,
1367
+ scope: ObjectScope = ObjectScope.DEFAULT,
1368
+ **kw: Any,
1369
+ ) -> Dict[TableKey, ReflectedTableComment]:
1370
+ r"""Return information about the table comment in all objects
1371
+ in the given schema.
1372
+
1373
+ The objects can be filtered by passing the names to use to
1374
+ ``filter_names``.
1375
+
1376
+ For each table the value is a :class:`.ReflectedTableComment`.
1377
+
1378
+ Raises ``NotImplementedError`` for a dialect that does not support
1379
+ comments.
1380
+
1381
+ :param schema: string schema name; if omitted, uses the default schema
1382
+ of the database connection. For special quoting,
1383
+ use :class:`.quoted_name`.
1384
+
1385
+ :param filter_names: optionally return information only for the
1386
+ objects listed here.
1387
+
1388
+ :param kind: a :class:`.ObjectKind` that specifies the type of objects
1389
+ to reflect. Defaults to ``ObjectKind.TABLE``.
1390
+
1391
+ :param scope: a :class:`.ObjectScope` that specifies if comments of
1392
+ default, temporary or any tables should be reflected.
1393
+ Defaults to ``ObjectScope.DEFAULT``.
1394
+
1395
+ :param \**kw: Additional keyword argument to pass to the dialect
1396
+ specific implementation. See the documentation of the dialect
1397
+ in use for more information.
1398
+
1399
+ :return: a dictionary where the keys are two-tuple schema,table-name
1400
+ and the values are dictionaries, representing the
1401
+ table comments.
1402
+ The schema is ``None`` if no schema is provided.
1403
+
1404
+ .. versionadded:: 2.0
1405
+
1406
+ .. seealso:: :meth:`Inspector.get_table_comment`
1407
+ """
1408
+
1409
+ with self._operation_context() as conn:
1410
+ return dict(
1411
+ self.dialect.get_multi_table_comment(
1412
+ conn,
1413
+ schema=schema,
1414
+ filter_names=filter_names,
1415
+ kind=kind,
1416
+ scope=scope,
1417
+ info_cache=self.info_cache,
1418
+ **kw,
1419
+ )
1420
+ )
1421
+
1422
+ def get_check_constraints(
1423
+ self, table_name: str, schema: Optional[str] = None, **kw: Any
1424
+ ) -> List[ReflectedCheckConstraint]:
1425
+ r"""Return information about check constraints in ``table_name``.
1426
+
1427
+ Given a string ``table_name`` and an optional string `schema`, return
1428
+ check constraint information as a list of
1429
+ :class:`.ReflectedCheckConstraint`.
1430
+
1431
+ :param table_name: string name of the table. For special quoting,
1432
+ use :class:`.quoted_name`.
1433
+
1434
+ :param schema: string schema name; if omitted, uses the default schema
1435
+ of the database connection. For special quoting,
1436
+ use :class:`.quoted_name`.
1437
+
1438
+ :param \**kw: Additional keyword argument to pass to the dialect
1439
+ specific implementation. See the documentation of the dialect
1440
+ in use for more information.
1441
+
1442
+ :return: a list of dictionaries, each representing the
1443
+ definition of a check constraints.
1444
+
1445
+ .. seealso:: :meth:`Inspector.get_multi_check_constraints`
1446
+ """
1447
+
1448
+ with self._operation_context() as conn:
1449
+ return self.dialect.get_check_constraints(
1450
+ conn, table_name, schema, info_cache=self.info_cache, **kw
1451
+ )
1452
+
1453
+ def get_multi_check_constraints(
1454
+ self,
1455
+ schema: Optional[str] = None,
1456
+ filter_names: Optional[Sequence[str]] = None,
1457
+ kind: ObjectKind = ObjectKind.TABLE,
1458
+ scope: ObjectScope = ObjectScope.DEFAULT,
1459
+ **kw: Any,
1460
+ ) -> Dict[TableKey, List[ReflectedCheckConstraint]]:
1461
+ r"""Return information about check constraints in all tables
1462
+ in the given schema.
1463
+
1464
+ The tables can be filtered by passing the names to use to
1465
+ ``filter_names``.
1466
+
1467
+ For each table the value is a list of
1468
+ :class:`.ReflectedCheckConstraint`.
1469
+
1470
+ :param schema: string schema name; if omitted, uses the default schema
1471
+ of the database connection. For special quoting,
1472
+ use :class:`.quoted_name`.
1473
+
1474
+ :param filter_names: optionally return information only for the
1475
+ objects listed here.
1476
+
1477
+ :param kind: a :class:`.ObjectKind` that specifies the type of objects
1478
+ to reflect. Defaults to ``ObjectKind.TABLE``.
1479
+
1480
+ :param scope: a :class:`.ObjectScope` that specifies if constraints of
1481
+ default, temporary or any tables should be reflected.
1482
+ Defaults to ``ObjectScope.DEFAULT``.
1483
+
1484
+ :param \**kw: Additional keyword argument to pass to the dialect
1485
+ specific implementation. See the documentation of the dialect
1486
+ in use for more information.
1487
+
1488
+ :return: a dictionary where the keys are two-tuple schema,table-name
1489
+ and the values are list of dictionaries, each representing the
1490
+ definition of a check constraints.
1491
+ The schema is ``None`` if no schema is provided.
1492
+
1493
+ .. versionadded:: 2.0
1494
+
1495
+ .. seealso:: :meth:`Inspector.get_check_constraints`
1496
+ """
1497
+
1498
+ with self._operation_context() as conn:
1499
+ return dict(
1500
+ self.dialect.get_multi_check_constraints(
1501
+ conn,
1502
+ schema=schema,
1503
+ filter_names=filter_names,
1504
+ kind=kind,
1505
+ scope=scope,
1506
+ info_cache=self.info_cache,
1507
+ **kw,
1508
+ )
1509
+ )
1510
+
1511
+ def reflect_table(
1512
+ self,
1513
+ table: sa_schema.Table,
1514
+ include_columns: Optional[Collection[str]],
1515
+ exclude_columns: Collection[str] = (),
1516
+ resolve_fks: bool = True,
1517
+ _extend_on: Optional[Set[sa_schema.Table]] = None,
1518
+ _reflect_info: Optional[_ReflectionInfo] = None,
1519
+ ) -> None:
1520
+ """Given a :class:`_schema.Table` object, load its internal
1521
+ constructs based on introspection.
1522
+
1523
+ This is the underlying method used by most dialects to produce
1524
+ table reflection. Direct usage is like::
1525
+
1526
+ from sqlalchemy import create_engine, MetaData, Table
1527
+ from sqlalchemy import inspect
1528
+
1529
+ engine = create_engine("...")
1530
+ meta = MetaData()
1531
+ user_table = Table("user", meta)
1532
+ insp = inspect(engine)
1533
+ insp.reflect_table(user_table, None)
1534
+
1535
+ .. versionchanged:: 1.4 Renamed from ``reflecttable`` to
1536
+ ``reflect_table``
1537
+
1538
+ :param table: a :class:`~sqlalchemy.schema.Table` instance.
1539
+ :param include_columns: a list of string column names to include
1540
+ in the reflection process. If ``None``, all columns are reflected.
1541
+
1542
+ """
1543
+
1544
+ if _extend_on is not None:
1545
+ if table in _extend_on:
1546
+ return
1547
+ else:
1548
+ _extend_on.add(table)
1549
+
1550
+ dialect = self.bind.dialect
1551
+
1552
+ with self._operation_context() as conn:
1553
+ schema = conn.schema_for_object(table)
1554
+
1555
+ table_name = table.name
1556
+
1557
+ # get table-level arguments that are specifically
1558
+ # intended for reflection, e.g. oracle_resolve_synonyms.
1559
+ # these are unconditionally passed to related Table
1560
+ # objects
1561
+ reflection_options = {
1562
+ k: table.dialect_kwargs.get(k)
1563
+ for k in dialect.reflection_options
1564
+ if k in table.dialect_kwargs
1565
+ }
1566
+
1567
+ table_key = (schema, table_name)
1568
+ if _reflect_info is None or table_key not in _reflect_info.columns:
1569
+ _reflect_info = self._get_reflection_info(
1570
+ schema,
1571
+ filter_names=[table_name],
1572
+ kind=ObjectKind.ANY,
1573
+ scope=ObjectScope.ANY,
1574
+ _reflect_info=_reflect_info,
1575
+ **table.dialect_kwargs,
1576
+ )
1577
+ if table_key in _reflect_info.unreflectable:
1578
+ raise _reflect_info.unreflectable[table_key]
1579
+
1580
+ if table_key not in _reflect_info.columns:
1581
+ raise exc.NoSuchTableError(table_name)
1582
+
1583
+ # reflect table options, like mysql_engine
1584
+ if _reflect_info.table_options:
1585
+ tbl_opts = _reflect_info.table_options.get(table_key)
1586
+ if tbl_opts:
1587
+ # add additional kwargs to the Table if the dialect
1588
+ # returned them
1589
+ table._validate_dialect_kwargs(tbl_opts)
1590
+
1591
+ found_table = False
1592
+ cols_by_orig_name: Dict[str, sa_schema.Column[Any]] = {}
1593
+
1594
+ for col_d in _reflect_info.columns[table_key]:
1595
+ found_table = True
1596
+
1597
+ self._reflect_column(
1598
+ table,
1599
+ col_d,
1600
+ include_columns,
1601
+ exclude_columns,
1602
+ cols_by_orig_name,
1603
+ )
1604
+
1605
+ # NOTE: support tables/views with no columns
1606
+ if not found_table and not self.has_table(table_name, schema):
1607
+ raise exc.NoSuchTableError(table_name)
1608
+
1609
+ self._reflect_pk(
1610
+ _reflect_info, table_key, table, cols_by_orig_name, exclude_columns
1611
+ )
1612
+
1613
+ self._reflect_fk(
1614
+ _reflect_info,
1615
+ table_key,
1616
+ table,
1617
+ cols_by_orig_name,
1618
+ include_columns,
1619
+ exclude_columns,
1620
+ resolve_fks,
1621
+ _extend_on,
1622
+ reflection_options,
1623
+ )
1624
+
1625
+ self._reflect_indexes(
1626
+ _reflect_info,
1627
+ table_key,
1628
+ table,
1629
+ cols_by_orig_name,
1630
+ include_columns,
1631
+ exclude_columns,
1632
+ reflection_options,
1633
+ )
1634
+
1635
+ self._reflect_unique_constraints(
1636
+ _reflect_info,
1637
+ table_key,
1638
+ table,
1639
+ cols_by_orig_name,
1640
+ include_columns,
1641
+ exclude_columns,
1642
+ reflection_options,
1643
+ )
1644
+
1645
+ self._reflect_check_constraints(
1646
+ _reflect_info,
1647
+ table_key,
1648
+ table,
1649
+ cols_by_orig_name,
1650
+ include_columns,
1651
+ exclude_columns,
1652
+ reflection_options,
1653
+ )
1654
+
1655
+ self._reflect_table_comment(
1656
+ _reflect_info,
1657
+ table_key,
1658
+ table,
1659
+ reflection_options,
1660
+ )
1661
+
1662
+ def _reflect_column(
1663
+ self,
1664
+ table: sa_schema.Table,
1665
+ col_d: ReflectedColumn,
1666
+ include_columns: Optional[Collection[str]],
1667
+ exclude_columns: Collection[str],
1668
+ cols_by_orig_name: Dict[str, sa_schema.Column[Any]],
1669
+ ) -> None:
1670
+ orig_name = col_d["name"]
1671
+
1672
+ table.metadata.dispatch.column_reflect(self, table, col_d)
1673
+ table.dispatch.column_reflect(self, table, col_d)
1674
+
1675
+ # fetch name again as column_reflect is allowed to
1676
+ # change it
1677
+ name = col_d["name"]
1678
+ if (include_columns and name not in include_columns) or (
1679
+ exclude_columns and name in exclude_columns
1680
+ ):
1681
+ return
1682
+
1683
+ coltype = col_d["type"]
1684
+
1685
+ col_kw = {
1686
+ k: col_d[k] # type: ignore[literal-required]
1687
+ for k in [
1688
+ "nullable",
1689
+ "autoincrement",
1690
+ "quote",
1691
+ "info",
1692
+ "key",
1693
+ "comment",
1694
+ ]
1695
+ if k in col_d
1696
+ }
1697
+
1698
+ if "dialect_options" in col_d:
1699
+ col_kw.update(col_d["dialect_options"])
1700
+
1701
+ colargs = []
1702
+ default: Any
1703
+ if col_d.get("default") is not None:
1704
+ default_text = col_d["default"]
1705
+ assert default_text is not None
1706
+ if isinstance(default_text, TextClause):
1707
+ default = sa_schema.DefaultClause(
1708
+ default_text, _reflected=True
1709
+ )
1710
+ elif not isinstance(default_text, sa_schema.FetchedValue):
1711
+ default = sa_schema.DefaultClause(
1712
+ sql.text(default_text), _reflected=True
1713
+ )
1714
+ else:
1715
+ default = default_text
1716
+ colargs.append(default)
1717
+
1718
+ if "computed" in col_d:
1719
+ computed = sa_schema.Computed(**col_d["computed"])
1720
+ colargs.append(computed)
1721
+
1722
+ if "identity" in col_d:
1723
+ identity = sa_schema.Identity(**col_d["identity"])
1724
+ colargs.append(identity)
1725
+
1726
+ cols_by_orig_name[orig_name] = col = sa_schema.Column(
1727
+ name, coltype, *colargs, **col_kw
1728
+ )
1729
+
1730
+ if col.key in table.primary_key:
1731
+ col.primary_key = True
1732
+ table.append_column(col, replace_existing=True)
1733
+
1734
+ def _reflect_pk(
1735
+ self,
1736
+ _reflect_info: _ReflectionInfo,
1737
+ table_key: TableKey,
1738
+ table: sa_schema.Table,
1739
+ cols_by_orig_name: Dict[str, sa_schema.Column[Any]],
1740
+ exclude_columns: Collection[str],
1741
+ ) -> None:
1742
+ pk_cons = _reflect_info.pk_constraint.get(table_key)
1743
+ if pk_cons:
1744
+ pk_cols = [
1745
+ cols_by_orig_name[pk]
1746
+ for pk in pk_cons["constrained_columns"]
1747
+ if pk in cols_by_orig_name and pk not in exclude_columns
1748
+ ]
1749
+
1750
+ # update pk constraint name, comment and dialect_kwargs
1751
+ table.primary_key.name = pk_cons.get("name")
1752
+ table.primary_key.comment = pk_cons.get("comment", None)
1753
+ dialect_options = pk_cons.get("dialect_options")
1754
+ if dialect_options:
1755
+ table.primary_key.dialect_kwargs.update(dialect_options)
1756
+
1757
+ # tell the PKConstraint to re-initialize
1758
+ # its column collection
1759
+ table.primary_key._reload(pk_cols)
1760
+
1761
+ def _reflect_fk(
1762
+ self,
1763
+ _reflect_info: _ReflectionInfo,
1764
+ table_key: TableKey,
1765
+ table: sa_schema.Table,
1766
+ cols_by_orig_name: Dict[str, sa_schema.Column[Any]],
1767
+ include_columns: Optional[Collection[str]],
1768
+ exclude_columns: Collection[str],
1769
+ resolve_fks: bool,
1770
+ _extend_on: Optional[Set[sa_schema.Table]],
1771
+ reflection_options: Dict[str, Any],
1772
+ ) -> None:
1773
+ fkeys = _reflect_info.foreign_keys.get(table_key, [])
1774
+ for fkey_d in fkeys:
1775
+ conname = fkey_d["name"]
1776
+ # look for columns by orig name in cols_by_orig_name,
1777
+ # but support columns that are in-Python only as fallback
1778
+ constrained_columns = [
1779
+ cols_by_orig_name[c].key if c in cols_by_orig_name else c
1780
+ for c in fkey_d["constrained_columns"]
1781
+ ]
1782
+
1783
+ if (
1784
+ exclude_columns
1785
+ and set(constrained_columns).intersection(exclude_columns)
1786
+ or (
1787
+ include_columns
1788
+ and set(constrained_columns).difference(include_columns)
1789
+ )
1790
+ ):
1791
+ continue
1792
+
1793
+ referred_schema = fkey_d["referred_schema"]
1794
+ referred_table = fkey_d["referred_table"]
1795
+ referred_columns = fkey_d["referred_columns"]
1796
+ refspec: List[sa_schema.ForeignKeyTarget] = []
1797
+ if referred_schema is not None:
1798
+ if resolve_fks:
1799
+ sa_schema.Table(
1800
+ referred_table,
1801
+ table.metadata,
1802
+ schema=referred_schema,
1803
+ autoload_with=self.bind,
1804
+ _extend_on=_extend_on,
1805
+ _reflect_info=_reflect_info,
1806
+ **reflection_options,
1807
+ )
1808
+ for column in referred_columns:
1809
+ refspec.append(
1810
+ sa_schema.ForeignKeyTarget(
1811
+ referred_schema, referred_table, column
1812
+ )
1813
+ )
1814
+ else:
1815
+ if resolve_fks:
1816
+ sa_schema.Table(
1817
+ referred_table,
1818
+ table.metadata,
1819
+ autoload_with=self.bind,
1820
+ schema=sa_schema.BLANK_SCHEMA,
1821
+ _extend_on=_extend_on,
1822
+ _reflect_info=_reflect_info,
1823
+ **reflection_options,
1824
+ )
1825
+ for column in referred_columns:
1826
+ refspec.append(
1827
+ sa_schema.ForeignKeyTarget(
1828
+ None, referred_table, column
1829
+ )
1830
+ )
1831
+ if "options" in fkey_d:
1832
+ options = fkey_d["options"]
1833
+ else:
1834
+ options = {}
1835
+
1836
+ try:
1837
+ table.append_constraint(
1838
+ sa_schema.ForeignKeyConstraint(
1839
+ constrained_columns,
1840
+ refspec,
1841
+ conname,
1842
+ link_to_name=True,
1843
+ comment=fkey_d.get("comment"),
1844
+ **options,
1845
+ )
1846
+ )
1847
+ except exc.ConstraintColumnNotFoundError:
1848
+ util.warn(
1849
+ f"On reflected table {table.name}, skipping reflection of "
1850
+ "foreign key constraint "
1851
+ f"{conname}; one or more subject columns within "
1852
+ f"name(s) {', '.join(constrained_columns)} are not "
1853
+ "present in the table"
1854
+ )
1855
+
1856
+ _index_sort_exprs = {
1857
+ "asc": operators.asc_op,
1858
+ "desc": operators.desc_op,
1859
+ "nulls_first": operators.nulls_first_op,
1860
+ "nulls_last": operators.nulls_last_op,
1861
+ }
1862
+
1863
+ def _reflect_indexes(
1864
+ self,
1865
+ _reflect_info: _ReflectionInfo,
1866
+ table_key: TableKey,
1867
+ table: sa_schema.Table,
1868
+ cols_by_orig_name: Dict[str, sa_schema.Column[Any]],
1869
+ include_columns: Optional[Collection[str]],
1870
+ exclude_columns: Collection[str],
1871
+ reflection_options: Dict[str, Any],
1872
+ ) -> None:
1873
+ # Indexes
1874
+ indexes = _reflect_info.indexes.get(table_key, [])
1875
+ for index_d in indexes:
1876
+ name = index_d["name"]
1877
+ columns = index_d["column_names"]
1878
+ expressions = index_d.get("expressions")
1879
+ column_sorting = index_d.get("column_sorting", {})
1880
+ unique = index_d["unique"]
1881
+ flavor = index_d.get("type", "index")
1882
+ dialect_options = index_d.get("dialect_options", {})
1883
+
1884
+ duplicates = index_d.get("duplicates_constraint")
1885
+ if include_columns and not set(columns).issubset(include_columns):
1886
+ continue
1887
+ if duplicates:
1888
+ continue
1889
+ # look for columns by orig name in cols_by_orig_name,
1890
+ # but support columns that are in-Python only as fallback
1891
+ idx_element: Any
1892
+ idx_elements = []
1893
+ for index, c in enumerate(columns):
1894
+ if c is None:
1895
+ if not expressions:
1896
+ util.warn(
1897
+ f"Skipping {flavor} {name!r} because key "
1898
+ f"{index + 1} reflected as None but no "
1899
+ "'expressions' were returned"
1900
+ )
1901
+ break
1902
+ idx_element = sql.text(expressions[index])
1903
+ else:
1904
+ try:
1905
+ if c in cols_by_orig_name:
1906
+ idx_element = cols_by_orig_name[c]
1907
+ else:
1908
+ idx_element = table.c[c]
1909
+ except KeyError:
1910
+ util.warn(
1911
+ f"{flavor} key {c!r} was not located in "
1912
+ f"columns for table {table.name!r}"
1913
+ )
1914
+ continue
1915
+ for option in column_sorting.get(c, ()):
1916
+ if option in self._index_sort_exprs:
1917
+ op = self._index_sort_exprs[option]
1918
+ idx_element = op(idx_element)
1919
+ idx_elements.append(idx_element)
1920
+ else:
1921
+ sa_schema.Index(
1922
+ name,
1923
+ *idx_elements,
1924
+ _table=table,
1925
+ unique=unique,
1926
+ **dialect_options,
1927
+ )
1928
+
1929
+ def _reflect_unique_constraints(
1930
+ self,
1931
+ _reflect_info: _ReflectionInfo,
1932
+ table_key: TableKey,
1933
+ table: sa_schema.Table,
1934
+ cols_by_orig_name: Dict[str, sa_schema.Column[Any]],
1935
+ include_columns: Optional[Collection[str]],
1936
+ exclude_columns: Collection[str],
1937
+ reflection_options: Dict[str, Any],
1938
+ ) -> None:
1939
+ constraints = _reflect_info.unique_constraints.get(table_key, [])
1940
+ # Unique Constraints
1941
+ for const_d in constraints:
1942
+ conname = const_d["name"]
1943
+ columns = const_d["column_names"]
1944
+ comment = const_d.get("comment")
1945
+ duplicates = const_d.get("duplicates_index")
1946
+ dialect_options = const_d.get("dialect_options", {})
1947
+ if include_columns and not set(columns).issubset(include_columns):
1948
+ continue
1949
+ if duplicates:
1950
+ continue
1951
+ # look for columns by orig name in cols_by_orig_name,
1952
+ # but support columns that are in-Python only as fallback
1953
+ constrained_cols = []
1954
+ for c in columns:
1955
+ try:
1956
+ constrained_col = (
1957
+ cols_by_orig_name[c]
1958
+ if c in cols_by_orig_name
1959
+ else table.c[c]
1960
+ )
1961
+ except KeyError:
1962
+ util.warn(
1963
+ "unique constraint key '%s' was not located in "
1964
+ "columns for table '%s'" % (c, table.name)
1965
+ )
1966
+ else:
1967
+ constrained_cols.append(constrained_col)
1968
+ table.append_constraint(
1969
+ sa_schema.UniqueConstraint(
1970
+ *constrained_cols,
1971
+ name=conname,
1972
+ comment=comment,
1973
+ **dialect_options,
1974
+ )
1975
+ )
1976
+
1977
+ def _reflect_check_constraints(
1978
+ self,
1979
+ _reflect_info: _ReflectionInfo,
1980
+ table_key: TableKey,
1981
+ table: sa_schema.Table,
1982
+ cols_by_orig_name: Dict[str, sa_schema.Column[Any]],
1983
+ include_columns: Optional[Collection[str]],
1984
+ exclude_columns: Collection[str],
1985
+ reflection_options: Dict[str, Any],
1986
+ ) -> None:
1987
+ constraints = _reflect_info.check_constraints.get(table_key, [])
1988
+ for const_d in constraints:
1989
+ table.append_constraint(sa_schema.CheckConstraint(**const_d))
1990
+
1991
+ def _reflect_table_comment(
1992
+ self,
1993
+ _reflect_info: _ReflectionInfo,
1994
+ table_key: TableKey,
1995
+ table: sa_schema.Table,
1996
+ reflection_options: Dict[str, Any],
1997
+ ) -> None:
1998
+ comment_dict = _reflect_info.table_comment.get(table_key)
1999
+ if comment_dict:
2000
+ table.comment = comment_dict["text"]
2001
+
2002
+ def _get_reflection_info(
2003
+ self,
2004
+ schema: Optional[str] = None,
2005
+ filter_names: Optional[Collection[str]] = None,
2006
+ available: Optional[Collection[str]] = None,
2007
+ _reflect_info: Optional[_ReflectionInfo] = None,
2008
+ **kw: Any,
2009
+ ) -> _ReflectionInfo:
2010
+ kw["schema"] = schema
2011
+
2012
+ if filter_names and available and len(filter_names) > 100:
2013
+ fraction = len(filter_names) / len(available)
2014
+ else:
2015
+ fraction = None
2016
+
2017
+ unreflectable: Dict[TableKey, exc.UnreflectableTableError]
2018
+ kw["unreflectable"] = unreflectable = {}
2019
+
2020
+ has_result: bool = True
2021
+
2022
+ def run(
2023
+ meth: Any,
2024
+ *,
2025
+ optional: bool = False,
2026
+ check_filter_names_from_meth: bool = False,
2027
+ ) -> Any:
2028
+ nonlocal has_result
2029
+ # simple heuristic to improve reflection performance if a
2030
+ # dialect implements multi_reflection:
2031
+ # if more than 50% of the tables in the db are in filter_names
2032
+ # load all the tables, since it's most likely faster to avoid
2033
+ # a filter on that many tables.
2034
+ if (
2035
+ fraction is None
2036
+ or fraction <= 0.5
2037
+ or not self.dialect._overrides_default(meth.__name__)
2038
+ ):
2039
+ _fn = filter_names
2040
+ else:
2041
+ _fn = None
2042
+ try:
2043
+ if has_result:
2044
+ res = meth(filter_names=_fn, **kw)
2045
+ if check_filter_names_from_meth and not res:
2046
+ # method returned no result data.
2047
+ # skip any future call methods
2048
+ has_result = False
2049
+ else:
2050
+ res = {}
2051
+ except NotImplementedError:
2052
+ if not optional:
2053
+ raise
2054
+ res = {}
2055
+ return res
2056
+
2057
+ info = _ReflectionInfo(
2058
+ columns=run(
2059
+ self.get_multi_columns, check_filter_names_from_meth=True
2060
+ ),
2061
+ pk_constraint=run(self.get_multi_pk_constraint),
2062
+ foreign_keys=run(self.get_multi_foreign_keys),
2063
+ indexes=run(self.get_multi_indexes),
2064
+ unique_constraints=run(
2065
+ self.get_multi_unique_constraints, optional=True
2066
+ ),
2067
+ table_comment=run(self.get_multi_table_comment, optional=True),
2068
+ check_constraints=run(
2069
+ self.get_multi_check_constraints, optional=True
2070
+ ),
2071
+ table_options=run(self.get_multi_table_options, optional=True),
2072
+ unreflectable=unreflectable,
2073
+ )
2074
+ if _reflect_info:
2075
+ _reflect_info.update(info)
2076
+ return _reflect_info
2077
+ else:
2078
+ return info
2079
+
2080
+
2081
+ @final
2082
+ class ReflectionDefaults:
2083
+ """provides blank default values for reflection methods."""
2084
+
2085
+ @classmethod
2086
+ def columns(cls) -> List[ReflectedColumn]:
2087
+ return []
2088
+
2089
+ @classmethod
2090
+ def pk_constraint(cls) -> ReflectedPrimaryKeyConstraint:
2091
+ return {
2092
+ "name": None,
2093
+ "constrained_columns": [],
2094
+ }
2095
+
2096
+ @classmethod
2097
+ def foreign_keys(cls) -> List[ReflectedForeignKeyConstraint]:
2098
+ return []
2099
+
2100
+ @classmethod
2101
+ def indexes(cls) -> List[ReflectedIndex]:
2102
+ return []
2103
+
2104
+ @classmethod
2105
+ def unique_constraints(cls) -> List[ReflectedUniqueConstraint]:
2106
+ return []
2107
+
2108
+ @classmethod
2109
+ def check_constraints(cls) -> List[ReflectedCheckConstraint]:
2110
+ return []
2111
+
2112
+ @classmethod
2113
+ def table_options(cls) -> Dict[str, Any]:
2114
+ return {}
2115
+
2116
+ @classmethod
2117
+ def table_comment(cls) -> ReflectedTableComment:
2118
+ return {"text": None}
2119
+
2120
+
2121
+ @dataclass
2122
+ class _ReflectionInfo:
2123
+ columns: Dict[TableKey, List[ReflectedColumn]]
2124
+ pk_constraint: Dict[TableKey, Optional[ReflectedPrimaryKeyConstraint]]
2125
+ foreign_keys: Dict[TableKey, List[ReflectedForeignKeyConstraint]]
2126
+ indexes: Dict[TableKey, List[ReflectedIndex]]
2127
+ # optionals
2128
+ unique_constraints: Dict[TableKey, List[ReflectedUniqueConstraint]]
2129
+ table_comment: Dict[TableKey, Optional[ReflectedTableComment]]
2130
+ check_constraints: Dict[TableKey, List[ReflectedCheckConstraint]]
2131
+ table_options: Dict[TableKey, Dict[str, Any]]
2132
+ unreflectable: Dict[TableKey, exc.UnreflectableTableError]
2133
+
2134
+ def update(self, other: _ReflectionInfo) -> None:
2135
+ for k, v in self.__dict__.items():
2136
+ ov = getattr(other, k)
2137
+ if ov is not None:
2138
+ if v is None:
2139
+ setattr(self, k, ov)
2140
+ else:
2141
+ v.update(ov)