Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/engine/reflection.py: 31%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

503 statements  

1# engine/reflection.py 

2# Copyright (C) 2005-2024 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 

10Usage Notes: 

11 

12Here are some general conventions when accessing the low level inspector 

13methods such as get_table_names, get_columns, etc. 

14 

151. 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 

232. 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""" 

27from __future__ import annotations 

28 

29import contextlib 

30from dataclasses import dataclass 

31from enum import auto 

32from enum import Flag 

33from enum import unique 

34from typing import Any 

35from typing import Callable 

36from typing import Collection 

37from typing import Dict 

38from typing import final 

39from typing import Generator 

40from typing import Iterable 

41from typing import List 

42from typing import Optional 

43from typing import Sequence 

44from typing import Set 

45from typing import Tuple 

46from typing import TYPE_CHECKING 

47from typing import TypeVar 

48from typing import Union 

49 

50from .base import Connection 

51from .base import Engine 

52from .. import exc 

53from .. import inspection 

54from .. import sql 

55from .. import util 

56from ..sql import operators 

57from ..sql import schema as sa_schema 

58from ..sql.cache_key import _ad_hoc_cache_key_from_args 

59from ..sql.elements import TextClause 

60from ..sql.type_api import TypeEngine 

61from ..sql.visitors import InternalTraversal 

62from ..util import topological 

63 

64if TYPE_CHECKING: 

65 from .interfaces import Dialect 

66 from .interfaces import ReflectedCheckConstraint 

67 from .interfaces import ReflectedColumn 

68 from .interfaces import ReflectedForeignKeyConstraint 

69 from .interfaces import ReflectedIndex 

70 from .interfaces import ReflectedPrimaryKeyConstraint 

71 from .interfaces import ReflectedTableComment 

72 from .interfaces import ReflectedUniqueConstraint 

73 from .interfaces import TableKey 

74 

75_R = TypeVar("_R") 

76 

77 

78@util.decorator 

79def cache( 

80 fn: Callable[..., _R], 

81 self: Dialect, 

82 con: Connection, 

83 *args: Any, 

84 **kw: Any, 

85) -> _R: 

86 info_cache = kw.get("info_cache", None) 

87 if info_cache is None: 

88 return fn(self, con, *args, **kw) 

89 exclude = {"info_cache", "unreflectable"} 

90 key = ( 

91 fn.__name__, 

92 tuple(a for a in args if isinstance(a, str)), 

93 tuple((k, v) for k, v in kw.items() if k not in exclude), 

94 ) 

95 ret: _R = info_cache.get(key) 

96 if ret is None: 

97 ret = fn(self, con, *args, **kw) 

98 info_cache[key] = ret 

99 return ret 

100 

101 

102def flexi_cache( 

103 *traverse_args: Tuple[str, InternalTraversal] 

104) -> Callable[[Callable[..., _R]], Callable[..., _R]]: 

105 @util.decorator 

106 def go( 

107 fn: Callable[..., _R], 

108 self: Dialect, 

109 con: Connection, 

110 *args: Any, 

111 **kw: Any, 

112 ) -> _R: 

113 info_cache = kw.get("info_cache", None) 

114 if info_cache is None: 

115 return fn(self, con, *args, **kw) 

116 key = _ad_hoc_cache_key_from_args((fn.__name__,), traverse_args, args) 

117 ret: _R = info_cache.get(key) 

118 if ret is None: 

119 ret = fn(self, con, *args, **kw) 

120 info_cache[key] = ret 

121 return ret 

122 

123 return go 

124 

125 

126@unique 

127class ObjectKind(Flag): 

128 """Enumerator that indicates which kind of object to return when calling 

129 the ``get_multi`` methods. 

130 

131 This is a Flag enum, so custom combinations can be passed. For example, 

132 to reflect tables and plain views ``ObjectKind.TABLE | ObjectKind.VIEW`` 

133 may be used. 

134 

135 .. note:: 

136 Not all dialect may support all kind of object. If a dialect does 

137 not support a particular object an empty dict is returned. 

138 In case a dialect supports an object, but the requested method 

139 is not applicable for the specified kind the default value 

140 will be returned for each reflected object. For example reflecting 

141 check constraints of view return a dict with all the views with 

142 empty lists as values. 

143 """ 

144 

145 TABLE = auto() 

146 "Reflect table objects" 

147 VIEW = auto() 

148 "Reflect plain view objects" 

149 MATERIALIZED_VIEW = auto() 

150 "Reflect materialized view object" 

151 

152 ANY_VIEW = VIEW | MATERIALIZED_VIEW 

153 "Reflect any kind of view objects" 

154 ANY = TABLE | VIEW | MATERIALIZED_VIEW 

155 "Reflect all type of objects" 

156 

157 

158@unique 

159class ObjectScope(Flag): 

160 """Enumerator that indicates which scope to use when calling 

161 the ``get_multi`` methods. 

162 """ 

163 

164 DEFAULT = auto() 

165 "Include default scope" 

166 TEMPORARY = auto() 

167 "Include only temp scope" 

168 ANY = DEFAULT | TEMPORARY 

169 "Include both default and temp scope" 

170 

171 

172@inspection._self_inspects 

173class Inspector(inspection.Inspectable["Inspector"]): 

174 """Performs database schema inspection. 

175 

176 The Inspector acts as a proxy to the reflection methods of the 

177 :class:`~sqlalchemy.engine.interfaces.Dialect`, providing a 

178 consistent interface as well as caching support for previously 

179 fetched metadata. 

180 

181 A :class:`_reflection.Inspector` object is usually created via the 

182 :func:`_sa.inspect` function, which may be passed an 

183 :class:`_engine.Engine` 

184 or a :class:`_engine.Connection`:: 

185 

186 from sqlalchemy import inspect, create_engine 

187 engine = create_engine('...') 

188 insp = inspect(engine) 

189 

190 Where above, the :class:`~sqlalchemy.engine.interfaces.Dialect` associated 

191 with the engine may opt to return an :class:`_reflection.Inspector` 

192 subclass that 

193 provides additional methods specific to the dialect's target database. 

194 

195 """ 

196 

197 bind: Union[Engine, Connection] 

198 engine: Engine 

199 _op_context_requires_connect: bool 

200 dialect: Dialect 

201 info_cache: Dict[Any, Any] 

202 

203 @util.deprecated( 

204 "1.4", 

205 "The __init__() method on :class:`_reflection.Inspector` " 

206 "is deprecated and " 

207 "will be removed in a future release. Please use the " 

208 ":func:`.sqlalchemy.inspect` " 

209 "function on an :class:`_engine.Engine` or " 

210 ":class:`_engine.Connection` " 

211 "in order to " 

212 "acquire an :class:`_reflection.Inspector`.", 

213 ) 

214 def __init__(self, bind: Union[Engine, Connection]): 

215 """Initialize a new :class:`_reflection.Inspector`. 

216 

217 :param bind: a :class:`~sqlalchemy.engine.Connection`, 

218 which is typically an instance of 

219 :class:`~sqlalchemy.engine.Engine` or 

220 :class:`~sqlalchemy.engine.Connection`. 

221 

222 For a dialect-specific instance of :class:`_reflection.Inspector`, see 

223 :meth:`_reflection.Inspector.from_engine` 

224 

225 """ 

226 self._init_legacy(bind) 

227 

228 @classmethod 

229 def _construct( 

230 cls, init: Callable[..., Any], bind: Union[Engine, Connection] 

231 ) -> Inspector: 

232 if hasattr(bind.dialect, "inspector"): 

233 cls = bind.dialect.inspector 

234 

235 self = cls.__new__(cls) 

236 init(self, bind) 

237 return self 

238 

239 def _init_legacy(self, bind: Union[Engine, Connection]) -> None: 

240 if hasattr(bind, "exec_driver_sql"): 

241 self._init_connection(bind) # type: ignore[arg-type] 

242 else: 

243 self._init_engine(bind) 

244 

245 def _init_engine(self, engine: Engine) -> None: 

246 self.bind = self.engine = engine 

247 engine.connect().close() 

248 self._op_context_requires_connect = True 

249 self.dialect = self.engine.dialect 

250 self.info_cache = {} 

251 

252 def _init_connection(self, connection: Connection) -> None: 

253 self.bind = connection 

254 self.engine = connection.engine 

255 self._op_context_requires_connect = False 

256 self.dialect = self.engine.dialect 

257 self.info_cache = {} 

258 

259 def clear_cache(self) -> None: 

260 """reset the cache for this :class:`.Inspector`. 

261 

262 Inspection methods that have data cached will emit SQL queries 

263 when next called to get new data. 

264 

265 .. versionadded:: 2.0 

266 

267 """ 

268 self.info_cache.clear() 

269 

270 @classmethod 

271 @util.deprecated( 

272 "1.4", 

273 "The from_engine() method on :class:`_reflection.Inspector` " 

274 "is deprecated and " 

275 "will be removed in a future release. Please use the " 

276 ":func:`.sqlalchemy.inspect` " 

277 "function on an :class:`_engine.Engine` or " 

278 ":class:`_engine.Connection` " 

279 "in order to " 

280 "acquire an :class:`_reflection.Inspector`.", 

281 ) 

282 def from_engine(cls, bind: Engine) -> Inspector: 

283 """Construct a new dialect-specific Inspector object from the given 

284 engine or connection. 

285 

286 :param bind: a :class:`~sqlalchemy.engine.Connection` 

287 or :class:`~sqlalchemy.engine.Engine`. 

288 

289 This method differs from direct a direct constructor call of 

290 :class:`_reflection.Inspector` in that the 

291 :class:`~sqlalchemy.engine.interfaces.Dialect` is given a chance to 

292 provide a dialect-specific :class:`_reflection.Inspector` instance, 

293 which may 

294 provide additional methods. 

295 

296 See the example at :class:`_reflection.Inspector`. 

297 

298 """ 

299 return cls._construct(cls._init_legacy, bind) 

300 

301 @inspection._inspects(Engine) 

302 def _engine_insp(bind: Engine) -> Inspector: # type: ignore[misc] 

303 return Inspector._construct(Inspector._init_engine, bind) 

304 

305 @inspection._inspects(Connection) 

306 def _connection_insp(bind: Connection) -> Inspector: # type: ignore[misc] 

307 return Inspector._construct(Inspector._init_connection, bind) 

308 

309 @contextlib.contextmanager 

310 def _operation_context(self) -> Generator[Connection, None, None]: 

311 """Return a context that optimizes for multiple operations on a single 

312 transaction. 

313 

314 This essentially allows connect()/close() to be called if we detected 

315 that we're against an :class:`_engine.Engine` and not a 

316 :class:`_engine.Connection`. 

317 

318 """ 

319 conn: Connection 

320 if self._op_context_requires_connect: 

321 conn = self.bind.connect() # type: ignore[union-attr] 

322 else: 

323 conn = self.bind # type: ignore[assignment] 

324 try: 

325 yield conn 

326 finally: 

327 if self._op_context_requires_connect: 

328 conn.close() 

329 

330 @contextlib.contextmanager 

331 def _inspection_context(self) -> Generator[Inspector, None, None]: 

332 """Return an :class:`_reflection.Inspector` 

333 from this one that will run all 

334 operations on a single connection. 

335 

336 """ 

337 

338 with self._operation_context() as conn: 

339 sub_insp = self._construct(self.__class__._init_connection, conn) 

340 sub_insp.info_cache = self.info_cache 

341 yield sub_insp 

342 

343 @property 

344 def default_schema_name(self) -> Optional[str]: 

345 """Return the default schema name presented by the dialect 

346 for the current engine's database user. 

347 

348 E.g. this is typically ``public`` for PostgreSQL and ``dbo`` 

349 for SQL Server. 

350 

351 """ 

352 return self.dialect.default_schema_name 

353 

354 def get_schema_names(self, **kw: Any) -> List[str]: 

355 r"""Return all schema names. 

356 

357 :param \**kw: Additional keyword argument to pass to the dialect 

358 specific implementation. See the documentation of the dialect 

359 in use for more information. 

360 """ 

361 

362 with self._operation_context() as conn: 

363 return self.dialect.get_schema_names( 

364 conn, info_cache=self.info_cache, **kw 

365 ) 

366 

367 def get_table_names( 

368 self, schema: Optional[str] = None, **kw: Any 

369 ) -> List[str]: 

370 r"""Return all table names within a particular schema. 

371 

372 The names are expected to be real tables only, not views. 

373 Views are instead returned using the 

374 :meth:`_reflection.Inspector.get_view_names` and/or 

375 :meth:`_reflection.Inspector.get_materialized_view_names` 

376 methods. 

377 

378 :param schema: Schema name. If ``schema`` is left at ``None``, the 

379 database's default schema is 

380 used, else the named schema is searched. If the database does not 

381 support named schemas, behavior is undefined if ``schema`` is not 

382 passed as ``None``. For special quoting, use :class:`.quoted_name`. 

383 :param \**kw: Additional keyword argument to pass to the dialect 

384 specific implementation. See the documentation of the dialect 

385 in use for more information. 

386 

387 .. seealso:: 

388 

389 :meth:`_reflection.Inspector.get_sorted_table_and_fkc_names` 

390 

391 :attr:`_schema.MetaData.sorted_tables` 

392 

393 """ 

394 

395 with self._operation_context() as conn: 

396 return self.dialect.get_table_names( 

397 conn, schema, info_cache=self.info_cache, **kw 

398 ) 

399 

400 def has_table( 

401 self, table_name: str, schema: Optional[str] = None, **kw: Any 

402 ) -> bool: 

403 r"""Return True if the backend has a table, view, or temporary 

404 table of the given name. 

405 

406 :param table_name: name of the table to check 

407 :param schema: schema name to query, if not the default schema. 

408 :param \**kw: Additional keyword argument to pass to the dialect 

409 specific implementation. See the documentation of the dialect 

410 in use for more information. 

411 

412 .. versionadded:: 1.4 - the :meth:`.Inspector.has_table` method 

413 replaces the :meth:`_engine.Engine.has_table` method. 

414 

415 .. versionchanged:: 2.0:: :meth:`.Inspector.has_table` now formally 

416 supports checking for additional table-like objects: 

417 

418 * any type of views (plain or materialized) 

419 * temporary tables of any kind 

420 

421 Previously, these two checks were not formally specified and 

422 different dialects would vary in their behavior. The dialect 

423 testing suite now includes tests for all of these object types 

424 and should be supported by all SQLAlchemy-included dialects. 

425 Support among third party dialects may be lagging, however. 

426 

427 """ 

428 with self._operation_context() as conn: 

429 return self.dialect.has_table( 

430 conn, table_name, schema, info_cache=self.info_cache, **kw 

431 ) 

432 

433 def has_sequence( 

434 self, sequence_name: str, schema: Optional[str] = None, **kw: Any 

435 ) -> bool: 

436 r"""Return True if the backend has a sequence with the given name. 

437 

438 :param sequence_name: name of the sequence to check 

439 :param schema: schema name to query, if not the default schema. 

440 :param \**kw: Additional keyword argument to pass to the dialect 

441 specific implementation. See the documentation of the dialect 

442 in use for more information. 

443 

444 .. versionadded:: 1.4 

445 

446 """ 

447 with self._operation_context() as conn: 

448 return self.dialect.has_sequence( 

449 conn, sequence_name, schema, info_cache=self.info_cache, **kw 

450 ) 

451 

452 def has_index( 

453 self, 

454 table_name: str, 

455 index_name: str, 

456 schema: Optional[str] = None, 

457 **kw: Any, 

458 ) -> bool: 

459 r"""Check the existence of a particular index name in the database. 

460 

461 :param table_name: the name of the table the index belongs to 

462 :param index_name: the name of the index to check 

463 :param schema: schema name to query, if not the default schema. 

464 :param \**kw: Additional keyword argument to pass to the dialect 

465 specific implementation. See the documentation of the dialect 

466 in use for more information. 

467 

468 .. versionadded:: 2.0 

469 

470 """ 

471 with self._operation_context() as conn: 

472 return self.dialect.has_index( 

473 conn, 

474 table_name, 

475 index_name, 

476 schema, 

477 info_cache=self.info_cache, 

478 **kw, 

479 ) 

480 

481 def has_schema(self, schema_name: str, **kw: Any) -> bool: 

482 r"""Return True if the backend has a schema with the given name. 

483 

484 :param schema_name: name of the schema to check 

485 :param \**kw: Additional keyword argument to pass to the dialect 

486 specific implementation. See the documentation of the dialect 

487 in use for more information. 

488 

489 .. versionadded:: 2.0 

490 

491 """ 

492 with self._operation_context() as conn: 

493 return self.dialect.has_schema( 

494 conn, schema_name, info_cache=self.info_cache, **kw 

495 ) 

496 

497 def get_sorted_table_and_fkc_names( 

498 self, 

499 schema: Optional[str] = None, 

500 **kw: Any, 

501 ) -> List[Tuple[Optional[str], List[Tuple[str, Optional[str]]]]]: 

502 r"""Return dependency-sorted table and foreign key constraint names in 

503 referred to within a particular schema. 

504 

505 This will yield 2-tuples of 

506 ``(tablename, [(tname, fkname), (tname, fkname), ...])`` 

507 consisting of table names in CREATE order grouped with the foreign key 

508 constraint names that are not detected as belonging to a cycle. 

509 The final element 

510 will be ``(None, [(tname, fkname), (tname, fkname), ..])`` 

511 which will consist of remaining 

512 foreign key constraint names that would require a separate CREATE 

513 step after-the-fact, based on dependencies between tables. 

514 

515 :param schema: schema name to query, if not the default schema. 

516 :param \**kw: Additional keyword argument to pass to the dialect 

517 specific implementation. See the documentation of the dialect 

518 in use for more information. 

519 

520 .. seealso:: 

521 

522 :meth:`_reflection.Inspector.get_table_names` 

523 

524 :func:`.sort_tables_and_constraints` - similar method which works 

525 with an already-given :class:`_schema.MetaData`. 

526 

527 """ 

528 

529 return [ 

530 ( 

531 table_key[1] if table_key else None, 

532 [(tname, fks) for (_, tname), fks in fk_collection], 

533 ) 

534 for ( 

535 table_key, 

536 fk_collection, 

537 ) in self.sort_tables_on_foreign_key_dependency( 

538 consider_schemas=(schema,) 

539 ) 

540 ] 

541 

542 def sort_tables_on_foreign_key_dependency( 

543 self, 

544 consider_schemas: Collection[Optional[str]] = (None,), 

545 **kw: Any, 

546 ) -> List[ 

547 Tuple[ 

548 Optional[Tuple[Optional[str], str]], 

549 List[Tuple[Tuple[Optional[str], str], Optional[str]]], 

550 ] 

551 ]: 

552 r"""Return dependency-sorted table and foreign key constraint names 

553 referred to within multiple schemas. 

554 

555 This method may be compared to 

556 :meth:`.Inspector.get_sorted_table_and_fkc_names`, which 

557 works on one schema at a time; here, the method is a generalization 

558 that will consider multiple schemas at once including that it will 

559 resolve for cross-schema foreign keys. 

560 

561 .. versionadded:: 2.0 

562 

563 """ 

564 SchemaTab = Tuple[Optional[str], str] 

565 

566 tuples: Set[Tuple[SchemaTab, SchemaTab]] = set() 

567 remaining_fkcs: Set[Tuple[SchemaTab, Optional[str]]] = set() 

568 fknames_for_table: Dict[SchemaTab, Set[Optional[str]]] = {} 

569 tnames: List[SchemaTab] = [] 

570 

571 for schname in consider_schemas: 

572 schema_fkeys = self.get_multi_foreign_keys(schname, **kw) 

573 tnames.extend(schema_fkeys) 

574 for (_, tname), fkeys in schema_fkeys.items(): 

575 fknames_for_table[(schname, tname)] = { 

576 fk["name"] for fk in fkeys 

577 } 

578 for fkey in fkeys: 

579 if ( 

580 tname != fkey["referred_table"] 

581 or schname != fkey["referred_schema"] 

582 ): 

583 tuples.add( 

584 ( 

585 ( 

586 fkey["referred_schema"], 

587 fkey["referred_table"], 

588 ), 

589 (schname, tname), 

590 ) 

591 ) 

592 try: 

593 candidate_sort = list(topological.sort(tuples, tnames)) 

594 except exc.CircularDependencyError as err: 

595 edge: Tuple[SchemaTab, SchemaTab] 

596 for edge in err.edges: 

597 tuples.remove(edge) 

598 remaining_fkcs.update( 

599 (edge[1], fkc) for fkc in fknames_for_table[edge[1]] 

600 ) 

601 

602 candidate_sort = list(topological.sort(tuples, tnames)) 

603 ret: List[ 

604 Tuple[Optional[SchemaTab], List[Tuple[SchemaTab, Optional[str]]]] 

605 ] 

606 ret = [ 

607 ( 

608 (schname, tname), 

609 [ 

610 ((schname, tname), fk) 

611 for fk in fknames_for_table[(schname, tname)].difference( 

612 name for _, name in remaining_fkcs 

613 ) 

614 ], 

615 ) 

616 for (schname, tname) in candidate_sort 

617 ] 

618 return ret + [(None, list(remaining_fkcs))] 

619 

620 def get_temp_table_names(self, **kw: Any) -> List[str]: 

621 r"""Return a list of temporary table names for the current bind. 

622 

623 This method is unsupported by most dialects; currently 

624 only Oracle, PostgreSQL and SQLite implements it. 

625 

626 :param \**kw: Additional keyword argument to pass to the dialect 

627 specific implementation. See the documentation of the dialect 

628 in use for more information. 

629 

630 """ 

631 

632 with self._operation_context() as conn: 

633 return self.dialect.get_temp_table_names( 

634 conn, info_cache=self.info_cache, **kw 

635 ) 

636 

637 def get_temp_view_names(self, **kw: Any) -> List[str]: 

638 r"""Return a list of temporary view names for the current bind. 

639 

640 This method is unsupported by most dialects; currently 

641 only PostgreSQL and SQLite implements it. 

642 

643 :param \**kw: Additional keyword argument to pass to the dialect 

644 specific implementation. See the documentation of the dialect 

645 in use for more information. 

646 

647 """ 

648 with self._operation_context() as conn: 

649 return self.dialect.get_temp_view_names( 

650 conn, info_cache=self.info_cache, **kw 

651 ) 

652 

653 def get_table_options( 

654 self, table_name: str, schema: Optional[str] = None, **kw: Any 

655 ) -> Dict[str, Any]: 

656 r"""Return a dictionary of options specified when the table of the 

657 given name was created. 

658 

659 This currently includes some options that apply to MySQL and Oracle 

660 tables. 

661 

662 :param table_name: string name of the table. For special quoting, 

663 use :class:`.quoted_name`. 

664 

665 :param schema: string schema name; if omitted, uses the default schema 

666 of the database connection. For special quoting, 

667 use :class:`.quoted_name`. 

668 

669 :param \**kw: Additional keyword argument to pass to the dialect 

670 specific implementation. See the documentation of the dialect 

671 in use for more information. 

672 

673 :return: a dict with the table options. The returned keys depend on the 

674 dialect in use. Each one is prefixed with the dialect name. 

675 

676 .. seealso:: :meth:`Inspector.get_multi_table_options` 

677 

678 """ 

679 with self._operation_context() as conn: 

680 return self.dialect.get_table_options( 

681 conn, table_name, schema, info_cache=self.info_cache, **kw 

682 ) 

683 

684 def get_multi_table_options( 

685 self, 

686 schema: Optional[str] = None, 

687 filter_names: Optional[Sequence[str]] = None, 

688 kind: ObjectKind = ObjectKind.TABLE, 

689 scope: ObjectScope = ObjectScope.DEFAULT, 

690 **kw: Any, 

691 ) -> Dict[TableKey, Dict[str, Any]]: 

692 r"""Return a dictionary of options specified when the tables in the 

693 given schema were created. 

694 

695 The tables can be filtered by passing the names to use to 

696 ``filter_names``. 

697 

698 This currently includes some options that apply to MySQL and Oracle 

699 tables. 

700 

701 :param schema: string schema name; if omitted, uses the default schema 

702 of the database connection. For special quoting, 

703 use :class:`.quoted_name`. 

704 

705 :param filter_names: optionally return information only for the 

706 objects listed here. 

707 

708 :param kind: a :class:`.ObjectKind` that specifies the type of objects 

709 to reflect. Defaults to ``ObjectKind.TABLE``. 

710 

711 :param scope: a :class:`.ObjectScope` that specifies if options of 

712 default, temporary or any tables should be reflected. 

713 Defaults to ``ObjectScope.DEFAULT``. 

714 

715 :param \**kw: Additional keyword argument to pass to the dialect 

716 specific implementation. See the documentation of the dialect 

717 in use for more information. 

718 

719 :return: a dictionary where the keys are two-tuple schema,table-name 

720 and the values are dictionaries with the table options. 

721 The returned keys in each dict depend on the 

722 dialect in use. Each one is prefixed with the dialect name. 

723 The schema is ``None`` if no schema is provided. 

724 

725 .. versionadded:: 2.0 

726 

727 .. seealso:: :meth:`Inspector.get_table_options` 

728 """ 

729 with self._operation_context() as conn: 

730 res = self.dialect.get_multi_table_options( 

731 conn, 

732 schema=schema, 

733 filter_names=filter_names, 

734 kind=kind, 

735 scope=scope, 

736 info_cache=self.info_cache, 

737 **kw, 

738 ) 

739 return dict(res) 

740 

741 def get_view_names( 

742 self, schema: Optional[str] = None, **kw: Any 

743 ) -> List[str]: 

744 r"""Return all non-materialized view names in `schema`. 

745 

746 :param schema: Optional, retrieve names from a non-default schema. 

747 For special quoting, use :class:`.quoted_name`. 

748 :param \**kw: Additional keyword argument to pass to the dialect 

749 specific implementation. See the documentation of the dialect 

750 in use for more information. 

751 

752 

753 .. versionchanged:: 2.0 For those dialects that previously included 

754 the names of materialized views in this list (currently PostgreSQL), 

755 this method no longer returns the names of materialized views. 

756 the :meth:`.Inspector.get_materialized_view_names` method should 

757 be used instead. 

758 

759 .. seealso:: 

760 

761 :meth:`.Inspector.get_materialized_view_names` 

762 

763 """ 

764 

765 with self._operation_context() as conn: 

766 return self.dialect.get_view_names( 

767 conn, schema, info_cache=self.info_cache, **kw 

768 ) 

769 

770 def get_materialized_view_names( 

771 self, schema: Optional[str] = None, **kw: Any 

772 ) -> List[str]: 

773 r"""Return all materialized view names in `schema`. 

774 

775 :param schema: Optional, retrieve names from a non-default schema. 

776 For special quoting, use :class:`.quoted_name`. 

777 :param \**kw: Additional keyword argument to pass to the dialect 

778 specific implementation. See the documentation of the dialect 

779 in use for more information. 

780 

781 .. versionadded:: 2.0 

782 

783 .. seealso:: 

784 

785 :meth:`.Inspector.get_view_names` 

786 

787 """ 

788 

789 with self._operation_context() as conn: 

790 return self.dialect.get_materialized_view_names( 

791 conn, schema, info_cache=self.info_cache, **kw 

792 ) 

793 

794 def get_sequence_names( 

795 self, schema: Optional[str] = None, **kw: Any 

796 ) -> List[str]: 

797 r"""Return all sequence names in `schema`. 

798 

799 :param schema: Optional, retrieve names from a non-default schema. 

800 For special quoting, use :class:`.quoted_name`. 

801 :param \**kw: Additional keyword argument to pass to the dialect 

802 specific implementation. See the documentation of the dialect 

803 in use for more information. 

804 

805 """ 

806 

807 with self._operation_context() as conn: 

808 return self.dialect.get_sequence_names( 

809 conn, schema, info_cache=self.info_cache, **kw 

810 ) 

811 

812 def get_view_definition( 

813 self, view_name: str, schema: Optional[str] = None, **kw: Any 

814 ) -> str: 

815 r"""Return definition for the plain or materialized view called 

816 ``view_name``. 

817 

818 :param view_name: Name of the view. 

819 :param schema: Optional, retrieve names from a non-default schema. 

820 For special quoting, use :class:`.quoted_name`. 

821 :param \**kw: Additional keyword argument to pass to the dialect 

822 specific implementation. See the documentation of the dialect 

823 in use for more information. 

824 

825 """ 

826 

827 with self._operation_context() as conn: 

828 return self.dialect.get_view_definition( 

829 conn, view_name, schema, info_cache=self.info_cache, **kw 

830 ) 

831 

832 def get_columns( 

833 self, table_name: str, schema: Optional[str] = None, **kw: Any 

834 ) -> List[ReflectedColumn]: 

835 r"""Return information about columns in ``table_name``. 

836 

837 Given a string ``table_name`` and an optional string ``schema``, 

838 return column information as a list of :class:`.ReflectedColumn`. 

839 

840 :param table_name: string name of the table. For special quoting, 

841 use :class:`.quoted_name`. 

842 

843 :param schema: string schema name; if omitted, uses the default schema 

844 of the database connection. For special quoting, 

845 use :class:`.quoted_name`. 

846 

847 :param \**kw: Additional keyword argument to pass to the dialect 

848 specific implementation. See the documentation of the dialect 

849 in use for more information. 

850 

851 :return: list of dictionaries, each representing the definition of 

852 a database column. 

853 

854 .. seealso:: :meth:`Inspector.get_multi_columns`. 

855 

856 """ 

857 

858 with self._operation_context() as conn: 

859 col_defs = self.dialect.get_columns( 

860 conn, table_name, schema, info_cache=self.info_cache, **kw 

861 ) 

862 if col_defs: 

863 self._instantiate_types([col_defs]) 

864 return col_defs 

865 

866 def _instantiate_types( 

867 self, data: Iterable[List[ReflectedColumn]] 

868 ) -> None: 

869 # make this easy and only return instances for coltype 

870 for col_defs in data: 

871 for col_def in col_defs: 

872 coltype = col_def["type"] 

873 if not isinstance(coltype, TypeEngine): 

874 col_def["type"] = coltype() 

875 

876 def get_multi_columns( 

877 self, 

878 schema: Optional[str] = None, 

879 filter_names: Optional[Sequence[str]] = None, 

880 kind: ObjectKind = ObjectKind.TABLE, 

881 scope: ObjectScope = ObjectScope.DEFAULT, 

882 **kw: Any, 

883 ) -> Dict[TableKey, List[ReflectedColumn]]: 

884 r"""Return information about columns in all objects in the given 

885 schema. 

886 

887 The objects can be filtered by passing the names to use to 

888 ``filter_names``. 

889 

890 For each table the value is a list of :class:`.ReflectedColumn`. 

891 

892 :param schema: string schema name; if omitted, uses the default schema 

893 of the database connection. For special quoting, 

894 use :class:`.quoted_name`. 

895 

896 :param filter_names: optionally return information only for the 

897 objects listed here. 

898 

899 :param kind: a :class:`.ObjectKind` that specifies the type of objects 

900 to reflect. Defaults to ``ObjectKind.TABLE``. 

901 

902 :param scope: a :class:`.ObjectScope` that specifies if columns of 

903 default, temporary or any tables should be reflected. 

904 Defaults to ``ObjectScope.DEFAULT``. 

905 

906 :param \**kw: Additional keyword argument to pass to the dialect 

907 specific implementation. See the documentation of the dialect 

908 in use for more information. 

909 

910 :return: a dictionary where the keys are two-tuple schema,table-name 

911 and the values are list of dictionaries, each representing the 

912 definition of a database column. 

913 The schema is ``None`` if no schema is provided. 

914 

915 .. versionadded:: 2.0 

916 

917 .. seealso:: :meth:`Inspector.get_columns` 

918 """ 

919 

920 with self._operation_context() as conn: 

921 table_col_defs = dict( 

922 self.dialect.get_multi_columns( 

923 conn, 

924 schema=schema, 

925 filter_names=filter_names, 

926 kind=kind, 

927 scope=scope, 

928 info_cache=self.info_cache, 

929 **kw, 

930 ) 

931 ) 

932 self._instantiate_types(table_col_defs.values()) 

933 return table_col_defs 

934 

935 def get_pk_constraint( 

936 self, table_name: str, schema: Optional[str] = None, **kw: Any 

937 ) -> ReflectedPrimaryKeyConstraint: 

938 r"""Return information about primary key constraint in ``table_name``. 

939 

940 Given a string ``table_name``, and an optional string `schema`, return 

941 primary key information as a :class:`.ReflectedPrimaryKeyConstraint`. 

942 

943 :param table_name: string name of the table. For special quoting, 

944 use :class:`.quoted_name`. 

945 

946 :param schema: string schema name; if omitted, uses the default schema 

947 of the database connection. For special quoting, 

948 use :class:`.quoted_name`. 

949 

950 :param \**kw: Additional keyword argument to pass to the dialect 

951 specific implementation. See the documentation of the dialect 

952 in use for more information. 

953 

954 :return: a dictionary representing the definition of 

955 a primary key constraint. 

956 

957 .. seealso:: :meth:`Inspector.get_multi_pk_constraint` 

958 """ 

959 with self._operation_context() as conn: 

960 return self.dialect.get_pk_constraint( 

961 conn, table_name, schema, info_cache=self.info_cache, **kw 

962 ) 

963 

964 def get_multi_pk_constraint( 

965 self, 

966 schema: Optional[str] = None, 

967 filter_names: Optional[Sequence[str]] = None, 

968 kind: ObjectKind = ObjectKind.TABLE, 

969 scope: ObjectScope = ObjectScope.DEFAULT, 

970 **kw: Any, 

971 ) -> Dict[TableKey, ReflectedPrimaryKeyConstraint]: 

972 r"""Return information about primary key constraints in 

973 all tables in the given schema. 

974 

975 The tables can be filtered by passing the names to use to 

976 ``filter_names``. 

977 

978 For each table the value is a :class:`.ReflectedPrimaryKeyConstraint`. 

979 

980 :param schema: string schema name; if omitted, uses the default schema 

981 of the database connection. For special quoting, 

982 use :class:`.quoted_name`. 

983 

984 :param filter_names: optionally return information only for the 

985 objects listed here. 

986 

987 :param kind: a :class:`.ObjectKind` that specifies the type of objects 

988 to reflect. Defaults to ``ObjectKind.TABLE``. 

989 

990 :param scope: a :class:`.ObjectScope` that specifies if primary keys of 

991 default, temporary or any tables should be reflected. 

992 Defaults to ``ObjectScope.DEFAULT``. 

993 

994 :param \**kw: Additional keyword argument to pass to the dialect 

995 specific implementation. See the documentation of the dialect 

996 in use for more information. 

997 

998 :return: a dictionary where the keys are two-tuple schema,table-name 

999 and the values are dictionaries, each representing the 

1000 definition of a primary key constraint. 

1001 The schema is ``None`` if no schema is provided. 

1002 

1003 .. versionadded:: 2.0 

1004 

1005 .. seealso:: :meth:`Inspector.get_pk_constraint` 

1006 """ 

1007 with self._operation_context() as conn: 

1008 return dict( 

1009 self.dialect.get_multi_pk_constraint( 

1010 conn, 

1011 schema=schema, 

1012 filter_names=filter_names, 

1013 kind=kind, 

1014 scope=scope, 

1015 info_cache=self.info_cache, 

1016 **kw, 

1017 ) 

1018 ) 

1019 

1020 def get_foreign_keys( 

1021 self, table_name: str, schema: Optional[str] = None, **kw: Any 

1022 ) -> List[ReflectedForeignKeyConstraint]: 

1023 r"""Return information about foreign_keys in ``table_name``. 

1024 

1025 Given a string ``table_name``, and an optional string `schema`, return 

1026 foreign key information as a list of 

1027 :class:`.ReflectedForeignKeyConstraint`. 

1028 

1029 :param table_name: string name of the table. For special quoting, 

1030 use :class:`.quoted_name`. 

1031 

1032 :param schema: string schema name; if omitted, uses the default schema 

1033 of the database connection. For special quoting, 

1034 use :class:`.quoted_name`. 

1035 

1036 :param \**kw: Additional keyword argument to pass to the dialect 

1037 specific implementation. See the documentation of the dialect 

1038 in use for more information. 

1039 

1040 :return: a list of dictionaries, each representing the 

1041 a foreign key definition. 

1042 

1043 .. seealso:: :meth:`Inspector.get_multi_foreign_keys` 

1044 """ 

1045 

1046 with self._operation_context() as conn: 

1047 return self.dialect.get_foreign_keys( 

1048 conn, table_name, schema, info_cache=self.info_cache, **kw 

1049 ) 

1050 

1051 def get_multi_foreign_keys( 

1052 self, 

1053 schema: Optional[str] = None, 

1054 filter_names: Optional[Sequence[str]] = None, 

1055 kind: ObjectKind = ObjectKind.TABLE, 

1056 scope: ObjectScope = ObjectScope.DEFAULT, 

1057 **kw: Any, 

1058 ) -> Dict[TableKey, List[ReflectedForeignKeyConstraint]]: 

1059 r"""Return information about foreign_keys in all tables 

1060 in the given schema. 

1061 

1062 The tables can be filtered by passing the names to use to 

1063 ``filter_names``. 

1064 

1065 For each table the value is a list of 

1066 :class:`.ReflectedForeignKeyConstraint`. 

1067 

1068 :param schema: string schema name; if omitted, uses the default schema 

1069 of the database connection. For special quoting, 

1070 use :class:`.quoted_name`. 

1071 

1072 :param filter_names: optionally return information only for the 

1073 objects listed here. 

1074 

1075 :param kind: a :class:`.ObjectKind` that specifies the type of objects 

1076 to reflect. Defaults to ``ObjectKind.TABLE``. 

1077 

1078 :param scope: a :class:`.ObjectScope` that specifies if foreign keys of 

1079 default, temporary or any tables should be reflected. 

1080 Defaults to ``ObjectScope.DEFAULT``. 

1081 

1082 :param \**kw: Additional keyword argument to pass to the dialect 

1083 specific implementation. See the documentation of the dialect 

1084 in use for more information. 

1085 

1086 :return: a dictionary where the keys are two-tuple schema,table-name 

1087 and the values are list of dictionaries, each representing 

1088 a foreign key definition. 

1089 The schema is ``None`` if no schema is provided. 

1090 

1091 .. versionadded:: 2.0 

1092 

1093 .. seealso:: :meth:`Inspector.get_foreign_keys` 

1094 """ 

1095 

1096 with self._operation_context() as conn: 

1097 return dict( 

1098 self.dialect.get_multi_foreign_keys( 

1099 conn, 

1100 schema=schema, 

1101 filter_names=filter_names, 

1102 kind=kind, 

1103 scope=scope, 

1104 info_cache=self.info_cache, 

1105 **kw, 

1106 ) 

1107 ) 

1108 

1109 def get_indexes( 

1110 self, table_name: str, schema: Optional[str] = None, **kw: Any 

1111 ) -> List[ReflectedIndex]: 

1112 r"""Return information about indexes in ``table_name``. 

1113 

1114 Given a string ``table_name`` and an optional string `schema`, return 

1115 index information as a list of :class:`.ReflectedIndex`. 

1116 

1117 :param table_name: string name of the table. For special quoting, 

1118 use :class:`.quoted_name`. 

1119 

1120 :param schema: string schema name; if omitted, uses the default schema 

1121 of the database connection. For special quoting, 

1122 use :class:`.quoted_name`. 

1123 

1124 :param \**kw: Additional keyword argument to pass to the dialect 

1125 specific implementation. See the documentation of the dialect 

1126 in use for more information. 

1127 

1128 :return: a list of dictionaries, each representing the 

1129 definition of an index. 

1130 

1131 .. seealso:: :meth:`Inspector.get_multi_indexes` 

1132 """ 

1133 

1134 with self._operation_context() as conn: 

1135 return self.dialect.get_indexes( 

1136 conn, table_name, schema, info_cache=self.info_cache, **kw 

1137 ) 

1138 

1139 def get_multi_indexes( 

1140 self, 

1141 schema: Optional[str] = None, 

1142 filter_names: Optional[Sequence[str]] = None, 

1143 kind: ObjectKind = ObjectKind.TABLE, 

1144 scope: ObjectScope = ObjectScope.DEFAULT, 

1145 **kw: Any, 

1146 ) -> Dict[TableKey, List[ReflectedIndex]]: 

1147 r"""Return information about indexes in in all objects 

1148 in the given schema. 

1149 

1150 The objects can be filtered by passing the names to use to 

1151 ``filter_names``. 

1152 

1153 For each table the value is a list of :class:`.ReflectedIndex`. 

1154 

1155 :param schema: string schema name; if omitted, uses the default schema 

1156 of the database connection. For special quoting, 

1157 use :class:`.quoted_name`. 

1158 

1159 :param filter_names: optionally return information only for the 

1160 objects listed here. 

1161 

1162 :param kind: a :class:`.ObjectKind` that specifies the type of objects 

1163 to reflect. Defaults to ``ObjectKind.TABLE``. 

1164 

1165 :param scope: a :class:`.ObjectScope` that specifies if indexes of 

1166 default, temporary or any tables should be reflected. 

1167 Defaults to ``ObjectScope.DEFAULT``. 

1168 

1169 :param \**kw: Additional keyword argument to pass to the dialect 

1170 specific implementation. See the documentation of the dialect 

1171 in use for more information. 

1172 

1173 :return: a dictionary where the keys are two-tuple schema,table-name 

1174 and the values are list of dictionaries, each representing the 

1175 definition of an index. 

1176 The schema is ``None`` if no schema is provided. 

1177 

1178 .. versionadded:: 2.0 

1179 

1180 .. seealso:: :meth:`Inspector.get_indexes` 

1181 """ 

1182 

1183 with self._operation_context() as conn: 

1184 return dict( 

1185 self.dialect.get_multi_indexes( 

1186 conn, 

1187 schema=schema, 

1188 filter_names=filter_names, 

1189 kind=kind, 

1190 scope=scope, 

1191 info_cache=self.info_cache, 

1192 **kw, 

1193 ) 

1194 ) 

1195 

1196 def get_unique_constraints( 

1197 self, table_name: str, schema: Optional[str] = None, **kw: Any 

1198 ) -> List[ReflectedUniqueConstraint]: 

1199 r"""Return information about unique constraints in ``table_name``. 

1200 

1201 Given a string ``table_name`` and an optional string `schema`, return 

1202 unique constraint information as a list of 

1203 :class:`.ReflectedUniqueConstraint`. 

1204 

1205 :param table_name: string name of the table. For special quoting, 

1206 use :class:`.quoted_name`. 

1207 

1208 :param schema: string schema name; if omitted, uses the default schema 

1209 of the database connection. For special quoting, 

1210 use :class:`.quoted_name`. 

1211 

1212 :param \**kw: Additional keyword argument to pass to the dialect 

1213 specific implementation. See the documentation of the dialect 

1214 in use for more information. 

1215 

1216 :return: a list of dictionaries, each representing the 

1217 definition of an unique constraint. 

1218 

1219 .. seealso:: :meth:`Inspector.get_multi_unique_constraints` 

1220 """ 

1221 

1222 with self._operation_context() as conn: 

1223 return self.dialect.get_unique_constraints( 

1224 conn, table_name, schema, info_cache=self.info_cache, **kw 

1225 ) 

1226 

1227 def get_multi_unique_constraints( 

1228 self, 

1229 schema: Optional[str] = None, 

1230 filter_names: Optional[Sequence[str]] = None, 

1231 kind: ObjectKind = ObjectKind.TABLE, 

1232 scope: ObjectScope = ObjectScope.DEFAULT, 

1233 **kw: Any, 

1234 ) -> Dict[TableKey, List[ReflectedUniqueConstraint]]: 

1235 r"""Return information about unique constraints in all tables 

1236 in the given schema. 

1237 

1238 The tables can be filtered by passing the names to use to 

1239 ``filter_names``. 

1240 

1241 For each table the value is a list of 

1242 :class:`.ReflectedUniqueConstraint`. 

1243 

1244 :param schema: string schema name; if omitted, uses the default schema 

1245 of the database connection. For special quoting, 

1246 use :class:`.quoted_name`. 

1247 

1248 :param filter_names: optionally return information only for the 

1249 objects listed here. 

1250 

1251 :param kind: a :class:`.ObjectKind` that specifies the type of objects 

1252 to reflect. Defaults to ``ObjectKind.TABLE``. 

1253 

1254 :param scope: a :class:`.ObjectScope` that specifies if constraints of 

1255 default, temporary or any tables should be reflected. 

1256 Defaults to ``ObjectScope.DEFAULT``. 

1257 

1258 :param \**kw: Additional keyword argument to pass to the dialect 

1259 specific implementation. See the documentation of the dialect 

1260 in use for more information. 

1261 

1262 :return: a dictionary where the keys are two-tuple schema,table-name 

1263 and the values are list of dictionaries, each representing the 

1264 definition of an unique constraint. 

1265 The schema is ``None`` if no schema is provided. 

1266 

1267 .. versionadded:: 2.0 

1268 

1269 .. seealso:: :meth:`Inspector.get_unique_constraints` 

1270 """ 

1271 

1272 with self._operation_context() as conn: 

1273 return dict( 

1274 self.dialect.get_multi_unique_constraints( 

1275 conn, 

1276 schema=schema, 

1277 filter_names=filter_names, 

1278 kind=kind, 

1279 scope=scope, 

1280 info_cache=self.info_cache, 

1281 **kw, 

1282 ) 

1283 ) 

1284 

1285 def get_table_comment( 

1286 self, table_name: str, schema: Optional[str] = None, **kw: Any 

1287 ) -> ReflectedTableComment: 

1288 r"""Return information about the table comment for ``table_name``. 

1289 

1290 Given a string ``table_name`` and an optional string ``schema``, 

1291 return table comment information as a :class:`.ReflectedTableComment`. 

1292 

1293 Raises ``NotImplementedError`` for a dialect that does not support 

1294 comments. 

1295 

1296 :param table_name: string name of the table. For special quoting, 

1297 use :class:`.quoted_name`. 

1298 

1299 :param schema: string schema name; if omitted, uses the default schema 

1300 of the database connection. For special quoting, 

1301 use :class:`.quoted_name`. 

1302 

1303 :param \**kw: Additional keyword argument to pass to the dialect 

1304 specific implementation. See the documentation of the dialect 

1305 in use for more information. 

1306 

1307 :return: a dictionary, with the table comment. 

1308 

1309 .. versionadded:: 1.2 

1310 

1311 .. seealso:: :meth:`Inspector.get_multi_table_comment` 

1312 """ 

1313 

1314 with self._operation_context() as conn: 

1315 return self.dialect.get_table_comment( 

1316 conn, table_name, schema, info_cache=self.info_cache, **kw 

1317 ) 

1318 

1319 def get_multi_table_comment( 

1320 self, 

1321 schema: Optional[str] = None, 

1322 filter_names: Optional[Sequence[str]] = None, 

1323 kind: ObjectKind = ObjectKind.TABLE, 

1324 scope: ObjectScope = ObjectScope.DEFAULT, 

1325 **kw: Any, 

1326 ) -> Dict[TableKey, ReflectedTableComment]: 

1327 r"""Return information about the table comment in all objects 

1328 in the given schema. 

1329 

1330 The objects can be filtered by passing the names to use to 

1331 ``filter_names``. 

1332 

1333 For each table the value is a :class:`.ReflectedTableComment`. 

1334 

1335 Raises ``NotImplementedError`` for a dialect that does not support 

1336 comments. 

1337 

1338 :param schema: string schema name; if omitted, uses the default schema 

1339 of the database connection. For special quoting, 

1340 use :class:`.quoted_name`. 

1341 

1342 :param filter_names: optionally return information only for the 

1343 objects listed here. 

1344 

1345 :param kind: a :class:`.ObjectKind` that specifies the type of objects 

1346 to reflect. Defaults to ``ObjectKind.TABLE``. 

1347 

1348 :param scope: a :class:`.ObjectScope` that specifies if comments of 

1349 default, temporary or any tables should be reflected. 

1350 Defaults to ``ObjectScope.DEFAULT``. 

1351 

1352 :param \**kw: Additional keyword argument to pass to the dialect 

1353 specific implementation. See the documentation of the dialect 

1354 in use for more information. 

1355 

1356 :return: a dictionary where the keys are two-tuple schema,table-name 

1357 and the values are dictionaries, representing the 

1358 table comments. 

1359 The schema is ``None`` if no schema is provided. 

1360 

1361 .. versionadded:: 2.0 

1362 

1363 .. seealso:: :meth:`Inspector.get_table_comment` 

1364 """ 

1365 

1366 with self._operation_context() as conn: 

1367 return dict( 

1368 self.dialect.get_multi_table_comment( 

1369 conn, 

1370 schema=schema, 

1371 filter_names=filter_names, 

1372 kind=kind, 

1373 scope=scope, 

1374 info_cache=self.info_cache, 

1375 **kw, 

1376 ) 

1377 ) 

1378 

1379 def get_check_constraints( 

1380 self, table_name: str, schema: Optional[str] = None, **kw: Any 

1381 ) -> List[ReflectedCheckConstraint]: 

1382 r"""Return information about check constraints in ``table_name``. 

1383 

1384 Given a string ``table_name`` and an optional string `schema`, return 

1385 check constraint information as a list of 

1386 :class:`.ReflectedCheckConstraint`. 

1387 

1388 :param table_name: string name of the table. For special quoting, 

1389 use :class:`.quoted_name`. 

1390 

1391 :param schema: string schema name; if omitted, uses the default schema 

1392 of the database connection. For special quoting, 

1393 use :class:`.quoted_name`. 

1394 

1395 :param \**kw: Additional keyword argument to pass to the dialect 

1396 specific implementation. See the documentation of the dialect 

1397 in use for more information. 

1398 

1399 :return: a list of dictionaries, each representing the 

1400 definition of a check constraints. 

1401 

1402 .. seealso:: :meth:`Inspector.get_multi_check_constraints` 

1403 """ 

1404 

1405 with self._operation_context() as conn: 

1406 return self.dialect.get_check_constraints( 

1407 conn, table_name, schema, info_cache=self.info_cache, **kw 

1408 ) 

1409 

1410 def get_multi_check_constraints( 

1411 self, 

1412 schema: Optional[str] = None, 

1413 filter_names: Optional[Sequence[str]] = None, 

1414 kind: ObjectKind = ObjectKind.TABLE, 

1415 scope: ObjectScope = ObjectScope.DEFAULT, 

1416 **kw: Any, 

1417 ) -> Dict[TableKey, List[ReflectedCheckConstraint]]: 

1418 r"""Return information about check constraints in all tables 

1419 in the given schema. 

1420 

1421 The tables can be filtered by passing the names to use to 

1422 ``filter_names``. 

1423 

1424 For each table the value is a list of 

1425 :class:`.ReflectedCheckConstraint`. 

1426 

1427 :param schema: string schema name; if omitted, uses the default schema 

1428 of the database connection. For special quoting, 

1429 use :class:`.quoted_name`. 

1430 

1431 :param filter_names: optionally return information only for the 

1432 objects listed here. 

1433 

1434 :param kind: a :class:`.ObjectKind` that specifies the type of objects 

1435 to reflect. Defaults to ``ObjectKind.TABLE``. 

1436 

1437 :param scope: a :class:`.ObjectScope` that specifies if constraints of 

1438 default, temporary or any tables should be reflected. 

1439 Defaults to ``ObjectScope.DEFAULT``. 

1440 

1441 :param \**kw: Additional keyword argument to pass to the dialect 

1442 specific implementation. See the documentation of the dialect 

1443 in use for more information. 

1444 

1445 :return: a dictionary where the keys are two-tuple schema,table-name 

1446 and the values are list of dictionaries, each representing the 

1447 definition of a check constraints. 

1448 The schema is ``None`` if no schema is provided. 

1449 

1450 .. versionadded:: 2.0 

1451 

1452 .. seealso:: :meth:`Inspector.get_check_constraints` 

1453 """ 

1454 

1455 with self._operation_context() as conn: 

1456 return dict( 

1457 self.dialect.get_multi_check_constraints( 

1458 conn, 

1459 schema=schema, 

1460 filter_names=filter_names, 

1461 kind=kind, 

1462 scope=scope, 

1463 info_cache=self.info_cache, 

1464 **kw, 

1465 ) 

1466 ) 

1467 

1468 def reflect_table( 

1469 self, 

1470 table: sa_schema.Table, 

1471 include_columns: Optional[Collection[str]], 

1472 exclude_columns: Collection[str] = (), 

1473 resolve_fks: bool = True, 

1474 _extend_on: Optional[Set[sa_schema.Table]] = None, 

1475 _reflect_info: Optional[_ReflectionInfo] = None, 

1476 ) -> None: 

1477 """Given a :class:`_schema.Table` object, load its internal 

1478 constructs based on introspection. 

1479 

1480 This is the underlying method used by most dialects to produce 

1481 table reflection. Direct usage is like:: 

1482 

1483 from sqlalchemy import create_engine, MetaData, Table 

1484 from sqlalchemy import inspect 

1485 

1486 engine = create_engine('...') 

1487 meta = MetaData() 

1488 user_table = Table('user', meta) 

1489 insp = inspect(engine) 

1490 insp.reflect_table(user_table, None) 

1491 

1492 .. versionchanged:: 1.4 Renamed from ``reflecttable`` to 

1493 ``reflect_table`` 

1494 

1495 :param table: a :class:`~sqlalchemy.schema.Table` instance. 

1496 :param include_columns: a list of string column names to include 

1497 in the reflection process. If ``None``, all columns are reflected. 

1498 

1499 """ 

1500 

1501 if _extend_on is not None: 

1502 if table in _extend_on: 

1503 return 

1504 else: 

1505 _extend_on.add(table) 

1506 

1507 dialect = self.bind.dialect 

1508 

1509 with self._operation_context() as conn: 

1510 schema = conn.schema_for_object(table) 

1511 

1512 table_name = table.name 

1513 

1514 # get table-level arguments that are specifically 

1515 # intended for reflection, e.g. oracle_resolve_synonyms. 

1516 # these are unconditionally passed to related Table 

1517 # objects 

1518 reflection_options = { 

1519 k: table.dialect_kwargs.get(k) 

1520 for k in dialect.reflection_options 

1521 if k in table.dialect_kwargs 

1522 } 

1523 

1524 table_key = (schema, table_name) 

1525 if _reflect_info is None or table_key not in _reflect_info.columns: 

1526 _reflect_info = self._get_reflection_info( 

1527 schema, 

1528 filter_names=[table_name], 

1529 kind=ObjectKind.ANY, 

1530 scope=ObjectScope.ANY, 

1531 _reflect_info=_reflect_info, 

1532 **table.dialect_kwargs, 

1533 ) 

1534 if table_key in _reflect_info.unreflectable: 

1535 raise _reflect_info.unreflectable[table_key] 

1536 

1537 if table_key not in _reflect_info.columns: 

1538 raise exc.NoSuchTableError(table_name) 

1539 

1540 # reflect table options, like mysql_engine 

1541 if _reflect_info.table_options: 

1542 tbl_opts = _reflect_info.table_options.get(table_key) 

1543 if tbl_opts: 

1544 # add additional kwargs to the Table if the dialect 

1545 # returned them 

1546 table._validate_dialect_kwargs(tbl_opts) 

1547 

1548 found_table = False 

1549 cols_by_orig_name: Dict[str, sa_schema.Column[Any]] = {} 

1550 

1551 for col_d in _reflect_info.columns[table_key]: 

1552 found_table = True 

1553 

1554 self._reflect_column( 

1555 table, 

1556 col_d, 

1557 include_columns, 

1558 exclude_columns, 

1559 cols_by_orig_name, 

1560 ) 

1561 

1562 # NOTE: support tables/views with no columns 

1563 if not found_table and not self.has_table(table_name, schema): 

1564 raise exc.NoSuchTableError(table_name) 

1565 

1566 self._reflect_pk( 

1567 _reflect_info, table_key, table, cols_by_orig_name, exclude_columns 

1568 ) 

1569 

1570 self._reflect_fk( 

1571 _reflect_info, 

1572 table_key, 

1573 table, 

1574 cols_by_orig_name, 

1575 include_columns, 

1576 exclude_columns, 

1577 resolve_fks, 

1578 _extend_on, 

1579 reflection_options, 

1580 ) 

1581 

1582 self._reflect_indexes( 

1583 _reflect_info, 

1584 table_key, 

1585 table, 

1586 cols_by_orig_name, 

1587 include_columns, 

1588 exclude_columns, 

1589 reflection_options, 

1590 ) 

1591 

1592 self._reflect_unique_constraints( 

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_check_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_table_comment( 

1613 _reflect_info, 

1614 table_key, 

1615 table, 

1616 reflection_options, 

1617 ) 

1618 

1619 def _reflect_column( 

1620 self, 

1621 table: sa_schema.Table, 

1622 col_d: ReflectedColumn, 

1623 include_columns: Optional[Collection[str]], 

1624 exclude_columns: Collection[str], 

1625 cols_by_orig_name: Dict[str, sa_schema.Column[Any]], 

1626 ) -> None: 

1627 orig_name = col_d["name"] 

1628 

1629 table.metadata.dispatch.column_reflect(self, table, col_d) 

1630 table.dispatch.column_reflect(self, table, col_d) 

1631 

1632 # fetch name again as column_reflect is allowed to 

1633 # change it 

1634 name = col_d["name"] 

1635 if (include_columns and name not in include_columns) or ( 

1636 exclude_columns and name in exclude_columns 

1637 ): 

1638 return 

1639 

1640 coltype = col_d["type"] 

1641 

1642 col_kw = { 

1643 k: col_d[k] # type: ignore[literal-required] 

1644 for k in [ 

1645 "nullable", 

1646 "autoincrement", 

1647 "quote", 

1648 "info", 

1649 "key", 

1650 "comment", 

1651 ] 

1652 if k in col_d 

1653 } 

1654 

1655 if "dialect_options" in col_d: 

1656 col_kw.update(col_d["dialect_options"]) 

1657 

1658 colargs = [] 

1659 default: Any 

1660 if col_d.get("default") is not None: 

1661 default_text = col_d["default"] 

1662 assert default_text is not None 

1663 if isinstance(default_text, TextClause): 

1664 default = sa_schema.DefaultClause( 

1665 default_text, _reflected=True 

1666 ) 

1667 elif not isinstance(default_text, sa_schema.FetchedValue): 

1668 default = sa_schema.DefaultClause( 

1669 sql.text(default_text), _reflected=True 

1670 ) 

1671 else: 

1672 default = default_text 

1673 colargs.append(default) 

1674 

1675 if "computed" in col_d: 

1676 computed = sa_schema.Computed(**col_d["computed"]) 

1677 colargs.append(computed) 

1678 

1679 if "identity" in col_d: 

1680 identity = sa_schema.Identity(**col_d["identity"]) 

1681 colargs.append(identity) 

1682 

1683 cols_by_orig_name[orig_name] = col = sa_schema.Column( 

1684 name, coltype, *colargs, **col_kw 

1685 ) 

1686 

1687 if col.key in table.primary_key: 

1688 col.primary_key = True 

1689 table.append_column(col, replace_existing=True) 

1690 

1691 def _reflect_pk( 

1692 self, 

1693 _reflect_info: _ReflectionInfo, 

1694 table_key: TableKey, 

1695 table: sa_schema.Table, 

1696 cols_by_orig_name: Dict[str, sa_schema.Column[Any]], 

1697 exclude_columns: Collection[str], 

1698 ) -> None: 

1699 pk_cons = _reflect_info.pk_constraint.get(table_key) 

1700 if pk_cons: 

1701 pk_cols = [ 

1702 cols_by_orig_name[pk] 

1703 for pk in pk_cons["constrained_columns"] 

1704 if pk in cols_by_orig_name and pk not in exclude_columns 

1705 ] 

1706 

1707 # update pk constraint name and comment 

1708 table.primary_key.name = pk_cons.get("name") 

1709 table.primary_key.comment = pk_cons.get("comment", None) 

1710 

1711 # tell the PKConstraint to re-initialize 

1712 # its column collection 

1713 table.primary_key._reload(pk_cols) 

1714 

1715 def _reflect_fk( 

1716 self, 

1717 _reflect_info: _ReflectionInfo, 

1718 table_key: TableKey, 

1719 table: sa_schema.Table, 

1720 cols_by_orig_name: Dict[str, sa_schema.Column[Any]], 

1721 include_columns: Optional[Collection[str]], 

1722 exclude_columns: Collection[str], 

1723 resolve_fks: bool, 

1724 _extend_on: Optional[Set[sa_schema.Table]], 

1725 reflection_options: Dict[str, Any], 

1726 ) -> None: 

1727 fkeys = _reflect_info.foreign_keys.get(table_key, []) 

1728 for fkey_d in fkeys: 

1729 conname = fkey_d["name"] 

1730 # look for columns by orig name in cols_by_orig_name, 

1731 # but support columns that are in-Python only as fallback 

1732 constrained_columns = [ 

1733 cols_by_orig_name[c].key if c in cols_by_orig_name else c 

1734 for c in fkey_d["constrained_columns"] 

1735 ] 

1736 

1737 if ( 

1738 exclude_columns 

1739 and set(constrained_columns).intersection(exclude_columns) 

1740 or ( 

1741 include_columns 

1742 and set(constrained_columns).difference(include_columns) 

1743 ) 

1744 ): 

1745 continue 

1746 

1747 referred_schema = fkey_d["referred_schema"] 

1748 referred_table = fkey_d["referred_table"] 

1749 referred_columns = fkey_d["referred_columns"] 

1750 refspec = [] 

1751 if referred_schema is not None: 

1752 if resolve_fks: 

1753 sa_schema.Table( 

1754 referred_table, 

1755 table.metadata, 

1756 schema=referred_schema, 

1757 autoload_with=self.bind, 

1758 _extend_on=_extend_on, 

1759 _reflect_info=_reflect_info, 

1760 **reflection_options, 

1761 ) 

1762 for column in referred_columns: 

1763 refspec.append( 

1764 ".".join([referred_schema, referred_table, column]) 

1765 ) 

1766 else: 

1767 if resolve_fks: 

1768 sa_schema.Table( 

1769 referred_table, 

1770 table.metadata, 

1771 autoload_with=self.bind, 

1772 schema=sa_schema.BLANK_SCHEMA, 

1773 _extend_on=_extend_on, 

1774 _reflect_info=_reflect_info, 

1775 **reflection_options, 

1776 ) 

1777 for column in referred_columns: 

1778 refspec.append(".".join([referred_table, column])) 

1779 if "options" in fkey_d: 

1780 options = fkey_d["options"] 

1781 else: 

1782 options = {} 

1783 

1784 try: 

1785 table.append_constraint( 

1786 sa_schema.ForeignKeyConstraint( 

1787 constrained_columns, 

1788 refspec, 

1789 conname, 

1790 link_to_name=True, 

1791 comment=fkey_d.get("comment"), 

1792 **options, 

1793 ) 

1794 ) 

1795 except exc.ConstraintColumnNotFoundError: 

1796 util.warn( 

1797 f"On reflected table {table.name}, skipping reflection of " 

1798 "foreign key constraint " 

1799 f"{conname}; one or more subject columns within " 

1800 f"name(s) {', '.join(constrained_columns)} are not " 

1801 "present in the table" 

1802 ) 

1803 

1804 _index_sort_exprs = { 

1805 "asc": operators.asc_op, 

1806 "desc": operators.desc_op, 

1807 "nulls_first": operators.nulls_first_op, 

1808 "nulls_last": operators.nulls_last_op, 

1809 } 

1810 

1811 def _reflect_indexes( 

1812 self, 

1813 _reflect_info: _ReflectionInfo, 

1814 table_key: TableKey, 

1815 table: sa_schema.Table, 

1816 cols_by_orig_name: Dict[str, sa_schema.Column[Any]], 

1817 include_columns: Optional[Collection[str]], 

1818 exclude_columns: Collection[str], 

1819 reflection_options: Dict[str, Any], 

1820 ) -> None: 

1821 # Indexes 

1822 indexes = _reflect_info.indexes.get(table_key, []) 

1823 for index_d in indexes: 

1824 name = index_d["name"] 

1825 columns = index_d["column_names"] 

1826 expressions = index_d.get("expressions") 

1827 column_sorting = index_d.get("column_sorting", {}) 

1828 unique = index_d["unique"] 

1829 flavor = index_d.get("type", "index") 

1830 dialect_options = index_d.get("dialect_options", {}) 

1831 

1832 duplicates = index_d.get("duplicates_constraint") 

1833 if include_columns and not set(columns).issubset(include_columns): 

1834 continue 

1835 if duplicates: 

1836 continue 

1837 # look for columns by orig name in cols_by_orig_name, 

1838 # but support columns that are in-Python only as fallback 

1839 idx_element: Any 

1840 idx_elements = [] 

1841 for index, c in enumerate(columns): 

1842 if c is None: 

1843 if not expressions: 

1844 util.warn( 

1845 f"Skipping {flavor} {name!r} because key " 

1846 f"{index + 1} reflected as None but no " 

1847 "'expressions' were returned" 

1848 ) 

1849 break 

1850 idx_element = sql.text(expressions[index]) 

1851 else: 

1852 try: 

1853 if c in cols_by_orig_name: 

1854 idx_element = cols_by_orig_name[c] 

1855 else: 

1856 idx_element = table.c[c] 

1857 except KeyError: 

1858 util.warn( 

1859 f"{flavor} key {c!r} was not located in " 

1860 f"columns for table {table.name!r}" 

1861 ) 

1862 continue 

1863 for option in column_sorting.get(c, ()): 

1864 if option in self._index_sort_exprs: 

1865 op = self._index_sort_exprs[option] 

1866 idx_element = op(idx_element) 

1867 idx_elements.append(idx_element) 

1868 else: 

1869 sa_schema.Index( 

1870 name, 

1871 *idx_elements, 

1872 _table=table, 

1873 unique=unique, 

1874 **dialect_options, 

1875 ) 

1876 

1877 def _reflect_unique_constraints( 

1878 self, 

1879 _reflect_info: _ReflectionInfo, 

1880 table_key: TableKey, 

1881 table: sa_schema.Table, 

1882 cols_by_orig_name: Dict[str, sa_schema.Column[Any]], 

1883 include_columns: Optional[Collection[str]], 

1884 exclude_columns: Collection[str], 

1885 reflection_options: Dict[str, Any], 

1886 ) -> None: 

1887 constraints = _reflect_info.unique_constraints.get(table_key, []) 

1888 # Unique Constraints 

1889 for const_d in constraints: 

1890 conname = const_d["name"] 

1891 columns = const_d["column_names"] 

1892 comment = const_d.get("comment") 

1893 duplicates = const_d.get("duplicates_index") 

1894 dialect_options = const_d.get("dialect_options", {}) 

1895 if include_columns and not set(columns).issubset(include_columns): 

1896 continue 

1897 if duplicates: 

1898 continue 

1899 # look for columns by orig name in cols_by_orig_name, 

1900 # but support columns that are in-Python only as fallback 

1901 constrained_cols = [] 

1902 for c in columns: 

1903 try: 

1904 constrained_col = ( 

1905 cols_by_orig_name[c] 

1906 if c in cols_by_orig_name 

1907 else table.c[c] 

1908 ) 

1909 except KeyError: 

1910 util.warn( 

1911 "unique constraint key '%s' was not located in " 

1912 "columns for table '%s'" % (c, table.name) 

1913 ) 

1914 else: 

1915 constrained_cols.append(constrained_col) 

1916 table.append_constraint( 

1917 sa_schema.UniqueConstraint( 

1918 *constrained_cols, 

1919 name=conname, 

1920 comment=comment, 

1921 **dialect_options, 

1922 ) 

1923 ) 

1924 

1925 def _reflect_check_constraints( 

1926 self, 

1927 _reflect_info: _ReflectionInfo, 

1928 table_key: TableKey, 

1929 table: sa_schema.Table, 

1930 cols_by_orig_name: Dict[str, sa_schema.Column[Any]], 

1931 include_columns: Optional[Collection[str]], 

1932 exclude_columns: Collection[str], 

1933 reflection_options: Dict[str, Any], 

1934 ) -> None: 

1935 constraints = _reflect_info.check_constraints.get(table_key, []) 

1936 for const_d in constraints: 

1937 table.append_constraint(sa_schema.CheckConstraint(**const_d)) 

1938 

1939 def _reflect_table_comment( 

1940 self, 

1941 _reflect_info: _ReflectionInfo, 

1942 table_key: TableKey, 

1943 table: sa_schema.Table, 

1944 reflection_options: Dict[str, Any], 

1945 ) -> None: 

1946 comment_dict = _reflect_info.table_comment.get(table_key) 

1947 if comment_dict: 

1948 table.comment = comment_dict["text"] 

1949 

1950 def _get_reflection_info( 

1951 self, 

1952 schema: Optional[str] = None, 

1953 filter_names: Optional[Collection[str]] = None, 

1954 available: Optional[Collection[str]] = None, 

1955 _reflect_info: Optional[_ReflectionInfo] = None, 

1956 **kw: Any, 

1957 ) -> _ReflectionInfo: 

1958 kw["schema"] = schema 

1959 

1960 if filter_names and available and len(filter_names) > 100: 

1961 fraction = len(filter_names) / len(available) 

1962 else: 

1963 fraction = None 

1964 

1965 unreflectable: Dict[TableKey, exc.UnreflectableTableError] 

1966 kw["unreflectable"] = unreflectable = {} 

1967 

1968 has_result: bool = True 

1969 

1970 def run( 

1971 meth: Any, 

1972 *, 

1973 optional: bool = False, 

1974 check_filter_names_from_meth: bool = False, 

1975 ) -> Any: 

1976 nonlocal has_result 

1977 # simple heuristic to improve reflection performance if a 

1978 # dialect implements multi_reflection: 

1979 # if more than 50% of the tables in the db are in filter_names 

1980 # load all the tables, since it's most likely faster to avoid 

1981 # a filter on that many tables. 

1982 if ( 

1983 fraction is None 

1984 or fraction <= 0.5 

1985 or not self.dialect._overrides_default(meth.__name__) 

1986 ): 

1987 _fn = filter_names 

1988 else: 

1989 _fn = None 

1990 try: 

1991 if has_result: 

1992 res = meth(filter_names=_fn, **kw) 

1993 if check_filter_names_from_meth and not res: 

1994 # method returned no result data. 

1995 # skip any future call methods 

1996 has_result = False 

1997 else: 

1998 res = {} 

1999 except NotImplementedError: 

2000 if not optional: 

2001 raise 

2002 res = {} 

2003 return res 

2004 

2005 info = _ReflectionInfo( 

2006 columns=run( 

2007 self.get_multi_columns, check_filter_names_from_meth=True 

2008 ), 

2009 pk_constraint=run(self.get_multi_pk_constraint), 

2010 foreign_keys=run(self.get_multi_foreign_keys), 

2011 indexes=run(self.get_multi_indexes), 

2012 unique_constraints=run( 

2013 self.get_multi_unique_constraints, optional=True 

2014 ), 

2015 table_comment=run(self.get_multi_table_comment, optional=True), 

2016 check_constraints=run( 

2017 self.get_multi_check_constraints, optional=True 

2018 ), 

2019 table_options=run(self.get_multi_table_options, optional=True), 

2020 unreflectable=unreflectable, 

2021 ) 

2022 if _reflect_info: 

2023 _reflect_info.update(info) 

2024 return _reflect_info 

2025 else: 

2026 return info 

2027 

2028 

2029@final 

2030class ReflectionDefaults: 

2031 """provides blank default values for reflection methods.""" 

2032 

2033 @classmethod 

2034 def columns(cls) -> List[ReflectedColumn]: 

2035 return [] 

2036 

2037 @classmethod 

2038 def pk_constraint(cls) -> ReflectedPrimaryKeyConstraint: 

2039 return { 

2040 "name": None, 

2041 "constrained_columns": [], 

2042 } 

2043 

2044 @classmethod 

2045 def foreign_keys(cls) -> List[ReflectedForeignKeyConstraint]: 

2046 return [] 

2047 

2048 @classmethod 

2049 def indexes(cls) -> List[ReflectedIndex]: 

2050 return [] 

2051 

2052 @classmethod 

2053 def unique_constraints(cls) -> List[ReflectedUniqueConstraint]: 

2054 return [] 

2055 

2056 @classmethod 

2057 def check_constraints(cls) -> List[ReflectedCheckConstraint]: 

2058 return [] 

2059 

2060 @classmethod 

2061 def table_options(cls) -> Dict[str, Any]: 

2062 return {} 

2063 

2064 @classmethod 

2065 def table_comment(cls) -> ReflectedTableComment: 

2066 return {"text": None} 

2067 

2068 

2069@dataclass 

2070class _ReflectionInfo: 

2071 columns: Dict[TableKey, List[ReflectedColumn]] 

2072 pk_constraint: Dict[TableKey, Optional[ReflectedPrimaryKeyConstraint]] 

2073 foreign_keys: Dict[TableKey, List[ReflectedForeignKeyConstraint]] 

2074 indexes: Dict[TableKey, List[ReflectedIndex]] 

2075 # optionals 

2076 unique_constraints: Dict[TableKey, List[ReflectedUniqueConstraint]] 

2077 table_comment: Dict[TableKey, Optional[ReflectedTableComment]] 

2078 check_constraints: Dict[TableKey, List[ReflectedCheckConstraint]] 

2079 table_options: Dict[TableKey, Dict[str, Any]] 

2080 unreflectable: Dict[TableKey, exc.UnreflectableTableError] 

2081 

2082 def update(self, other: _ReflectionInfo) -> None: 

2083 for k, v in self.__dict__.items(): 

2084 ov = getattr(other, k) 

2085 if ov is not None: 

2086 if v is None: 

2087 setattr(self, k, ov) 

2088 else: 

2089 v.update(ov)