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