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