1# sql/schema.py
2# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7
8"""The schema module provides the building blocks for database metadata.
9
10Each element within this module describes a database entity which can be
11created and dropped, or is otherwise part of such an entity. Examples include
12tables, columns, sequences, and indexes.
13
14All entities are subclasses of :class:`~sqlalchemy.schema.SchemaItem`, and as
15defined in this module they are intended to be agnostic of any vendor-specific
16constructs.
17
18A collection of entities are grouped into a unit called
19:class:`~sqlalchemy.schema.MetaData`. MetaData serves as a logical grouping of
20schema elements, and can also be associated with an actual database connection
21such that operations involving the contained elements can contact the database
22as needed.
23
24Two of the elements here also build upon their "syntactic" counterparts, which
25are defined in :class:`~sqlalchemy.sql.expression.`, specifically
26:class:`~sqlalchemy.schema.Table` and :class:`~sqlalchemy.schema.Column`.
27Since these objects are part of the SQL expression language, they are usable
28as components in SQL expressions.
29
30"""
31from __future__ import annotations
32
33from abc import ABC
34import collections
35from enum import Enum
36import operator
37import typing
38from typing import Any
39from typing import Callable
40from typing import cast
41from typing import Collection
42from typing import Dict
43from typing import Final
44from typing import Iterable
45from typing import Iterator
46from typing import List
47from typing import Literal
48from typing import Mapping
49from typing import NoReturn
50from typing import Optional
51from typing import overload
52from typing import Protocol
53from typing import Sequence as _typing_Sequence
54from typing import Set
55from typing import Tuple
56from typing import Type
57from typing import TYPE_CHECKING
58from typing import TypedDict
59from typing import TypeGuard
60from typing import TypeVar
61from typing import Union
62
63from . import coercions
64from . import ddl
65from . import roles
66from . import type_api
67from . import visitors
68from ._annotated_cols import _ColCC_co
69from ._annotated_cols import _extract_columns_from_class
70from ._annotated_cols import _TC_co
71from ._annotated_cols import Named
72from ._annotated_cols import TypedColumns
73from ._typing import _T
74from .base import _DefaultDescriptionTuple
75from .base import _NoArg
76from .base import _NoneName
77from .base import _SentinelColumnCharacterization
78from .base import _SentinelDefaultCharacterization
79from .base import DedupeColumnCollection
80from .base import DialectKWArgs
81from .base import Executable
82from .base import SchemaEventTarget as SchemaEventTarget
83from .base import SchemaVisitable as SchemaVisitable
84from .coercions import _document_text_coercion
85from .ddl import CheckFirst
86from .elements import ClauseElement
87from .elements import ColumnClause
88from .elements import ColumnElement
89from .elements import quoted_name
90from .elements import TextClause
91from .selectable import TableClause
92from .type_api import to_instance
93from .visitors import ExternallyTraversible
94from .. import event
95from .. import exc
96from .. import inspection
97from .. import util
98from ..util import HasMemoized
99from ..util.typing import Self
100
101if typing.TYPE_CHECKING:
102 from ._typing import _AutoIncrementType
103 from ._typing import _CreateDropBind
104 from ._typing import _DDLColumnArgument
105 from ._typing import _DDLColumnReferenceArgument
106 from ._typing import _InfoType
107 from ._typing import _TextCoercedExpressionArgument
108 from ._typing import _TypeEngineArgument
109 from .base import ColumnSet
110 from .base import ReadOnlyColumnCollection
111 from .compiler import DDLCompiler
112 from .ddl import TableCreateDDL
113 from .ddl import TableDropDDL
114 from .elements import BindParameter
115 from .elements import KeyedColumnElement
116 from .functions import Function
117 from .sqltypes import SchemaType
118 from .type_api import TypeEngine
119 from .visitors import anon_map
120 from ..engine import Connection
121 from ..engine import Engine
122 from ..engine.interfaces import _CoreMultiExecuteParams
123 from ..engine.interfaces import CoreExecuteOptionsParameter
124 from ..engine.interfaces import ExecutionContext
125 from ..engine.reflection import _ReflectionInfo
126 from ..sql.selectable import FromClause
127
128_SI = TypeVar("_SI", bound="SchemaItem")
129_TAB = TypeVar("_TAB", bound="Table")
130
131
132_ConstraintNameArgument = Optional[Union[str, _NoneName]]
133
134_ServerDefaultArgument = Union[
135 "FetchedValue", str, TextClause, ColumnElement[Any]
136]
137
138_ServerOnUpdateArgument = _ServerDefaultArgument
139
140
141class SchemaConst(Enum):
142 RETAIN_SCHEMA = 1
143 """Symbol indicating that a :class:`_schema.Table`, :class:`.Sequence`
144 or in some cases a :class:`_schema.ForeignKey` object, in situations
145 where the object is being copied for a :meth:`.Table.to_metadata`
146 operation, should retain the schema name that it already has.
147
148 """
149
150 BLANK_SCHEMA = 2
151 """Symbol indicating that a :class:`_schema.Table` or :class:`.Sequence`
152 should have 'None' for its schema, even if the parent
153 :class:`_schema.MetaData` has specified a schema.
154
155 .. seealso::
156
157 :paramref:`_schema.MetaData.schema`
158
159 :paramref:`_schema.Table.schema`
160
161 :paramref:`.Sequence.schema`
162
163 """
164
165 NULL_UNSPECIFIED = 3
166 """Symbol indicating the "nullable" keyword was not passed to a Column.
167
168 This is used to distinguish between the use case of passing
169 ``nullable=None`` to a :class:`.Column`, which has special meaning
170 on some backends such as SQL Server.
171
172 """
173
174
175RETAIN_SCHEMA: Final[Literal[SchemaConst.RETAIN_SCHEMA]] = (
176 SchemaConst.RETAIN_SCHEMA
177)
178BLANK_SCHEMA: Final[Literal[SchemaConst.BLANK_SCHEMA]] = (
179 SchemaConst.BLANK_SCHEMA
180)
181NULL_UNSPECIFIED: Final[Literal[SchemaConst.NULL_UNSPECIFIED]] = (
182 SchemaConst.NULL_UNSPECIFIED
183)
184
185
186def _get_table_key(name: str, schema: Optional[str]) -> str:
187 if schema is None:
188 return name
189 else:
190 return schema + "." + name
191
192
193# this should really be in sql/util.py but we'd have to
194# break an import cycle
195def _copy_expression(
196 expression: ColumnElement[Any],
197 source_table: Optional[Table],
198 target_table: Optional[Table],
199) -> ColumnElement[Any]:
200 if source_table is None or target_table is None:
201 return expression
202
203 fixed_source_table = source_table
204 fixed_target_table = target_table
205
206 def replace(
207 element: ExternallyTraversible, **kw: Any
208 ) -> Optional[ExternallyTraversible]:
209 if (
210 isinstance(element, Column)
211 and element.table is fixed_source_table
212 and element.key in fixed_source_table.c
213 ):
214 return fixed_target_table.c[element.key]
215 else:
216 return None
217
218 return cast(
219 ColumnElement[Any],
220 visitors.replacement_traverse(expression, {}, replace),
221 )
222
223
224@inspection._self_inspects
225class SchemaItem(SchemaVisitable):
226 """Base class for items that define a database schema."""
227
228 __visit_name__ = "schema_item"
229
230 create_drop_stringify_dialect = "default"
231
232 def _init_items(self, *args: SchemaItem, **kw: Any) -> None:
233 """Initialize the list of child items for this SchemaItem."""
234 for item in args:
235 if item is not None:
236 try:
237 spwd = item._set_parent_with_dispatch
238 except AttributeError as err:
239 raise exc.ArgumentError(
240 "'SchemaItem' object, such as a 'Column' or a "
241 f"'Constraint' expected, got {item!r}"
242 ) from err
243 else:
244 spwd(self, **kw)
245
246 def __repr__(self) -> str:
247 return util.generic_repr(self, omit_kwarg=["info"])
248
249 @util.memoized_property
250 def info(self) -> _InfoType:
251 """Info dictionary associated with the object, allowing user-defined
252 data to be associated with this :class:`.SchemaItem`.
253
254 The dictionary is automatically generated when first accessed.
255 It can also be specified in the constructor of some objects,
256 such as :class:`_schema.Table` and :class:`_schema.Column`.
257
258 """
259 return {}
260
261 def _schema_item_copy(self, schema_item: _SI) -> _SI:
262 if "info" in self.__dict__:
263 schema_item.info = self.info.copy()
264 schema_item.dispatch._update(self.dispatch)
265 return schema_item
266
267 _use_schema_map = True
268
269
270class HasConditionalDDL:
271 """define a class that includes the :meth:`.HasConditionalDDL.ddl_if`
272 method, allowing for conditional rendering of DDL.
273
274 Currently applies to constraints and indexes.
275
276 .. versionadded:: 2.0
277
278
279 """
280
281 _ddl_if: Optional[ddl.DDLIf] = None
282
283 def ddl_if(
284 self,
285 dialect: Optional[str] = None,
286 callable_: Optional[ddl.DDLIfCallable] = None,
287 state: Optional[Any] = None,
288 ) -> Self:
289 r"""apply a conditional DDL rule to this schema item.
290
291 These rules work in a similar manner to the
292 :meth:`.ExecutableDDLElement.execute_if` callable, with the added
293 feature that the criteria may be checked within the DDL compilation
294 phase for a construct such as :class:`.CreateTable`.
295 :meth:`.HasConditionalDDL.ddl_if` currently applies towards the
296 :class:`.Index` construct as well as all :class:`.Constraint`
297 constructs.
298
299 :param dialect: string name of a dialect, or a tuple of string names
300 to indicate multiple dialect types.
301
302 :param callable\_: a callable that is constructed using the same form
303 as that described in
304 :paramref:`.ExecutableDDLElement.execute_if.callable_`.
305
306 :param state: any arbitrary object that will be passed to the
307 callable, if present.
308
309 .. versionadded:: 2.0
310
311 .. seealso::
312
313 :ref:`schema_ddl_ddl_if` - background and usage examples
314
315
316 """
317 self._ddl_if = ddl.DDLIf(dialect, callable_, state)
318 return self
319
320
321class HasSchemaAttr(SchemaItem):
322 """schema item that includes a top-level schema name"""
323
324 schema: Optional[str]
325
326
327class Table(
328 DialectKWArgs,
329 HasSchemaAttr,
330 TableClause[_ColCC_co],
331 inspection.Inspectable["Table"],
332):
333 r"""Represent a table in a database.
334
335 e.g.::
336
337 from sqlalchemy import Table, MetaData, Integer, String, Column
338
339 metadata = MetaData()
340
341 mytable = Table(
342 "mytable",
343 metadata,
344 Column("mytable_id", Integer, primary_key=True),
345 Column("value", String(50)),
346 )
347
348 The :class:`_schema.Table`
349 object constructs a unique instance of itself based
350 on its name and optional schema name within the given
351 :class:`_schema.MetaData` object. Calling the :class:`_schema.Table`
352 constructor with the same name and same :class:`_schema.MetaData` argument
353 a second time will return the *same* :class:`_schema.Table`
354 object - in this way
355 the :class:`_schema.Table` constructor acts as a registry function.
356
357 May also be defined as "typed table" by passing a subclass of
358 :class:`_schema.TypedColumns` as the 3rd argument::
359
360 from sqlalchemy import TypedColumns, select
361
362
363 class user_cols(TypedColumns):
364 id = Column(Integer, primary_key=True)
365 name: Column[str]
366 age: Column[int]
367 middle_name: Column[str | None]
368
369 # optional, used to infer the select types when selecting the table
370 __row_pos__: tuple[int, str, int, str | None]
371
372
373 user = Table("user", metadata, user_cols)
374
375 # the columns are typed: the statement has type Select[int, str]
376 stmt = sa.select(user.c.id, user.c.name).where(user.c.age > 30)
377
378 # Inferred as Select[int, str, int, str | None] thanks to __row_pos__
379 stmt1 = user.select()
380 stmt2 = sa.select(user)
381
382 The :attr:`sqlalchemy.sql._annotated_cols.HasRowPos.__row_pos__`
383 annotation is optional, and it's used to infer the types in a
384 :class:`_sql.Select` when selecting the complete table.
385 If a :class:`_schema.TypedColumns` does not define it,
386 the default ``Select[*tuple[Any]]`` will be inferred.
387
388 An existing :class:`Table` can be casted as "typed table" using
389 the :meth:`Table.with_cols`::
390
391 class mytable_cols(TypedColumns):
392 mytable_id: Column[int]
393 value: Column[str | None]
394
395
396 typed_mytable = mytable.with_cols(mytable_cols)
397
398 .. seealso::
399
400 :ref:`metadata_describing` Introduction to database metadata
401
402 :class:`_schema.TypedColumns` More information about typed column
403 definition
404
405 .. versionchanged:: 2.1.0b2 - :class:`_schema.Table` is now generic to
406 support "typed tables"
407 """
408
409 __visit_name__ = "table"
410
411 if TYPE_CHECKING:
412
413 @util.ro_non_memoized_property
414 def primary_key(self) -> PrimaryKeyConstraint: ...
415
416 @util.ro_non_memoized_property
417 def foreign_keys(self) -> Set[ForeignKey]: ...
418
419 def with_cols(self, type_: type[_TC_co]) -> Table[_TC_co]: ...
420
421 _columns: DedupeColumnCollection[Column[Any]] # type: ignore[assignment]
422
423 _sentinel_column: Optional[Column[Any]]
424
425 constraints: Set[Constraint]
426 """A collection of all :class:`_schema.Constraint` objects associated with
427 this :class:`_schema.Table`.
428
429 Includes :class:`_schema.PrimaryKeyConstraint`,
430 :class:`_schema.ForeignKeyConstraint`, :class:`_schema.UniqueConstraint`,
431 :class:`_schema.CheckConstraint`. A separate collection
432 :attr:`_schema.Table.foreign_key_constraints` refers to the collection
433 of all :class:`_schema.ForeignKeyConstraint` objects, and the
434 :attr:`_schema.Table.primary_key` attribute refers to the single
435 :class:`_schema.PrimaryKeyConstraint` associated with the
436 :class:`_schema.Table`.
437
438 .. seealso::
439
440 :attr:`_schema.Table.constraints`
441
442 :attr:`_schema.Table.primary_key`
443
444 :attr:`_schema.Table.foreign_key_constraints`
445
446 :attr:`_schema.Table.indexes`
447
448 :class:`_reflection.Inspector`
449
450
451 """
452
453 indexes: Set[Index]
454 """A collection of all :class:`_schema.Index` objects associated with this
455 :class:`_schema.Table`.
456
457 .. seealso::
458
459 :meth:`_reflection.Inspector.get_indexes`
460
461 """
462
463 def _gen_cache_key(
464 self, anon_map: anon_map, bindparams: List[BindParameter[Any]]
465 ) -> Tuple[Any, ...]:
466 if self._annotations:
467 return (self,) + self._annotations_cache_key
468 else:
469 return (self,)
470
471 if not typing.TYPE_CHECKING:
472 # typing tools seem to be inconsistent in how they handle
473 # __new__, so suggest this pattern for classes that use
474 # __new__. apply typing to the __init__ method normally
475 @util.deprecated_params(
476 mustexist=(
477 "1.4",
478 "Deprecated alias of :paramref:`_schema.Table.must_exist`",
479 ),
480 )
481 def __new__(cls, *args: Any, **kw: Any) -> Any:
482 return cls._new(*args, **kw)
483
484 @classmethod
485 def _new(cls, *args: Any, **kw: Any) -> Any:
486 if not args and not kw:
487 # python3k pickle seems to call this
488 return object.__new__(cls)
489
490 try:
491 name, metadata, *other_args = args
492 except ValueError:
493 raise TypeError(
494 "Table() takes at least two positional-only "
495 "arguments 'name', and 'metadata'"
496 ) from None
497 if other_args and isinstance(other_args[0], type):
498 typed_columns_cls = other_args[0]
499 if not issubclass(typed_columns_cls, TypedColumns):
500 raise exc.InvalidRequestError(
501 "The ``typed_columns_cls`` argument requires a "
502 "TypedColumns subclass."
503 )
504 elif hasattr(typed_columns_cls, "_sa_class_manager"):
505 # an orm class subclassed with TypedColumns. Reject it
506 raise exc.InvalidRequestError(
507 "To get a typed table from an ORM class, use the "
508 "`as_typed_table()` function instead."
509 )
510
511 extracted_columns = _extract_columns_from_class(typed_columns_cls)
512 other_args = extracted_columns + other_args[1:]
513 elif "typed_columns_cls" in kw:
514 raise TypeError(
515 "The ``typed_columns_cls`` argument may be passed "
516 "only positionally"
517 )
518
519 schema = kw.get("schema", None)
520 if schema is None:
521 schema = metadata.schema
522 elif schema is BLANK_SCHEMA:
523 schema = None
524 keep_existing = kw.get("keep_existing", False)
525 extend_existing = kw.get("extend_existing", False)
526
527 if keep_existing and extend_existing:
528 msg = "keep_existing and extend_existing are mutually exclusive."
529 raise exc.ArgumentError(msg)
530
531 must_exist = kw.pop("must_exist", kw.pop("mustexist", False))
532 key = _get_table_key(name, schema)
533 if key in metadata.tables:
534 if not keep_existing and not extend_existing and bool(other_args):
535 raise exc.InvalidRequestError(
536 f"Table '{key}' is already defined for this MetaData "
537 "instance. Specify 'extend_existing=True' "
538 "to redefine "
539 "options and columns on an "
540 "existing Table object."
541 )
542 table = metadata.tables[key]
543 if extend_existing:
544 table._init_existing(*other_args, **kw)
545 return table
546 else:
547 if must_exist:
548 raise exc.InvalidRequestError(f"Table '{key}' not defined")
549 table = object.__new__(cls)
550 table.dispatch.before_parent_attach(table, metadata)
551 metadata._add_table(name, schema, table)
552 try:
553 table.__init__(name, metadata, *other_args, _no_init=False, **kw) # type: ignore[misc] # noqa: E501
554 table.dispatch.after_parent_attach(table, metadata)
555 return table
556 except Exception:
557 with util.safe_reraise():
558 metadata._remove_table(name, schema)
559
560 @overload
561 def __init__(
562 self: Table[_TC_co],
563 name: str,
564 metadata: MetaData,
565 typed_columns_cls: type[_TC_co],
566 /,
567 *args: SchemaItem,
568 schema: str | Literal[SchemaConst.BLANK_SCHEMA] | None = None,
569 quote: bool | None = None,
570 quote_schema: bool | None = None,
571 keep_existing: bool = False,
572 extend_existing: bool = False,
573 implicit_returning: bool = True,
574 comment: str | None = None,
575 info: dict[Any, Any] | None = None,
576 listeners: (
577 _typing_Sequence[tuple[str, Callable[..., Any]]] | None
578 ) = None,
579 prefixes: _typing_Sequence[str] | None = None,
580 **kw: Any,
581 ) -> None: ...
582
583 @overload
584 def __init__(
585 self: Table[ReadOnlyColumnCollection[str, Column[Any]]],
586 name: str,
587 metadata: MetaData,
588 /,
589 *args: SchemaItem,
590 schema: str | Literal[SchemaConst.BLANK_SCHEMA] | None = None,
591 quote: bool | None = None,
592 quote_schema: Optional[bool] = None,
593 autoload_with: Optional[Union[Engine, Connection]] = None,
594 autoload_replace: bool = True,
595 keep_existing: bool = False,
596 extend_existing: bool = False,
597 resolve_fks: bool = True,
598 include_columns: Optional[Collection[str]] = None,
599 implicit_returning: bool = True,
600 comment: str | None = None,
601 info: dict[Any, Any] | None = None,
602 listeners: (
603 _typing_Sequence[tuple[str, Callable[..., Any]]] | None
604 ) = None,
605 prefixes: _typing_Sequence[str] | None = None,
606 _creator_ddl: TableCreateDDL | None = None,
607 _dropper_ddl: TableDropDDL | None = None,
608 # used internally in the metadata.reflect() process
609 _extend_on: Optional[Set[Table]] = None,
610 # used by __new__ to bypass __init__
611 _no_init: bool = True,
612 # dialect-specific keyword args
613 **kw: Any,
614 ) -> None: ...
615
616 def __init__(
617 self,
618 name: str,
619 metadata: MetaData,
620 /,
621 *args: Any,
622 schema: str | Literal[SchemaConst.BLANK_SCHEMA] | None = None,
623 quote: bool | None = None,
624 quote_schema: Optional[bool] = None,
625 autoload_with: Optional[Union[Engine, Connection]] = None,
626 autoload_replace: bool = True,
627 keep_existing: bool = False,
628 extend_existing: bool = False,
629 resolve_fks: bool = True,
630 include_columns: Optional[Collection[str]] = None,
631 implicit_returning: bool = True,
632 comment: str | None = None,
633 info: dict[Any, Any] | None = None,
634 listeners: (
635 _typing_Sequence[tuple[str, Callable[..., Any]]] | None
636 ) = None,
637 prefixes: _typing_Sequence[str] | None = None,
638 _creator_ddl: TableCreateDDL | None = None,
639 _dropper_ddl: TableDropDDL | None = None,
640 # used internally in the metadata.reflect() process
641 _extend_on: Optional[Set[Table]] = None,
642 # used by __new__ to bypass __init__
643 _no_init: bool = True,
644 # dialect-specific keyword args
645 **kw: Any,
646 ) -> None:
647 r"""Constructor for :class:`_schema.Table`.
648
649
650 :param name: The name of this table as represented in the database.
651
652 The table name, along with the value of the ``schema`` parameter,
653 forms a key which uniquely identifies this :class:`_schema.Table`
654 within
655 the owning :class:`_schema.MetaData` collection.
656 Additional calls to :class:`_schema.Table` with the same name,
657 metadata,
658 and schema name will return the same :class:`_schema.Table` object.
659
660 Names which contain no upper case characters
661 will be treated as case insensitive names, and will not be quoted
662 unless they are a reserved word or contain special characters.
663 A name with any number of upper case characters is considered
664 to be case sensitive, and will be sent as quoted.
665
666 To enable unconditional quoting for the table name, specify the flag
667 ``quote=True`` to the constructor, or use the :class:`.quoted_name`
668 construct to specify the name.
669
670 :param metadata: a :class:`_schema.MetaData`
671 object which will contain this
672 table. The metadata is used as a point of association of this table
673 with other tables which are referenced via foreign key. It also
674 may be used to associate this table with a particular
675 :class:`.Connection` or :class:`.Engine`.
676
677 :param table_columns_cls: a subclass of :class:`_schema.TypedColumns`
678 that defines the columns that will be "typed" when accessing
679 them from the :attr:`_schema.Table.c` attribute.
680
681 .. versionadded:: 2.1.0b2
682
683 :param \*args: Additional positional arguments are used primarily
684 to add the list of :class:`_schema.Column`
685 objects contained within this
686 table. Similar to the style of a CREATE TABLE statement, other
687 :class:`.SchemaItem` constructs may be added here, including
688 :class:`.PrimaryKeyConstraint`, and
689 :class:`_schema.ForeignKeyConstraint`.
690 Additional columns may be provided also when using a
691 :paramref:`_schema.Table.table_columns_cls` class; they will
692 be appended to the "typed" columns and will appear as untyped
693 when accessing them via the :attr:`_schema.Table.c` collection.
694
695 :param autoload_replace: Defaults to ``True``; when using
696 :paramref:`_schema.Table.autoload_with`
697 in conjunction with :paramref:`_schema.Table.extend_existing`,
698 indicates
699 that :class:`_schema.Column` objects present in the already-existing
700 :class:`_schema.Table`
701 object should be replaced with columns of the same
702 name retrieved from the autoload process. When ``False``, columns
703 already present under existing names will be omitted from the
704 reflection process.
705
706 Note that this setting does not impact :class:`_schema.Column` objects
707 specified programmatically within the call to :class:`_schema.Table`
708 that
709 also is autoloading; those :class:`_schema.Column` objects will always
710 replace existing columns of the same name when
711 :paramref:`_schema.Table.extend_existing` is ``True``.
712
713 .. seealso::
714
715 :paramref:`_schema.Table.autoload_with`
716
717 :paramref:`_schema.Table.extend_existing`
718
719 :param autoload_with: An :class:`_engine.Engine` or
720 :class:`_engine.Connection` object,
721 or a :class:`_reflection.Inspector` object as returned by
722 :func:`_sa.inspect`
723 against one, with which this :class:`_schema.Table`
724 object will be reflected.
725 When set to a non-None value, the autoload process will take place
726 for this table against the given engine or connection.
727
728 .. seealso::
729
730 :ref:`metadata_reflection_toplevel`
731
732 :meth:`_events.DDLEvents.column_reflect`
733
734 :ref:`metadata_reflection_dbagnostic_types`
735
736 :param extend_existing: When ``True``, indicates that if this
737 :class:`_schema.Table` is already present in the given
738 :class:`_schema.MetaData`,
739 apply further arguments within the constructor to the existing
740 :class:`_schema.Table`.
741
742 If :paramref:`_schema.Table.extend_existing` or
743 :paramref:`_schema.Table.keep_existing` are not set,
744 and the given name
745 of the new :class:`_schema.Table` refers to a :class:`_schema.Table`
746 that is
747 already present in the target :class:`_schema.MetaData` collection,
748 and
749 this :class:`_schema.Table`
750 specifies additional columns or other constructs
751 or flags that modify the table's state, an
752 error is raised. The purpose of these two mutually-exclusive flags
753 is to specify what action should be taken when a
754 :class:`_schema.Table`
755 is specified that matches an existing :class:`_schema.Table`,
756 yet specifies
757 additional constructs.
758
759 :paramref:`_schema.Table.extend_existing`
760 will also work in conjunction
761 with :paramref:`_schema.Table.autoload_with` to run a new reflection
762 operation against the database, even if a :class:`_schema.Table`
763 of the same name is already present in the target
764 :class:`_schema.MetaData`; newly reflected :class:`_schema.Column`
765 objects
766 and other options will be added into the state of the
767 :class:`_schema.Table`, potentially overwriting existing columns
768 and options of the same name.
769
770 As is always the case with :paramref:`_schema.Table.autoload_with`,
771 :class:`_schema.Column` objects can be specified in the same
772 :class:`_schema.Table`
773 constructor, which will take precedence. Below, the existing
774 table ``mytable`` will be augmented with :class:`_schema.Column`
775 objects
776 both reflected from the database, as well as the given
777 :class:`_schema.Column`
778 named "y"::
779
780 Table(
781 "mytable",
782 metadata,
783 Column("y", Integer),
784 extend_existing=True,
785 autoload_with=engine,
786 )
787
788 .. seealso::
789
790 :paramref:`_schema.Table.autoload_with`
791
792 :paramref:`_schema.Table.autoload_replace`
793
794 :paramref:`_schema.Table.keep_existing`
795
796
797 :param implicit_returning: True by default - indicates that
798 RETURNING can be used, typically by the ORM, in order to fetch
799 server-generated values such as primary key values and
800 server side defaults, on those backends which support RETURNING.
801
802 In modern SQLAlchemy there is generally no reason to alter this
803 setting, except for some backend specific cases
804 (see :ref:`mssql_triggers` in the SQL Server dialect documentation
805 for one such example).
806
807 :param include_columns: A list of strings indicating a subset of
808 columns to be loaded via the ``autoload`` operation; table columns who
809 aren't present in this list will not be represented on the resulting
810 ``Table`` object. Defaults to ``None`` which indicates all columns
811 should be reflected.
812
813 :param resolve_fks: Whether or not to reflect :class:`_schema.Table`
814 objects
815 related to this one via :class:`_schema.ForeignKey` objects, when
816 :paramref:`_schema.Table.autoload_with` is
817 specified. Defaults to True. Set to False to disable reflection of
818 related tables as :class:`_schema.ForeignKey`
819 objects are encountered; may be
820 used either to save on SQL calls or to avoid issues with related tables
821 that can't be accessed. Note that if a related table is already present
822 in the :class:`_schema.MetaData` collection, or becomes present later,
823 a
824 :class:`_schema.ForeignKey` object associated with this
825 :class:`_schema.Table` will
826 resolve to that table normally.
827
828 .. seealso::
829
830 :paramref:`.MetaData.reflect.resolve_fks`
831
832
833 :param info: Optional data dictionary which will be populated into the
834 :attr:`.SchemaItem.info` attribute of this object.
835
836 :param keep_existing: When ``True``, indicates that if this Table
837 is already present in the given :class:`_schema.MetaData`, ignore
838 further arguments within the constructor to the existing
839 :class:`_schema.Table`, and return the :class:`_schema.Table`
840 object as
841 originally created. This is to allow a function that wishes
842 to define a new :class:`_schema.Table` on first call, but on
843 subsequent calls will return the same :class:`_schema.Table`,
844 without any of the declarations (particularly constraints)
845 being applied a second time.
846
847 If :paramref:`_schema.Table.extend_existing` or
848 :paramref:`_schema.Table.keep_existing` are not set,
849 and the given name
850 of the new :class:`_schema.Table` refers to a :class:`_schema.Table`
851 that is
852 already present in the target :class:`_schema.MetaData` collection,
853 and
854 this :class:`_schema.Table`
855 specifies additional columns or other constructs
856 or flags that modify the table's state, an
857 error is raised. The purpose of these two mutually-exclusive flags
858 is to specify what action should be taken when a
859 :class:`_schema.Table`
860 is specified that matches an existing :class:`_schema.Table`,
861 yet specifies
862 additional constructs.
863
864 .. seealso::
865
866 :paramref:`_schema.Table.extend_existing`
867
868 :param listeners: A list of tuples of the form ``(<eventname>, <fn>)``
869 which will be passed to :func:`.event.listen` upon construction.
870 This alternate hook to :func:`.event.listen` allows the establishment
871 of a listener function specific to this :class:`_schema.Table` before
872 the "autoload" process begins. Historically this has been intended
873 for use with the :meth:`.DDLEvents.column_reflect` event, however
874 note that this event hook may now be associated with the
875 :class:`_schema.MetaData` object directly::
876
877 def listen_for_reflect(table, column_info):
878 "handle the column reflection event"
879 # ...
880
881
882 t = Table(
883 "sometable",
884 autoload_with=engine,
885 listeners=[("column_reflect", listen_for_reflect)],
886 )
887
888 .. seealso::
889
890 :meth:`_events.DDLEvents.column_reflect`
891
892 :param must_exist: When ``True``, indicates that this Table must already
893 be present in the given :class:`_schema.MetaData` collection, else
894 an exception is raised.
895
896 :param prefixes:
897 A list of strings to insert after CREATE in the CREATE TABLE
898 statement. They will be separated by spaces.
899
900 :param quote: Force quoting of this table's name on or off, corresponding
901 to ``True`` or ``False``. When left at its default of ``None``,
902 the column identifier will be quoted according to whether the name is
903 case sensitive (identifiers with at least one upper case character are
904 treated as case sensitive), or if it's a reserved word. This flag
905 is only needed to force quoting of a reserved word which is not known
906 by the SQLAlchemy dialect.
907
908 .. note:: setting this flag to ``False`` will not provide
909 case-insensitive behavior for table reflection; table reflection
910 will always search for a mixed-case name in a case sensitive
911 fashion. Case insensitive names are specified in SQLAlchemy only
912 by stating the name with all lower case characters.
913
914 :param quote_schema: same as 'quote' but applies to the schema identifier.
915
916 :param schema: The schema name for this table, which is required if
917 the table resides in a schema other than the default selected schema
918 for the engine's database connection. Defaults to ``None``.
919
920 If the owning :class:`_schema.MetaData` of this :class:`_schema.Table`
921 specifies its
922 own :paramref:`_schema.MetaData.schema` parameter,
923 then that schema name will
924 be applied to this :class:`_schema.Table`
925 if the schema parameter here is set
926 to ``None``. To set a blank schema name on a :class:`_schema.Table`
927 that
928 would otherwise use the schema set on the owning
929 :class:`_schema.MetaData`,
930 specify the special symbol :attr:`.BLANK_SCHEMA`.
931
932 The quoting rules for the schema name are the same as those for the
933 ``name`` parameter, in that quoting is applied for reserved words or
934 case-sensitive names; to enable unconditional quoting for the schema
935 name, specify the flag ``quote_schema=True`` to the constructor, or use
936 the :class:`.quoted_name` construct to specify the name.
937
938 :param comment: Optional string that will render an SQL comment on table
939 creation.
940
941 :param \**kw: Additional keyword arguments not mentioned above are
942 dialect specific, and passed in the form ``<dialectname>_<argname>``.
943 See the documentation regarding an individual dialect at
944 :ref:`dialect_toplevel` for detail on documented arguments.
945
946 """ # noqa: E501
947 if _no_init:
948 # don't run __init__ from __new__ by default;
949 # __new__ has a specific place that __init__ is called
950 return
951 if args:
952 # this is the call done by `__new__` that should have resolved
953 # TypedColumns to the individual columns
954 assert not (
955 isinstance(args[0], type) and issubclass(args[0], TypedColumns)
956 )
957
958 super().__init__(quoted_name(name, quote))
959 self.metadata = metadata
960
961 if schema is None:
962 self.schema = metadata.schema
963 elif schema is BLANK_SCHEMA:
964 self.schema = None
965 else:
966 quote_schema = quote_schema
967 assert isinstance(schema, str)
968 self.schema = quoted_name(schema, quote_schema)
969
970 self._sentinel_column = None
971 self._creator_ddl = _creator_ddl
972 self._dropper_ddl = _dropper_ddl
973
974 self.indexes = set()
975 self.constraints = set()
976 PrimaryKeyConstraint(
977 _implicit_generated=True
978 )._set_parent_with_dispatch(self)
979 self.foreign_keys = set() # type: ignore
980 self._extra_dependencies: Set[Table] = set()
981 if self.schema is not None:
982 self.fullname = "%s.%s" % (self.schema, self.name)
983 else:
984 self.fullname = self.name
985
986 self.implicit_returning = implicit_returning
987 _reflect_info = kw.pop("_reflect_info", None)
988
989 self.comment = comment
990
991 if info is not None:
992 self.info = info
993
994 if listeners is not None:
995 for evt, fn in listeners:
996 event.listen(self, evt, fn)
997
998 self._prefixes = prefixes if prefixes else []
999
1000 self._extra_kwargs(**kw)
1001
1002 # load column definitions from the database if 'autoload' is defined
1003 # we do it after the table is in the singleton dictionary to support
1004 # circular foreign keys
1005 if autoload_with is not None:
1006 self._autoload(
1007 metadata,
1008 autoload_with,
1009 include_columns,
1010 _extend_on=_extend_on,
1011 _reflect_info=_reflect_info,
1012 resolve_fks=resolve_fks,
1013 )
1014
1015 # initialize all the column, etc. objects. done after reflection to
1016 # allow user-overrides
1017
1018 self._init_items(
1019 *args,
1020 allow_replacements=extend_existing
1021 or keep_existing
1022 or autoload_with,
1023 all_names={},
1024 )
1025
1026 def set_creator_ddl(self, ddl: TableCreateDDL) -> None:
1027 """Set the table create DDL for this :class:`.Table`.
1028
1029 This allows the CREATE TABLE statement to be controlled or replaced
1030 entirely when :meth:`.Table.create` or :meth:`.MetaData.create_all` is
1031 used.
1032
1033 E.g.::
1034
1035 from sqlalchemy.schema import CreateTable
1036
1037 table.set_creator_ddl(CreateTable(table, if_not_exists=True))
1038
1039 .. versionadded:: 2.1
1040
1041 .. seealso::
1042
1043 :meth:`.Table.set_dropper_ddl`
1044
1045 """
1046 self._creator_ddl = ddl
1047
1048 def set_dropper_ddl(self, ddl: TableDropDDL) -> None:
1049 """Set the table drop DDL for this :class:`.Table`.
1050
1051 This allows the DROP TABLE statement to be controlled or replaced
1052 entirely when :meth:`.Table.drop` or :meth:`.MetaData.drop_all` is
1053 used.
1054
1055 E.g.::
1056
1057 from sqlalchemy.schema import DropTable
1058
1059 table.set_dropper_ddl(DropTable(table, if_exists=True))
1060
1061 .. versionadded:: 2.1
1062
1063 .. seealso::
1064
1065 :meth:`.Table.set_creator_ddl`
1066
1067 """
1068 self._dropper_ddl = ddl
1069
1070 @property
1071 def is_view(self) -> bool:
1072 """True if this table, when DDL for CREATE is emitted, will emit
1073 CREATE VIEW rather than CREATE TABLE.
1074
1075 .. versionadded:: 2.1
1076
1077 """
1078 return isinstance(self._creator_ddl, ddl.CreateView)
1079
1080 def _autoload(
1081 self,
1082 metadata: MetaData,
1083 autoload_with: Union[Engine, Connection],
1084 include_columns: Optional[Collection[str]],
1085 exclude_columns: Collection[str] = (),
1086 resolve_fks: bool = True,
1087 _extend_on: Optional[Set[Table]] = None,
1088 _reflect_info: _ReflectionInfo | None = None,
1089 ) -> None:
1090 insp = inspection.inspect(autoload_with)
1091 with insp._inspection_context() as conn_insp:
1092 conn_insp.reflect_table(
1093 self,
1094 include_columns,
1095 exclude_columns,
1096 resolve_fks,
1097 _extend_on=_extend_on,
1098 _reflect_info=_reflect_info,
1099 )
1100
1101 @property
1102 def _sorted_constraints(self) -> List[Constraint]:
1103 """Return the set of constraints as a list, sorted by creation
1104 order.
1105
1106 """
1107
1108 return sorted(self.constraints, key=lambda c: c._creation_order)
1109
1110 @property
1111 def foreign_key_constraints(self) -> Set[ForeignKeyConstraint]:
1112 """:class:`_schema.ForeignKeyConstraint` objects referred to by this
1113 :class:`_schema.Table`.
1114
1115 This list is produced from the collection of
1116 :class:`_schema.ForeignKey`
1117 objects currently associated.
1118
1119
1120 .. seealso::
1121
1122 :attr:`_schema.Table.constraints`
1123
1124 :attr:`_schema.Table.foreign_keys`
1125
1126 :attr:`_schema.Table.indexes`
1127
1128 """
1129 return {
1130 fkc.constraint
1131 for fkc in self.foreign_keys
1132 if fkc.constraint is not None
1133 }
1134
1135 def _init_existing(self, *args: Any, **kwargs: Any) -> None:
1136 autoload_with = kwargs.pop("autoload_with", None)
1137 autoload = kwargs.pop("autoload", autoload_with is not None)
1138 autoload_replace = kwargs.pop("autoload_replace", True)
1139 schema = kwargs.pop("schema", None)
1140 _extend_on = kwargs.pop("_extend_on", None)
1141 _reflect_info = kwargs.pop("_reflect_info", None)
1142
1143 # these arguments are only used with _init()
1144 extend_existing = kwargs.pop("extend_existing", False)
1145 keep_existing = kwargs.pop("keep_existing", False)
1146
1147 assert extend_existing
1148 assert not keep_existing
1149
1150 if schema and schema != self.schema:
1151 raise exc.ArgumentError(
1152 f"Can't change schema of existing table "
1153 f"from '{self.schema}' to '{schema}'",
1154 )
1155
1156 include_columns = kwargs.pop("include_columns", None)
1157 if include_columns is not None:
1158 for c in self.c:
1159 if c.name not in include_columns:
1160 self._columns.remove(c)
1161
1162 resolve_fks = kwargs.pop("resolve_fks", True)
1163
1164 for key in ("quote", "quote_schema"):
1165 if key in kwargs:
1166 raise exc.ArgumentError(
1167 "Can't redefine 'quote' or 'quote_schema' arguments"
1168 )
1169
1170 # update `self` with these kwargs, if provided
1171 self.comment = kwargs.pop("comment", self.comment)
1172 self.implicit_returning = kwargs.pop(
1173 "implicit_returning", self.implicit_returning
1174 )
1175 self.info = kwargs.pop("info", self.info)
1176
1177 exclude_columns: _typing_Sequence[str]
1178
1179 if autoload:
1180 if not autoload_replace:
1181 # don't replace columns already present.
1182 # we'd like to do this for constraints also however we don't
1183 # have simple de-duping for unnamed constraints.
1184 exclude_columns = [c.name for c in self.c]
1185 else:
1186 exclude_columns = ()
1187 self._autoload(
1188 self.metadata,
1189 autoload_with,
1190 include_columns,
1191 exclude_columns,
1192 resolve_fks,
1193 _extend_on=_extend_on,
1194 _reflect_info=_reflect_info,
1195 )
1196
1197 all_names = {c.name: c for c in self.c}
1198 self._extra_kwargs(**kwargs)
1199 self._init_items(*args, allow_replacements=True, all_names=all_names)
1200
1201 def _extra_kwargs(self, **kwargs: Any) -> None:
1202 self._validate_dialect_kwargs(kwargs)
1203
1204 def _init_collections(self) -> None:
1205 pass
1206
1207 def _reset_exported(self) -> None:
1208 pass
1209
1210 @util.ro_non_memoized_property
1211 def _autoincrement_column(self) -> Optional[Column[int]]:
1212 return self.primary_key._autoincrement_column
1213
1214 @util.ro_memoized_property
1215 def _sentinel_column_characteristics(
1216 self,
1217 ) -> _SentinelColumnCharacterization:
1218 """determine a candidate column (or columns, in case of a client
1219 generated composite primary key) which can be used as an
1220 "insert sentinel" for an INSERT statement.
1221
1222 The returned structure, :class:`_SentinelColumnCharacterization`,
1223 includes all the details needed by :class:`.Dialect` and
1224 :class:`.SQLCompiler` to determine if these column(s) can be used
1225 as an INSERT..RETURNING sentinel for a particular database
1226 dialect.
1227
1228 .. versionadded:: 2.0.10
1229
1230 """
1231
1232 sentinel_is_explicit = False
1233 sentinel_is_autoinc = False
1234 the_sentinel: Optional[_typing_Sequence[Column[Any]]] = None
1235
1236 # see if a column was explicitly marked "insert_sentinel=True".
1237 explicit_sentinel_col = self._sentinel_column
1238
1239 if explicit_sentinel_col is not None:
1240 the_sentinel = (explicit_sentinel_col,)
1241 sentinel_is_explicit = True
1242
1243 autoinc_col = self._autoincrement_column
1244 if sentinel_is_explicit and explicit_sentinel_col is autoinc_col:
1245 assert autoinc_col is not None
1246 sentinel_is_autoinc = True
1247 elif explicit_sentinel_col is None and autoinc_col is not None:
1248 the_sentinel = (autoinc_col,)
1249 sentinel_is_autoinc = True
1250
1251 default_characterization = _SentinelDefaultCharacterization.UNKNOWN
1252
1253 if the_sentinel:
1254 the_sentinel_zero = the_sentinel[0]
1255 if the_sentinel_zero.identity:
1256 if the_sentinel_zero.identity._increment_is_negative:
1257 if sentinel_is_explicit:
1258 raise exc.InvalidRequestError(
1259 "Can't use IDENTITY default with negative "
1260 "increment as an explicit sentinel column"
1261 )
1262 else:
1263 if sentinel_is_autoinc:
1264 autoinc_col = None
1265 sentinel_is_autoinc = False
1266 the_sentinel = None
1267 else:
1268 default_characterization = (
1269 _SentinelDefaultCharacterization.IDENTITY
1270 )
1271 elif (
1272 the_sentinel_zero.default is None
1273 and the_sentinel_zero.server_default is None
1274 ):
1275 if the_sentinel_zero.nullable:
1276 raise exc.InvalidRequestError(
1277 f"Column {the_sentinel_zero} has been marked as a "
1278 "sentinel "
1279 "column with no default generation function; it "
1280 "at least needs to be marked nullable=False assuming "
1281 "user-populated sentinel values will be used."
1282 )
1283 default_characterization = (
1284 _SentinelDefaultCharacterization.NONE
1285 )
1286 elif the_sentinel_zero.default is not None:
1287 if the_sentinel_zero.default.is_sentinel:
1288 default_characterization = (
1289 _SentinelDefaultCharacterization.SENTINEL_DEFAULT
1290 )
1291 elif the_sentinel_zero.default._is_monotonic_fn:
1292 default_characterization = (
1293 _SentinelDefaultCharacterization.MONOTONIC_FUNCTION
1294 )
1295 elif default_is_sequence(the_sentinel_zero.default):
1296 if the_sentinel_zero.default._increment_is_negative:
1297 if sentinel_is_explicit:
1298 raise exc.InvalidRequestError(
1299 "Can't use SEQUENCE default with negative "
1300 "increment as an explicit sentinel column"
1301 )
1302 else:
1303 if sentinel_is_autoinc:
1304 autoinc_col = None
1305 sentinel_is_autoinc = False
1306 the_sentinel = None
1307
1308 default_characterization = (
1309 _SentinelDefaultCharacterization.SEQUENCE
1310 )
1311 elif the_sentinel_zero.default.is_callable:
1312 default_characterization = (
1313 _SentinelDefaultCharacterization.CLIENTSIDE
1314 )
1315 elif the_sentinel_zero.server_default is not None:
1316 if sentinel_is_explicit:
1317 if not the_sentinel_zero.server_default._is_monotonic_fn:
1318 raise exc.InvalidRequestError(
1319 f"Column {the_sentinel[0]} can't be a sentinel "
1320 "column "
1321 "because it uses an explicit server side default "
1322 "that's not the Identity() default."
1323 )
1324 else:
1325 default_characterization = (
1326 _SentinelDefaultCharacterization.MONOTONIC_FUNCTION
1327 )
1328 else:
1329 default_characterization = (
1330 _SentinelDefaultCharacterization.SERVERSIDE
1331 )
1332
1333 if the_sentinel is None and self.primary_key:
1334 assert autoinc_col is None
1335
1336 # determine for non-autoincrement pk if all elements are
1337 # client side
1338 for _pkc in self.primary_key:
1339 if (
1340 _pkc.server_default is not None
1341 and not _pkc.server_default._is_monotonic_fn
1342 ):
1343 break
1344
1345 if (
1346 _pkc.default
1347 and not _pkc.default.is_callable
1348 and not _pkc.default._is_monotonic_fn
1349 ):
1350 break
1351 else:
1352 the_sentinel = tuple(self.primary_key)
1353 default_characterization = (
1354 _SentinelDefaultCharacterization.CLIENTSIDE
1355 )
1356
1357 return _SentinelColumnCharacterization(
1358 the_sentinel,
1359 sentinel_is_explicit,
1360 sentinel_is_autoinc,
1361 default_characterization,
1362 )
1363
1364 @property
1365 def autoincrement_column(self) -> Optional[Column[int]]:
1366 """Returns the :class:`.Column` object which currently represents
1367 the "auto increment" column, if any, else returns None.
1368
1369 This is based on the rules for :class:`.Column` as defined by the
1370 :paramref:`.Column.autoincrement` parameter, which generally means the
1371 column within a single integer column primary key constraint that is
1372 not constrained by a foreign key. If the table does not have such
1373 a primary key constraint, then there's no "autoincrement" column.
1374 A :class:`.Table` may have only one column defined as the
1375 "autoincrement" column.
1376
1377 .. versionadded:: 2.0.4
1378
1379 .. seealso::
1380
1381 :paramref:`.Column.autoincrement`
1382
1383 """
1384 return self._autoincrement_column
1385
1386 @property
1387 def key(self) -> str:
1388 """Return the 'key' for this :class:`_schema.Table`.
1389
1390 This value is used as the dictionary key within the
1391 :attr:`_schema.MetaData.tables` collection. It is typically the same
1392 as that of :attr:`_schema.Table.name` for a table with no
1393 :attr:`_schema.Table.schema`
1394 set; otherwise it is typically of the form
1395 ``schemaname.tablename``.
1396
1397 """
1398 return _get_table_key(self.name, self.schema)
1399
1400 def __repr__(self) -> str:
1401 return "Table(%s)" % ", ".join(
1402 [repr(self.name)]
1403 + [repr(self.metadata)]
1404 + [repr(x) for x in self.columns]
1405 + ["%s=%s" % (k, repr(getattr(self, k))) for k in ["schema"]]
1406 )
1407
1408 def __str__(self) -> str:
1409 return _get_table_key(self.description, self.schema)
1410
1411 def add_is_dependent_on(self, table: Table) -> None:
1412 """Add a 'dependency' for this Table.
1413
1414 This is another Table object which must be created
1415 first before this one can, or dropped after this one.
1416
1417 Usually, dependencies between tables are determined via
1418 ForeignKey objects. However, for other situations that
1419 create dependencies outside of foreign keys (rules, inheriting),
1420 this method can manually establish such a link.
1421
1422 """
1423 self._extra_dependencies.add(table)
1424
1425 def _insert_col_impl(
1426 self,
1427 column: ColumnClause[Any],
1428 *,
1429 index: Optional[int] = None,
1430 replace_existing: bool = False,
1431 ) -> None:
1432 try:
1433 column._set_parent_with_dispatch(
1434 self,
1435 allow_replacements=replace_existing,
1436 all_names={c.name: c for c in self.c},
1437 index=index,
1438 )
1439 except exc.DuplicateColumnError as de:
1440 raise exc.DuplicateColumnError(
1441 f"{de.args[0]} Specify replace_existing=True to "
1442 "Table.append_column() or Table.insert_column() to replace an "
1443 "existing column."
1444 ) from de
1445
1446 def insert_column(
1447 self,
1448 column: ColumnClause[Any],
1449 index: int,
1450 *,
1451 replace_existing: bool = False,
1452 ) -> None:
1453 """Insert a :class:`_schema.Column` to this :class:`_schema.Table` at
1454 a specific position.
1455
1456 Behavior is identical to :meth:`.Table.append_column` except that
1457 the index position can be controlled using the
1458 :paramref:`.Table.insert_column.index`
1459 parameter.
1460
1461 :param replace_existing:
1462 see :paramref:`.Table.append_column.replace_existing`
1463 :param index: integer index to insert the new column.
1464
1465 .. versionadded:: 2.1
1466
1467 """
1468 self._insert_col_impl(
1469 column, index=index, replace_existing=replace_existing
1470 )
1471
1472 def append_column(
1473 self, column: ColumnClause[Any], *, replace_existing: bool = False
1474 ) -> None:
1475 """Append a :class:`_schema.Column` to this :class:`_schema.Table`.
1476
1477 The "key" of the newly added :class:`_schema.Column`, i.e. the
1478 value of its ``.key`` attribute, will then be available
1479 in the ``.c`` collection of this :class:`_schema.Table`, and the
1480 column definition will be included in any CREATE TABLE, SELECT,
1481 UPDATE, etc. statements generated from this :class:`_schema.Table`
1482 construct.
1483
1484 Note that this does **not** change the definition of the table
1485 as it exists within any underlying database, assuming that
1486 table has already been created in the database. Relational
1487 databases support the addition of columns to existing tables
1488 using the SQL ALTER command, which would need to be
1489 emitted for an already-existing table that doesn't contain
1490 the newly added column.
1491
1492 :param replace_existing: When ``True``, allows replacing existing
1493 columns. When ``False``, the default, an warning will be raised
1494 if a column with the same ``.key`` already exists. A future
1495 version of sqlalchemy will instead rise a warning.
1496
1497 .. versionadded:: 1.4.0
1498
1499 .. seealso::
1500
1501 :meth:`.Table.insert_column`
1502
1503 """
1504 self._insert_col_impl(column, replace_existing=replace_existing)
1505
1506 def append_constraint(self, constraint: Union[Index, Constraint]) -> None:
1507 """Append a :class:`_schema.Constraint` to this
1508 :class:`_schema.Table`.
1509
1510 This has the effect of the constraint being included in any
1511 future CREATE TABLE statement, assuming specific DDL creation
1512 events have not been associated with the given
1513 :class:`_schema.Constraint` object.
1514
1515 Note that this does **not** produce the constraint within the
1516 relational database automatically, for a table that already exists
1517 in the database. To add a constraint to an
1518 existing relational database table, the SQL ALTER command must
1519 be used. SQLAlchemy also provides the
1520 :class:`.AddConstraint` construct which can produce this SQL when
1521 invoked as an executable clause.
1522
1523 """
1524
1525 constraint._set_parent_with_dispatch(self)
1526
1527 def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
1528 metadata = parent
1529 assert isinstance(metadata, MetaData)
1530 metadata._add_table(self.name, self.schema, self)
1531 self.metadata = metadata
1532
1533 def create(
1534 self,
1535 bind: _CreateDropBind,
1536 checkfirst: Union[bool, CheckFirst] = CheckFirst.TYPES,
1537 ) -> None:
1538 """Issue a ``CREATE`` statement for this
1539 :class:`_schema.Table`, using the given
1540 :class:`.Connection` or :class:`.Engine`
1541 for connectivity.
1542
1543 .. seealso::
1544
1545 :meth:`_schema.MetaData.create_all`.
1546
1547 """
1548
1549 # the default is to only check for schema objects
1550 bind._run_ddl_visitor(ddl.SchemaGenerator, self, checkfirst=checkfirst)
1551
1552 def drop(
1553 self,
1554 bind: _CreateDropBind,
1555 checkfirst: Union[bool, CheckFirst] = CheckFirst.NONE,
1556 ) -> None:
1557 """Issue a ``DROP`` statement for this
1558 :class:`_schema.Table`, using the given
1559 :class:`.Connection` or :class:`.Engine` for connectivity.
1560
1561 .. seealso::
1562
1563 :meth:`_schema.MetaData.drop_all`.
1564
1565 """
1566 bind._run_ddl_visitor(ddl.SchemaDropper, self, checkfirst=checkfirst)
1567
1568 @util.deprecated(
1569 "1.4",
1570 ":meth:`_schema.Table.tometadata` is renamed to "
1571 ":meth:`_schema.Table.to_metadata`",
1572 )
1573 def tometadata(
1574 self,
1575 metadata: MetaData,
1576 schema: Union[str, Literal[SchemaConst.RETAIN_SCHEMA]] = RETAIN_SCHEMA,
1577 referred_schema_fn: Optional[
1578 Callable[
1579 [Table, Optional[str], ForeignKeyConstraint, Optional[str]],
1580 Optional[str],
1581 ]
1582 ] = None,
1583 name: Optional[str] = None,
1584 ) -> Table[_ColCC_co]:
1585 """Return a copy of this :class:`_schema.Table`
1586 associated with a different
1587 :class:`_schema.MetaData`.
1588
1589 See :meth:`_schema.Table.to_metadata` for a full description.
1590
1591 """
1592 return self.to_metadata(
1593 metadata,
1594 schema=schema,
1595 referred_schema_fn=referred_schema_fn,
1596 name=name,
1597 )
1598
1599 def to_metadata(
1600 self,
1601 metadata: MetaData,
1602 schema: Union[str, Literal[SchemaConst.RETAIN_SCHEMA]] = RETAIN_SCHEMA,
1603 referred_schema_fn: Optional[
1604 Callable[
1605 [Table, Optional[str], ForeignKeyConstraint, Optional[str]],
1606 Optional[str],
1607 ]
1608 ] = None,
1609 name: Optional[str] = None,
1610 ) -> Table[_ColCC_co]:
1611 """Return a copy of this :class:`_schema.Table` associated with a
1612 different :class:`_schema.MetaData`.
1613
1614 E.g.::
1615
1616 m1 = MetaData()
1617
1618 user = Table("user", m1, Column("id", Integer, primary_key=True))
1619
1620 m2 = MetaData()
1621 user_copy = user.to_metadata(m2)
1622
1623 .. versionchanged:: 1.4 The :meth:`_schema.Table.to_metadata` function
1624 was renamed from :meth:`_schema.Table.tometadata`.
1625
1626
1627 :param metadata: Target :class:`_schema.MetaData` object,
1628 into which the
1629 new :class:`_schema.Table` object will be created.
1630
1631 :param schema: optional string name indicating the target schema.
1632 Defaults to the special symbol :attr:`.RETAIN_SCHEMA` which indicates
1633 that no change to the schema name should be made in the new
1634 :class:`_schema.Table`. If set to a string name, the new
1635 :class:`_schema.Table`
1636 will have this new name as the ``.schema``. If set to ``None``, the
1637 schema will be set to that of the schema set on the target
1638 :class:`_schema.MetaData`, which is typically ``None`` as well,
1639 unless
1640 set explicitly::
1641
1642 m2 = MetaData(schema="newschema")
1643
1644 # user_copy_one will have "newschema" as the schema name
1645 user_copy_one = user.to_metadata(m2, schema=None)
1646
1647 m3 = MetaData() # schema defaults to None
1648
1649 # user_copy_two will have None as the schema name
1650 user_copy_two = user.to_metadata(m3, schema=None)
1651
1652 :param referred_schema_fn: optional callable which can be supplied
1653 in order to provide for the schema name that should be assigned
1654 to the referenced table of a :class:`_schema.ForeignKeyConstraint`.
1655 The callable accepts this parent :class:`_schema.Table`, the
1656 target schema that we are changing to, the
1657 :class:`_schema.ForeignKeyConstraint` object, and the existing
1658 "target schema" of that constraint. The function should return the
1659 string schema name that should be applied. To reset the schema
1660 to "none", return the symbol :data:`.BLANK_SCHEMA`. To effect no
1661 change, return ``None`` or :data:`.RETAIN_SCHEMA`.
1662
1663 .. versionchanged:: 1.4.33 The ``referred_schema_fn`` function
1664 may return the :data:`.BLANK_SCHEMA` or :data:`.RETAIN_SCHEMA`
1665 symbols.
1666
1667 E.g.::
1668
1669 def referred_schema_fn(table, to_schema, constraint, referred_schema):
1670 if referred_schema == "base_tables":
1671 return referred_schema
1672 else:
1673 return to_schema
1674
1675
1676 new_table = table.to_metadata(
1677 m2, schema="alt_schema", referred_schema_fn=referred_schema_fn
1678 )
1679
1680 :param name: optional string name indicating the target table name.
1681 If not specified or None, the table name is retained. This allows
1682 a :class:`_schema.Table` to be copied to the same
1683 :class:`_schema.MetaData` target
1684 with a new name.
1685
1686 """ # noqa: E501
1687 if name is None:
1688 name = self.name
1689
1690 actual_schema: Optional[str]
1691
1692 if schema is RETAIN_SCHEMA:
1693 actual_schema = self.schema
1694 elif schema is None:
1695 actual_schema = metadata.schema
1696 else:
1697 actual_schema = schema
1698 key = _get_table_key(name, actual_schema)
1699 if key in metadata.tables:
1700 util.warn(
1701 f"Table '{self.description}' already exists within the given "
1702 "MetaData - not copying."
1703 )
1704 return metadata.tables[key]
1705
1706 args = []
1707 for col in self.columns:
1708 args.append(col._copy(schema=actual_schema, _to_metadata=metadata))
1709
1710 table: Table[_ColCC_co] = Table( # type: ignore[assignment]
1711 name,
1712 metadata,
1713 schema=actual_schema,
1714 comment=self.comment,
1715 *args,
1716 **self.kwargs,
1717 )
1718
1719 if self._creator_ddl is not None:
1720 table._creator_ddl = self._creator_ddl.to_metadata(metadata, table)
1721 if self._dropper_ddl is not None:
1722 table._dropper_ddl = self._dropper_ddl.to_metadata(metadata, table)
1723
1724 for const in self.constraints:
1725 if isinstance(const, ForeignKeyConstraint):
1726 referred_schema = const._referred_schema
1727 if referred_schema_fn:
1728 fk_constraint_schema = referred_schema_fn(
1729 self, actual_schema, const, referred_schema
1730 )
1731 else:
1732 fk_constraint_schema = (
1733 actual_schema
1734 if referred_schema == self.schema
1735 else None
1736 )
1737 table.append_constraint(
1738 const._copy(
1739 schema=fk_constraint_schema, target_table=table
1740 )
1741 )
1742 elif not const._type_bound:
1743 # skip unique constraints that would be generated
1744 # by the 'unique' flag on Column
1745 if const._column_flag:
1746 continue
1747
1748 table.append_constraint(
1749 const._copy(schema=actual_schema, target_table=table)
1750 )
1751 for index in self.indexes:
1752 # skip indexes that would be generated
1753 # by the 'index' flag on Column
1754 if index._column_flag:
1755 continue
1756 Index(
1757 index.name,
1758 unique=index.unique,
1759 *[
1760 _copy_expression(expr, self, table)
1761 for expr in index._table_bound_expressions
1762 ],
1763 _table=table,
1764 **index.kwargs,
1765 )
1766 return self._schema_item_copy(table)
1767
1768
1769class Column(DialectKWArgs, SchemaItem, ColumnClause[_T], Named[_T]):
1770 """Represents a column in a database table."""
1771
1772 __visit_name__ = "column"
1773
1774 inherit_cache = True
1775 key: str
1776
1777 server_default: Optional[FetchedValue]
1778
1779 def __init__(
1780 self,
1781 __name_pos: Optional[
1782 Union[str, _TypeEngineArgument[_T], SchemaEventTarget]
1783 ] = None,
1784 __type_pos: Optional[
1785 Union[_TypeEngineArgument[_T], SchemaEventTarget]
1786 ] = None,
1787 /,
1788 *args: SchemaEventTarget,
1789 name: Optional[str] = None,
1790 type_: Optional[_TypeEngineArgument[_T]] = None,
1791 autoincrement: _AutoIncrementType = "auto",
1792 default: Optional[Any] = _NoArg.NO_ARG,
1793 insert_default: Optional[Any] = _NoArg.NO_ARG,
1794 doc: Optional[str] = None,
1795 key: Optional[str] = None,
1796 index: Optional[bool] = None,
1797 unique: Optional[bool] = None,
1798 info: Optional[_InfoType] = None,
1799 nullable: Optional[
1800 Union[bool, Literal[SchemaConst.NULL_UNSPECIFIED]]
1801 ] = SchemaConst.NULL_UNSPECIFIED,
1802 onupdate: Optional[Any] = None,
1803 primary_key: bool = False,
1804 server_default: Optional[_ServerDefaultArgument] = None,
1805 server_onupdate: Optional[_ServerOnUpdateArgument] = None,
1806 quote: Optional[bool] = None,
1807 system: bool = False,
1808 comment: Optional[str] = None,
1809 insert_sentinel: bool = False,
1810 _omit_from_statements: bool = False,
1811 _proxies: Optional[Any] = None,
1812 **dialect_kwargs: Any,
1813 ):
1814 r"""
1815 Construct a new ``Column`` object.
1816
1817 :param name: The name of this column as represented in the database.
1818 This argument may be the first positional argument, or specified
1819 via keyword.
1820
1821 Names which contain no upper case characters
1822 will be treated as case insensitive names, and will not be quoted
1823 unless they are a reserved word. Names with any number of upper
1824 case characters will be quoted and sent exactly. Note that this
1825 behavior applies even for databases which standardize upper
1826 case names as case insensitive such as Oracle Database.
1827
1828 The name field may be omitted at construction time and applied
1829 later, at any time before the Column is associated with a
1830 :class:`_schema.Table`. This is to support convenient
1831 usage within the :mod:`~sqlalchemy.ext.declarative` extension.
1832
1833 :param type\_: The column's type, indicated using an instance which
1834 subclasses :class:`~sqlalchemy.types.TypeEngine`. If no arguments
1835 are required for the type, the class of the type can be sent
1836 as well, e.g.::
1837
1838 # use a type with arguments
1839 Column("data", String(50))
1840
1841 # use no arguments
1842 Column("level", Integer)
1843
1844 The ``type`` argument may be the second positional argument
1845 or specified by keyword.
1846
1847 If the ``type`` is ``None`` or is omitted, it will first default to
1848 the special type :class:`.NullType`. If and when this
1849 :class:`_schema.Column` is made to refer to another column using
1850 :class:`_schema.ForeignKey` and/or
1851 :class:`_schema.ForeignKeyConstraint`, the type
1852 of the remote-referenced column will be copied to this column as
1853 well, at the moment that the foreign key is resolved against that
1854 remote :class:`_schema.Column` object.
1855
1856 :param \*args: Additional positional arguments include various
1857 :class:`.SchemaItem` derived constructs which will be applied
1858 as options to the column. These include instances of
1859 :class:`.Constraint`, :class:`_schema.ForeignKey`,
1860 :class:`.ColumnDefault`, :class:`.Sequence`, :class:`.Computed`
1861 :class:`.Identity`. In some cases an
1862 equivalent keyword argument is available such as ``server_default``,
1863 ``default`` and ``unique``.
1864
1865 :param autoincrement: Set up "auto increment" semantics for an
1866 **integer primary key column with no foreign key dependencies**
1867 (see later in this docstring for a more specific definition).
1868 This may influence the :term:`DDL` that will be emitted for
1869 this column during a table create, as well as how the column
1870 will be considered when INSERT statements are compiled and
1871 executed.
1872
1873 The default value is the string ``"auto"``,
1874 which indicates that a single-column (i.e. non-composite) primary key
1875 that is of an INTEGER type with no other client-side or server-side
1876 default constructs indicated should receive auto increment semantics
1877 automatically. Other values include ``True`` (force this column to
1878 have auto-increment semantics for a :term:`composite primary key` as
1879 well), ``False`` (this column should never have auto-increment
1880 semantics), and the string ``"ignore_fk"`` (special-case for foreign
1881 key columns, see below).
1882
1883 The term "auto increment semantics" refers both to the kind of DDL
1884 that will be emitted for the column within a CREATE TABLE statement,
1885 when methods such as :meth:`.MetaData.create_all` and
1886 :meth:`.Table.create` are invoked, as well as how the column will be
1887 considered when an INSERT statement is compiled and emitted to the
1888 database:
1889
1890 * **DDL rendering** (i.e. :meth:`.MetaData.create_all`,
1891 :meth:`.Table.create`): When used on a :class:`.Column` that has
1892 no other
1893 default-generating construct associated with it (such as a
1894 :class:`.Sequence` or :class:`.Identity` construct), the parameter
1895 will imply that database-specific keywords such as PostgreSQL
1896 ``SERIAL``, MySQL ``AUTO_INCREMENT``, or ``IDENTITY`` on SQL Server
1897 should also be rendered. Not every database backend has an
1898 "implied" default generator available; for example the Oracle Database
1899 backends always needs an explicit construct such as
1900 :class:`.Identity` to be included with a :class:`.Column` in order
1901 for the DDL rendered to include auto-generating constructs to also
1902 be produced in the database.
1903
1904 * **INSERT semantics** (i.e. when a :func:`_sql.insert` construct is
1905 compiled into a SQL string and is then executed on a database using
1906 :meth:`_engine.Connection.execute` or equivalent): A single-row
1907 INSERT statement will be known to produce a new integer primary key
1908 value automatically for this column, which will be accessible
1909 after the statement is invoked via the
1910 :attr:`.CursorResult.inserted_primary_key` attribute upon the
1911 :class:`_result.Result` object. This also applies towards use of the
1912 ORM when ORM-mapped objects are persisted to the database,
1913 indicating that a new integer primary key will be available to
1914 become part of the :term:`identity key` for that object. This
1915 behavior takes place regardless of what DDL constructs are
1916 associated with the :class:`_schema.Column` and is independent
1917 of the "DDL Rendering" behavior discussed in the previous note
1918 above.
1919
1920 The parameter may be set to ``True`` to indicate that a column which
1921 is part of a composite (i.e. multi-column) primary key should
1922 have autoincrement semantics, though note that only one column
1923 within a primary key may have this setting. It can also
1924 be set to ``True`` to indicate autoincrement semantics on a
1925 column that has a client-side or server-side default configured,
1926 however note that not all dialects can accommodate all styles
1927 of default as an "autoincrement". It can also be
1928 set to ``False`` on a single-column primary key that has a
1929 datatype of INTEGER in order to disable auto increment semantics
1930 for that column.
1931
1932 The setting *only* has an effect for columns which are:
1933
1934 * Integer derived (i.e. INT, SMALLINT, BIGINT).
1935
1936 * Part of the primary key
1937
1938 * Not referring to another column via :class:`_schema.ForeignKey`,
1939 unless
1940 the value is specified as ``'ignore_fk'``::
1941
1942 # turn on autoincrement for this column despite
1943 # the ForeignKey()
1944 Column(
1945 "id",
1946 ForeignKey("other.id"),
1947 primary_key=True,
1948 autoincrement="ignore_fk",
1949 )
1950
1951 It is typically not desirable to have "autoincrement" enabled on a
1952 column that refers to another via foreign key, as such a column is
1953 required to refer to a value that originates from elsewhere.
1954
1955 The setting has these effects on columns that meet the
1956 above criteria:
1957
1958 * DDL issued for the column, if the column does not already include
1959 a default generating construct supported by the backend such as
1960 :class:`.Identity`, will include database-specific
1961 keywords intended to signify this column as an
1962 "autoincrement" column for specific backends. Behavior for
1963 primary SQLAlchemy dialects includes:
1964
1965 * AUTO INCREMENT on MySQL and MariaDB
1966 * SERIAL on PostgreSQL
1967 * IDENTITY on MS-SQL - this occurs even without the
1968 :class:`.Identity` construct as the
1969 :paramref:`.Column.autoincrement` parameter pre-dates this
1970 construct.
1971 * SQLite - SQLite integer primary key columns are implicitly
1972 "auto incrementing" and no additional keywords are rendered;
1973 to render the special SQLite keyword ``AUTOINCREMENT``
1974 is not included as this is unnecessary and not recommended
1975 by the database vendor. See the section
1976 :ref:`sqlite_autoincrement` for more background.
1977 * Oracle Database - The Oracle Database dialects have no default "autoincrement"
1978 feature available at this time, instead the :class:`.Identity`
1979 construct is recommended to achieve this (the :class:`.Sequence`
1980 construct may also be used).
1981 * Third-party dialects - consult those dialects' documentation
1982 for details on their specific behaviors.
1983
1984 * When a single-row :func:`_sql.insert` construct is compiled and
1985 executed, which does not set the :meth:`_sql.Insert.inline`
1986 modifier, newly generated primary key values for this column
1987 will be automatically retrieved upon statement execution
1988 using a method specific to the database driver in use:
1989
1990 * MySQL, SQLite - calling upon ``cursor.lastrowid()``
1991 (see
1992 `https://www.python.org/dev/peps/pep-0249/#lastrowid
1993 <https://www.python.org/dev/peps/pep-0249/#lastrowid>`_)
1994 * PostgreSQL, SQL Server, Oracle Database - use RETURNING or an equivalent
1995 construct when rendering an INSERT statement, and then retrieving
1996 the newly generated primary key values after execution
1997 * PostgreSQL, Oracle Database for :class:`_schema.Table` objects that
1998 set :paramref:`_schema.Table.implicit_returning` to False -
1999 for a :class:`.Sequence` only, the :class:`.Sequence` is invoked
2000 explicitly before the INSERT statement takes place so that the
2001 newly generated primary key value is available to the client
2002 * SQL Server for :class:`_schema.Table` objects that
2003 set :paramref:`_schema.Table.implicit_returning` to False -
2004 the ``SELECT scope_identity()`` construct is used after the
2005 INSERT statement is invoked to retrieve the newly generated
2006 primary key value.
2007 * Third-party dialects - consult those dialects' documentation
2008 for details on their specific behaviors.
2009
2010 * For multiple-row :func:`_sql.insert` constructs invoked with
2011 a list of parameters (i.e. "executemany" semantics), primary-key
2012 retrieving behaviors are generally disabled, however there may
2013 be special APIs that may be used to retrieve lists of new
2014 primary key values for an "executemany", such as the psycopg2
2015 "fast insertmany" feature. Such features are very new and
2016 may not yet be well covered in documentation.
2017
2018 :param default: A scalar, Python callable, or
2019 :class:`_expression.ColumnElement` expression representing the
2020 *default value* for this column, which will be invoked upon insert
2021 if this column is otherwise not specified in the VALUES clause of
2022 the insert. This is a shortcut to using :class:`.ColumnDefault` as
2023 a positional argument; see that class for full detail on the
2024 structure of the argument.
2025
2026 Contrast this argument to
2027 :paramref:`_schema.Column.server_default`
2028 which creates a default generator on the database side.
2029
2030 .. seealso::
2031
2032 :ref:`metadata_defaults_toplevel`
2033
2034 :param insert_default: An alias of :paramref:`.Column.default`
2035 for compatibility with :func:`_orm.mapped_column`.
2036
2037 .. versionadded:: 2.0.31
2038
2039 :param doc: optional String that can be used by the ORM or similar
2040 to document attributes on the Python side. This attribute does
2041 **not** render SQL comments; use the
2042 :paramref:`_schema.Column.comment`
2043 parameter for this purpose.
2044
2045 :param key: An optional string identifier which will identify this
2046 ``Column`` object on the :class:`_schema.Table`.
2047 When a key is provided,
2048 this is the only identifier referencing the ``Column`` within the
2049 application, including ORM attribute mapping; the ``name`` field
2050 is used only when rendering SQL.
2051
2052 :param index: When ``True``, indicates that a :class:`_schema.Index`
2053 construct will be automatically generated for this
2054 :class:`_schema.Column`, which will result in a "CREATE INDEX"
2055 statement being emitted for the :class:`_schema.Table` when the DDL
2056 create operation is invoked.
2057
2058 Using this flag is equivalent to making use of the
2059 :class:`_schema.Index` construct explicitly at the level of the
2060 :class:`_schema.Table` construct itself::
2061
2062 Table(
2063 "some_table",
2064 metadata,
2065 Column("x", Integer),
2066 Index("ix_some_table_x", "x"),
2067 )
2068
2069 To add the :paramref:`_schema.Index.unique` flag to the
2070 :class:`_schema.Index`, set both the
2071 :paramref:`_schema.Column.unique` and
2072 :paramref:`_schema.Column.index` flags to True simultaneously,
2073 which will have the effect of rendering the "CREATE UNIQUE INDEX"
2074 DDL instruction instead of "CREATE INDEX".
2075
2076 The name of the index is generated using the
2077 :ref:`default naming convention <constraint_default_naming_convention>`
2078 which for the :class:`_schema.Index` construct is of the form
2079 ``ix_<tablename>_<columnname>``.
2080
2081 As this flag is intended only as a convenience for the common case
2082 of adding a single-column, default configured index to a table
2083 definition, explicit use of the :class:`_schema.Index` construct
2084 should be preferred for most use cases, including composite indexes
2085 that encompass more than one column, indexes with SQL expressions
2086 or ordering, backend-specific index configuration options, and
2087 indexes that use a specific name.
2088
2089 .. note:: the :attr:`_schema.Column.index` attribute on
2090 :class:`_schema.Column`
2091 **does not indicate** if this column is indexed or not, only
2092 if this flag was explicitly set here. To view indexes on
2093 a column, view the :attr:`_schema.Table.indexes` collection
2094 or use :meth:`_reflection.Inspector.get_indexes`.
2095
2096 .. seealso::
2097
2098 :ref:`schema_indexes`
2099
2100 :ref:`constraint_naming_conventions`
2101
2102 :paramref:`_schema.Column.unique`
2103
2104 :param info: Optional data dictionary which will be populated into the
2105 :attr:`.SchemaItem.info` attribute of this object.
2106
2107 :param nullable: When set to ``False``, will cause the "NOT NULL"
2108 phrase to be added when generating DDL for the column. When
2109 ``True``, will normally generate nothing (in SQL this defaults to
2110 "NULL"), except in some very specific backend-specific edge cases
2111 where "NULL" may render explicitly.
2112 Defaults to ``True`` unless :paramref:`_schema.Column.primary_key`
2113 is also ``True`` or the column specifies a :class:`_sql.Identity`,
2114 in which case it defaults to ``False``.
2115 This parameter is only used when issuing CREATE TABLE statements.
2116
2117 .. note::
2118
2119 When the column specifies a :class:`_sql.Identity` this
2120 parameter is in general ignored by the DDL compiler. The
2121 PostgreSQL database allows nullable identity column by
2122 setting this parameter to ``True`` explicitly.
2123
2124 :param onupdate: A scalar, Python callable, or
2125 :class:`~sqlalchemy.sql.expression.ClauseElement` representing a
2126 default value to be applied to the column within UPDATE
2127 statements, which will be invoked upon update if this column is not
2128 present in the SET clause of the update. This is a shortcut to
2129 using :class:`.ColumnDefault` as a positional argument with
2130 ``for_update=True``.
2131
2132 .. seealso::
2133
2134 :ref:`metadata_defaults` - complete discussion of onupdate
2135
2136 :param primary_key: If ``True``, marks this column as a primary key
2137 column. Multiple columns can have this flag set to specify
2138 composite primary keys. As an alternative, the primary key of a
2139 :class:`_schema.Table` can be specified via an explicit
2140 :class:`.PrimaryKeyConstraint` object.
2141
2142 :param server_default: A :class:`.FetchedValue` instance, str, Unicode
2143 or :func:`~sqlalchemy.sql.expression.text` construct representing
2144 the DDL DEFAULT value for the column.
2145
2146 String types will be emitted as-is, surrounded by single quotes::
2147
2148 Column("x", Text, server_default="val")
2149
2150 will render:
2151
2152 .. sourcecode:: sql
2153
2154 x TEXT DEFAULT 'val'
2155
2156 A :func:`~sqlalchemy.sql.expression.text` expression will be
2157 rendered as-is, without quotes::
2158
2159 Column("y", DateTime, server_default=text("NOW()"))
2160
2161 will render:
2162
2163 .. sourcecode:: sql
2164
2165 y DATETIME DEFAULT NOW()
2166
2167 Strings and text() will be converted into a
2168 :class:`.DefaultClause` object upon initialization.
2169
2170 This parameter can also accept complex combinations of contextually
2171 valid SQLAlchemy expressions or constructs::
2172
2173 from sqlalchemy import create_engine
2174 from sqlalchemy import Table, Column, MetaData, ARRAY, Text
2175 from sqlalchemy.dialects.postgresql import array
2176
2177 engine = create_engine(
2178 "postgresql+psycopg2://scott:tiger@localhost/mydatabase"
2179 )
2180 metadata_obj = MetaData()
2181 tbl = Table(
2182 "foo",
2183 metadata_obj,
2184 Column(
2185 "bar", ARRAY(Text), server_default=array(["biz", "bang", "bash"])
2186 ),
2187 )
2188 metadata_obj.create_all(engine)
2189
2190 The above results in a table created with the following SQL:
2191
2192 .. sourcecode:: sql
2193
2194 CREATE TABLE foo (
2195 bar TEXT[] DEFAULT ARRAY['biz', 'bang', 'bash']
2196 )
2197
2198 Use :class:`.FetchedValue` to indicate that an already-existing
2199 column will generate a default value on the database side which
2200 will be available to SQLAlchemy for post-fetch after inserts. This
2201 construct does not specify any DDL and the implementation is left
2202 to the database, such as via a trigger.
2203
2204 .. seealso::
2205
2206 :ref:`server_defaults` - complete discussion of server side
2207 defaults
2208
2209 :param server_onupdate: A :class:`.FetchedValue` instance
2210 representing a database-side default generation function,
2211 such as a trigger. This
2212 indicates to SQLAlchemy that a newly generated value will be
2213 available after updates. This construct does not actually
2214 implement any kind of generation function within the database,
2215 which instead must be specified separately.
2216
2217
2218 .. warning:: This directive **does not** currently produce MySQL's
2219 "ON UPDATE CURRENT_TIMESTAMP()" clause. See
2220 :ref:`mysql_timestamp_onupdate` for background on how to
2221 produce this clause.
2222
2223 .. seealso::
2224
2225 :ref:`triggered_columns`
2226
2227 :param quote: Force quoting of this column's name on or off,
2228 corresponding to ``True`` or ``False``. When left at its default
2229 of ``None``, the column identifier will be quoted according to
2230 whether the name is case sensitive (identifiers with at least one
2231 upper case character are treated as case sensitive), or if it's a
2232 reserved word. This flag is only needed to force quoting of a
2233 reserved word which is not known by the SQLAlchemy dialect.
2234
2235 :param unique: When ``True``, and the :paramref:`_schema.Column.index`
2236 parameter is left at its default value of ``False``,
2237 indicates that a :class:`_schema.UniqueConstraint`
2238 construct will be automatically generated for this
2239 :class:`_schema.Column`,
2240 which will result in a "UNIQUE CONSTRAINT" clause referring
2241 to this column being included
2242 in the ``CREATE TABLE`` statement emitted, when the DDL create
2243 operation for the :class:`_schema.Table` object is invoked.
2244
2245 When this flag is ``True`` while the
2246 :paramref:`_schema.Column.index` parameter is simultaneously
2247 set to ``True``, the effect instead is that a
2248 :class:`_schema.Index` construct which includes the
2249 :paramref:`_schema.Index.unique` parameter set to ``True``
2250 is generated. See the documentation for
2251 :paramref:`_schema.Column.index` for additional detail.
2252
2253 Using this flag is equivalent to making use of the
2254 :class:`_schema.UniqueConstraint` construct explicitly at the
2255 level of the :class:`_schema.Table` construct itself::
2256
2257 Table("some_table", metadata, Column("x", Integer), UniqueConstraint("x"))
2258
2259 The :paramref:`_schema.UniqueConstraint.name` parameter
2260 of the unique constraint object is left at its default value
2261 of ``None``; in the absence of a :ref:`naming convention <constraint_naming_conventions>`
2262 for the enclosing :class:`_schema.MetaData`, the UNIQUE CONSTRAINT
2263 construct will be emitted as unnamed, which typically invokes
2264 a database-specific naming convention to take place.
2265
2266 As this flag is intended only as a convenience for the common case
2267 of adding a single-column, default configured unique constraint to a table
2268 definition, explicit use of the :class:`_schema.UniqueConstraint` construct
2269 should be preferred for most use cases, including composite constraints
2270 that encompass more than one column, backend-specific index configuration options, and
2271 constraints that use a specific name.
2272
2273 .. note:: the :attr:`_schema.Column.unique` attribute on
2274 :class:`_schema.Column`
2275 **does not indicate** if this column has a unique constraint or
2276 not, only if this flag was explicitly set here. To view
2277 indexes and unique constraints that may involve this column,
2278 view the
2279 :attr:`_schema.Table.indexes` and/or
2280 :attr:`_schema.Table.constraints` collections or use
2281 :meth:`_reflection.Inspector.get_indexes` and/or
2282 :meth:`_reflection.Inspector.get_unique_constraints`
2283
2284 .. seealso::
2285
2286 :ref:`schema_unique_constraint`
2287
2288 :ref:`constraint_naming_conventions`
2289
2290 :paramref:`_schema.Column.index`
2291
2292 :param system: When ``True``, indicates this is a "system" column,
2293 that is a column which is automatically made available by the
2294 database, and should not be included in the columns list for a
2295 ``CREATE TABLE`` statement.
2296
2297 For more elaborate scenarios where columns should be
2298 conditionally rendered differently on different backends,
2299 consider custom compilation rules for :class:`.CreateColumn`.
2300
2301 :param comment: Optional string that will render an SQL comment on
2302 table creation.
2303
2304 :param insert_sentinel: Marks this :class:`_schema.Column` as an
2305 :term:`insert sentinel` used for optimizing the performance of the
2306 :term:`insertmanyvalues` feature for tables that don't
2307 otherwise have qualifying primary key configurations.
2308
2309 .. versionadded:: 2.0.10
2310
2311 .. seealso::
2312
2313 :func:`_schema.insert_sentinel` - all in one helper for declaring
2314 sentinel columns
2315
2316 :ref:`engine_insertmanyvalues`
2317
2318 :ref:`engine_insertmanyvalues_sentinel_columns`
2319
2320
2321 """ # noqa: E501, RST201, RST202
2322
2323 l_args = [__name_pos, __type_pos] + list(args)
2324 del args
2325
2326 if isinstance(l_args[0], str):
2327 if name is not None:
2328 raise exc.ArgumentError(
2329 "May not pass name positionally and as a keyword."
2330 )
2331 name = l_args.pop(0) # type: ignore
2332 elif l_args[0] is None:
2333 l_args.pop(0)
2334 if l_args:
2335 coltype = l_args[0]
2336
2337 if hasattr(coltype, "_sqla_type"):
2338 if type_ is not None:
2339 raise exc.ArgumentError(
2340 "May not pass type_ positionally and as a keyword."
2341 )
2342 type_ = l_args.pop(0) # type: ignore
2343 elif l_args[0] is None:
2344 l_args.pop(0)
2345
2346 if name is not None:
2347 name = quoted_name(name, quote)
2348 elif quote is not None:
2349 raise exc.ArgumentError(
2350 "Explicit 'name' is required when sending 'quote' argument"
2351 )
2352
2353 # name = None is expected to be an interim state
2354 # note this use case is legacy now that ORM declarative has a
2355 # dedicated "column" construct local to the ORM
2356 super().__init__(name, type_) # type: ignore
2357
2358 self.key = key if key is not None else name # type: ignore
2359 self.primary_key = primary_key
2360 self._insert_sentinel = insert_sentinel
2361 self._omit_from_statements = _omit_from_statements
2362 self._user_defined_nullable = udn = nullable
2363 if udn is not NULL_UNSPECIFIED:
2364 self.nullable = udn
2365 else:
2366 self.nullable = not primary_key
2367
2368 # these default to None because .index and .unique is *not*
2369 # an informational flag about Column - there can still be an
2370 # Index or UniqueConstraint referring to this Column.
2371 self.index = index
2372 self.unique = unique
2373
2374 self.system = system
2375 self.doc = doc
2376 self.autoincrement: _AutoIncrementType = autoincrement
2377 self.constraints = set()
2378 self.foreign_keys = set()
2379 self.comment = comment
2380 self.computed = None
2381 self.identity = None
2382
2383 # check if this Column is proxying another column
2384
2385 if _proxies is not None:
2386 self._proxies = _proxies
2387 else:
2388 # otherwise, add DDL-related events
2389 self._set_type(self.type)
2390
2391 if insert_default is not _NoArg.NO_ARG:
2392 if default is not _NoArg.NO_ARG:
2393 raise exc.ArgumentError(
2394 "The 'default' and 'insert_default' parameters "
2395 "of Column are mutually exclusive"
2396 )
2397 resolved_default = insert_default
2398 elif default is not _NoArg.NO_ARG:
2399 resolved_default = default
2400 else:
2401 resolved_default = None
2402
2403 if resolved_default is not None:
2404 if not isinstance(resolved_default, (ColumnDefault, Sequence)):
2405 resolved_default = ColumnDefault(resolved_default)
2406
2407 self.default = resolved_default
2408 l_args.append(resolved_default)
2409 else:
2410 self.default = None
2411
2412 if onupdate is not None:
2413 if not isinstance(onupdate, (ColumnDefault, Sequence)):
2414 onupdate = ColumnDefault(onupdate, for_update=True)
2415
2416 self.onupdate = onupdate
2417 l_args.append(onupdate)
2418 else:
2419 self.onupdate = None
2420
2421 if server_default is not None:
2422 if isinstance(server_default, FetchedValue):
2423 server_default = server_default._as_for_update(False)
2424 l_args.append(server_default)
2425 else:
2426 server_default = DefaultClause(server_default)
2427 l_args.append(server_default)
2428 self.server_default = server_default
2429
2430 if server_onupdate is not None:
2431 if isinstance(server_onupdate, FetchedValue):
2432 server_onupdate = server_onupdate._as_for_update(True)
2433 l_args.append(server_onupdate)
2434 else:
2435 server_onupdate = DefaultClause(
2436 server_onupdate, for_update=True
2437 )
2438 l_args.append(server_onupdate)
2439 self.server_onupdate = server_onupdate
2440
2441 self._init_items(*cast(_typing_Sequence[SchemaItem], l_args))
2442
2443 util.set_creation_order(self)
2444
2445 if info is not None:
2446 self.info = info
2447
2448 self._extra_kwargs(**dialect_kwargs)
2449
2450 table: Table
2451
2452 constraints: Set[Constraint]
2453
2454 foreign_keys: Set[ForeignKey]
2455 """A collection of all :class:`_schema.ForeignKey` marker objects
2456 associated with this :class:`_schema.Column`.
2457
2458 Each object is a member of a :class:`_schema.Table`-wide
2459 :class:`_schema.ForeignKeyConstraint`.
2460
2461 .. seealso::
2462
2463 :attr:`_schema.Table.foreign_keys`
2464
2465 """
2466
2467 index: Optional[bool]
2468 """The value of the :paramref:`_schema.Column.index` parameter.
2469
2470 Does not indicate if this :class:`_schema.Column` is actually indexed
2471 or not; use :attr:`_schema.Table.indexes`.
2472
2473 .. seealso::
2474
2475 :attr:`_schema.Table.indexes`
2476 """
2477
2478 unique: Optional[bool]
2479 """The value of the :paramref:`_schema.Column.unique` parameter.
2480
2481 Does not indicate if this :class:`_schema.Column` is actually subject to
2482 a unique constraint or not; use :attr:`_schema.Table.indexes` and
2483 :attr:`_schema.Table.constraints`.
2484
2485 .. seealso::
2486
2487 :attr:`_schema.Table.indexes`
2488
2489 :attr:`_schema.Table.constraints`.
2490
2491 """
2492
2493 computed: Optional[Computed]
2494
2495 identity: Optional[Identity]
2496
2497 def _set_type(self, type_: TypeEngine[Any]) -> None:
2498 assert self.type._isnull or type_ is self.type
2499
2500 self.type = type_
2501 if isinstance(self.type, SchemaEventTarget):
2502 self.type._set_parent_with_dispatch(self)
2503 for impl in self.type._variant_mapping.values():
2504 if isinstance(impl, SchemaEventTarget):
2505 impl._set_parent_with_dispatch(self)
2506
2507 @HasMemoized.memoized_attribute
2508 def _default_description_tuple(self) -> _DefaultDescriptionTuple:
2509 """used by default.py -> _process_execute_defaults()"""
2510
2511 return _DefaultDescriptionTuple._from_column_default(self.default)
2512
2513 @HasMemoized.memoized_attribute
2514 def _onupdate_description_tuple(self) -> _DefaultDescriptionTuple:
2515 """used by default.py -> _process_execute_defaults()"""
2516 return _DefaultDescriptionTuple._from_column_default(self.onupdate)
2517
2518 @util.memoized_property
2519 def _gen_static_annotations_cache_key(self) -> bool:
2520 """special attribute used by cache key gen, if true, we will
2521 use a static cache key for the annotations dictionary, else we
2522 will generate a new cache key for annotations each time.
2523
2524 Added for #8790
2525
2526 """
2527 return self.table is not None and self.table._is_table
2528
2529 def _extra_kwargs(self, **kwargs: Any) -> None:
2530 self._validate_dialect_kwargs(kwargs)
2531
2532 def __str__(self) -> str:
2533 if self.name is None:
2534 return "(no name)"
2535 elif self.table is not None:
2536 if self.table.named_with_column:
2537 return self.table.description + "." + self.description
2538 else:
2539 return self.description
2540 else:
2541 return self.description
2542
2543 def references(self, column: Column[Any]) -> bool:
2544 """Return True if this Column references the given column via foreign
2545 key."""
2546
2547 for fk in self.foreign_keys:
2548 if fk.column.proxy_set.intersection(column.proxy_set):
2549 return True
2550 else:
2551 return False
2552
2553 def append_foreign_key(self, fk: ForeignKey) -> None:
2554 fk._set_parent_with_dispatch(self)
2555
2556 def __repr__(self) -> str:
2557 kwarg = []
2558 if self.key != self.name:
2559 kwarg.append("key")
2560 if self.primary_key:
2561 kwarg.append("primary_key")
2562 if not self.nullable:
2563 kwarg.append("nullable")
2564 if self.onupdate:
2565 kwarg.append("onupdate")
2566 if self.default:
2567 kwarg.append("default")
2568 if self.server_default:
2569 kwarg.append("server_default")
2570 if self.comment:
2571 kwarg.append("comment")
2572 return "Column(%s)" % ", ".join(
2573 [repr(self.name)]
2574 + [repr(self.type)]
2575 + [repr(x) for x in self.foreign_keys if x is not None]
2576 + [repr(x) for x in self.constraints]
2577 + [
2578 (
2579 self.table is not None
2580 and "table=<%s>" % self.table.description
2581 or "table=None"
2582 )
2583 ]
2584 + ["%s=%s" % (k, repr(getattr(self, k))) for k in kwarg]
2585 )
2586
2587 def _set_parent( # type: ignore[override]
2588 self,
2589 parent: SchemaEventTarget,
2590 *,
2591 all_names: Dict[str, Column[Any]],
2592 allow_replacements: bool,
2593 index: Optional[int] = None,
2594 **kw: Any,
2595 ) -> None:
2596 table = parent
2597 assert isinstance(table, Table)
2598 if not self.name:
2599 raise exc.ArgumentError(
2600 "Column must be constructed with a non-blank name or "
2601 "assign a non-blank .name before adding to a Table."
2602 )
2603
2604 self._reset_memoizations()
2605
2606 if self.key is None:
2607 self.key = self.name
2608
2609 existing = getattr(self, "table", None)
2610 if existing is not None and existing is not table:
2611 raise exc.ArgumentError(
2612 f"Column object '{self.key}' already "
2613 f"assigned to Table '{existing.description}'"
2614 )
2615
2616 extra_remove = None
2617 existing_col = None
2618 conflicts_on = ""
2619
2620 if self.key in table._columns:
2621 existing_col = table._columns[self.key]
2622 if self.key == self.name:
2623 conflicts_on = "name"
2624 else:
2625 conflicts_on = "key"
2626 elif self.name in all_names:
2627 existing_col = all_names[self.name]
2628 extra_remove = {existing_col}
2629 conflicts_on = "name"
2630
2631 if existing_col is not None:
2632 if existing_col is not self:
2633 if not allow_replacements:
2634 raise exc.DuplicateColumnError(
2635 f"A column with {conflicts_on} "
2636 f"""'{
2637 self.key if conflicts_on == 'key' else self.name
2638 }' """
2639 f"is already present in table '{table.name}'."
2640 )
2641 for fk in existing_col.foreign_keys:
2642 table.foreign_keys.remove(fk)
2643 if fk.constraint in table.constraints:
2644 # this might have been removed
2645 # already, if it's a composite constraint
2646 # and more than one col being replaced
2647 table.constraints.remove(fk.constraint)
2648
2649 if extra_remove and existing_col is not None and self.key == self.name:
2650 util.warn(
2651 f'Column with user-specified key "{existing_col.key}" is '
2652 "being replaced with "
2653 f'plain named column "{self.name}", '
2654 f'key "{existing_col.key}" is being removed. If this is a '
2655 "reflection operation, specify autoload_replace=False to "
2656 "prevent this replacement."
2657 )
2658 table._columns.replace(self, extra_remove=extra_remove, index=index)
2659 all_names[self.name] = self
2660 self.table = table
2661
2662 if self._insert_sentinel:
2663 if self.table._sentinel_column is not None:
2664 raise exc.ArgumentError(
2665 "a Table may have only one explicit sentinel column"
2666 )
2667 self.table._sentinel_column = self
2668
2669 if self.primary_key:
2670 table.primary_key._replace(self)
2671 elif self.key in table.primary_key:
2672 raise exc.ArgumentError(
2673 f"Trying to redefine primary-key column '{self.key}' as a "
2674 f"non-primary-key column on table '{table.fullname}'"
2675 )
2676
2677 if self.index:
2678 if isinstance(self.index, str):
2679 raise exc.ArgumentError(
2680 "The 'index' keyword argument on Column is boolean only. "
2681 "To create indexes with a specific name, create an "
2682 "explicit Index object external to the Table."
2683 )
2684 table.append_constraint(
2685 Index(
2686 None, self.key, unique=bool(self.unique), _column_flag=True
2687 )
2688 )
2689
2690 elif self.unique:
2691 if isinstance(self.unique, str):
2692 raise exc.ArgumentError(
2693 "The 'unique' keyword argument on Column is boolean "
2694 "only. To create unique constraints or indexes with a "
2695 "specific name, append an explicit UniqueConstraint to "
2696 "the Table's list of elements, or create an explicit "
2697 "Index object external to the Table."
2698 )
2699 table.append_constraint(
2700 UniqueConstraint(self.key, _column_flag=True)
2701 )
2702
2703 self._setup_on_memoized_fks(lambda fk: fk._set_remote_table(table))
2704
2705 if self.identity and (
2706 isinstance(self.default, Sequence)
2707 or isinstance(self.onupdate, Sequence)
2708 ):
2709 raise exc.ArgumentError(
2710 "An column cannot specify both Identity and Sequence."
2711 )
2712
2713 def _setup_on_memoized_fks(self, fn: Callable[..., Any]) -> None:
2714 fk_keys = [
2715 ((self.table.key, self.key), False),
2716 ((self.table.key, self.name), True),
2717 ]
2718 for fk_key, link_to_name in fk_keys:
2719 if fk_key in self.table.metadata._fk_memos:
2720 for fk in self.table.metadata._fk_memos[fk_key]:
2721 if fk.link_to_name is link_to_name:
2722 fn(fk)
2723
2724 def _on_table_attach(self, fn: Callable[..., Any]) -> None:
2725 if self.table is not None:
2726 fn(self, self.table)
2727 else:
2728 event.listen(self, "after_parent_attach", fn)
2729
2730 @util.deprecated(
2731 "1.4",
2732 "The :meth:`_schema.Column.copy` method is deprecated "
2733 "and will be removed in a future release.",
2734 )
2735 def copy(self, **kw: Any) -> Column[Any]:
2736 return self._copy(**kw)
2737
2738 def _copy(self, **kw: Any) -> Column[Any]:
2739 """Create a copy of this ``Column``, uninitialized.
2740
2741 This is used in :meth:`_schema.Table.to_metadata` and by the ORM.
2742
2743 """
2744
2745 # Constraint objects plus non-constraint-bound ForeignKey objects
2746 args: List[SchemaItem] = [
2747 c._copy(**kw) for c in self.constraints if not c._type_bound
2748 ] + [c._copy(**kw) for c in self.foreign_keys if not c.constraint]
2749
2750 # ticket #5276
2751 column_kwargs = {}
2752 for dialect_name in self.dialect_options:
2753 dialect_options = self.dialect_options[dialect_name]._non_defaults
2754 for (
2755 dialect_option_key,
2756 dialect_option_value,
2757 ) in dialect_options.items():
2758 column_kwargs[dialect_name + "_" + dialect_option_key] = (
2759 dialect_option_value
2760 )
2761
2762 server_default = self.server_default
2763 server_onupdate = self.server_onupdate
2764 if isinstance(server_default, (Computed, Identity)):
2765 # TODO: likely should be copied in all cases
2766 # TODO: if a Sequence, we would need to transfer the Sequence
2767 # .metadata as well
2768 args.append(server_default._copy(**kw))
2769 server_default = server_onupdate = None
2770
2771 type_ = self.type
2772 if isinstance(type_, SchemaEventTarget):
2773 type_ = type_.copy(**kw)
2774
2775 # TODO: DefaultGenerator is not copied here! it's just used again
2776 # with _set_parent() pointing to the old column. see the new
2777 # use of _copy() in the new _merge() method
2778
2779 c = self._constructor(
2780 name=self.name,
2781 type_=type_,
2782 key=self.key,
2783 primary_key=self.primary_key,
2784 unique=self.unique,
2785 system=self.system,
2786 # quote=self.quote, # disabled 2013-08-27 (commit 031ef080)
2787 index=self.index,
2788 autoincrement=self.autoincrement,
2789 default=self.default,
2790 server_default=server_default,
2791 onupdate=self.onupdate,
2792 server_onupdate=server_onupdate,
2793 doc=self.doc,
2794 comment=self.comment,
2795 _omit_from_statements=self._omit_from_statements,
2796 insert_sentinel=self._insert_sentinel,
2797 *args,
2798 **column_kwargs,
2799 )
2800
2801 # copy the state of "nullable" exactly, to accommodate for
2802 # ORM flipping the .nullable flag directly
2803 c.nullable = self.nullable
2804 c._user_defined_nullable = self._user_defined_nullable
2805
2806 return self._schema_item_copy(c)
2807
2808 def _merge(
2809 self, other: Column[Any], *, omit_defaults: bool = False
2810 ) -> None:
2811 """merge the elements of this column onto "other"
2812
2813 this is used by ORM pep-593 merge and will likely need a lot
2814 of fixes.
2815
2816
2817 """
2818
2819 if self.primary_key:
2820 other.primary_key = True
2821
2822 if self.autoincrement != "auto" and other.autoincrement == "auto":
2823 other.autoincrement = self.autoincrement
2824
2825 if self.system:
2826 other.system = self.system
2827
2828 if self.info:
2829 other.info.update(self.info)
2830
2831 type_ = self.type
2832 if not type_._isnull and other.type._isnull:
2833 if isinstance(type_, SchemaEventTarget):
2834 type_ = type_.copy()
2835
2836 other.type = type_
2837
2838 if isinstance(type_, SchemaEventTarget):
2839 type_._set_parent_with_dispatch(other)
2840
2841 for impl in type_._variant_mapping.values():
2842 if isinstance(impl, SchemaEventTarget):
2843 impl._set_parent_with_dispatch(other)
2844
2845 if (
2846 self._user_defined_nullable is not NULL_UNSPECIFIED
2847 and other._user_defined_nullable is NULL_UNSPECIFIED
2848 ):
2849 other.nullable = self.nullable
2850 other._user_defined_nullable = self._user_defined_nullable
2851
2852 if (
2853 not omit_defaults
2854 and self.default is not None
2855 and other.default is None
2856 ):
2857 new_default = self.default._copy()
2858 new_default._set_parent(other)
2859
2860 if self.server_default and other.server_default is None:
2861 new_server_default = self.server_default
2862 if isinstance(new_server_default, FetchedValue):
2863 new_server_default = new_server_default._copy()
2864 new_server_default._set_parent(other)
2865 else:
2866 other.server_default = new_server_default
2867
2868 if self.server_onupdate and other.server_onupdate is None:
2869 new_server_onupdate = self.server_onupdate
2870 new_server_onupdate = new_server_onupdate._copy()
2871 new_server_onupdate._set_parent(other)
2872
2873 if self.onupdate and other.onupdate is None:
2874 new_onupdate = self.onupdate._copy()
2875 new_onupdate._set_parent(other)
2876
2877 if self.index in (True, False) and other.index is None:
2878 other.index = self.index
2879
2880 if self.unique in (True, False) and other.unique is None:
2881 other.unique = self.unique
2882
2883 if self.doc and other.doc is None:
2884 other.doc = self.doc
2885
2886 if self.comment and other.comment is None:
2887 other.comment = self.comment
2888
2889 for const in self.constraints:
2890 if not const._type_bound:
2891 new_const = const._copy()
2892 new_const._set_parent(other)
2893
2894 for fk in self.foreign_keys:
2895 if not fk.constraint:
2896 new_fk = fk._copy()
2897 new_fk._set_parent(other)
2898
2899 def _make_proxy(
2900 self,
2901 selectable: FromClause,
2902 primary_key: ColumnSet,
2903 foreign_keys: Set[KeyedColumnElement[Any]],
2904 name: Optional[str] = None,
2905 key: Optional[str] = None,
2906 name_is_truncatable: bool = False,
2907 compound_select_cols: Optional[
2908 _typing_Sequence[ColumnElement[Any]]
2909 ] = None,
2910 **kw: Any,
2911 ) -> Tuple[str, ColumnClause[_T]]:
2912 """Create a *proxy* for this column.
2913
2914 This is a copy of this ``Column`` referenced by a different parent
2915 (such as an alias or select statement). The column should
2916 be used only in select scenarios, as its full DDL/default
2917 information is not transferred.
2918
2919 """
2920
2921 fk = [
2922 ForeignKey(
2923 col if col is not None else f._colspec,
2924 _unresolvable=col is None,
2925 _constraint=f.constraint,
2926 )
2927 for f, col in [
2928 (fk, fk._resolve_column(raiseerr=False))
2929 for fk in self.foreign_keys
2930 ]
2931 ]
2932
2933 if name is None and self.name is None:
2934 raise exc.InvalidRequestError(
2935 "Cannot initialize a sub-selectable"
2936 " with this Column object until its 'name' has "
2937 "been assigned."
2938 )
2939 try:
2940 c = self._constructor(
2941 (
2942 coercions.expect(
2943 roles.TruncatedLabelRole, name if name else self.name
2944 )
2945 if name_is_truncatable
2946 else (name or self.name)
2947 ),
2948 self.type,
2949 # this may actually be ._proxy_key when the key is incoming
2950 key=key if key else name if name else self.key,
2951 primary_key=self.primary_key,
2952 nullable=self.nullable,
2953 _proxies=(
2954 list(compound_select_cols)
2955 if compound_select_cols
2956 else [self]
2957 ),
2958 *fk,
2959 )
2960 except TypeError as err:
2961 raise TypeError(
2962 "Could not create a copy of this %r object. "
2963 "Ensure the class includes a _constructor() "
2964 "attribute or method which accepts the "
2965 "standard Column constructor arguments, or "
2966 "references the Column class itself." % self.__class__
2967 ) from err
2968
2969 c.table = selectable
2970 c._propagate_attrs = selectable._propagate_attrs
2971 if selectable._is_clone_of is not None:
2972 c._is_clone_of = selectable._is_clone_of.columns.get(c.key)
2973
2974 if self.primary_key:
2975 primary_key.add(c)
2976
2977 if fk:
2978 foreign_keys.update(fk) # type: ignore
2979
2980 return c.key, c
2981
2982
2983def insert_sentinel(
2984 name: Optional[str] = None,
2985 type_: Optional[_TypeEngineArgument[_T]] = None,
2986 *,
2987 default: Optional[Any] = None,
2988 omit_from_statements: bool = True,
2989) -> Column[Any]:
2990 """Provides a surrogate :class:`_schema.Column` that will act as a
2991 dedicated insert :term:`sentinel` column, allowing efficient bulk
2992 inserts with deterministic RETURNING sorting for tables that
2993 don't otherwise have qualifying primary key configurations.
2994
2995 Adding this column to a :class:`.Table` object requires that a
2996 corresponding database table actually has this column present, so if adding
2997 it to an existing model, existing database tables would need to be migrated
2998 (e.g. using ALTER TABLE or similar) to include this column.
2999
3000 For background on how this object is used, see the section
3001 :ref:`engine_insertmanyvalues_sentinel_columns` as part of the
3002 section :ref:`engine_insertmanyvalues`.
3003
3004 The :class:`_schema.Column` returned will be a nullable integer column by
3005 default and make use of a sentinel-specific default generator used only in
3006 "insertmanyvalues" operations.
3007
3008 .. seealso::
3009
3010 :func:`_orm.orm_insert_sentinel`
3011
3012 :paramref:`_schema.Column.insert_sentinel`
3013
3014 :ref:`engine_insertmanyvalues`
3015
3016 :ref:`engine_insertmanyvalues_sentinel_columns`
3017
3018
3019 .. versionadded:: 2.0.10
3020
3021 """
3022 return Column(
3023 name=name,
3024 type_=type_api.INTEGERTYPE if type_ is None else type_,
3025 default=(
3026 default if default is not None else _InsertSentinelColumnDefault()
3027 ),
3028 _omit_from_statements=omit_from_statements,
3029 insert_sentinel=True,
3030 )
3031
3032
3033class ForeignKey(DialectKWArgs, SchemaItem):
3034 """Defines a dependency between two columns.
3035
3036 ``ForeignKey`` is specified as an argument to a :class:`_schema.Column`
3037 object,
3038 e.g.::
3039
3040 t = Table(
3041 "remote_table",
3042 metadata,
3043 Column("remote_id", ForeignKey("main_table.id")),
3044 )
3045
3046 Note that ``ForeignKey`` is only a marker object that defines
3047 a dependency between two columns. The actual constraint
3048 is in all cases represented by the :class:`_schema.ForeignKeyConstraint`
3049 object. This object will be generated automatically when
3050 a ``ForeignKey`` is associated with a :class:`_schema.Column` which
3051 in turn is associated with a :class:`_schema.Table`. Conversely,
3052 when :class:`_schema.ForeignKeyConstraint` is applied to a
3053 :class:`_schema.Table`,
3054 ``ForeignKey`` markers are automatically generated to be
3055 present on each associated :class:`_schema.Column`, which are also
3056 associated with the constraint object.
3057
3058 Note that you cannot define a "composite" foreign key constraint,
3059 that is a constraint between a grouping of multiple parent/child
3060 columns, using ``ForeignKey`` objects. To define this grouping,
3061 the :class:`_schema.ForeignKeyConstraint` object must be used, and applied
3062 to the :class:`_schema.Table`. The associated ``ForeignKey`` objects
3063 are created automatically.
3064
3065 The ``ForeignKey`` objects associated with an individual
3066 :class:`_schema.Column`
3067 object are available in the `foreign_keys` collection
3068 of that column.
3069
3070 Further examples of foreign key configuration are in
3071 :ref:`metadata_foreignkeys`.
3072
3073 """
3074
3075 __visit_name__ = "foreign_key"
3076
3077 parent: Column[Any]
3078
3079 _table_column: Optional[Column[Any]]
3080
3081 _colspec: Union[str, Column[Any]]
3082
3083 def __init__(
3084 self,
3085 column: _DDLColumnReferenceArgument,
3086 _constraint: Optional[ForeignKeyConstraint] = None,
3087 use_alter: bool = False,
3088 name: _ConstraintNameArgument = None,
3089 onupdate: Optional[str] = None,
3090 ondelete: Optional[str] = None,
3091 deferrable: Optional[bool] = None,
3092 initially: Optional[str] = None,
3093 link_to_name: bool = False,
3094 match: Optional[str] = None,
3095 info: Optional[_InfoType] = None,
3096 comment: Optional[str] = None,
3097 _unresolvable: bool = False,
3098 **dialect_kw: Any,
3099 ):
3100 r"""
3101 Construct a column-level FOREIGN KEY.
3102
3103 The :class:`_schema.ForeignKey` object when constructed generates a
3104 :class:`_schema.ForeignKeyConstraint`
3105 which is associated with the parent
3106 :class:`_schema.Table` object's collection of constraints.
3107
3108 :param column: A single target column for the key relationship. A
3109 :class:`_schema.Column` object or a column name as a string:
3110 ``tablename.columnkey`` or ``schema.tablename.columnkey``.
3111 ``columnkey`` is the ``key`` which has been assigned to the column
3112 (defaults to the column name itself), unless ``link_to_name`` is
3113 ``True`` in which case the rendered name of the column is used.
3114
3115 :param name: Optional string. An in-database name for the key if
3116 `constraint` is not provided.
3117
3118 :param onupdate: Optional string. If set, emit ON UPDATE <value> when
3119 issuing DDL for this constraint. Typical values include CASCADE,
3120 DELETE and RESTRICT.
3121
3122 .. seealso::
3123
3124 :ref:`on_update_on_delete`
3125
3126 :param ondelete: Optional string. If set, emit ON DELETE <value> when
3127 issuing DDL for this constraint. Typical values include CASCADE,
3128 SET NULL and RESTRICT. Some dialects may allow for additional
3129 syntaxes.
3130
3131 .. seealso::
3132
3133 :ref:`on_update_on_delete`
3134
3135 :param deferrable: Optional bool. If set, emit DEFERRABLE or NOT
3136 DEFERRABLE when issuing DDL for this constraint.
3137
3138 :param initially: Optional string. If set, emit INITIALLY <value> when
3139 issuing DDL for this constraint.
3140
3141 :param link_to_name: if True, the string name given in ``column`` is
3142 the rendered name of the referenced column, not its locally
3143 assigned ``key``.
3144
3145 :param use_alter: passed to the underlying
3146 :class:`_schema.ForeignKeyConstraint`
3147 to indicate the constraint should
3148 be generated/dropped externally from the CREATE TABLE/ DROP TABLE
3149 statement. See :paramref:`_schema.ForeignKeyConstraint.use_alter`
3150 for further description.
3151
3152 .. seealso::
3153
3154 :paramref:`_schema.ForeignKeyConstraint.use_alter`
3155
3156 :ref:`use_alter`
3157
3158 :param match: Optional string. If set, emit MATCH <value> when issuing
3159 DDL for this constraint. Typical values include SIMPLE, PARTIAL
3160 and FULL.
3161
3162 :param info: Optional data dictionary which will be populated into the
3163 :attr:`.SchemaItem.info` attribute of this object.
3164
3165 :param comment: Optional string that will render an SQL comment on
3166 foreign key constraint creation.
3167
3168 .. versionadded:: 2.0
3169
3170 :param \**dialect_kw: Additional keyword arguments are dialect
3171 specific, and passed in the form ``<dialectname>_<argname>``. The
3172 arguments are ultimately handled by a corresponding
3173 :class:`_schema.ForeignKeyConstraint`.
3174 See the documentation regarding
3175 an individual dialect at :ref:`dialect_toplevel` for detail on
3176 documented arguments.
3177
3178 """
3179
3180 self._unresolvable = _unresolvable
3181
3182 self._colspec, self._table_column = self._parse_colspec_argument(
3183 column
3184 )
3185
3186 # the linked ForeignKeyConstraint.
3187 # ForeignKey will create this when parent Column
3188 # is attached to a Table, *or* ForeignKeyConstraint
3189 # object passes itself in when creating ForeignKey
3190 # markers.
3191 self.constraint = _constraint
3192
3193 # .parent is not Optional under normal use
3194 self.parent = None # type: ignore
3195
3196 self.use_alter = use_alter
3197 self.name = name
3198 self.onupdate = onupdate
3199 self.ondelete = ondelete
3200 self.deferrable = deferrable
3201 self.initially = initially
3202 self.link_to_name = link_to_name
3203 self.match = match
3204 self.comment = comment
3205 if info:
3206 self.info = info
3207 self._unvalidated_dialect_kw = dialect_kw
3208
3209 def _resolve_colspec_argument(
3210 self,
3211 ) -> Tuple[
3212 Union[str, Column[Any]],
3213 Optional[Column[Any]],
3214 ]:
3215 argument = self._colspec
3216
3217 return self._parse_colspec_argument(argument)
3218
3219 def _parse_colspec_argument(
3220 self,
3221 argument: _DDLColumnArgument,
3222 ) -> Tuple[
3223 Union[str, Column[Any]],
3224 Optional[Column[Any]],
3225 ]:
3226 _colspec = coercions.expect(roles.DDLReferredColumnRole, argument)
3227
3228 if isinstance(_colspec, str):
3229 _table_column = None
3230 else:
3231 assert isinstance(_colspec, ColumnClause)
3232 _table_column = _colspec
3233
3234 if not isinstance(_table_column.table, (type(None), TableClause)):
3235 raise exc.ArgumentError(
3236 "ForeignKey received Column not bound "
3237 "to a Table, got: %r" % _table_column.table
3238 )
3239
3240 return _colspec, _table_column
3241
3242 def __repr__(self) -> str:
3243 return "ForeignKey(%r)" % self._get_colspec()
3244
3245 @util.deprecated(
3246 "1.4",
3247 "The :meth:`_schema.ForeignKey.copy` method is deprecated "
3248 "and will be removed in a future release.",
3249 )
3250 def copy(self, *, schema: Optional[str] = None, **kw: Any) -> ForeignKey:
3251 return self._copy(schema=schema, **kw)
3252
3253 def _copy(self, *, schema: Optional[str] = None, **kw: Any) -> ForeignKey:
3254 """Produce a copy of this :class:`_schema.ForeignKey` object.
3255
3256 The new :class:`_schema.ForeignKey` will not be bound
3257 to any :class:`_schema.Column`.
3258
3259 This method is usually used by the internal
3260 copy procedures of :class:`_schema.Column`, :class:`_schema.Table`,
3261 and :class:`_schema.MetaData`.
3262
3263 :param schema: The returned :class:`_schema.ForeignKey` will
3264 reference the original table and column name, qualified
3265 by the given string schema name.
3266
3267 """
3268 fk = ForeignKey(
3269 self._get_colspec(schema=schema),
3270 use_alter=self.use_alter,
3271 name=self.name,
3272 onupdate=self.onupdate,
3273 ondelete=self.ondelete,
3274 deferrable=self.deferrable,
3275 initially=self.initially,
3276 link_to_name=self.link_to_name,
3277 match=self.match,
3278 comment=self.comment,
3279 **self._unvalidated_dialect_kw,
3280 )
3281 return self._schema_item_copy(fk)
3282
3283 def _get_colspec(
3284 self,
3285 schema: Optional[
3286 Union[
3287 str,
3288 Literal[SchemaConst.RETAIN_SCHEMA, SchemaConst.BLANK_SCHEMA],
3289 ]
3290 ] = None,
3291 table_name: Optional[str] = None,
3292 _is_copy: bool = False,
3293 ) -> str:
3294 """Return a string based 'column specification' for this
3295 :class:`_schema.ForeignKey`.
3296
3297 This is usually the equivalent of the string-based "tablename.colname"
3298 argument first passed to the object's constructor.
3299
3300 """
3301
3302 _colspec, effective_table_column = self._resolve_colspec_argument()
3303
3304 if schema not in (None, RETAIN_SCHEMA):
3305 _schema, tname, colname = self._column_tokens
3306 if table_name is not None:
3307 tname = table_name
3308 if schema is BLANK_SCHEMA:
3309 return "%s.%s" % (tname, colname)
3310 else:
3311 return "%s.%s.%s" % (schema, tname, colname)
3312 elif table_name:
3313 schema, tname, colname = self._column_tokens
3314 if schema:
3315 return "%s.%s.%s" % (schema, table_name, colname)
3316 else:
3317 return "%s.%s" % (table_name, colname)
3318 elif effective_table_column is not None:
3319 if effective_table_column.table is None:
3320 if _is_copy:
3321 raise exc.InvalidRequestError(
3322 f"Can't copy ForeignKey object which refers to "
3323 f"non-table bound Column {effective_table_column!r}"
3324 )
3325 else:
3326 return effective_table_column.key
3327 return "%s.%s" % (
3328 effective_table_column.table.fullname,
3329 effective_table_column.key,
3330 )
3331 else:
3332 assert isinstance(_colspec, str)
3333 return _colspec
3334
3335 @property
3336 def _referred_schema(self) -> Optional[str]:
3337 return self._column_tokens[0]
3338
3339 def _table_key_within_construction(self) -> Any:
3340 """get the table key but only safely"""
3341
3342 if self._table_column is not None:
3343 if self._table_column.table is None:
3344 return None
3345 else:
3346 return self._table_column.table.key
3347 else:
3348 schema, tname, colname = self._column_tokens
3349 return _get_table_key(tname, schema)
3350
3351 target_fullname = property(_get_colspec)
3352
3353 def references(self, table: Table) -> bool:
3354 """Return True if the given :class:`_schema.Table`
3355 is referenced by this
3356 :class:`_schema.ForeignKey`."""
3357
3358 return table.corresponding_column(self.column) is not None
3359
3360 def get_referent(self, table: FromClause) -> Optional[Column[Any]]:
3361 """Return the :class:`_schema.Column` in the given
3362 :class:`_schema.Table` (or any :class:`.FromClause`)
3363 referenced by this :class:`_schema.ForeignKey`.
3364
3365 Returns None if this :class:`_schema.ForeignKey`
3366 does not reference the given
3367 :class:`_schema.Table`.
3368
3369 """
3370 # our column is a Column, and any subquery etc. proxying us
3371 # would be doing so via another Column, so that's what would
3372 # be returned here
3373 return table.columns.corresponding_column(self.column) # type: ignore
3374
3375 @util.memoized_property
3376 def _column_tokens(self) -> Tuple[Optional[str], str, Optional[str]]:
3377 """parse a string-based _colspec into its component parts."""
3378
3379 m = self._get_colspec().split(".")
3380 if len(m) == 1:
3381 tname = m.pop()
3382 colname = None
3383 else:
3384 colname = m.pop()
3385 tname = m.pop()
3386
3387 # A FK between column 'bar' and table 'foo' can be
3388 # specified as 'foo', 'foo.bar', 'dbo.foo.bar',
3389 # 'otherdb.dbo.foo.bar'. Once we have the column name and
3390 # the table name, treat everything else as the schema
3391 # name. Some databases (e.g. Sybase) support
3392 # inter-database foreign keys. See tickets#1341 and --
3393 # indirectly related -- Ticket #594. This assumes that '.'
3394 # will never appear *within* any component of the FK.
3395
3396 if len(m) > 0:
3397 schema = ".".join(m)
3398 else:
3399 schema = None
3400 return schema, tname, colname
3401
3402 def _resolve_col_tokens(self) -> Tuple[Table, str, Optional[str]]:
3403 if self.parent is None:
3404 raise exc.InvalidRequestError(
3405 "this ForeignKey object does not yet have a "
3406 "parent Column associated with it."
3407 )
3408
3409 elif self.parent.table is None:
3410 raise exc.InvalidRequestError(
3411 "this ForeignKey's parent column is not yet associated "
3412 "with a Table."
3413 )
3414
3415 parenttable = self.parent.table
3416
3417 if self._unresolvable:
3418 schema, tname, colname = self._column_tokens
3419 tablekey = _get_table_key(tname, schema)
3420 return parenttable, tablekey, colname
3421
3422 # assertion
3423 # basically Column._make_proxy() sends the actual
3424 # target Column to the ForeignKey object, so the
3425 # string resolution here is never called.
3426 for c in self.parent.base_columns:
3427 if isinstance(c, Column):
3428 assert c.table is parenttable
3429 break
3430 else:
3431 assert False
3432 ######################
3433
3434 schema, tname, colname = self._column_tokens
3435
3436 if schema is None and parenttable.metadata.schema is not None:
3437 schema = parenttable.metadata.schema
3438
3439 tablekey = _get_table_key(tname, schema)
3440 return parenttable, tablekey, colname
3441
3442 def _link_to_col_by_colstring(
3443 self, parenttable: Table, table: Table, colname: Optional[str]
3444 ) -> Column[Any]:
3445 _column = None
3446 if colname is None:
3447 # colname is None in the case that ForeignKey argument
3448 # was specified as table name only, in which case we
3449 # match the column name to the same column on the
3450 # parent.
3451 # this use case wasn't working in later 1.x series
3452 # as it had no test coverage; fixed in 2.0
3453 parent = self.parent
3454 assert parent is not None
3455 key = parent.key
3456 _column = table.c.get(key, None)
3457 elif self.link_to_name:
3458 key = colname
3459 for c in table.c:
3460 if c.name == colname:
3461 _column = c
3462 else:
3463 key = colname
3464 _column = table.c.get(colname, None)
3465
3466 if _column is None:
3467 raise exc.NoReferencedColumnError(
3468 "Could not initialize target column "
3469 f"for ForeignKey '{self._get_colspec()}' "
3470 f"on table '{parenttable.name}': "
3471 f"table '{table.name}' has no column named '{key}'",
3472 table.name,
3473 key,
3474 )
3475
3476 return _column
3477
3478 def _set_target_column(self, column: Column[Any]) -> None:
3479 assert self.parent is not None
3480
3481 # propagate TypeEngine to parent if it didn't have one
3482 if self.parent.type._isnull:
3483 self.parent.type = column.type
3484
3485 # super-edgy case, if other FKs point to our column,
3486 # they'd get the type propagated out also.
3487
3488 def set_type(fk: ForeignKey) -> None:
3489 if fk.parent.type._isnull:
3490 fk.parent.type = column.type
3491
3492 self.parent._setup_on_memoized_fks(set_type)
3493
3494 self.column = column # type: ignore
3495
3496 @util.ro_memoized_property
3497 def column(self) -> Column[Any]:
3498 """Return the target :class:`_schema.Column` referenced by this
3499 :class:`_schema.ForeignKey`.
3500
3501 If no target column has been established, an exception
3502 is raised.
3503
3504 """
3505 return self._resolve_column()
3506
3507 @overload
3508 def _resolve_column(
3509 self, *, raiseerr: Literal[True] = ...
3510 ) -> Column[Any]: ...
3511
3512 @overload
3513 def _resolve_column(
3514 self, *, raiseerr: bool = ...
3515 ) -> Optional[Column[Any]]: ...
3516
3517 def _resolve_column(
3518 self, *, raiseerr: bool = True
3519 ) -> Optional[Column[Any]]:
3520 _column: Column[Any]
3521
3522 _colspec, effective_table_column = self._resolve_colspec_argument()
3523
3524 if isinstance(_colspec, str):
3525 parenttable, tablekey, colname = self._resolve_col_tokens()
3526
3527 if self._unresolvable or tablekey not in parenttable.metadata:
3528 if not raiseerr:
3529 return None
3530 raise exc.NoReferencedTableError(
3531 f"Foreign key associated with column "
3532 f"'{self.parent}' could not find "
3533 f"table '{tablekey}' with which to generate a "
3534 f"foreign key to target column '{colname}'",
3535 tablekey,
3536 )
3537 elif parenttable.key not in parenttable.metadata:
3538 if not raiseerr:
3539 return None
3540 raise exc.InvalidRequestError(
3541 f"Table {parenttable} is no longer associated with its "
3542 "parent MetaData"
3543 )
3544 else:
3545 table = parenttable.metadata.tables[tablekey]
3546 return self._link_to_col_by_colstring(
3547 parenttable, table, colname
3548 )
3549
3550 elif hasattr(_colspec, "__clause_element__"):
3551 _column = _colspec.__clause_element__()
3552 return _column
3553 else:
3554 assert isinstance(_colspec, Column)
3555 _column = _colspec
3556 return _column
3557
3558 def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
3559 assert isinstance(parent, Column)
3560
3561 if self.parent is not None and self.parent is not parent:
3562 raise exc.InvalidRequestError(
3563 "This ForeignKey already has a parent !"
3564 )
3565 self.parent = parent
3566 self.parent.foreign_keys.add(self)
3567 self.parent._on_table_attach(self._set_table)
3568
3569 def _set_remote_table(self, table: Table) -> None:
3570 parenttable, _, colname = self._resolve_col_tokens()
3571 _column = self._link_to_col_by_colstring(parenttable, table, colname)
3572 self._set_target_column(_column)
3573 assert self.constraint is not None
3574 self.constraint._validate_dest_table(table)
3575
3576 def _remove_from_metadata(self, metadata: MetaData) -> None:
3577 parenttable, table_key, colname = self._resolve_col_tokens()
3578 fk_key = (table_key, colname)
3579
3580 if self in metadata._fk_memos[fk_key]:
3581 # TODO: no test coverage for self not in memos
3582 metadata._fk_memos[fk_key].remove(self)
3583
3584 def _set_table(self, column: Column[Any], table: Table) -> None:
3585 # standalone ForeignKey - create ForeignKeyConstraint
3586 # on the hosting Table when attached to the Table.
3587 assert isinstance(table, Table)
3588 if self.constraint is None:
3589 self.constraint = ForeignKeyConstraint(
3590 [],
3591 [],
3592 use_alter=self.use_alter,
3593 name=self.name,
3594 onupdate=self.onupdate,
3595 ondelete=self.ondelete,
3596 deferrable=self.deferrable,
3597 initially=self.initially,
3598 match=self.match,
3599 comment=self.comment,
3600 **self._unvalidated_dialect_kw,
3601 )
3602 self.constraint._append_element(column, self)
3603 self.constraint._set_parent_with_dispatch(table)
3604 table.foreign_keys.add(self)
3605 # set up remote ".column" attribute, or a note to pick it
3606 # up when the other Table/Column shows up
3607
3608 _colspec, _ = self._resolve_colspec_argument()
3609 if isinstance(_colspec, str):
3610 parenttable, table_key, colname = self._resolve_col_tokens()
3611 fk_key = (table_key, colname)
3612 if table_key in parenttable.metadata.tables:
3613 table = parenttable.metadata.tables[table_key]
3614 try:
3615 _column = self._link_to_col_by_colstring(
3616 parenttable, table, colname
3617 )
3618 except exc.NoReferencedColumnError:
3619 # this is OK, we'll try later
3620 pass
3621 else:
3622 self._set_target_column(_column)
3623
3624 parenttable.metadata._fk_memos[fk_key].append(self)
3625 elif hasattr(_colspec, "__clause_element__"):
3626 _column = _colspec.__clause_element__()
3627 self._set_target_column(_column)
3628 else:
3629 self._set_target_column(_colspec)
3630
3631
3632if TYPE_CHECKING:
3633
3634 def default_is_sequence(
3635 obj: Optional[DefaultGenerator],
3636 ) -> TypeGuard[Sequence]: ...
3637
3638 def default_is_clause_element(
3639 obj: Optional[DefaultGenerator],
3640 ) -> TypeGuard[ColumnElementColumnDefault]: ...
3641
3642 def default_is_scalar(
3643 obj: Optional[DefaultGenerator],
3644 ) -> TypeGuard[ScalarElementColumnDefault]: ...
3645
3646else:
3647 default_is_sequence = operator.attrgetter("is_sequence")
3648
3649 default_is_clause_element = operator.attrgetter("is_clause_element")
3650
3651 default_is_scalar = operator.attrgetter("is_scalar")
3652
3653
3654class DefaultGenerator(Executable, SchemaItem):
3655 """Base class for column *default* values.
3656
3657 This object is only present on column.default or column.onupdate.
3658 It's not valid as a server default.
3659
3660 """
3661
3662 __visit_name__ = "default_generator"
3663
3664 _is_default_generator = True
3665 is_sequence = False
3666 is_identity = False
3667 is_server_default = False
3668 is_clause_element = False
3669 is_callable = False
3670 is_scalar = False
3671 has_arg = False
3672 is_sentinel = False
3673 _is_monotonic_fn = False
3674 column: Optional[Column[Any]]
3675
3676 def __init__(self, for_update: bool = False) -> None:
3677 self.for_update = for_update
3678
3679 def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
3680 if TYPE_CHECKING:
3681 assert isinstance(parent, Column)
3682 self.column = parent
3683 if self.for_update:
3684 self.column.onupdate = self
3685 else:
3686 self.column.default = self
3687
3688 def _copy(self) -> DefaultGenerator:
3689 raise NotImplementedError()
3690
3691 def _execute_on_connection(
3692 self,
3693 connection: Connection,
3694 distilled_params: _CoreMultiExecuteParams,
3695 execution_options: CoreExecuteOptionsParameter,
3696 ) -> Any:
3697 util.warn_deprecated(
3698 "Using the .execute() method to invoke a "
3699 "DefaultGenerator object is deprecated; please use "
3700 "the .scalar() method.",
3701 "2.0",
3702 )
3703 return self._execute_on_scalar(
3704 connection, distilled_params, execution_options
3705 )
3706
3707 def _execute_on_scalar(
3708 self,
3709 connection: Connection,
3710 distilled_params: _CoreMultiExecuteParams,
3711 execution_options: CoreExecuteOptionsParameter,
3712 ) -> Any:
3713 return connection._execute_default(
3714 self, distilled_params, execution_options
3715 )
3716
3717
3718class ColumnDefault(DefaultGenerator, ABC):
3719 """A plain default value on a column.
3720
3721 This could correspond to a constant, a callable function,
3722 or a SQL clause.
3723
3724 :class:`.ColumnDefault` is generated automatically
3725 whenever the ``default``, ``onupdate`` arguments of
3726 :class:`_schema.Column` are used. A :class:`.ColumnDefault`
3727 can be passed positionally as well.
3728
3729 For example, the following::
3730
3731 Column("foo", Integer, default=50)
3732
3733 Is equivalent to::
3734
3735 Column("foo", Integer, ColumnDefault(50))
3736
3737 """
3738
3739 arg: Any
3740
3741 _is_monotonic_fn = False
3742
3743 @overload
3744 def __new__(
3745 cls, arg: Callable[..., Any], for_update: bool = ...
3746 ) -> CallableColumnDefault: ...
3747
3748 @overload
3749 def __new__(
3750 cls, arg: ColumnElement[Any], for_update: bool = ...
3751 ) -> ColumnElementColumnDefault: ...
3752
3753 # if I return ScalarElementColumnDefault here, which is what's actually
3754 # returned, mypy complains that
3755 # overloads overlap w/ incompatible return types.
3756 @overload
3757 def __new__(cls, arg: object, for_update: bool = ...) -> ColumnDefault: ...
3758
3759 def __new__(
3760 cls, arg: Any = None, for_update: bool = False
3761 ) -> ColumnDefault:
3762 """Construct a new :class:`.ColumnDefault`.
3763
3764
3765 :param arg: argument representing the default value.
3766 May be one of the following:
3767
3768 * a plain non-callable Python value, such as a
3769 string, integer, boolean, or other simple type.
3770 The default value will be used as is each time.
3771 * a SQL expression, that is one which derives from
3772 :class:`_expression.ColumnElement`. The SQL expression will
3773 be rendered into the INSERT or UPDATE statement,
3774 or in the case of a primary key column when
3775 RETURNING is not used may be
3776 pre-executed before an INSERT within a SELECT.
3777 * A Python callable. The function will be invoked for each
3778 new row subject to an INSERT or UPDATE.
3779 The callable must accept exactly
3780 zero or one positional arguments. The one-argument form
3781 will receive an instance of the :class:`.ExecutionContext`,
3782 which provides contextual information as to the current
3783 :class:`_engine.Connection` in use as well as the current
3784 statement and parameters.
3785
3786 """
3787
3788 if isinstance(arg, FetchedValue):
3789 raise exc.ArgumentError(
3790 "ColumnDefault may not be a server-side default type."
3791 )
3792 elif callable(arg):
3793 cls = CallableColumnDefault
3794 elif isinstance(arg, ClauseElement):
3795 cls = ColumnElementColumnDefault
3796 elif arg is not None:
3797 cls = ScalarElementColumnDefault
3798
3799 return object.__new__(cls)
3800
3801 def __repr__(self) -> str:
3802 return f"{self.__class__.__name__}({self.arg!r})"
3803
3804
3805class ScalarElementColumnDefault(ColumnDefault):
3806 """default generator for a fixed scalar Python value
3807
3808 .. versionadded:: 2.0
3809
3810 """
3811
3812 is_scalar = True
3813 has_arg = True
3814
3815 def __init__(self, arg: Any, for_update: bool = False) -> None:
3816 self.for_update = for_update
3817 self.arg = arg
3818
3819 def _copy(self) -> ScalarElementColumnDefault:
3820 return ScalarElementColumnDefault(
3821 arg=self.arg, for_update=self.for_update
3822 )
3823
3824
3825class _InsertSentinelColumnDefault(ColumnDefault):
3826 """Default generator that's specific to the use of a "sentinel" column
3827 when using the insertmanyvalues feature.
3828
3829 This default is used as part of the :func:`_schema.insert_sentinel`
3830 construct.
3831
3832 """
3833
3834 is_sentinel = True
3835 for_update = False
3836 arg = None
3837
3838 def __new__(cls) -> _InsertSentinelColumnDefault:
3839 return object.__new__(cls)
3840
3841 def __init__(self) -> None:
3842 pass
3843
3844 def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
3845 col = cast("Column[Any]", parent)
3846 if not col._insert_sentinel:
3847 raise exc.ArgumentError(
3848 "The _InsertSentinelColumnDefault may only be applied to a "
3849 "Column marked as insert_sentinel=True"
3850 )
3851 elif not col.nullable:
3852 raise exc.ArgumentError(
3853 "The _InsertSentinelColumnDefault may only be applied to a "
3854 "Column that is nullable"
3855 )
3856
3857 super()._set_parent(parent, **kw)
3858
3859 def _copy(self) -> _InsertSentinelColumnDefault:
3860 return _InsertSentinelColumnDefault()
3861
3862
3863_SQLExprDefault = Union["ColumnElement[Any]", "TextClause"]
3864
3865
3866class ColumnElementColumnDefault(ColumnDefault):
3867 """default generator for a SQL expression
3868
3869 .. versionadded:: 2.0
3870
3871 """
3872
3873 is_clause_element = True
3874 has_arg = True
3875 arg: _SQLExprDefault
3876
3877 def __init__(
3878 self,
3879 arg: _SQLExprDefault,
3880 for_update: bool = False,
3881 ) -> None:
3882 self.for_update = for_update
3883 self.arg = arg
3884
3885 def _copy(self) -> ColumnElementColumnDefault:
3886 return ColumnElementColumnDefault(
3887 arg=self.arg, for_update=self.for_update
3888 )
3889
3890 @util.memoized_property
3891 @util.preload_module("sqlalchemy.sql.functions")
3892 def _is_monotonic_fn(self) -> bool:
3893 functions = util.preloaded.sql_functions
3894 return (
3895 isinstance(self.arg, functions.FunctionElement)
3896 and self.arg.monotonic
3897 )
3898
3899 @util.memoized_property
3900 @util.preload_module("sqlalchemy.sql.sqltypes")
3901 def _arg_is_typed(self) -> bool:
3902 sqltypes = util.preloaded.sql_sqltypes
3903
3904 return not isinstance(self.arg.type, sqltypes.NullType)
3905
3906
3907class _CallableColumnDefaultProtocol(Protocol):
3908 def __call__(self, context: ExecutionContext) -> Any: ...
3909
3910
3911class CallableColumnDefault(ColumnDefault):
3912 """default generator for a callable Python function
3913
3914 .. versionadded:: 2.0
3915
3916 """
3917
3918 is_callable = True
3919 arg: _CallableColumnDefaultProtocol
3920 has_arg = True
3921
3922 def __init__(
3923 self,
3924 arg: Union[_CallableColumnDefaultProtocol, Callable[[], Any]],
3925 for_update: bool = False,
3926 ) -> None:
3927 self.for_update = for_update
3928 self.arg = self._maybe_wrap_callable(arg)
3929
3930 def _copy(self) -> CallableColumnDefault:
3931 return CallableColumnDefault(arg=self.arg, for_update=self.for_update)
3932
3933 def _maybe_wrap_callable(
3934 self, fn: Union[_CallableColumnDefaultProtocol, Callable[[], Any]]
3935 ) -> _CallableColumnDefaultProtocol:
3936 """Wrap callables that don't accept a context.
3937
3938 This is to allow easy compatibility with default callables
3939 that aren't specific to accepting of a context.
3940
3941 """
3942
3943 try:
3944 argspec = util.get_callable_argspec(fn, no_self=True)
3945 except TypeError:
3946 return util.wrap_callable(lambda ctx: fn(), fn) # type: ignore
3947
3948 defaulted = argspec[3] is not None and len(argspec[3]) or 0
3949 positionals = len(argspec[0]) - defaulted
3950
3951 if positionals == 0:
3952 return util.wrap_callable(lambda ctx: fn(), fn) # type: ignore
3953
3954 elif positionals == 1:
3955 return fn # type: ignore
3956 else:
3957 raise exc.ArgumentError(
3958 "ColumnDefault Python function takes zero or one "
3959 "positional arguments"
3960 )
3961
3962
3963class IdentityOptions(DialectKWArgs):
3964 """Defines options for a named database sequence or an identity column.
3965
3966 .. seealso::
3967
3968 :class:`.Sequence`
3969
3970 """
3971
3972 def __init__(
3973 self,
3974 start: Optional[int] = None,
3975 increment: Optional[int] = None,
3976 minvalue: Optional[int] = None,
3977 maxvalue: Optional[int] = None,
3978 nominvalue: Optional[bool] = None,
3979 nomaxvalue: Optional[bool] = None,
3980 cycle: Optional[bool] = None,
3981 cache: Optional[int] = None,
3982 order: Optional[bool] = None,
3983 **dialect_kw: Any,
3984 ) -> None:
3985 """Construct a :class:`.IdentityOptions` object.
3986
3987 See the :class:`.Sequence` documentation for a complete description
3988 of the parameters.
3989
3990 :param start: the starting index of the sequence.
3991 :param increment: the increment value of the sequence.
3992 :param minvalue: the minimum value of the sequence.
3993 :param maxvalue: the maximum value of the sequence.
3994 :param nominvalue: no minimum value of the sequence.
3995 :param nomaxvalue: no maximum value of the sequence.
3996 :param cycle: allows the sequence to wrap around when the maxvalue
3997 or minvalue has been reached.
3998 :param cache: optional integer value; number of future values in the
3999 sequence which are calculated in advance.
4000 :param order: optional boolean value; if ``True``, renders the
4001 ORDER keyword.
4002
4003 .. deprecated:: 2.1 Use ``oracle_order`` instead.
4004
4005 """
4006 self.start = start
4007 self.increment = increment
4008 self.minvalue = minvalue
4009 self.maxvalue = maxvalue
4010 self.nominvalue = nominvalue
4011 self.nomaxvalue = nomaxvalue
4012 self.cycle = cycle
4013 self.cache = cache
4014 if order is not None:
4015 if "oracle_order" in dialect_kw:
4016 raise exc.ArgumentError(
4017 "Cannot specify both 'order' and 'oracle_order'. "
4018 "Please use only 'oracle_order'."
4019 )
4020 dialect_kw["oracle_order"] = order
4021 self._validate_dialect_kwargs(dialect_kw)
4022
4023 @property
4024 def _increment_is_negative(self) -> bool:
4025 return self.increment is not None and self.increment < 0
4026
4027 @property
4028 def order(self) -> Optional[bool]:
4029 """Alias of the ``dialect_kwargs`` ``'oracle_order'``.
4030
4031 .. deprecated:: 2.1 The 'order' attribute is deprecated.
4032 """
4033 value: Optional[bool] = self.dialect_kwargs.get("oracle_order")
4034 return value
4035
4036 def _as_dict(self) -> Dict[str, Any]:
4037 return {
4038 k: v
4039 for k, v in {
4040 "start": self.start,
4041 "increment": self.increment,
4042 "minvalue": self.minvalue,
4043 "maxvalue": self.maxvalue,
4044 "nominvalue": self.nominvalue,
4045 "nomaxvalue": self.nomaxvalue,
4046 "cycle": self.cycle,
4047 "cache": self.cache,
4048 }.items()
4049 if v != None
4050 }
4051
4052
4053class Sequence(HasSchemaAttr, IdentityOptions, DefaultGenerator):
4054 """Represents a named database sequence.
4055
4056 The :class:`.Sequence` object represents the name and configurational
4057 parameters of a database sequence. It also represents
4058 a construct that can be "executed" by a SQLAlchemy :class:`_engine.Engine`
4059 or :class:`_engine.Connection`,
4060 rendering the appropriate "next value" function
4061 for the target database and returning a result.
4062
4063 The :class:`.Sequence` is typically associated with a primary key column::
4064
4065 some_table = Table(
4066 "some_table",
4067 metadata,
4068 Column(
4069 "id",
4070 Integer,
4071 Sequence("some_table_seq", start=1),
4072 primary_key=True,
4073 ),
4074 )
4075
4076 When CREATE TABLE is emitted for the above :class:`_schema.Table`, if the
4077 target platform supports sequences, a CREATE SEQUENCE statement will
4078 be emitted as well. For platforms that don't support sequences,
4079 the :class:`.Sequence` construct is ignored.
4080
4081 .. seealso::
4082
4083 :ref:`defaults_sequences`
4084
4085 :class:`.CreateSequence`
4086
4087 :class:`.DropSequence`
4088
4089 """
4090
4091 __visit_name__ = "sequence"
4092
4093 is_sequence = True
4094
4095 column: Optional[Column[Any]]
4096 data_type: Optional[TypeEngine[int]]
4097
4098 metadata: Optional[MetaData]
4099
4100 @util.deprecated_params(
4101 order=(
4102 "2.1",
4103 "This parameter is supported only by Oracle Database, "
4104 "use ``oracle_order`` instead.",
4105 )
4106 )
4107 def __init__(
4108 self,
4109 name: str,
4110 start: Optional[int] = None,
4111 increment: Optional[int] = None,
4112 minvalue: Optional[int] = None,
4113 maxvalue: Optional[int] = None,
4114 nominvalue: Optional[bool] = None,
4115 nomaxvalue: Optional[bool] = None,
4116 cycle: Optional[bool] = None,
4117 schema: Optional[Union[str, Literal[SchemaConst.BLANK_SCHEMA]]] = None,
4118 cache: Optional[int] = None,
4119 order: Optional[bool] = None,
4120 data_type: Optional[_TypeEngineArgument[int]] = None,
4121 optional: bool = False,
4122 quote: Optional[bool] = None,
4123 metadata: Optional[MetaData] = None,
4124 quote_schema: Optional[bool] = None,
4125 for_update: bool = False,
4126 **dialect_kw: Any,
4127 ) -> None:
4128 """Construct a :class:`.Sequence` object.
4129
4130 :param name: the name of the sequence.
4131
4132 :param start: the starting index of the sequence. This value is
4133 used when the CREATE SEQUENCE command is emitted to the database
4134 as the value of the "START WITH" clause. If ``None``, the
4135 clause is omitted, which on most platforms indicates a starting
4136 value of 1.
4137
4138 .. versionchanged:: 2.0 The :paramref:`.Sequence.start` parameter
4139 is required in order to have DDL emit "START WITH". This is a
4140 reversal of a change made in version 1.4 which would implicitly
4141 render "START WITH 1" if the :paramref:`.Sequence.start` were
4142 not included. See :ref:`change_7211` for more detail.
4143
4144 :param increment: the increment value of the sequence. This
4145 value is used when the CREATE SEQUENCE command is emitted to
4146 the database as the value of the "INCREMENT BY" clause. If ``None``,
4147 the clause is omitted, which on most platforms indicates an
4148 increment of 1.
4149 :param minvalue: the minimum value of the sequence. This
4150 value is used when the CREATE SEQUENCE command is emitted to
4151 the database as the value of the "MINVALUE" clause. If ``None``,
4152 the clause is omitted, which on most platforms indicates a
4153 minvalue of 1 and -2^63-1 for ascending and descending sequences,
4154 respectively.
4155
4156 :param maxvalue: the maximum value of the sequence. This
4157 value is used when the CREATE SEQUENCE command is emitted to
4158 the database as the value of the "MAXVALUE" clause. If ``None``,
4159 the clause is omitted, which on most platforms indicates a
4160 maxvalue of 2^63-1 and -1 for ascending and descending sequences,
4161 respectively.
4162
4163 :param nominvalue: no minimum value of the sequence. This
4164 value is used when the CREATE SEQUENCE command is emitted to
4165 the database as the value of the "NO MINVALUE" clause. If ``None``,
4166 the clause is omitted, which on most platforms indicates a
4167 minvalue of 1 and -2^63-1 for ascending and descending sequences,
4168 respectively.
4169
4170 :param nomaxvalue: no maximum value of the sequence. This
4171 value is used when the CREATE SEQUENCE command is emitted to
4172 the database as the value of the "NO MAXVALUE" clause. If ``None``,
4173 the clause is omitted, which on most platforms indicates a
4174 maxvalue of 2^63-1 and -1 for ascending and descending sequences,
4175 respectively.
4176
4177 :param cycle: allows the sequence to wrap around when the maxvalue
4178 or minvalue has been reached by an ascending or descending sequence
4179 respectively. This value is used when the CREATE SEQUENCE command
4180 is emitted to the database as the "CYCLE" clause. If the limit is
4181 reached, the next number generated will be the minvalue or maxvalue,
4182 respectively. If cycle=False (the default) any calls to nextval
4183 after the sequence has reached its maximum value will return an
4184 error.
4185
4186 :param schema: optional schema name for the sequence, if located
4187 in a schema other than the default. The rules for selecting the
4188 schema name when a :class:`_schema.MetaData`
4189 is also present are the same
4190 as that of :paramref:`_schema.Table.schema`.
4191
4192 :param cache: optional integer value; number of future values in the
4193 sequence which are calculated in advance. Renders the CACHE keyword
4194 understood by Oracle Database and PostgreSQL.
4195
4196 :param order: optional boolean value; if ``True``, renders the
4197 ORDER keyword, understood by Oracle Database, indicating the sequence
4198 is definitively ordered. May be necessary to provide deterministic
4199 ordering using Oracle RAC.
4200
4201 :param data_type: The type to be returned by the sequence, for
4202 dialects that allow us to choose between INTEGER, BIGINT, etc.
4203 (e.g., mssql).
4204
4205 .. versionadded:: 1.4.0
4206
4207 :param optional: boolean value, when ``True``, indicates that this
4208 :class:`.Sequence` object only needs to be explicitly generated
4209 on backends that don't provide another way to generate primary
4210 key identifiers. Currently, it essentially means, "don't create
4211 this sequence on the PostgreSQL backend, where the SERIAL keyword
4212 creates a sequence for us automatically".
4213 :param quote: boolean value, when ``True`` or ``False``, explicitly
4214 forces quoting of the :paramref:`_schema.Sequence.name` on or off.
4215 When left at its default of ``None``, normal quoting rules based
4216 on casing and reserved words take place.
4217 :param quote_schema: Set the quoting preferences for the ``schema``
4218 name.
4219
4220 :param metadata: optional :class:`_schema.MetaData` object which this
4221 :class:`.Sequence` will be associated with. A :class:`.Sequence`
4222 that is associated with a :class:`_schema.MetaData`
4223 gains the following
4224 capabilities:
4225
4226 * The :class:`.Sequence` will inherit the
4227 :paramref:`_schema.MetaData.schema`
4228 parameter specified to the target :class:`_schema.MetaData`, which
4229 affects the production of CREATE / DROP DDL, if any.
4230
4231 * The :meth:`.Sequence.create` and :meth:`.Sequence.drop` methods
4232 automatically use the engine bound to the :class:`_schema.MetaData`
4233 object, if any.
4234
4235 * The :meth:`_schema.MetaData.create_all` and
4236 :meth:`_schema.MetaData.drop_all`
4237 methods will emit CREATE / DROP for this :class:`.Sequence`,
4238 even if the :class:`.Sequence` is not associated with any
4239 :class:`_schema.Table` / :class:`_schema.Column`
4240 that's a member of this
4241 :class:`_schema.MetaData`.
4242
4243 The above behaviors can only occur if the :class:`.Sequence` is
4244 explicitly associated with the :class:`_schema.MetaData`
4245 via this parameter.
4246
4247 .. seealso::
4248
4249 :ref:`sequence_metadata` - full discussion of the
4250 :paramref:`.Sequence.metadata` parameter.
4251
4252 :param for_update: Indicates this :class:`.Sequence`, when associated
4253 with a :class:`_schema.Column`,
4254 should be invoked for UPDATE statements
4255 on that column's table, rather than for INSERT statements, when
4256 no value is otherwise present for that column in the statement.
4257
4258 """
4259 DefaultGenerator.__init__(self, for_update=for_update)
4260 IdentityOptions.__init__(
4261 self,
4262 start=start,
4263 increment=increment,
4264 minvalue=minvalue,
4265 maxvalue=maxvalue,
4266 nominvalue=nominvalue,
4267 nomaxvalue=nomaxvalue,
4268 cycle=cycle,
4269 cache=cache,
4270 order=order,
4271 **dialect_kw,
4272 )
4273 self.column = None
4274 self.name = quoted_name(name, quote)
4275 self.optional = optional
4276 if schema is BLANK_SCHEMA:
4277 self.schema = schema = None
4278 elif metadata is not None and schema is None and metadata.schema:
4279 self.schema = schema = metadata.schema
4280 else:
4281 self.schema = quoted_name.construct(schema, quote_schema)
4282 self._key = _get_table_key(name, schema)
4283 if data_type is not None:
4284 self.data_type = to_instance(data_type)
4285 else:
4286 self.data_type = None
4287
4288 if metadata:
4289 self._set_metadata(metadata)
4290 else:
4291 self.metadata = None
4292
4293 @util.preload_module("sqlalchemy.sql.functions")
4294 def next_value(self) -> Function[int]:
4295 """Return a :class:`.next_value` function element
4296 which will render the appropriate increment function
4297 for this :class:`.Sequence` within any SQL expression.
4298
4299 """
4300 return util.preloaded.sql_functions.func.next_value(self)
4301
4302 def _copy(self) -> Sequence:
4303 return Sequence(
4304 name=self.name,
4305 schema=self.schema,
4306 data_type=self.data_type,
4307 optional=self.optional,
4308 metadata=self.metadata,
4309 for_update=self.for_update,
4310 **self._as_dict(),
4311 **self.dialect_kwargs,
4312 )
4313
4314 def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
4315 assert isinstance(parent, Column)
4316 super()._set_parent(parent, **kw)
4317 parent._on_table_attach(self._set_table)
4318
4319 def _set_table(self, column: Column[Any], table: Table) -> None:
4320 self._set_metadata(table.metadata)
4321
4322 def _set_metadata(self, metadata: MetaData) -> None:
4323 self.metadata = metadata
4324 self.metadata._register_object(self)
4325 metadata._sequences[self._key] = self
4326
4327 def create(
4328 self,
4329 bind: _CreateDropBind,
4330 checkfirst: Union[bool, CheckFirst] = CheckFirst.SEQUENCES,
4331 ) -> None:
4332 """Creates this sequence in the database."""
4333
4334 bind._run_ddl_visitor(ddl.SchemaGenerator, self, checkfirst=checkfirst)
4335
4336 def drop(
4337 self,
4338 bind: _CreateDropBind,
4339 checkfirst: Union[bool, CheckFirst] = CheckFirst.SEQUENCES,
4340 ) -> None:
4341 """Drops this sequence from the database."""
4342
4343 bind._run_ddl_visitor(ddl.SchemaDropper, self, checkfirst=checkfirst)
4344
4345 def _not_a_column_expr(self) -> NoReturn:
4346 raise exc.InvalidRequestError(
4347 f"This {self.__class__.__name__} cannot be used directly "
4348 "as a column expression. Use func.next_value(sequence) "
4349 "to produce a 'next value' function that's usable "
4350 "as a column element."
4351 )
4352
4353
4354@inspection._self_inspects
4355class FetchedValue(SchemaEventTarget):
4356 """A marker for a transparent database-side default.
4357
4358 Use :class:`.FetchedValue` when the database is configured
4359 to provide some automatic default for a column.
4360
4361 E.g.::
4362
4363 Column("foo", Integer, FetchedValue())
4364
4365 Would indicate that some trigger or default generator
4366 will create a new value for the ``foo`` column during an
4367 INSERT.
4368
4369 .. seealso::
4370
4371 :ref:`triggered_columns`
4372
4373 """
4374
4375 is_server_default = True
4376 reflected = False
4377 has_argument = False
4378 is_clause_element = False
4379 is_identity = False
4380 _is_monotonic_fn = False
4381
4382 column: Optional[Column[Any]]
4383
4384 def __init__(self, for_update: bool = False) -> None:
4385 self.for_update = for_update
4386
4387 def _as_for_update(self, for_update: bool) -> FetchedValue:
4388 if for_update == self.for_update:
4389 return self
4390 else:
4391 return self._clone(for_update)
4392
4393 def _copy(self) -> FetchedValue:
4394 return FetchedValue(self.for_update)
4395
4396 def _clone(self, for_update: bool) -> Self:
4397 n = self.__class__.__new__(self.__class__)
4398 n.__dict__.update(self.__dict__)
4399 n.__dict__.pop("column", None)
4400 n.for_update = for_update
4401 return n
4402
4403 def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
4404 column = parent
4405 assert isinstance(column, Column)
4406 self.column = column
4407 if self.for_update:
4408 self.column.server_onupdate = self
4409 else:
4410 self.column.server_default = self
4411
4412 def __repr__(self) -> str:
4413 return util.generic_repr(self)
4414
4415
4416class DefaultClause(FetchedValue):
4417 """A DDL-specified DEFAULT column value.
4418
4419 :class:`.DefaultClause` is a :class:`.FetchedValue`
4420 that also generates a "DEFAULT" clause when
4421 "CREATE TABLE" is emitted.
4422
4423 :class:`.DefaultClause` is generated automatically
4424 whenever the ``server_default``, ``server_onupdate`` arguments of
4425 :class:`_schema.Column` are used. A :class:`.DefaultClause`
4426 can be passed positionally as well.
4427
4428 For example, the following::
4429
4430 Column("foo", Integer, server_default="50")
4431
4432 Is equivalent to::
4433
4434 Column("foo", Integer, DefaultClause("50"))
4435
4436 """
4437
4438 has_argument = True
4439
4440 def __init__(
4441 self,
4442 arg: Union[str, ClauseElement, TextClause],
4443 for_update: bool = False,
4444 _reflected: bool = False,
4445 ) -> None:
4446 util.assert_arg_type(arg, (str, ClauseElement, TextClause), "arg")
4447 super().__init__(for_update)
4448 self.arg = arg
4449 self.reflected = _reflected
4450
4451 @util.memoized_property
4452 @util.preload_module("sqlalchemy.sql.functions")
4453 def _is_monotonic_fn(self) -> bool:
4454 functions = util.preloaded.sql_functions
4455 return (
4456 isinstance(self.arg, functions.FunctionElement)
4457 and self.arg.monotonic
4458 )
4459
4460 def _copy(self) -> DefaultClause:
4461 return DefaultClause(
4462 arg=self.arg, for_update=self.for_update, _reflected=self.reflected
4463 )
4464
4465 def __repr__(self) -> str:
4466 return "DefaultClause(%r, for_update=%r)" % (self.arg, self.for_update)
4467
4468
4469class Constraint(DialectKWArgs, HasConditionalDDL, SchemaItem):
4470 """A table-level SQL constraint.
4471
4472 :class:`_schema.Constraint` serves as the base class for the series of
4473 constraint objects that can be associated with :class:`_schema.Table`
4474 objects, including :class:`_schema.PrimaryKeyConstraint`,
4475 :class:`_schema.ForeignKeyConstraint`
4476 :class:`_schema.UniqueConstraint`, and
4477 :class:`_schema.CheckConstraint`.
4478
4479 """
4480
4481 __visit_name__ = "constraint"
4482
4483 _creation_order: int
4484 _column_flag: bool
4485
4486 def __init__(
4487 self,
4488 name: _ConstraintNameArgument = None,
4489 deferrable: Optional[bool] = None,
4490 initially: Optional[str] = None,
4491 info: Optional[_InfoType] = None,
4492 comment: Optional[str] = None,
4493 _create_rule: Optional[Any] = None,
4494 _type_bound: bool = False,
4495 **dialect_kw: Any,
4496 ) -> None:
4497 r"""Create a SQL constraint.
4498
4499 :param name:
4500 Optional, the in-database name of this ``Constraint``.
4501
4502 :param deferrable:
4503 Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when
4504 issuing DDL for this constraint.
4505
4506 :param initially:
4507 Optional string. If set, emit INITIALLY <value> when issuing DDL
4508 for this constraint.
4509
4510 :param info: Optional data dictionary which will be populated into the
4511 :attr:`.SchemaItem.info` attribute of this object.
4512
4513 :param comment: Optional string that will render an SQL comment on
4514 foreign key constraint creation.
4515
4516 .. versionadded:: 2.0
4517
4518 :param \**dialect_kw: Additional keyword arguments are dialect
4519 specific, and passed in the form ``<dialectname>_<argname>``. See
4520 the documentation regarding an individual dialect at
4521 :ref:`dialect_toplevel` for detail on documented arguments.
4522
4523 :param _create_rule:
4524 used internally by some datatypes that also create constraints.
4525
4526 :param _type_bound:
4527 used internally to indicate that this constraint is associated with
4528 a specific datatype.
4529
4530 """
4531
4532 self.name = name
4533 self.deferrable = deferrable
4534 self.initially = initially
4535 if info:
4536 self.info = info
4537 self._create_rule = _create_rule
4538 self._type_bound = _type_bound
4539 util.set_creation_order(self)
4540 self._validate_dialect_kwargs(dialect_kw)
4541 self.comment = comment
4542
4543 def _should_create_for_compiler(
4544 self, compiler: DDLCompiler, **kw: Any
4545 ) -> bool:
4546 if self._create_rule is not None and not self._create_rule(compiler):
4547 return False
4548 elif self._ddl_if is not None:
4549 return self._ddl_if._should_execute(
4550 ddl.CreateConstraint(self), self, None, compiler=compiler, **kw
4551 )
4552 else:
4553 return True
4554
4555 @property
4556 def table(self) -> Table:
4557 try:
4558 if isinstance(self.parent, Table):
4559 return self.parent
4560 except AttributeError:
4561 pass
4562 raise exc.InvalidRequestError(
4563 "This constraint is not bound to a table. Did you "
4564 "mean to call table.append_constraint(constraint) ?"
4565 )
4566
4567 def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
4568 assert isinstance(parent, (Table, Column))
4569 self.parent = parent
4570 parent.constraints.add(self)
4571
4572 @util.deprecated(
4573 "1.4",
4574 "The :meth:`_schema.Constraint.copy` method is deprecated "
4575 "and will be removed in a future release.",
4576 )
4577 def copy(self, **kw: Any) -> Self:
4578 return self._copy(**kw)
4579
4580 def _copy(self, **kw: Any) -> Self:
4581 raise NotImplementedError()
4582
4583
4584class ColumnCollectionMixin:
4585 """A :class:`_expression.ColumnCollection` of :class:`_schema.Column`
4586 objects.
4587
4588 This collection represents the columns which are referred to by
4589 this object.
4590
4591 """
4592
4593 _columns: DedupeColumnCollection[Column[Any]]
4594
4595 _allow_multiple_tables = False
4596
4597 _pending_colargs: List[Optional[Union[str, Column[Any]]]]
4598
4599 if TYPE_CHECKING:
4600
4601 def _set_parent_with_dispatch(
4602 self, parent: SchemaEventTarget, **kw: Any
4603 ) -> None: ...
4604
4605 def __init__(
4606 self,
4607 *columns: _DDLColumnArgument,
4608 _autoattach: bool = True,
4609 _column_flag: bool = False,
4610 _gather_expressions: Optional[
4611 List[Union[str, ColumnElement[Any]]]
4612 ] = None,
4613 ) -> None:
4614 self._column_flag = _column_flag
4615 self._columns = DedupeColumnCollection()
4616
4617 processed_expressions: Optional[
4618 List[Union[ColumnElement[Any], str]]
4619 ] = _gather_expressions
4620
4621 if processed_expressions is not None:
4622
4623 # this is expected to be an empty list
4624 assert not processed_expressions
4625
4626 self._pending_colargs = []
4627 for (
4628 expr,
4629 _,
4630 _,
4631 add_element,
4632 ) in coercions.expect_col_expression_collection(
4633 roles.DDLConstraintColumnRole, columns
4634 ):
4635 self._pending_colargs.append(add_element)
4636 processed_expressions.append(expr)
4637 else:
4638 self._pending_colargs = [
4639 coercions.expect(roles.DDLConstraintColumnRole, column)
4640 for column in columns
4641 ]
4642
4643 if _autoattach and self._pending_colargs:
4644 self._check_attach()
4645
4646 def _check_attach(self, evt: bool = False) -> None:
4647 col_objs = [c for c in self._pending_colargs if isinstance(c, Column)]
4648
4649 cols_w_table = [c for c in col_objs if isinstance(c.table, Table)]
4650
4651 cols_wo_table = set(col_objs).difference(cols_w_table)
4652 if cols_wo_table:
4653 # feature #3341 - place event listeners for Column objects
4654 # such that when all those cols are attached, we autoattach.
4655 assert not evt, "Should not reach here on event call"
4656
4657 # issue #3411 - don't do the per-column auto-attach if some of the
4658 # columns are specified as strings.
4659 has_string_cols = {
4660 c for c in self._pending_colargs if c is not None
4661 }.difference(col_objs)
4662 if not has_string_cols:
4663
4664 def _col_attached(column: Column[Any], table: Table) -> None:
4665 # this isinstance() corresponds with the
4666 # isinstance() above; only want to count Table-bound
4667 # columns
4668 if isinstance(table, Table):
4669 cols_wo_table.discard(column)
4670 if not cols_wo_table:
4671 self._check_attach(evt=True)
4672
4673 self._cols_wo_table = cols_wo_table
4674 for col in cols_wo_table:
4675 col._on_table_attach(_col_attached)
4676 return
4677
4678 columns = cols_w_table
4679
4680 tables = {c.table for c in columns}
4681 if len(tables) == 1:
4682 self._set_parent_with_dispatch(tables.pop())
4683 elif len(tables) > 1 and not self._allow_multiple_tables:
4684 table = columns[0].table
4685 others = [c for c in columns[1:] if c.table is not table]
4686 if others:
4687 # black could not format this inline
4688 other_str = ", ".join("'%s'" % c for c in others)
4689 raise exc.ArgumentError(
4690 f"Column(s) {other_str} "
4691 f"are not part of table '{table.description}'."
4692 )
4693
4694 @util.ro_memoized_property
4695 def columns(self) -> ReadOnlyColumnCollection[str, Column[Any]]:
4696 return self._columns.as_readonly()
4697
4698 @util.ro_memoized_property
4699 def c(self) -> ReadOnlyColumnCollection[str, Column[Any]]:
4700 return self._columns.as_readonly()
4701
4702 def _col_expressions(
4703 self, parent: Union[Table, Column[Any]]
4704 ) -> List[Optional[Column[Any]]]:
4705 if isinstance(parent, Column):
4706 result: List[Optional[Column[Any]]] = [
4707 c for c in self._pending_colargs if isinstance(c, Column)
4708 ]
4709 assert len(result) == len(self._pending_colargs)
4710 return result
4711 else:
4712 try:
4713 return [
4714 parent.c[col] if isinstance(col, str) else col
4715 for col in self._pending_colargs
4716 ]
4717 except KeyError as ke:
4718 raise exc.ConstraintColumnNotFoundError(
4719 f"Can't create {self.__class__.__name__} "
4720 f"on table '{parent.description}': no column "
4721 f"named '{ke.args[0]}' is present."
4722 ) from ke
4723
4724 def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
4725 assert isinstance(parent, (Table, Column))
4726
4727 for col in self._col_expressions(parent):
4728 if col is not None:
4729 self._columns.add(col)
4730
4731
4732class ColumnCollectionConstraint(ColumnCollectionMixin, Constraint):
4733 """A constraint that proxies a ColumnCollection."""
4734
4735 def __init__(
4736 self,
4737 *columns: _DDLColumnArgument,
4738 name: _ConstraintNameArgument = None,
4739 deferrable: Optional[bool] = None,
4740 initially: Optional[str] = None,
4741 info: Optional[_InfoType] = None,
4742 _autoattach: bool = True,
4743 _column_flag: bool = False,
4744 _gather_expressions: Optional[List[_DDLColumnArgument]] = None,
4745 **dialect_kw: Any,
4746 ) -> None:
4747 r"""
4748 :param \*columns:
4749 A sequence of column names or Column objects.
4750
4751 :param name:
4752 Optional, the in-database name of this constraint.
4753
4754 :param deferrable:
4755 Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when
4756 issuing DDL for this constraint.
4757
4758 :param initially:
4759 Optional string. If set, emit INITIALLY <value> when issuing DDL
4760 for this constraint.
4761
4762 :param \**dialect_kw: other keyword arguments including
4763 dialect-specific arguments are propagated to the :class:`.Constraint`
4764 superclass.
4765
4766 """
4767 Constraint.__init__(
4768 self,
4769 name=name,
4770 deferrable=deferrable,
4771 initially=initially,
4772 info=info,
4773 **dialect_kw,
4774 )
4775 ColumnCollectionMixin.__init__(
4776 self, *columns, _autoattach=_autoattach, _column_flag=_column_flag
4777 )
4778
4779 columns: ReadOnlyColumnCollection[str, Column[Any]]
4780 """A :class:`_expression.ColumnCollection` representing the set of columns
4781 for this constraint.
4782
4783 """
4784
4785 def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
4786 assert isinstance(parent, (Column, Table))
4787 Constraint._set_parent(self, parent)
4788 ColumnCollectionMixin._set_parent(self, parent)
4789
4790 def __contains__(self, x: Any) -> bool:
4791 return x in self._columns
4792
4793 @util.deprecated(
4794 "1.4",
4795 "The :meth:`_schema.ColumnCollectionConstraint.copy` method "
4796 "is deprecated and will be removed in a future release.",
4797 )
4798 def copy(
4799 self,
4800 *,
4801 target_table: Optional[Table] = None,
4802 **kw: Any,
4803 ) -> ColumnCollectionConstraint:
4804 return self._copy(target_table=target_table, **kw)
4805
4806 def _copy(
4807 self,
4808 *,
4809 target_table: Optional[Table] = None,
4810 **kw: Any,
4811 ) -> ColumnCollectionConstraint:
4812 # ticket #5276
4813 constraint_kwargs = {}
4814 for dialect_name in self.dialect_options:
4815 dialect_options = self.dialect_options[dialect_name]._non_defaults
4816 for (
4817 dialect_option_key,
4818 dialect_option_value,
4819 ) in dialect_options.items():
4820 constraint_kwargs[dialect_name + "_" + dialect_option_key] = (
4821 dialect_option_value
4822 )
4823
4824 assert isinstance(self.parent, Table)
4825 c = self.__class__(
4826 name=self.name,
4827 deferrable=self.deferrable,
4828 initially=self.initially,
4829 *[
4830 _copy_expression(expr, self.parent, target_table)
4831 for expr in self._columns
4832 ],
4833 comment=self.comment,
4834 **constraint_kwargs,
4835 )
4836 return self._schema_item_copy(c)
4837
4838 def contains_column(self, col: Column[Any]) -> bool:
4839 """Return True if this constraint contains the given column.
4840
4841 Note that this object also contains an attribute ``.columns``
4842 which is a :class:`_expression.ColumnCollection` of
4843 :class:`_schema.Column` objects.
4844
4845 """
4846
4847 return self._columns.contains_column(col)
4848
4849 def __iter__(self) -> Iterator[Column[Any]]:
4850 return iter(self._columns)
4851
4852 def __len__(self) -> int:
4853 return len(self._columns)
4854
4855
4856class CheckConstraint(ColumnCollectionConstraint):
4857 """A table- or column-level CHECK constraint.
4858
4859 Can be included in the definition of a Table or Column.
4860 """
4861
4862 _allow_multiple_tables = True
4863
4864 __visit_name__ = "table_or_column_check_constraint"
4865
4866 @_document_text_coercion(
4867 "sqltext",
4868 ":class:`.CheckConstraint`",
4869 ":paramref:`.CheckConstraint.sqltext`",
4870 )
4871 def __init__(
4872 self,
4873 sqltext: _TextCoercedExpressionArgument[Any],
4874 name: _ConstraintNameArgument = None,
4875 deferrable: Optional[bool] = None,
4876 initially: Optional[str] = None,
4877 table: Optional[Table] = None,
4878 info: Optional[_InfoType] = None,
4879 _create_rule: Optional[Any] = None,
4880 _autoattach: bool = True,
4881 _type_bound: bool = False,
4882 **dialect_kw: Any,
4883 ) -> None:
4884 r"""Construct a CHECK constraint.
4885
4886 :param sqltext:
4887 A string containing the constraint definition, which will be used
4888 verbatim, or a SQL expression construct. If given as a string,
4889 the object is converted to a :func:`_expression.text` object.
4890 If the textual
4891 string includes a colon character, escape this using a backslash::
4892
4893 CheckConstraint(r"foo ~ E'a(?\:b|c)d")
4894
4895 :param name:
4896 Optional, the in-database name of the constraint.
4897
4898 :param deferrable:
4899 Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when
4900 issuing DDL for this constraint.
4901
4902 :param initially:
4903 Optional string. If set, emit INITIALLY <value> when issuing DDL
4904 for this constraint.
4905
4906 :param info: Optional data dictionary which will be populated into the
4907 :attr:`.SchemaItem.info` attribute of this object.
4908
4909 """
4910
4911 self.sqltext = coercions.expect(roles.DDLExpressionRole, sqltext)
4912 columns: List[Column[Any]] = []
4913 visitors.traverse(self.sqltext, {}, {"column": columns.append})
4914
4915 super().__init__(
4916 name=name,
4917 deferrable=deferrable,
4918 initially=initially,
4919 _create_rule=_create_rule,
4920 info=info,
4921 _type_bound=_type_bound,
4922 _autoattach=_autoattach,
4923 *columns,
4924 **dialect_kw,
4925 )
4926 if table is not None:
4927 self._set_parent_with_dispatch(table)
4928
4929 @property
4930 def is_column_level(self) -> bool:
4931 return not isinstance(self.parent, Table)
4932
4933 @util.deprecated(
4934 "1.4",
4935 "The :meth:`_schema.CheckConstraint.copy` method is deprecated "
4936 "and will be removed in a future release.",
4937 )
4938 def copy(
4939 self, *, target_table: Optional[Table] = None, **kw: Any
4940 ) -> CheckConstraint:
4941 return self._copy(target_table=target_table, **kw)
4942
4943 def _copy(
4944 self, *, target_table: Optional[Table] = None, **kw: Any
4945 ) -> CheckConstraint:
4946 if target_table is not None:
4947 # note that target_table is None for the copy process of
4948 # a column-bound CheckConstraint, so this path is not reached
4949 # in that case.
4950 sqltext = _copy_expression(self.sqltext, self.table, target_table)
4951 else:
4952 sqltext = self.sqltext
4953 c = CheckConstraint(
4954 sqltext,
4955 name=self.name,
4956 initially=self.initially,
4957 deferrable=self.deferrable,
4958 _create_rule=self._create_rule,
4959 table=target_table,
4960 comment=self.comment,
4961 _autoattach=False,
4962 _type_bound=self._type_bound,
4963 )
4964 return self._schema_item_copy(c)
4965
4966
4967class ForeignKeyConstraint(ColumnCollectionConstraint):
4968 """A table-level FOREIGN KEY constraint.
4969
4970 Defines a single column or composite FOREIGN KEY ... REFERENCES
4971 constraint. For a no-frills, single column foreign key, adding a
4972 :class:`_schema.ForeignKey` to the definition of a :class:`_schema.Column`
4973 is a
4974 shorthand equivalent for an unnamed, single column
4975 :class:`_schema.ForeignKeyConstraint`.
4976
4977 Examples of foreign key configuration are in :ref:`metadata_foreignkeys`.
4978
4979 """
4980
4981 __visit_name__ = "foreign_key_constraint"
4982
4983 def __init__(
4984 self,
4985 columns: _typing_Sequence[_DDLColumnArgument],
4986 refcolumns: _typing_Sequence[_DDLColumnReferenceArgument],
4987 name: _ConstraintNameArgument = None,
4988 onupdate: Optional[str] = None,
4989 ondelete: Optional[str] = None,
4990 deferrable: Optional[bool] = None,
4991 initially: Optional[str] = None,
4992 use_alter: bool = False,
4993 link_to_name: bool = False,
4994 match: Optional[str] = None,
4995 table: Optional[Table] = None,
4996 info: Optional[_InfoType] = None,
4997 comment: Optional[str] = None,
4998 **dialect_kw: Any,
4999 ) -> None:
5000 r"""Construct a composite-capable FOREIGN KEY.
5001
5002 :param columns: A sequence of local column names. The named columns
5003 must be defined and present in the parent Table. The names should
5004 match the ``key`` given to each column (defaults to the name) unless
5005 ``link_to_name`` is True.
5006
5007 :param refcolumns: A sequence of foreign column names or Column
5008 objects. The columns must all be located within the same Table.
5009
5010 :param name: Optional, the in-database name of the key.
5011
5012 :param onupdate: Optional string. If set, emit ON UPDATE <value> when
5013 issuing DDL for this constraint. Typical values include CASCADE,
5014 DELETE and RESTRICT.
5015
5016 .. seealso::
5017
5018 :ref:`on_update_on_delete`
5019
5020 :param ondelete: Optional string. If set, emit ON DELETE <value> when
5021 issuing DDL for this constraint. Typical values include CASCADE,
5022 SET NULL and RESTRICT. Some dialects may allow for additional
5023 syntaxes.
5024
5025 .. seealso::
5026
5027 :ref:`on_update_on_delete`
5028
5029 :param deferrable: Optional bool. If set, emit DEFERRABLE or NOT
5030 DEFERRABLE when issuing DDL for this constraint.
5031
5032 :param initially: Optional string. If set, emit INITIALLY <value> when
5033 issuing DDL for this constraint.
5034
5035 :param link_to_name: if True, the string name given in ``column`` is
5036 the rendered name of the referenced column, not its locally assigned
5037 ``key``.
5038
5039 :param use_alter: If True, do not emit the DDL for this constraint as
5040 part of the CREATE TABLE definition. Instead, generate it via an
5041 ALTER TABLE statement issued after the full collection of tables
5042 have been created, and drop it via an ALTER TABLE statement before
5043 the full collection of tables are dropped.
5044
5045 The use of :paramref:`_schema.ForeignKeyConstraint.use_alter` is
5046 particularly geared towards the case where two or more tables
5047 are established within a mutually-dependent foreign key constraint
5048 relationship; however, the :meth:`_schema.MetaData.create_all` and
5049 :meth:`_schema.MetaData.drop_all`
5050 methods will perform this resolution
5051 automatically, so the flag is normally not needed.
5052
5053 .. seealso::
5054
5055 :ref:`use_alter`
5056
5057 :param match: Optional string. If set, emit MATCH <value> when issuing
5058 DDL for this constraint. Typical values include SIMPLE, PARTIAL
5059 and FULL.
5060
5061 :param info: Optional data dictionary which will be populated into the
5062 :attr:`.SchemaItem.info` attribute of this object.
5063
5064 :param comment: Optional string that will render an SQL comment on
5065 foreign key constraint creation.
5066
5067 .. versionadded:: 2.0
5068
5069 :param \**dialect_kw: Additional keyword arguments are dialect
5070 specific, and passed in the form ``<dialectname>_<argname>``. See
5071 the documentation regarding an individual dialect at
5072 :ref:`dialect_toplevel` for detail on documented arguments.
5073
5074 """
5075
5076 Constraint.__init__(
5077 self,
5078 name=name,
5079 deferrable=deferrable,
5080 initially=initially,
5081 info=info,
5082 comment=comment,
5083 **dialect_kw,
5084 )
5085 self.onupdate = onupdate
5086 self.ondelete = ondelete
5087 self.link_to_name = link_to_name
5088 self.use_alter = use_alter
5089 self.match = match
5090
5091 if len(set(columns)) != len(refcolumns):
5092 if len(set(columns)) != len(columns):
5093 # e.g. FOREIGN KEY (a, a) REFERENCES r (b, c)
5094 raise exc.ArgumentError(
5095 "ForeignKeyConstraint with duplicate source column "
5096 "references are not supported."
5097 )
5098 else:
5099 # e.g. FOREIGN KEY (a) REFERENCES r (b, c)
5100 # paraphrasing
5101 # https://www.postgresql.org/docs/current/static/ddl-constraints.html
5102 raise exc.ArgumentError(
5103 "ForeignKeyConstraint number "
5104 "of constrained columns must match the number of "
5105 "referenced columns."
5106 )
5107
5108 # standalone ForeignKeyConstraint - create
5109 # associated ForeignKey objects which will be applied to hosted
5110 # Column objects (in col.foreign_keys), either now or when attached
5111 # to the Table for string-specified names
5112 self.elements = [
5113 ForeignKey(
5114 refcol,
5115 _constraint=self,
5116 name=self.name,
5117 onupdate=self.onupdate,
5118 ondelete=self.ondelete,
5119 use_alter=self.use_alter,
5120 link_to_name=self.link_to_name,
5121 match=self.match,
5122 deferrable=self.deferrable,
5123 initially=self.initially,
5124 **self.dialect_kwargs,
5125 )
5126 for refcol in refcolumns
5127 ]
5128
5129 ColumnCollectionMixin.__init__(self, *columns)
5130 if table is not None:
5131 if hasattr(self, "parent"):
5132 assert table is self.parent
5133 self._set_parent_with_dispatch(table)
5134
5135 def _append_element(self, column: Column[Any], fk: ForeignKey) -> None:
5136 self._columns.add(column)
5137 self.elements.append(fk)
5138
5139 columns: ReadOnlyColumnCollection[str, Column[Any]]
5140 """A :class:`_expression.ColumnCollection` representing the set of columns
5141 for this constraint.
5142
5143 """
5144
5145 elements: List[ForeignKey]
5146 """A sequence of :class:`_schema.ForeignKey` objects.
5147
5148 Each :class:`_schema.ForeignKey`
5149 represents a single referring column/referred
5150 column pair.
5151
5152 This collection is intended to be read-only.
5153
5154 """
5155
5156 @property
5157 def _elements(self) -> util.OrderedDict[str, ForeignKey]:
5158 # legacy - provide a dictionary view of (column_key, fk)
5159 return util.OrderedDict(zip(self.column_keys, self.elements))
5160
5161 @property
5162 def _referred_schema(self) -> Optional[str]:
5163 for elem in self.elements:
5164 return elem._referred_schema
5165 else:
5166 return None
5167
5168 @property
5169 def referred_table(self) -> Table:
5170 """The :class:`_schema.Table` object to which this
5171 :class:`_schema.ForeignKeyConstraint` references.
5172
5173 This is a dynamically calculated attribute which may not be available
5174 if the constraint and/or parent table is not yet associated with
5175 a metadata collection that contains the referred table.
5176
5177 """
5178 return self.elements[0].column.table
5179
5180 def _validate_dest_table(self, table: Table) -> None:
5181 table_keys = {
5182 elem._table_key_within_construction() for elem in self.elements
5183 }
5184 if None not in table_keys and len(table_keys) > 1:
5185 elem0, elem1 = sorted(table_keys)[0:2]
5186 raise exc.ArgumentError(
5187 f"ForeignKeyConstraint on "
5188 f"{table.fullname}({self._col_description}) refers to "
5189 f"multiple remote tables: {elem0} and {elem1}"
5190 )
5191
5192 @property
5193 def column_keys(self) -> _typing_Sequence[str]:
5194 """Return a list of string keys representing the local
5195 columns in this :class:`_schema.ForeignKeyConstraint`.
5196
5197 This list is either the original string arguments sent
5198 to the constructor of the :class:`_schema.ForeignKeyConstraint`,
5199 or if the constraint has been initialized with :class:`_schema.Column`
5200 objects, is the string ``.key`` of each element.
5201
5202 """
5203 if hasattr(self, "parent"):
5204 return self._columns.keys()
5205 else:
5206 return [
5207 col.key if isinstance(col, ColumnElement) else str(col)
5208 for col in self._pending_colargs
5209 ]
5210
5211 @property
5212 def _col_description(self) -> str:
5213 return ", ".join(self.column_keys)
5214
5215 def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
5216 table = parent
5217 assert isinstance(table, Table)
5218 Constraint._set_parent(self, table)
5219
5220 ColumnCollectionConstraint._set_parent(self, table)
5221
5222 for col, fk in zip(self._columns, self.elements):
5223 if not hasattr(fk, "parent") or fk.parent is not col:
5224 fk._set_parent_with_dispatch(col)
5225
5226 self._validate_dest_table(table)
5227
5228 @util.deprecated(
5229 "1.4",
5230 "The :meth:`_schema.ForeignKeyConstraint.copy` method is deprecated "
5231 "and will be removed in a future release.",
5232 )
5233 def copy(
5234 self,
5235 *,
5236 schema: Optional[str] = None,
5237 target_table: Optional[Table] = None,
5238 **kw: Any,
5239 ) -> ForeignKeyConstraint:
5240 return self._copy(schema=schema, target_table=target_table, **kw)
5241
5242 def _copy(
5243 self,
5244 *,
5245 schema: Optional[str] = None,
5246 target_table: Optional[Table] = None,
5247 **kw: Any,
5248 ) -> ForeignKeyConstraint:
5249 fkc = ForeignKeyConstraint(
5250 [x.parent.key for x in self.elements],
5251 [
5252 x._get_colspec(
5253 schema=schema,
5254 table_name=(
5255 target_table.name
5256 if target_table is not None
5257 and x._table_key_within_construction()
5258 == x.parent.table.key
5259 else None
5260 ),
5261 _is_copy=True,
5262 )
5263 for x in self.elements
5264 ],
5265 name=self.name,
5266 onupdate=self.onupdate,
5267 ondelete=self.ondelete,
5268 use_alter=self.use_alter,
5269 deferrable=self.deferrable,
5270 initially=self.initially,
5271 link_to_name=self.link_to_name,
5272 match=self.match,
5273 comment=self.comment,
5274 )
5275 for self_fk, other_fk in zip(self.elements, fkc.elements):
5276 self_fk._schema_item_copy(other_fk)
5277 return self._schema_item_copy(fkc)
5278
5279
5280class PrimaryKeyConstraint(ColumnCollectionConstraint):
5281 """A table-level PRIMARY KEY constraint.
5282
5283 The :class:`.PrimaryKeyConstraint` object is present automatically
5284 on any :class:`_schema.Table` object; it is assigned a set of
5285 :class:`_schema.Column` objects corresponding to those marked with
5286 the :paramref:`_schema.Column.primary_key` flag::
5287
5288 >>> my_table = Table(
5289 ... "mytable",
5290 ... metadata,
5291 ... Column("id", Integer, primary_key=True),
5292 ... Column("version_id", Integer, primary_key=True),
5293 ... Column("data", String(50)),
5294 ... )
5295 >>> my_table.primary_key
5296 PrimaryKeyConstraint(
5297 Column('id', Integer(), table=<mytable>,
5298 primary_key=True, nullable=False),
5299 Column('version_id', Integer(), table=<mytable>,
5300 primary_key=True, nullable=False)
5301 )
5302
5303 The primary key of a :class:`_schema.Table` can also be specified by using
5304 a :class:`.PrimaryKeyConstraint` object explicitly; in this mode of usage,
5305 the "name" of the constraint can also be specified, as well as other
5306 options which may be recognized by dialects::
5307
5308 my_table = Table(
5309 "mytable",
5310 metadata,
5311 Column("id", Integer),
5312 Column("version_id", Integer),
5313 Column("data", String(50)),
5314 PrimaryKeyConstraint("id", "version_id", name="mytable_pk"),
5315 )
5316
5317 The two styles of column-specification should generally not be mixed.
5318 An warning is emitted if the columns present in the
5319 :class:`.PrimaryKeyConstraint`
5320 don't match the columns that were marked as ``primary_key=True``, if both
5321 are present; in this case, the columns are taken strictly from the
5322 :class:`.PrimaryKeyConstraint` declaration, and those columns otherwise
5323 marked as ``primary_key=True`` are ignored. This behavior is intended to
5324 be backwards compatible with previous behavior.
5325
5326 For the use case where specific options are to be specified on the
5327 :class:`.PrimaryKeyConstraint`, but the usual style of using
5328 ``primary_key=True`` flags is still desirable, an empty
5329 :class:`.PrimaryKeyConstraint` may be specified, which will take on the
5330 primary key column collection from the :class:`_schema.Table` based on the
5331 flags::
5332
5333 my_table = Table(
5334 "mytable",
5335 metadata,
5336 Column("id", Integer, primary_key=True),
5337 Column("version_id", Integer, primary_key=True),
5338 Column("data", String(50)),
5339 PrimaryKeyConstraint(name="mytable_pk", mssql_clustered=True),
5340 )
5341
5342 """
5343
5344 __visit_name__ = "primary_key_constraint"
5345
5346 def __init__(
5347 self,
5348 *columns: _DDLColumnArgument,
5349 name: Optional[str] = None,
5350 deferrable: Optional[bool] = None,
5351 initially: Optional[str] = None,
5352 info: Optional[_InfoType] = None,
5353 _implicit_generated: bool = False,
5354 **dialect_kw: Any,
5355 ) -> None:
5356 self._implicit_generated = _implicit_generated
5357 super().__init__(
5358 *columns,
5359 name=name,
5360 deferrable=deferrable,
5361 initially=initially,
5362 info=info,
5363 **dialect_kw,
5364 )
5365
5366 def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
5367 table = parent
5368 assert isinstance(table, Table)
5369 super()._set_parent(table)
5370
5371 if table.primary_key is not self:
5372 table.constraints.discard(table.primary_key)
5373 table.primary_key = self # type: ignore
5374 table.constraints.add(self)
5375
5376 table_pks = [c for c in table.c if c.primary_key]
5377 if (
5378 self._columns
5379 and table_pks
5380 and set(table_pks) != set(self._columns)
5381 ):
5382 # black could not format these inline
5383 table_pk_str = ", ".join("'%s'" % c.name for c in table_pks)
5384 col_str = ", ".join("'%s'" % c.name for c in self._columns)
5385
5386 util.warn(
5387 f"Table '{table.name}' specifies columns "
5388 f"{table_pk_str} as "
5389 f"primary_key=True, "
5390 f"not matching locally specified columns {col_str}; "
5391 f"setting the "
5392 f"current primary key columns to "
5393 f"{col_str}. "
5394 f"This warning "
5395 f"may become an exception in a future release"
5396 )
5397 table_pks[:] = []
5398
5399 for c in self._columns:
5400 c.primary_key = True
5401 if c._user_defined_nullable is NULL_UNSPECIFIED:
5402 c.nullable = False
5403 if table_pks:
5404 self._columns.extend(table_pks)
5405
5406 def _reload(self, columns: Iterable[Column[Any]]) -> None:
5407 """repopulate this :class:`.PrimaryKeyConstraint` given
5408 a set of columns.
5409
5410 Existing columns in the table that are marked as primary_key=True
5411 are maintained.
5412
5413 Also fires a new event.
5414
5415 This is basically like putting a whole new
5416 :class:`.PrimaryKeyConstraint` object on the parent
5417 :class:`_schema.Table` object without actually replacing the object.
5418
5419 The ordering of the given list of columns is also maintained; these
5420 columns will be appended to the list of columns after any which
5421 are already present.
5422
5423 """
5424 # set the primary key flag on new columns.
5425 # note any existing PK cols on the table also have their
5426 # flag still set.
5427 for col in columns:
5428 col.primary_key = True
5429
5430 self._columns.extend(columns)
5431
5432 PrimaryKeyConstraint._autoincrement_column._reset(self) # type: ignore
5433 self._set_parent_with_dispatch(self.table)
5434
5435 def _replace(self, col: Column[Any]) -> None:
5436 PrimaryKeyConstraint._autoincrement_column._reset(self) # type: ignore
5437 self._columns.replace(col)
5438
5439 self.dispatch._sa_event_column_added_to_pk_constraint(self, col)
5440
5441 @property
5442 def columns_autoinc_first(self) -> List[Column[Any]]:
5443 autoinc = self._autoincrement_column
5444
5445 if autoinc is not None:
5446 return [autoinc] + [c for c in self._columns if c is not autoinc]
5447 else:
5448 return list(self._columns)
5449
5450 @util.ro_memoized_property
5451 def _autoincrement_column(self) -> Optional[Column[int]]:
5452 def _validate_autoinc(col: Column[Any], autoinc_true: bool) -> bool:
5453 if col.type._type_affinity is not None and issubclass(
5454 col.type._type_affinity, type_api.NUMERICTYPE._type_affinity
5455 ):
5456 scale = col.type.scale # type: ignore[attr-defined]
5457 if scale != 0 and autoinc_true:
5458 raise exc.ArgumentError(
5459 f"Column type {col.type} with non-zero scale "
5460 f"{scale} on column '{col}' is not "
5461 f"compatible with autoincrement=True"
5462 )
5463 elif not autoinc_true:
5464 return False
5465 elif col.type._type_affinity is None or not issubclass(
5466 col.type._type_affinity, type_api.INTEGERTYPE._type_affinity
5467 ):
5468 if autoinc_true:
5469 raise exc.ArgumentError(
5470 f"Column type {col.type} on column '{col}' is not "
5471 f"compatible with autoincrement=True"
5472 )
5473 else:
5474 return False
5475 elif (
5476 col.default is not None
5477 and not isinstance(col.default, Sequence)
5478 and not autoinc_true
5479 ):
5480 return False
5481 elif (
5482 col.server_default is not None
5483 and not isinstance(col.server_default, Identity)
5484 and not autoinc_true
5485 ):
5486 return False
5487 elif col.foreign_keys and col.autoincrement not in (
5488 True,
5489 "ignore_fk",
5490 ):
5491 return False
5492 return True
5493
5494 if len(self._columns) == 1:
5495 col = list(self._columns)[0]
5496
5497 if col.autoincrement is True:
5498 _validate_autoinc(col, True)
5499 return col
5500 elif col.autoincrement in (
5501 "auto",
5502 "ignore_fk",
5503 ) and _validate_autoinc(col, False):
5504 return col
5505 else:
5506 return None
5507
5508 else:
5509 autoinc = None
5510 for col in self._columns:
5511 if col.autoincrement is True:
5512 _validate_autoinc(col, True)
5513 if autoinc is not None:
5514 raise exc.ArgumentError(
5515 f"Only one Column may be marked "
5516 f"autoincrement=True, found both "
5517 f"{col.name} and {autoinc.name}."
5518 )
5519 else:
5520 autoinc = col
5521
5522 return autoinc
5523
5524
5525class UniqueConstraint(ColumnCollectionConstraint):
5526 """A table-level UNIQUE constraint.
5527
5528 Defines a single column or composite UNIQUE constraint. For a no-frills,
5529 single column constraint, adding ``unique=True`` to the ``Column``
5530 definition is a shorthand equivalent for an unnamed, single column
5531 UniqueConstraint.
5532 """
5533
5534 __visit_name__ = "unique_constraint"
5535
5536
5537class Index(
5538 DialectKWArgs, ColumnCollectionMixin, HasConditionalDDL, SchemaItem
5539):
5540 """A table-level INDEX.
5541
5542 Defines a composite (one or more column) INDEX.
5543
5544 E.g.::
5545
5546 sometable = Table(
5547 "sometable",
5548 metadata,
5549 Column("name", String(50)),
5550 Column("address", String(100)),
5551 )
5552
5553 Index("some_index", sometable.c.name)
5554
5555 For a no-frills, single column index, adding
5556 :class:`_schema.Column` also supports ``index=True``::
5557
5558 sometable = Table(
5559 "sometable", metadata, Column("name", String(50), index=True)
5560 )
5561
5562 For a composite index, multiple columns can be specified::
5563
5564 Index("some_index", sometable.c.name, sometable.c.address)
5565
5566 Functional indexes are supported as well, typically by using the
5567 :data:`.func` construct in conjunction with table-bound
5568 :class:`_schema.Column` objects::
5569
5570 Index("some_index", func.lower(sometable.c.name))
5571
5572 An :class:`.Index` can also be manually associated with a
5573 :class:`_schema.Table`,
5574 either through inline declaration or using
5575 :meth:`_schema.Table.append_constraint`. When this approach is used,
5576 the names
5577 of the indexed columns can be specified as strings::
5578
5579 Table(
5580 "sometable",
5581 metadata,
5582 Column("name", String(50)),
5583 Column("address", String(100)),
5584 Index("some_index", "name", "address"),
5585 )
5586
5587 To support functional or expression-based indexes in this form, the
5588 :func:`_expression.text` construct may be used::
5589
5590 from sqlalchemy import text
5591
5592 Table(
5593 "sometable",
5594 metadata,
5595 Column("name", String(50)),
5596 Column("address", String(100)),
5597 Index("some_index", text("lower(name)")),
5598 )
5599
5600 .. seealso::
5601
5602 :ref:`schema_indexes` - General information on :class:`.Index`.
5603
5604 :ref:`postgresql_indexes` - PostgreSQL-specific options available for
5605 the :class:`.Index` construct.
5606
5607 :ref:`mysql_indexes` - MySQL-specific options available for the
5608 :class:`.Index` construct.
5609
5610 :ref:`mssql_indexes` - MSSQL-specific options available for the
5611 :class:`.Index` construct.
5612
5613 """
5614
5615 __visit_name__ = "index"
5616
5617 table: Optional[Table]
5618 expressions: _typing_Sequence[Union[str, ColumnElement[Any]]]
5619 _table_bound_expressions: _typing_Sequence[ColumnElement[Any]]
5620
5621 def __init__(
5622 self,
5623 name: Optional[str],
5624 *expressions: _DDLColumnArgument,
5625 unique: bool = False,
5626 quote: Optional[bool] = None,
5627 info: Optional[_InfoType] = None,
5628 _table: Optional[Table] = None,
5629 _column_flag: bool = False,
5630 **dialect_kw: Any,
5631 ) -> None:
5632 r"""Construct an index object.
5633
5634 :param name:
5635 The name of the index
5636
5637 :param \*expressions:
5638 Column expressions to include in the index. The expressions
5639 are normally instances of :class:`_schema.Column`, but may also
5640 be arbitrary SQL expressions which ultimately refer to a
5641 :class:`_schema.Column`.
5642
5643 :param unique=False:
5644 Keyword only argument; if True, create a unique index.
5645
5646 :param quote=None:
5647 Keyword only argument; whether to apply quoting to the name of
5648 the index. Works in the same manner as that of
5649 :paramref:`_schema.Column.quote`.
5650
5651 :param info=None: Optional data dictionary which will be populated
5652 into the :attr:`.SchemaItem.info` attribute of this object.
5653
5654 :param \**dialect_kw: Additional keyword arguments not mentioned above
5655 are dialect specific, and passed in the form
5656 ``<dialectname>_<argname>``. See the documentation regarding an
5657 individual dialect at :ref:`dialect_toplevel` for detail on
5658 documented arguments.
5659
5660 """
5661 self.table = table = None
5662
5663 self.name = quoted_name.construct(name, quote)
5664 self.unique = unique
5665 if info is not None:
5666 self.info = info
5667
5668 # TODO: consider "table" argument being public, but for
5669 # the purpose of the fix here, it starts as private.
5670 if _table is not None:
5671 table = _table
5672
5673 self._validate_dialect_kwargs(dialect_kw)
5674
5675 self.expressions = []
5676 # will call _set_parent() if table-bound column
5677 # objects are present
5678 ColumnCollectionMixin.__init__(
5679 self,
5680 *expressions,
5681 _column_flag=_column_flag,
5682 _gather_expressions=self.expressions,
5683 )
5684 if table is not None:
5685 self._set_parent(table)
5686
5687 def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
5688 table = parent
5689 assert isinstance(table, Table)
5690 ColumnCollectionMixin._set_parent(self, table)
5691
5692 if self.table is not None and table is not self.table:
5693 raise exc.ArgumentError(
5694 f"Index '{self.name}' is against table "
5695 f"'{self.table.description}', and "
5696 f"cannot be associated with table '{table.description}'."
5697 )
5698 self.table = table
5699 table.indexes.add(self)
5700
5701 expressions = self.expressions
5702 col_expressions = self._col_expressions(table)
5703 assert len(expressions) == len(col_expressions)
5704
5705 exprs = []
5706 for expr, colexpr in zip(expressions, col_expressions):
5707 if isinstance(expr, ClauseElement):
5708 exprs.append(expr)
5709 elif colexpr is not None:
5710 exprs.append(colexpr)
5711 else:
5712 assert False
5713 self.expressions = self._table_bound_expressions = exprs
5714
5715 def create(
5716 self,
5717 bind: _CreateDropBind,
5718 checkfirst: Union[bool, CheckFirst] = CheckFirst.NONE,
5719 ) -> None:
5720 """Issue a ``CREATE`` statement for this
5721 :class:`.Index`, using the given
5722 :class:`.Connection` or :class:`.Engine`` for connectivity.
5723
5724 .. seealso::
5725
5726 :meth:`_schema.MetaData.create_all`.
5727
5728 """
5729 bind._run_ddl_visitor(ddl.SchemaGenerator, self, checkfirst=checkfirst)
5730
5731 def drop(
5732 self,
5733 bind: _CreateDropBind,
5734 checkfirst: Union[bool, CheckFirst] = CheckFirst.NONE,
5735 ) -> None:
5736 """Issue a ``DROP`` statement for this
5737 :class:`.Index`, using the given
5738 :class:`.Connection` or :class:`.Engine` for connectivity.
5739
5740 .. seealso::
5741
5742 :meth:`_schema.MetaData.drop_all`.
5743
5744 """
5745 bind._run_ddl_visitor(ddl.SchemaDropper, self, checkfirst=checkfirst)
5746
5747 def __repr__(self) -> str:
5748 exprs: _typing_Sequence[Any] # noqa: F842
5749
5750 return "Index(%s)" % (
5751 ", ".join(
5752 [repr(self.name)]
5753 + [repr(e) for e in self.expressions]
5754 + (self.unique and ["unique=True"] or [])
5755 )
5756 )
5757
5758
5759_NamingSchemaCallable = Union[
5760 Callable[[Constraint, Table], str],
5761 Callable[[Index, Table], str],
5762]
5763_NamingSchemaDirective = Union[str, _NamingSchemaCallable]
5764
5765
5766class _NamingSchemaTD(TypedDict, total=False):
5767 fk: _NamingSchemaDirective
5768 pk: _NamingSchemaDirective
5769 ix: _NamingSchemaDirective
5770 ck: _NamingSchemaDirective
5771 uq: _NamingSchemaDirective
5772
5773
5774_NamingSchemaParameter = Union[
5775 # it seems like the TypedDict here is useful for pylance typeahead,
5776 # and not much else
5777 _NamingSchemaTD,
5778 # there is no form that allows Union[Type[Any], str] to work in all
5779 # cases, including breaking out Mapping[] entries for each combination
5780 # even, therefore keys must be `Any` (see #10264)
5781 Mapping[Any, _NamingSchemaDirective],
5782]
5783
5784
5785DEFAULT_NAMING_CONVENTION: _NamingSchemaParameter = util.immutabledict(
5786 {"ix": "ix_%(column_0_label)s"}
5787)
5788
5789
5790class MetaData(HasSchemaAttr):
5791 """A collection of :class:`_schema.Table`
5792 objects and their associated schema
5793 constructs.
5794
5795 Holds a collection of :class:`_schema.Table` objects as well as
5796 an optional binding to an :class:`_engine.Engine` or
5797 :class:`_engine.Connection`. If bound, the :class:`_schema.Table` objects
5798 in the collection and their columns may participate in implicit SQL
5799 execution.
5800
5801 The :class:`_schema.Table` objects themselves are stored in the
5802 :attr:`_schema.MetaData.tables` dictionary.
5803
5804 :class:`_schema.MetaData` is a thread-safe object for read operations.
5805 Construction of new tables within a single :class:`_schema.MetaData`
5806 object,
5807 either explicitly or via reflection, may not be completely thread-safe.
5808
5809 .. seealso::
5810
5811 :ref:`metadata_describing` - Introduction to database metadata
5812
5813 """
5814
5815 __visit_name__ = "metadata"
5816
5817 def __init__(
5818 self,
5819 schema: Optional[str] = None,
5820 quote_schema: Optional[bool] = None,
5821 naming_convention: Optional[_NamingSchemaParameter] = None,
5822 info: Optional[_InfoType] = None,
5823 ) -> None:
5824 """Create a new MetaData object.
5825
5826 :param schema:
5827 The default schema to use for the :class:`_schema.Table`,
5828 :class:`.Sequence`, and potentially other objects associated with
5829 this :class:`_schema.MetaData`. Defaults to ``None``.
5830
5831 .. seealso::
5832
5833 :ref:`schema_metadata_schema_name` - details on how the
5834 :paramref:`_schema.MetaData.schema` parameter is used.
5835
5836 :paramref:`_schema.Table.schema`
5837
5838 :paramref:`.Sequence.schema`
5839
5840 :param quote_schema:
5841 Sets the ``quote_schema`` flag for those :class:`_schema.Table`,
5842 :class:`.Sequence`, and other objects which make usage of the
5843 local ``schema`` name.
5844
5845 :param info: Optional data dictionary which will be populated into the
5846 :attr:`.SchemaItem.info` attribute of this object.
5847
5848 :param naming_convention: a dictionary referring to values which
5849 will establish default naming conventions for :class:`.Constraint`
5850 and :class:`.Index` objects, for those objects which are not given
5851 a name explicitly.
5852
5853 The keys of this dictionary may be:
5854
5855 * a constraint or Index class, e.g. the :class:`.UniqueConstraint`,
5856 :class:`_schema.ForeignKeyConstraint` class, the :class:`.Index`
5857 class
5858
5859 * a string mnemonic for one of the known constraint classes;
5860 ``"fk"``, ``"pk"``, ``"ix"``, ``"ck"``, ``"uq"`` for foreign key,
5861 primary key, index, check, and unique constraint, respectively.
5862
5863 * the string name of a user-defined "token" that can be used
5864 to define new naming tokens.
5865
5866 The values associated with each "constraint class" or "constraint
5867 mnemonic" key are string naming templates, such as
5868 ``"uq_%(table_name)s_%(column_0_name)s"``,
5869 which describe how the name should be composed. The values
5870 associated with user-defined "token" keys should be callables of the
5871 form ``fn(constraint, table)``, which accepts the constraint/index
5872 object and :class:`_schema.Table` as arguments, returning a string
5873 result.
5874
5875 The built-in names are as follows, some of which may only be
5876 available for certain types of constraint:
5877
5878 * ``%(table_name)s`` - the name of the :class:`_schema.Table`
5879 object
5880 associated with the constraint.
5881
5882 * ``%(referred_table_name)s`` - the name of the
5883 :class:`_schema.Table`
5884 object associated with the referencing target of a
5885 :class:`_schema.ForeignKeyConstraint`.
5886
5887 * ``%(column_0_name)s`` - the name of the :class:`_schema.Column`
5888 at
5889 index position "0" within the constraint.
5890
5891 * ``%(column_0N_name)s`` - the name of all :class:`_schema.Column`
5892 objects in order within the constraint, joined without a
5893 separator.
5894
5895 * ``%(column_0_N_name)s`` - the name of all
5896 :class:`_schema.Column`
5897 objects in order within the constraint, joined with an
5898 underscore as a separator.
5899
5900 * ``%(column_0_label)s``, ``%(column_0N_label)s``,
5901 ``%(column_0_N_label)s`` - the label of either the zeroth
5902 :class:`_schema.Column` or all :class:`.Columns`, separated with
5903 or without an underscore
5904
5905 * ``%(column_0_key)s``, ``%(column_0N_key)s``,
5906 ``%(column_0_N_key)s`` - the key of either the zeroth
5907 :class:`_schema.Column` or all :class:`.Columns`, separated with
5908 or without an underscore
5909
5910 * ``%(referred_column_0_name)s``, ``%(referred_column_0N_name)s``
5911 ``%(referred_column_0_N_name)s``, ``%(referred_column_0_key)s``,
5912 ``%(referred_column_0N_key)s``, ... column tokens which
5913 render the names/keys/labels of columns that are referenced
5914 by a :class:`_schema.ForeignKeyConstraint`.
5915
5916 * ``%(constraint_name)s`` - a special key that refers to the
5917 existing name given to the constraint. When this key is
5918 present, the :class:`.Constraint` object's existing name will be
5919 replaced with one that is composed from template string that
5920 uses this token. When this token is present, it is required that
5921 the :class:`.Constraint` is given an explicit name ahead of time.
5922
5923 * user-defined: any additional token may be implemented by passing
5924 it along with a ``fn(constraint, table)`` callable to the
5925 naming_convention dictionary.
5926
5927 .. seealso::
5928
5929 :ref:`constraint_naming_conventions` - for detailed usage
5930 examples.
5931
5932 """
5933 if schema is not None and not isinstance(schema, str):
5934 raise exc.ArgumentError(
5935 "expected schema argument to be a string, "
5936 f"got {type(schema)}."
5937 )
5938 self.tables = util.FacadeDict()
5939 self.schema = quoted_name.construct(schema, quote_schema)
5940 self.naming_convention = (
5941 naming_convention
5942 if naming_convention
5943 else DEFAULT_NAMING_CONVENTION
5944 )
5945 if info:
5946 self.info = info
5947 self._schemas: Set[str] = set()
5948 self._sequences: Dict[str, Sequence] = {}
5949 self._fk_memos: Dict[Tuple[str, Optional[str]], List[ForeignKey]] = (
5950 collections.defaultdict(list)
5951 )
5952 self._objects: Set[Union[HasSchemaAttr, SchemaType]] = set()
5953
5954 tables: util.FacadeDict[str, Table]
5955 """A dictionary of :class:`_schema.Table`
5956 objects keyed to their name or "table key".
5957
5958 The exact key is that determined by the :attr:`_schema.Table.key`
5959 attribute;
5960 for a table with no :attr:`_schema.Table.schema` attribute,
5961 this is the same
5962 as :attr:`_schema.Table.name`. For a table with a schema,
5963 it is typically of the
5964 form ``schemaname.tablename``.
5965
5966 .. seealso::
5967
5968 :attr:`_schema.MetaData.sorted_tables`
5969
5970 """
5971
5972 def __repr__(self) -> str:
5973 return "MetaData()"
5974
5975 def __contains__(self, table_or_key: Union[str, Table]) -> bool:
5976 if not isinstance(table_or_key, str):
5977 table_or_key = table_or_key.key
5978 return table_or_key in self.tables
5979
5980 def _add_table(
5981 self, name: str, schema: Optional[str], table: Table
5982 ) -> None:
5983 key = _get_table_key(name, schema)
5984 self.tables._insert_item(key, table)
5985 if schema:
5986 self._schemas.add(schema)
5987
5988 def _remove_table(self, name: str, schema: Optional[str]) -> None:
5989 key = _get_table_key(name, schema)
5990 removed = dict.pop(self.tables, key, None)
5991 if removed is not None:
5992 for fk in removed.foreign_keys:
5993 fk._remove_from_metadata(self)
5994 if self._schemas:
5995 self._schemas = {
5996 t.schema for t in self.tables.values() if t.schema is not None
5997 }
5998
5999 def __getstate__(self) -> Dict[str, Any]:
6000 return {
6001 "tables": self.tables,
6002 "schema": self.schema,
6003 "schemas": self._schemas,
6004 "sequences": self._sequences,
6005 "fk_memos": self._fk_memos,
6006 "naming_convention": self.naming_convention,
6007 "objects": self._objects,
6008 }
6009
6010 def __setstate__(self, state: Dict[str, Any]) -> None:
6011 self.tables = state["tables"]
6012 self.schema = state["schema"]
6013 self.naming_convention = state["naming_convention"]
6014 self._sequences = state["sequences"]
6015 self._schemas = state["schemas"]
6016 self._fk_memos = state["fk_memos"]
6017 self._objects = state.get("objects", set())
6018
6019 def clear(self) -> None:
6020 """Clear all objects from this MetaData."""
6021
6022 dict.clear(self.tables)
6023 self._schemas.clear()
6024 self._fk_memos.clear()
6025 self._sequences.clear()
6026 self._objects.clear()
6027
6028 def remove(self, table: Table) -> None:
6029 """Remove the given Table object from this MetaData."""
6030
6031 self._remove_table(table.name, table.schema)
6032
6033 @property
6034 def sorted_tables(self) -> List[Table]:
6035 """Returns a list of :class:`_schema.Table` objects sorted in order of
6036 foreign key dependency.
6037
6038 The sorting will place :class:`_schema.Table`
6039 objects that have dependencies
6040 first, before the dependencies themselves, representing the
6041 order in which they can be created. To get the order in which
6042 the tables would be dropped, use the ``reversed()`` Python built-in.
6043
6044 .. warning::
6045
6046 The :attr:`.MetaData.sorted_tables` attribute cannot by itself
6047 accommodate automatic resolution of dependency cycles between
6048 tables, which are usually caused by mutually dependent foreign key
6049 constraints. When these cycles are detected, the foreign keys
6050 of these tables are omitted from consideration in the sort.
6051 A warning is emitted when this condition occurs, which will be an
6052 exception raise in a future release. Tables which are not part
6053 of the cycle will still be returned in dependency order.
6054
6055 To resolve these cycles, the
6056 :paramref:`_schema.ForeignKeyConstraint.use_alter` parameter may be
6057 applied to those constraints which create a cycle. Alternatively,
6058 the :func:`_schema.sort_tables_and_constraints` function will
6059 automatically return foreign key constraints in a separate
6060 collection when cycles are detected so that they may be applied
6061 to a schema separately.
6062
6063 .. seealso::
6064
6065 :func:`_schema.sort_tables`
6066
6067 :func:`_schema.sort_tables_and_constraints`
6068
6069 :attr:`_schema.MetaData.tables`
6070
6071 :meth:`_reflection.Inspector.get_table_names`
6072
6073 :meth:`_reflection.Inspector.get_sorted_table_and_fkc_names`
6074
6075
6076 """
6077 return ddl.sort_tables(
6078 sorted(self.tables.values(), key=lambda t: t.key) # type: ignore
6079 )
6080
6081 # overload needed to work around mypy this mypy
6082 # https://github.com/python/mypy/issues/17093
6083 @overload
6084 def reflect(
6085 self,
6086 bind: Engine,
6087 schema: Optional[str] = ...,
6088 views: bool = ...,
6089 only: Union[
6090 _typing_Sequence[str], Callable[[str, MetaData], bool], None
6091 ] = ...,
6092 extend_existing: bool = ...,
6093 autoload_replace: bool = ...,
6094 resolve_fks: bool = ...,
6095 **dialect_kwargs: Any,
6096 ) -> None: ...
6097
6098 @overload
6099 def reflect(
6100 self,
6101 bind: Connection,
6102 schema: Optional[str] = ...,
6103 views: bool = ...,
6104 only: Union[
6105 _typing_Sequence[str], Callable[[str, MetaData], bool], None
6106 ] = ...,
6107 extend_existing: bool = ...,
6108 autoload_replace: bool = ...,
6109 resolve_fks: bool = ...,
6110 **dialect_kwargs: Any,
6111 ) -> None: ...
6112
6113 @util.preload_module("sqlalchemy.engine.reflection")
6114 def reflect(
6115 self,
6116 bind: Union[Engine, Connection],
6117 schema: Optional[str] = None,
6118 views: bool = False,
6119 only: Union[
6120 _typing_Sequence[str], Callable[[str, MetaData], bool], None
6121 ] = None,
6122 extend_existing: bool = False,
6123 autoload_replace: bool = True,
6124 resolve_fks: bool = True,
6125 **dialect_kwargs: Any,
6126 ) -> None:
6127 r"""Load all available table definitions from the database.
6128
6129 Automatically creates ``Table`` entries in this ``MetaData`` for any
6130 table available in the database but not yet present in the
6131 ``MetaData``. May be called multiple times to pick up tables recently
6132 added to the database, however no special action is taken if a table
6133 in this ``MetaData`` no longer exists in the database.
6134
6135 :param bind:
6136 A :class:`.Connection` or :class:`.Engine` used to access the
6137 database.
6138
6139 :param schema:
6140 Optional, query and reflect tables from an alternate schema.
6141 If None, the schema associated with this :class:`_schema.MetaData`
6142 is used, if any.
6143
6144 :param views:
6145 If True, also reflect views (materialized and plain).
6146
6147 :param only:
6148 Optional. Load only a sub-set of available named tables. May be
6149 specified as a sequence of names or a callable.
6150
6151 If a sequence of names is provided, only those tables will be
6152 reflected. An error is raised if a table is requested but not
6153 available. Named tables already present in this ``MetaData`` are
6154 ignored.
6155
6156 If a callable is provided, it will be used as a boolean predicate to
6157 filter the list of potential table names. The callable is called
6158 with a table name and this ``MetaData`` instance as positional
6159 arguments and should return a true value for any table to reflect.
6160
6161 :param extend_existing: Passed along to each :class:`_schema.Table` as
6162 :paramref:`_schema.Table.extend_existing`.
6163
6164 :param autoload_replace: Passed along to each :class:`_schema.Table`
6165 as
6166 :paramref:`_schema.Table.autoload_replace`.
6167
6168 :param resolve_fks: if True, reflect :class:`_schema.Table`
6169 objects linked
6170 to :class:`_schema.ForeignKey` objects located in each
6171 :class:`_schema.Table`.
6172 For :meth:`_schema.MetaData.reflect`,
6173 this has the effect of reflecting
6174 related tables that might otherwise not be in the list of tables
6175 being reflected, for example if the referenced table is in a
6176 different schema or is omitted via the
6177 :paramref:`.MetaData.reflect.only` parameter. When False,
6178 :class:`_schema.ForeignKey` objects are not followed to the
6179 :class:`_schema.Table`
6180 in which they link, however if the related table is also part of the
6181 list of tables that would be reflected in any case, the
6182 :class:`_schema.ForeignKey` object will still resolve to its related
6183 :class:`_schema.Table` after the :meth:`_schema.MetaData.reflect`
6184 operation is
6185 complete. Defaults to True.
6186
6187 .. seealso::
6188
6189 :paramref:`_schema.Table.resolve_fks`
6190
6191 :param \**dialect_kwargs: Additional keyword arguments not mentioned
6192 above are dialect specific, and passed in the form
6193 ``<dialectname>_<argname>``. See the documentation regarding an
6194 individual dialect at :ref:`dialect_toplevel` for detail on
6195 documented arguments.
6196
6197 .. seealso::
6198
6199 :ref:`metadata_reflection_toplevel`
6200
6201 :meth:`_events.DDLEvents.column_reflect` - Event used to customize
6202 the reflected columns. Usually used to generalize the types using
6203 :meth:`_types.TypeEngine.as_generic`
6204
6205 :ref:`metadata_reflection_dbagnostic_types` - describes how to
6206 reflect tables using general types.
6207
6208 """
6209
6210 with inspection.inspect(bind)._inspection_context() as insp:
6211 reflect_opts: Any = {
6212 "autoload_with": insp,
6213 "extend_existing": extend_existing,
6214 "autoload_replace": autoload_replace,
6215 "resolve_fks": resolve_fks,
6216 "_extend_on": set(),
6217 }
6218
6219 reflect_opts.update(dialect_kwargs)
6220
6221 if schema is None:
6222 schema = self.schema
6223
6224 if schema is not None:
6225 reflect_opts["schema"] = schema
6226
6227 kind = util.preloaded.engine_reflection.ObjectKind.TABLE
6228 available: util.OrderedSet[str] = util.OrderedSet(
6229 insp.get_table_names(schema, **dialect_kwargs)
6230 )
6231 if views:
6232 kind = util.preloaded.engine_reflection.ObjectKind.ANY
6233 available.update(insp.get_view_names(schema, **dialect_kwargs))
6234 try:
6235 available.update(
6236 insp.get_materialized_view_names(
6237 schema, **dialect_kwargs
6238 )
6239 )
6240 except NotImplementedError:
6241 pass
6242
6243 if schema is not None:
6244 available_w_schema: util.OrderedSet[str] = util.OrderedSet(
6245 [f"{schema}.{name}" for name in available]
6246 )
6247 else:
6248 available_w_schema = available
6249
6250 current = set(self.tables)
6251
6252 if only is None:
6253 load = [
6254 name
6255 for name, schname in zip(available, available_w_schema)
6256 if extend_existing or schname not in current
6257 ]
6258 elif callable(only):
6259 load = [
6260 name
6261 for name, schname in zip(available, available_w_schema)
6262 if (extend_existing or schname not in current)
6263 and only(name, self)
6264 ]
6265 else:
6266 missing = [name for name in only if name not in available]
6267 if missing:
6268 s = schema and (" schema '%s'" % schema) or ""
6269 missing_str = ", ".join(missing)
6270 raise exc.InvalidRequestError(
6271 f"Could not reflect: requested table(s) not available "
6272 f"in {bind.engine!r}{s}: ({missing_str})"
6273 )
6274 load = [
6275 name
6276 for name in only
6277 if extend_existing or name not in current
6278 ]
6279 # pass the available tables so the inspector can
6280 # choose to ignore the filter_names
6281 _reflect_info = insp._get_reflection_info(
6282 schema=schema,
6283 filter_names=load,
6284 available=available,
6285 kind=kind,
6286 scope=util.preloaded.engine_reflection.ObjectScope.ANY,
6287 **dialect_kwargs,
6288 )
6289 reflect_opts["_reflect_info"] = _reflect_info
6290
6291 for name in load:
6292 try:
6293 Table(name, self, **reflect_opts)
6294 except exc.UnreflectableTableError as uerr:
6295 util.warn(f"Skipping table {name}: {uerr}")
6296
6297 def create_all(
6298 self,
6299 bind: _CreateDropBind,
6300 tables: Optional[_typing_Sequence[Table]] = None,
6301 checkfirst: Union[bool, CheckFirst] = CheckFirst.ALL,
6302 ) -> None:
6303 """Create all tables stored in this metadata.
6304
6305 Conditional by default, will not attempt to recreate tables already
6306 present in the target database.
6307
6308 :param bind:
6309 A :class:`.Connection` or :class:`.Engine` used to access the
6310 database.
6311
6312 :param tables:
6313 Optional list of ``Table`` objects, which is a subset of the total
6314 tables in the ``MetaData`` (others are ignored).
6315
6316 :param checkfirst: A boolean value or instance of :class:`.CheckFirst`.
6317 Indicates which objects should be checked for within a separate pass
6318 before creating schema objects.
6319
6320 """
6321 bind._run_ddl_visitor(
6322 ddl.SchemaGenerator, self, checkfirst=checkfirst, tables=tables
6323 )
6324
6325 def drop_all(
6326 self,
6327 bind: _CreateDropBind,
6328 tables: Optional[_typing_Sequence[Table]] = None,
6329 checkfirst: Union[bool, CheckFirst] = CheckFirst.ALL,
6330 ) -> None:
6331 """Drop all tables stored in this metadata.
6332
6333 Conditional by default, will not attempt to drop tables not present in
6334 the target database.
6335
6336 :param bind:
6337 A :class:`.Connection` or :class:`.Engine` used to access the
6338 database.
6339
6340 :param tables:
6341 Optional list of ``Table`` objects, which is a subset of the
6342 total tables in the ``MetaData`` (others are ignored).
6343
6344 :param checkfirst: A boolean value or instance of :class:`.CheckFirst`.
6345 Indicates which objects should be checked for within a separate pass
6346 before dropping schema objects.
6347
6348 """
6349 bind._run_ddl_visitor(
6350 ddl.SchemaDropper, self, checkfirst=checkfirst, tables=tables
6351 )
6352
6353 @property
6354 def schemas(self) -> _typing_Sequence[str]:
6355 """A sequence of schema names that are present in this MetaData."""
6356 schemas = self._schemas
6357 if self.schema:
6358 schemas = schemas | {self.schema}
6359 return tuple(schemas)
6360
6361 def get_schema_objects(
6362 self,
6363 kind: Type[_T],
6364 *,
6365 schema: Union[str, None, Literal[_NoArg.NO_ARG]] = _NoArg.NO_ARG,
6366 ) -> _typing_Sequence[_T]:
6367 """Return a sequence of schema objects of the given kind.
6368
6369 This method can be used to return :class:`_sqltypes.Enum`,
6370 :class:`.Sequence`, etc. objects registered in this
6371 :class:`_schema.MetaData`.
6372
6373 :param kind: a type that indicates what object to return, such as
6374 :class:`Enum` or :class:`Sequence`.
6375 :param schema: Optional, a schema name to filter the objects by. If
6376 not provided the default schema of the metadata is used.
6377
6378 """
6379
6380 if schema is _NoArg.NO_ARG:
6381 schema = self.schema
6382 return tuple(
6383 obj
6384 for obj in self._objects
6385 if isinstance(obj, kind) and obj.schema == schema
6386 )
6387
6388 def get_schema_object_by_name(
6389 self,
6390 kind: Type[_T],
6391 name: str,
6392 *,
6393 schema: Union[str, None, Literal[_NoArg.NO_ARG]] = _NoArg.NO_ARG,
6394 ) -> Optional[_T]:
6395 """Return a schema objects of the given kind and name if found.
6396
6397 This method can be used to return :class:`_sqltypes.Enum`,
6398 :class:`.Sequence`, etc. objects registered in this
6399 :class:`_schema.MetaData`.
6400
6401 :param kind: a type that indicates what object to return, such as
6402 :class:`Enum` or :class:`Sequence`.
6403 :param name: the name of the object to return.
6404 :param schema: Optional, a schema name to filter the objects by. If
6405 not provided the default schema of the metadata is used.
6406
6407 """
6408
6409 for obj in self.get_schema_objects(kind, schema=schema):
6410 if getattr(obj, "name", None) == name:
6411 return obj
6412 return None
6413
6414 def _register_object(self, obj: Union[HasSchemaAttr, SchemaType]) -> None:
6415 self._objects.add(obj)
6416
6417
6418class Computed(FetchedValue, SchemaItem):
6419 """Defines a generated column, i.e. "GENERATED ALWAYS AS" syntax.
6420
6421 The :class:`.Computed` construct is an inline construct added to the
6422 argument list of a :class:`_schema.Column` object::
6423
6424 from sqlalchemy import Computed
6425
6426 Table(
6427 "square",
6428 metadata_obj,
6429 Column("side", Float, nullable=False),
6430 Column("area", Float, Computed("side * side")),
6431 )
6432
6433 See the linked documentation below for complete details.
6434
6435 .. seealso::
6436
6437 :ref:`computed_ddl`
6438
6439 """
6440
6441 __visit_name__ = "computed_column"
6442
6443 column: Optional[Column[Any]]
6444
6445 @_document_text_coercion(
6446 "sqltext", ":class:`.Computed`", ":paramref:`.Computed.sqltext`"
6447 )
6448 def __init__(
6449 self, sqltext: _DDLColumnArgument, persisted: Optional[bool] = None
6450 ) -> None:
6451 """Construct a GENERATED ALWAYS AS DDL construct to accompany a
6452 :class:`_schema.Column`.
6453
6454 :param sqltext:
6455 A string containing the column generation expression, which will be
6456 used verbatim, or a SQL expression construct, such as a
6457 :func:`_expression.text`
6458 object. If given as a string, the object is converted to a
6459 :func:`_expression.text` object.
6460
6461 :param persisted:
6462 Optional, controls how this column should be persisted by the
6463 database. Possible values are:
6464
6465 * ``None``, the default, it will use the default persistence
6466 defined by the database.
6467 * ``True``, will render ``GENERATED ALWAYS AS ... STORED``, or the
6468 equivalent for the target database if supported.
6469 * ``False``, will render ``GENERATED ALWAYS AS ... VIRTUAL``, or
6470 the equivalent for the target database if supported.
6471
6472 Specifying ``True`` or ``False`` may raise an error when the DDL
6473 is emitted to the target database if the database does not support
6474 that persistence option. Leaving this parameter at its default
6475 of ``None`` is guaranteed to succeed for all databases that support
6476 ``GENERATED ALWAYS AS``.
6477
6478 """
6479 self.sqltext = coercions.expect(roles.DDLExpressionRole, sqltext)
6480 self.persisted = persisted
6481 self.column = None
6482
6483 def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
6484 assert isinstance(parent, Column)
6485
6486 if not isinstance(
6487 parent.server_default, (type(None), Computed)
6488 ) or not isinstance(parent.server_onupdate, (type(None), Computed)):
6489 raise exc.ArgumentError(
6490 "A generated column cannot specify a server_default or a "
6491 "server_onupdate argument"
6492 )
6493 self.column = parent
6494 parent.computed = self
6495 self.column.server_onupdate = self
6496 self.column.server_default = self
6497
6498 def _as_for_update(self, for_update: bool) -> FetchedValue:
6499 return self
6500
6501 @util.deprecated(
6502 "1.4",
6503 "The :meth:`_schema.Computed.copy` method is deprecated "
6504 "and will be removed in a future release.",
6505 )
6506 def copy(
6507 self, *, target_table: Optional[Table] = None, **kw: Any
6508 ) -> Computed:
6509 return self._copy(target_table=target_table, **kw)
6510
6511 def _copy(
6512 self, *, target_table: Optional[Table] = None, **kw: Any
6513 ) -> Computed:
6514 sqltext = _copy_expression(
6515 self.sqltext,
6516 self.column.table if self.column is not None else None,
6517 target_table,
6518 )
6519 g = Computed(sqltext, persisted=self.persisted)
6520
6521 return self._schema_item_copy(g)
6522
6523
6524class Identity(IdentityOptions, FetchedValue, SchemaItem):
6525 """Defines an identity column, i.e. "GENERATED { ALWAYS | BY DEFAULT }
6526 AS IDENTITY" syntax.
6527
6528 The :class:`.Identity` construct is an inline construct added to the
6529 argument list of a :class:`_schema.Column` object::
6530
6531 from sqlalchemy import Identity
6532
6533 Table(
6534 "foo",
6535 metadata_obj,
6536 Column("id", Integer, Identity()),
6537 Column("description", Text),
6538 )
6539
6540 See the linked documentation below for complete details.
6541
6542 .. versionadded:: 1.4
6543
6544 .. seealso::
6545
6546 :ref:`identity_ddl`
6547
6548 """
6549
6550 __visit_name__ = "identity_column"
6551
6552 is_identity = True
6553
6554 @util.deprecated_params(
6555 order=(
6556 "2.1",
6557 "This parameter is supported only by Oracle Database, "
6558 "use ``oracle_order`` instead.",
6559 ),
6560 on_null=(
6561 "2.1",
6562 "This parameter is supported only by Oracle Database, "
6563 "use ``oracle_on_null`` instead.",
6564 ),
6565 )
6566 def __init__(
6567 self,
6568 always: Optional[bool] = False,
6569 on_null: Optional[bool] = None,
6570 start: Optional[int] = None,
6571 increment: Optional[int] = None,
6572 minvalue: Optional[int] = None,
6573 maxvalue: Optional[int] = None,
6574 nominvalue: Optional[bool] = None,
6575 nomaxvalue: Optional[bool] = None,
6576 cycle: Optional[bool] = None,
6577 cache: Optional[int] = None,
6578 order: Optional[bool] = None,
6579 **dialect_kw: Any,
6580 ) -> None:
6581 """Construct a GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY DDL
6582 construct to accompany a :class:`_schema.Column`.
6583
6584 See the :class:`.Sequence` documentation for a complete description
6585 of most parameters.
6586
6587 .. note::
6588 MSSQL supports this construct as the preferred alternative to
6589 generate an IDENTITY on a column, but it uses non standard
6590 syntax that only support :paramref:`_schema.Identity.start`
6591 and :paramref:`_schema.Identity.increment`.
6592 All other parameters are ignored.
6593
6594 :param always:
6595 A boolean, that indicates the type of identity column.
6596 If ``False`` is specified, the default, then the user-specified
6597 value takes precedence.
6598 If ``True`` is specified, a user-specified value is not accepted (
6599 on some backends, like PostgreSQL, OVERRIDING SYSTEM VALUE, or
6600 similar, may be specified in an INSERT to override the sequence
6601 value).
6602 Some backends also have a default value for this parameter,
6603 ``None`` can be used to omit rendering this part in the DDL. It
6604 will be treated as ``False`` if a backend does not have a default
6605 value.
6606
6607 :param on_null:
6608 Set to ``True`` to specify ON NULL in conjunction with a
6609 ``always=False`` identity column. This option is only supported on
6610 some backends, like Oracle Database.
6611
6612 :param start: the starting index of the sequence.
6613 :param increment: the increment value of the sequence.
6614 :param minvalue: the minimum value of the sequence.
6615 :param maxvalue: the maximum value of the sequence.
6616 :param nominvalue: no minimum value of the sequence.
6617 :param nomaxvalue: no maximum value of the sequence.
6618 :param cycle: allows the sequence to wrap around when the maxvalue
6619 or minvalue has been reached.
6620 :param cache: optional integer value; number of future values in the
6621 sequence which are calculated in advance.
6622 :param order: optional boolean value; if true, renders the
6623 ORDER keyword.
6624
6625 """
6626 self.dialect_options
6627 if on_null is not None:
6628 if "oracle_on_null" in dialect_kw:
6629 raise exc.ArgumentError(
6630 "Cannot specify both 'on_null' and 'oracle_on_null'. "
6631 "Please use only 'oracle_on_null'."
6632 )
6633 dialect_kw["oracle_on_null"] = on_null
6634
6635 IdentityOptions.__init__(
6636 self,
6637 start=start,
6638 increment=increment,
6639 minvalue=minvalue,
6640 maxvalue=maxvalue,
6641 nominvalue=nominvalue,
6642 nomaxvalue=nomaxvalue,
6643 cycle=cycle,
6644 cache=cache,
6645 order=order,
6646 **dialect_kw,
6647 )
6648 self.always = always
6649 self.column = None
6650
6651 @property
6652 def on_null(self) -> Optional[bool]:
6653 """Alias of the ``dialect_kwargs`` ``'oracle_on_null'``.
6654
6655 .. deprecated:: 2.1 The 'on_null' attribute is deprecated.
6656 """
6657 value: Optional[bool] = self.dialect_kwargs.get("oracle_on_null")
6658 return value
6659
6660 def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
6661 assert isinstance(parent, Column)
6662 if not isinstance(
6663 parent.server_default, (type(None), Identity)
6664 ) or not isinstance(parent.server_onupdate, type(None)):
6665 raise exc.ArgumentError(
6666 "A column with an Identity object cannot specify a "
6667 "server_default or a server_onupdate argument"
6668 )
6669 if parent.autoincrement is False:
6670 raise exc.ArgumentError(
6671 "A column with an Identity object cannot specify "
6672 "autoincrement=False"
6673 )
6674 self.column = parent
6675
6676 parent.identity = self
6677 if parent._user_defined_nullable is NULL_UNSPECIFIED:
6678 parent.nullable = False
6679
6680 parent.server_default = self
6681
6682 def _as_for_update(self, for_update: bool) -> FetchedValue:
6683 return self
6684
6685 @util.deprecated(
6686 "1.4",
6687 "The :meth:`_schema.Identity.copy` method is deprecated "
6688 "and will be removed in a future release.",
6689 )
6690 def copy(self, **kw: Any) -> Identity:
6691 return self._copy(**kw)
6692
6693 def _copy(self, **kw: Any) -> Identity:
6694 i = Identity(**self._as_dict(), **self.dialect_kwargs)
6695
6696 return self._schema_item_copy(i)
6697
6698 def _as_dict(self) -> Dict[str, Any]:
6699 return {
6700 # always=None means something different than always=False
6701 "always": self.always,
6702 **super()._as_dict(),
6703 }