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

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

514 statements  

1# engine/reflection.py 

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

60from ..sql.elements import TextClause 

61from ..sql.type_api import TypeEngine 

62from ..sql.visitors import InternalTraversal 

63from ..util import topological 

64 

65if 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 

80def 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 

111def 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 

136class 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 

168class 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 

182class 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 .. seealso:: :meth:`Inspector.get_multi_table_comment` 

1320 """ 

1321 

1322 with self._operation_context() as conn: 

1323 return self.dialect.get_table_comment( 

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

1325 ) 

1326 

1327 def get_multi_table_comment( 

1328 self, 

1329 schema: Optional[str] = None, 

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

1331 kind: ObjectKind = ObjectKind.TABLE, 

1332 scope: ObjectScope = ObjectScope.DEFAULT, 

1333 **kw: Any, 

1334 ) -> Dict[TableKey, ReflectedTableComment]: 

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

1336 in the given schema. 

1337 

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

1339 ``filter_names``. 

1340 

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

1342 

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

1344 comments. 

1345 

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

1347 of the database connection. For special quoting, 

1348 use :class:`.quoted_name`. 

1349 

1350 :param filter_names: optionally return information only for the 

1351 objects listed here. 

1352 

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

1354 to reflect. Defaults to ``ObjectKind.TABLE``. 

1355 

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

1357 default, temporary or any tables should be reflected. 

1358 Defaults to ``ObjectScope.DEFAULT``. 

1359 

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

1361 specific implementation. See the documentation of the dialect 

1362 in use for more information. 

1363 

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

1365 and the values are dictionaries, representing the 

1366 table comments. 

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

1368 

1369 .. versionadded:: 2.0 

1370 

1371 .. seealso:: :meth:`Inspector.get_table_comment` 

1372 """ 

1373 

1374 with self._operation_context() as conn: 

1375 return dict( 

1376 self.dialect.get_multi_table_comment( 

1377 conn, 

1378 schema=schema, 

1379 filter_names=filter_names, 

1380 kind=kind, 

1381 scope=scope, 

1382 info_cache=self.info_cache, 

1383 **kw, 

1384 ) 

1385 ) 

1386 

1387 def get_check_constraints( 

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

1389 ) -> List[ReflectedCheckConstraint]: 

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

1391 

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

1393 check constraint information as a list of 

1394 :class:`.ReflectedCheckConstraint`. 

1395 

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

1397 use :class:`.quoted_name`. 

1398 

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

1400 of the database connection. For special quoting, 

1401 use :class:`.quoted_name`. 

1402 

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

1404 specific implementation. See the documentation of the dialect 

1405 in use for more information. 

1406 

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

1408 definition of a check constraints. 

1409 

1410 .. seealso:: :meth:`Inspector.get_multi_check_constraints` 

1411 """ 

1412 

1413 with self._operation_context() as conn: 

1414 return self.dialect.get_check_constraints( 

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

1416 ) 

1417 

1418 def get_multi_check_constraints( 

1419 self, 

1420 schema: Optional[str] = None, 

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

1422 kind: ObjectKind = ObjectKind.TABLE, 

1423 scope: ObjectScope = ObjectScope.DEFAULT, 

1424 **kw: Any, 

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

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

1427 in the given schema. 

1428 

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

1430 ``filter_names``. 

1431 

1432 For each table the value is a list of 

1433 :class:`.ReflectedCheckConstraint`. 

1434 

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

1436 of the database connection. For special quoting, 

1437 use :class:`.quoted_name`. 

1438 

1439 :param filter_names: optionally return information only for the 

1440 objects listed here. 

1441 

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

1443 to reflect. Defaults to ``ObjectKind.TABLE``. 

1444 

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

1446 default, temporary or any tables should be reflected. 

1447 Defaults to ``ObjectScope.DEFAULT``. 

1448 

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

1450 specific implementation. See the documentation of the dialect 

1451 in use for more information. 

1452 

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

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

1455 definition of a check constraints. 

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

1457 

1458 .. versionadded:: 2.0 

1459 

1460 .. seealso:: :meth:`Inspector.get_check_constraints` 

1461 """ 

1462 

1463 with self._operation_context() as conn: 

1464 return dict( 

1465 self.dialect.get_multi_check_constraints( 

1466 conn, 

1467 schema=schema, 

1468 filter_names=filter_names, 

1469 kind=kind, 

1470 scope=scope, 

1471 info_cache=self.info_cache, 

1472 **kw, 

1473 ) 

1474 ) 

1475 

1476 def reflect_table( 

1477 self, 

1478 table: sa_schema.Table, 

1479 include_columns: Optional[Collection[str]], 

1480 exclude_columns: Collection[str] = (), 

1481 resolve_fks: bool = True, 

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

1483 _reflect_info: Optional[_ReflectionInfo] = None, 

1484 ) -> None: 

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

1486 constructs based on introspection. 

1487 

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

1489 table reflection. Direct usage is like:: 

1490 

1491 from sqlalchemy import create_engine, MetaData, Table 

1492 from sqlalchemy import inspect 

1493 

1494 engine = create_engine("...") 

1495 meta = MetaData() 

1496 user_table = Table("user", meta) 

1497 insp = inspect(engine) 

1498 insp.reflect_table(user_table, None) 

1499 

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

1501 ``reflect_table`` 

1502 

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

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

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

1506 

1507 """ 

1508 

1509 if _extend_on is not None: 

1510 if table in _extend_on: 

1511 return 

1512 else: 

1513 _extend_on.add(table) 

1514 

1515 dialect = self.bind.dialect 

1516 

1517 with self._operation_context() as conn: 

1518 schema = conn.schema_for_object(table) 

1519 

1520 table_name = table.name 

1521 

1522 # get table-level arguments that are specifically 

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

1524 # these are unconditionally passed to related Table 

1525 # objects 

1526 reflection_options = { 

1527 k: table.dialect_kwargs.get(k) 

1528 for k in dialect.reflection_options 

1529 if k in table.dialect_kwargs 

1530 } 

1531 

1532 table_key = (schema, table_name) 

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

1534 _reflect_info = self._get_reflection_info( 

1535 schema, 

1536 filter_names=[table_name], 

1537 kind=ObjectKind.ANY, 

1538 scope=ObjectScope.ANY, 

1539 _reflect_info=_reflect_info, 

1540 **table.dialect_kwargs, 

1541 ) 

1542 if table_key in _reflect_info.unreflectable: 

1543 raise _reflect_info.unreflectable[table_key] 

1544 

1545 if table_key not in _reflect_info.columns: 

1546 raise exc.NoSuchTableError(table_name) 

1547 

1548 # reflect table options, like mysql_engine 

1549 if _reflect_info.table_options: 

1550 tbl_opts = _reflect_info.table_options.get(table_key) 

1551 if tbl_opts: 

1552 # add additional kwargs to the Table if the dialect 

1553 # returned them 

1554 table._validate_dialect_kwargs(tbl_opts) 

1555 

1556 found_table = False 

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

1558 

1559 for col_d in _reflect_info.columns[table_key]: 

1560 found_table = True 

1561 

1562 self._reflect_column( 

1563 table, 

1564 col_d, 

1565 include_columns, 

1566 exclude_columns, 

1567 cols_by_orig_name, 

1568 ) 

1569 

1570 # NOTE: support tables/views with no columns 

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

1572 raise exc.NoSuchTableError(table_name) 

1573 

1574 self._reflect_pk( 

1575 _reflect_info, table_key, table, cols_by_orig_name, exclude_columns 

1576 ) 

1577 

1578 self._reflect_fk( 

1579 _reflect_info, 

1580 table_key, 

1581 table, 

1582 cols_by_orig_name, 

1583 include_columns, 

1584 exclude_columns, 

1585 resolve_fks, 

1586 _extend_on, 

1587 reflection_options, 

1588 ) 

1589 

1590 self._reflect_indexes( 

1591 _reflect_info, 

1592 table_key, 

1593 table, 

1594 cols_by_orig_name, 

1595 include_columns, 

1596 exclude_columns, 

1597 reflection_options, 

1598 ) 

1599 

1600 self._reflect_unique_constraints( 

1601 _reflect_info, 

1602 table_key, 

1603 table, 

1604 cols_by_orig_name, 

1605 include_columns, 

1606 exclude_columns, 

1607 reflection_options, 

1608 ) 

1609 

1610 self._reflect_check_constraints( 

1611 _reflect_info, 

1612 table_key, 

1613 table, 

1614 cols_by_orig_name, 

1615 include_columns, 

1616 exclude_columns, 

1617 reflection_options, 

1618 ) 

1619 

1620 self._reflect_table_comment( 

1621 _reflect_info, 

1622 table_key, 

1623 table, 

1624 reflection_options, 

1625 ) 

1626 

1627 def _reflect_column( 

1628 self, 

1629 table: sa_schema.Table, 

1630 col_d: ReflectedColumn, 

1631 include_columns: Optional[Collection[str]], 

1632 exclude_columns: Collection[str], 

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

1634 ) -> None: 

1635 orig_name = col_d["name"] 

1636 

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

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

1639 

1640 # fetch name again as column_reflect is allowed to 

1641 # change it 

1642 name = col_d["name"] 

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

1644 exclude_columns and name in exclude_columns 

1645 ): 

1646 return 

1647 

1648 coltype = col_d["type"] 

1649 

1650 col_kw = { 

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

1652 for k in [ 

1653 "nullable", 

1654 "autoincrement", 

1655 "quote", 

1656 "info", 

1657 "key", 

1658 "comment", 

1659 ] 

1660 if k in col_d 

1661 } 

1662 

1663 if "dialect_options" in col_d: 

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

1665 

1666 colargs = [] 

1667 default: Any 

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

1669 default_text = col_d["default"] 

1670 assert default_text is not None 

1671 if isinstance(default_text, TextClause): 

1672 default = sa_schema.DefaultClause( 

1673 default_text, _reflected=True 

1674 ) 

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

1676 default = sa_schema.DefaultClause( 

1677 sql.text(default_text), _reflected=True 

1678 ) 

1679 else: 

1680 default = default_text 

1681 colargs.append(default) 

1682 

1683 if "computed" in col_d: 

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

1685 colargs.append(computed) 

1686 

1687 if "identity" in col_d: 

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

1689 colargs.append(identity) 

1690 

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

1692 name, coltype, *colargs, **col_kw 

1693 ) 

1694 

1695 if col.key in table.primary_key: 

1696 col.primary_key = True 

1697 table.append_column(col, replace_existing=True) 

1698 

1699 def _reflect_pk( 

1700 self, 

1701 _reflect_info: _ReflectionInfo, 

1702 table_key: TableKey, 

1703 table: sa_schema.Table, 

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

1705 exclude_columns: Collection[str], 

1706 ) -> None: 

1707 pk_cons = _reflect_info.pk_constraint.get(table_key) 

1708 if pk_cons: 

1709 pk_cols = [ 

1710 cols_by_orig_name[pk] 

1711 for pk in pk_cons["constrained_columns"] 

1712 if pk in cols_by_orig_name and pk not in exclude_columns 

1713 ] 

1714 

1715 # update pk constraint name, comment and dialect_kwargs 

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

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

1718 dialect_options = pk_cons.get("dialect_options") 

1719 if dialect_options: 

1720 table.primary_key.dialect_kwargs.update(dialect_options) 

1721 

1722 # tell the PKConstraint to re-initialize 

1723 # its column collection 

1724 table.primary_key._reload(pk_cols) 

1725 

1726 def _reflect_fk( 

1727 self, 

1728 _reflect_info: _ReflectionInfo, 

1729 table_key: TableKey, 

1730 table: sa_schema.Table, 

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

1732 include_columns: Optional[Collection[str]], 

1733 exclude_columns: Collection[str], 

1734 resolve_fks: bool, 

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

1736 reflection_options: Dict[str, Any], 

1737 ) -> None: 

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

1739 for fkey_d in fkeys: 

1740 conname = fkey_d["name"] 

1741 # look for columns by orig name in cols_by_orig_name, 

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

1743 constrained_columns = [ 

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

1745 for c in fkey_d["constrained_columns"] 

1746 ] 

1747 

1748 if ( 

1749 exclude_columns 

1750 and set(constrained_columns).intersection(exclude_columns) 

1751 or ( 

1752 include_columns 

1753 and set(constrained_columns).difference(include_columns) 

1754 ) 

1755 ): 

1756 continue 

1757 

1758 referred_schema = fkey_d["referred_schema"] 

1759 referred_table = fkey_d["referred_table"] 

1760 referred_columns = fkey_d["referred_columns"] 

1761 refspec = [] 

1762 if referred_schema is not None: 

1763 if resolve_fks: 

1764 sa_schema.Table( 

1765 referred_table, 

1766 table.metadata, 

1767 schema=referred_schema, 

1768 autoload_with=self.bind, 

1769 _extend_on=_extend_on, 

1770 _reflect_info=_reflect_info, 

1771 **reflection_options, 

1772 ) 

1773 for column in referred_columns: 

1774 refspec.append( 

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

1776 ) 

1777 else: 

1778 if resolve_fks: 

1779 sa_schema.Table( 

1780 referred_table, 

1781 table.metadata, 

1782 autoload_with=self.bind, 

1783 schema=sa_schema.BLANK_SCHEMA, 

1784 _extend_on=_extend_on, 

1785 _reflect_info=_reflect_info, 

1786 **reflection_options, 

1787 ) 

1788 for column in referred_columns: 

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

1790 if "options" in fkey_d: 

1791 options = fkey_d["options"] 

1792 else: 

1793 options = {} 

1794 

1795 try: 

1796 table.append_constraint( 

1797 sa_schema.ForeignKeyConstraint( 

1798 constrained_columns, 

1799 refspec, 

1800 conname, 

1801 link_to_name=True, 

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

1803 **options, 

1804 ) 

1805 ) 

1806 except exc.ConstraintColumnNotFoundError: 

1807 util.warn( 

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

1809 "foreign key constraint " 

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

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

1812 "present in the table" 

1813 ) 

1814 

1815 _index_sort_exprs = { 

1816 "asc": operators.asc_op, 

1817 "desc": operators.desc_op, 

1818 "nulls_first": operators.nulls_first_op, 

1819 "nulls_last": operators.nulls_last_op, 

1820 } 

1821 

1822 def _reflect_indexes( 

1823 self, 

1824 _reflect_info: _ReflectionInfo, 

1825 table_key: TableKey, 

1826 table: sa_schema.Table, 

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

1828 include_columns: Optional[Collection[str]], 

1829 exclude_columns: Collection[str], 

1830 reflection_options: Dict[str, Any], 

1831 ) -> None: 

1832 # Indexes 

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

1834 for index_d in indexes: 

1835 name = index_d["name"] 

1836 columns = index_d["column_names"] 

1837 expressions = index_d.get("expressions") 

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

1839 unique = index_d["unique"] 

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

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

1842 

1843 duplicates = index_d.get("duplicates_constraint") 

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

1845 continue 

1846 if duplicates: 

1847 continue 

1848 # look for columns by orig name in cols_by_orig_name, 

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

1850 idx_element: Any 

1851 idx_elements = [] 

1852 for index, c in enumerate(columns): 

1853 if c is None: 

1854 if not expressions: 

1855 util.warn( 

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

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

1858 "'expressions' were returned" 

1859 ) 

1860 break 

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

1862 else: 

1863 try: 

1864 if c in cols_by_orig_name: 

1865 idx_element = cols_by_orig_name[c] 

1866 else: 

1867 idx_element = table.c[c] 

1868 except KeyError: 

1869 util.warn( 

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

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

1872 ) 

1873 continue 

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

1875 if option in self._index_sort_exprs: 

1876 op = self._index_sort_exprs[option] 

1877 idx_element = op(idx_element) 

1878 idx_elements.append(idx_element) 

1879 else: 

1880 sa_schema.Index( 

1881 name, 

1882 *idx_elements, 

1883 _table=table, 

1884 unique=unique, 

1885 **dialect_options, 

1886 ) 

1887 

1888 def _reflect_unique_constraints( 

1889 self, 

1890 _reflect_info: _ReflectionInfo, 

1891 table_key: TableKey, 

1892 table: sa_schema.Table, 

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

1894 include_columns: Optional[Collection[str]], 

1895 exclude_columns: Collection[str], 

1896 reflection_options: Dict[str, Any], 

1897 ) -> None: 

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

1899 # Unique Constraints 

1900 for const_d in constraints: 

1901 conname = const_d["name"] 

1902 columns = const_d["column_names"] 

1903 comment = const_d.get("comment") 

1904 duplicates = const_d.get("duplicates_index") 

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

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

1907 continue 

1908 if duplicates: 

1909 continue 

1910 # look for columns by orig name in cols_by_orig_name, 

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

1912 constrained_cols = [] 

1913 for c in columns: 

1914 try: 

1915 constrained_col = ( 

1916 cols_by_orig_name[c] 

1917 if c in cols_by_orig_name 

1918 else table.c[c] 

1919 ) 

1920 except KeyError: 

1921 util.warn( 

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

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

1924 ) 

1925 else: 

1926 constrained_cols.append(constrained_col) 

1927 table.append_constraint( 

1928 sa_schema.UniqueConstraint( 

1929 *constrained_cols, 

1930 name=conname, 

1931 comment=comment, 

1932 **dialect_options, 

1933 ) 

1934 ) 

1935 

1936 def _reflect_check_constraints( 

1937 self, 

1938 _reflect_info: _ReflectionInfo, 

1939 table_key: TableKey, 

1940 table: sa_schema.Table, 

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

1942 include_columns: Optional[Collection[str]], 

1943 exclude_columns: Collection[str], 

1944 reflection_options: Dict[str, Any], 

1945 ) -> None: 

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

1947 for const_d in constraints: 

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

1949 

1950 def _reflect_table_comment( 

1951 self, 

1952 _reflect_info: _ReflectionInfo, 

1953 table_key: TableKey, 

1954 table: sa_schema.Table, 

1955 reflection_options: Dict[str, Any], 

1956 ) -> None: 

1957 comment_dict = _reflect_info.table_comment.get(table_key) 

1958 if comment_dict: 

1959 table.comment = comment_dict["text"] 

1960 

1961 def _get_reflection_info( 

1962 self, 

1963 schema: Optional[str] = None, 

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

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

1966 _reflect_info: Optional[_ReflectionInfo] = None, 

1967 **kw: Any, 

1968 ) -> _ReflectionInfo: 

1969 kw["schema"] = schema 

1970 

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

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

1973 else: 

1974 fraction = None 

1975 

1976 unreflectable: Dict[TableKey, exc.UnreflectableTableError] 

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

1978 

1979 has_result: bool = True 

1980 

1981 def run( 

1982 meth: Any, 

1983 *, 

1984 optional: bool = False, 

1985 check_filter_names_from_meth: bool = False, 

1986 ) -> Any: 

1987 nonlocal has_result 

1988 # simple heuristic to improve reflection performance if a 

1989 # dialect implements multi_reflection: 

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

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

1992 # a filter on that many tables. 

1993 if ( 

1994 fraction is None 

1995 or fraction <= 0.5 

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

1997 ): 

1998 _fn = filter_names 

1999 else: 

2000 _fn = None 

2001 try: 

2002 if has_result: 

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

2004 if check_filter_names_from_meth and not res: 

2005 # method returned no result data. 

2006 # skip any future call methods 

2007 has_result = False 

2008 else: 

2009 res = {} 

2010 except NotImplementedError: 

2011 if not optional: 

2012 raise 

2013 res = {} 

2014 return res 

2015 

2016 info = _ReflectionInfo( 

2017 columns=run( 

2018 self.get_multi_columns, check_filter_names_from_meth=True 

2019 ), 

2020 pk_constraint=run(self.get_multi_pk_constraint), 

2021 foreign_keys=run(self.get_multi_foreign_keys), 

2022 indexes=run(self.get_multi_indexes), 

2023 unique_constraints=run( 

2024 self.get_multi_unique_constraints, optional=True 

2025 ), 

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

2027 check_constraints=run( 

2028 self.get_multi_check_constraints, optional=True 

2029 ), 

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

2031 unreflectable=unreflectable, 

2032 ) 

2033 if _reflect_info: 

2034 _reflect_info.update(info) 

2035 return _reflect_info 

2036 else: 

2037 return info 

2038 

2039 

2040@final 

2041class ReflectionDefaults: 

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

2043 

2044 @classmethod 

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

2046 return [] 

2047 

2048 @classmethod 

2049 def pk_constraint(cls) -> ReflectedPrimaryKeyConstraint: 

2050 return { 

2051 "name": None, 

2052 "constrained_columns": [], 

2053 } 

2054 

2055 @classmethod 

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

2057 return [] 

2058 

2059 @classmethod 

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

2061 return [] 

2062 

2063 @classmethod 

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

2065 return [] 

2066 

2067 @classmethod 

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

2069 return [] 

2070 

2071 @classmethod 

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

2073 return {} 

2074 

2075 @classmethod 

2076 def table_comment(cls) -> ReflectedTableComment: 

2077 return {"text": None} 

2078 

2079 

2080@dataclass 

2081class _ReflectionInfo: 

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

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

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

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

2086 # optionals 

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

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

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

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

2091 unreflectable: Dict[TableKey, exc.UnreflectableTableError] 

2092 

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

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

2095 ov = getattr(other, k) 

2096 if ov is not None: 

2097 if v is None: 

2098 setattr(self, k, ov) 

2099 else: 

2100 v.update(ov)