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