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