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