summaryrefslogtreecommitdiff
path: root/venv/lib/python3.11/site-packages/sqlalchemy/sql/schema.py
diff options
context:
space:
mode:
Diffstat (limited to 'venv/lib/python3.11/site-packages/sqlalchemy/sql/schema.py')
-rw-r--r--venv/lib/python3.11/site-packages/sqlalchemy/sql/schema.py6115
1 files changed, 0 insertions, 6115 deletions
diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/schema.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/schema.py
deleted file mode 100644
index 6d5f941..0000000
--- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/schema.py
+++ /dev/null
@@ -1,6115 +0,0 @@
-# sql/schema.py
-# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
-# <see AUTHORS file>
-#
-# This module is part of SQLAlchemy and is released under
-# the MIT License: https://www.opensource.org/licenses/mit-license.php
-
-"""The schema module provides the building blocks for database metadata.
-
-Each element within this module describes a database entity which can be
-created and dropped, or is otherwise part of such an entity. Examples include
-tables, columns, sequences, and indexes.
-
-All entities are subclasses of :class:`~sqlalchemy.schema.SchemaItem`, and as
-defined in this module they are intended to be agnostic of any vendor-specific
-constructs.
-
-A collection of entities are grouped into a unit called
-:class:`~sqlalchemy.schema.MetaData`. MetaData serves as a logical grouping of
-schema elements, and can also be associated with an actual database connection
-such that operations involving the contained elements can contact the database
-as needed.
-
-Two of the elements here also build upon their "syntactic" counterparts, which
-are defined in :class:`~sqlalchemy.sql.expression.`, specifically
-:class:`~sqlalchemy.schema.Table` and :class:`~sqlalchemy.schema.Column`.
-Since these objects are part of the SQL expression language, they are usable
-as components in SQL expressions.
-
-"""
-from __future__ import annotations
-
-from abc import ABC
-import collections
-from enum import Enum
-import operator
-import typing
-from typing import Any
-from typing import Callable
-from typing import cast
-from typing import Collection
-from typing import Dict
-from typing import Iterable
-from typing import Iterator
-from typing import List
-from typing import Mapping
-from typing import NoReturn
-from typing import Optional
-from typing import overload
-from typing import Sequence as _typing_Sequence
-from typing import Set
-from typing import Tuple
-from typing import TYPE_CHECKING
-from typing import TypeVar
-from typing import Union
-
-from . import coercions
-from . import ddl
-from . import roles
-from . import type_api
-from . import visitors
-from .base import _DefaultDescriptionTuple
-from .base import _NoneName
-from .base import _SentinelColumnCharacterization
-from .base import _SentinelDefaultCharacterization
-from .base import DedupeColumnCollection
-from .base import DialectKWArgs
-from .base import Executable
-from .base import SchemaEventTarget as SchemaEventTarget
-from .coercions import _document_text_coercion
-from .elements import ClauseElement
-from .elements import ColumnClause
-from .elements import ColumnElement
-from .elements import quoted_name
-from .elements import TextClause
-from .selectable import TableClause
-from .type_api import to_instance
-from .visitors import ExternallyTraversible
-from .visitors import InternalTraversal
-from .. import event
-from .. import exc
-from .. import inspection
-from .. import util
-from ..util import HasMemoized
-from ..util.typing import Final
-from ..util.typing import Literal
-from ..util.typing import Protocol
-from ..util.typing import Self
-from ..util.typing import TypedDict
-from ..util.typing import TypeGuard
-
-if typing.TYPE_CHECKING:
- from ._typing import _AutoIncrementType
- from ._typing import _DDLColumnArgument
- from ._typing import _InfoType
- from ._typing import _TextCoercedExpressionArgument
- from ._typing import _TypeEngineArgument
- from .base import ReadOnlyColumnCollection
- from .compiler import DDLCompiler
- from .elements import BindParameter
- from .functions import Function
- from .type_api import TypeEngine
- from .visitors import _TraverseInternalsType
- from .visitors import anon_map
- from ..engine import Connection
- from ..engine import Engine
- from ..engine.interfaces import _CoreMultiExecuteParams
- from ..engine.interfaces import CoreExecuteOptionsParameter
- from ..engine.interfaces import ExecutionContext
- from ..engine.mock import MockConnection
- from ..engine.reflection import _ReflectionInfo
- from ..sql.selectable import FromClause
-
-_T = TypeVar("_T", bound="Any")
-_SI = TypeVar("_SI", bound="SchemaItem")
-_TAB = TypeVar("_TAB", bound="Table")
-
-
-_CreateDropBind = Union["Engine", "Connection", "MockConnection"]
-
-_ConstraintNameArgument = Optional[Union[str, _NoneName]]
-
-_ServerDefaultArgument = Union[
- "FetchedValue", str, TextClause, ColumnElement[Any]
-]
-
-
-class SchemaConst(Enum):
- RETAIN_SCHEMA = 1
- """Symbol indicating that a :class:`_schema.Table`, :class:`.Sequence`
- or in some cases a :class:`_schema.ForeignKey` object, in situations
- where the object is being copied for a :meth:`.Table.to_metadata`
- operation, should retain the schema name that it already has.
-
- """
-
- BLANK_SCHEMA = 2
- """Symbol indicating that a :class:`_schema.Table` or :class:`.Sequence`
- should have 'None' for its schema, even if the parent
- :class:`_schema.MetaData` has specified a schema.
-
- .. seealso::
-
- :paramref:`_schema.MetaData.schema`
-
- :paramref:`_schema.Table.schema`
-
- :paramref:`.Sequence.schema`
-
- """
-
- NULL_UNSPECIFIED = 3
- """Symbol indicating the "nullable" keyword was not passed to a Column.
-
- This is used to distinguish between the use case of passing
- ``nullable=None`` to a :class:`.Column`, which has special meaning
- on some backends such as SQL Server.
-
- """
-
-
-RETAIN_SCHEMA: Final[Literal[SchemaConst.RETAIN_SCHEMA]] = (
- SchemaConst.RETAIN_SCHEMA
-)
-BLANK_SCHEMA: Final[Literal[SchemaConst.BLANK_SCHEMA]] = (
- SchemaConst.BLANK_SCHEMA
-)
-NULL_UNSPECIFIED: Final[Literal[SchemaConst.NULL_UNSPECIFIED]] = (
- SchemaConst.NULL_UNSPECIFIED
-)
-
-
-def _get_table_key(name: str, schema: Optional[str]) -> str:
- if schema is None:
- return name
- else:
- return schema + "." + name
-
-
-# this should really be in sql/util.py but we'd have to
-# break an import cycle
-def _copy_expression(
- expression: ColumnElement[Any],
- source_table: Optional[Table],
- target_table: Optional[Table],
-) -> ColumnElement[Any]:
- if source_table is None or target_table is None:
- return expression
-
- fixed_source_table = source_table
- fixed_target_table = target_table
-
- def replace(
- element: ExternallyTraversible, **kw: Any
- ) -> Optional[ExternallyTraversible]:
- if (
- isinstance(element, Column)
- and element.table is fixed_source_table
- and element.key in fixed_source_table.c
- ):
- return fixed_target_table.c[element.key]
- else:
- return None
-
- return cast(
- ColumnElement[Any],
- visitors.replacement_traverse(expression, {}, replace),
- )
-
-
-@inspection._self_inspects
-class SchemaItem(SchemaEventTarget, visitors.Visitable):
- """Base class for items that define a database schema."""
-
- __visit_name__ = "schema_item"
-
- create_drop_stringify_dialect = "default"
-
- def _init_items(self, *args: SchemaItem, **kw: Any) -> None:
- """Initialize the list of child items for this SchemaItem."""
- for item in args:
- if item is not None:
- try:
- spwd = item._set_parent_with_dispatch
- except AttributeError as err:
- raise exc.ArgumentError(
- "'SchemaItem' object, such as a 'Column' or a "
- f"'Constraint' expected, got {item!r}"
- ) from err
- else:
- spwd(self, **kw)
-
- def __repr__(self) -> str:
- return util.generic_repr(self, omit_kwarg=["info"])
-
- @util.memoized_property
- def info(self) -> _InfoType:
- """Info dictionary associated with the object, allowing user-defined
- data to be associated with this :class:`.SchemaItem`.
-
- The dictionary is automatically generated when first accessed.
- It can also be specified in the constructor of some objects,
- such as :class:`_schema.Table` and :class:`_schema.Column`.
-
- """
- return {}
-
- def _schema_item_copy(self, schema_item: _SI) -> _SI:
- if "info" in self.__dict__:
- schema_item.info = self.info.copy()
- schema_item.dispatch._update(self.dispatch)
- return schema_item
-
- _use_schema_map = True
-
-
-class HasConditionalDDL:
- """define a class that includes the :meth:`.HasConditionalDDL.ddl_if`
- method, allowing for conditional rendering of DDL.
-
- Currently applies to constraints and indexes.
-
- .. versionadded:: 2.0
-
-
- """
-
- _ddl_if: Optional[ddl.DDLIf] = None
-
- def ddl_if(
- self,
- dialect: Optional[str] = None,
- callable_: Optional[ddl.DDLIfCallable] = None,
- state: Optional[Any] = None,
- ) -> Self:
- r"""apply a conditional DDL rule to this schema item.
-
- These rules work in a similar manner to the
- :meth:`.ExecutableDDLElement.execute_if` callable, with the added
- feature that the criteria may be checked within the DDL compilation
- phase for a construct such as :class:`.CreateTable`.
- :meth:`.HasConditionalDDL.ddl_if` currently applies towards the
- :class:`.Index` construct as well as all :class:`.Constraint`
- constructs.
-
- :param dialect: string name of a dialect, or a tuple of string names
- to indicate multiple dialect types.
-
- :param callable\_: a callable that is constructed using the same form
- as that described in
- :paramref:`.ExecutableDDLElement.execute_if.callable_`.
-
- :param state: any arbitrary object that will be passed to the
- callable, if present.
-
- .. versionadded:: 2.0
-
- .. seealso::
-
- :ref:`schema_ddl_ddl_if` - background and usage examples
-
-
- """
- self._ddl_if = ddl.DDLIf(dialect, callable_, state)
- return self
-
-
-class HasSchemaAttr(SchemaItem):
- """schema item that includes a top-level schema name"""
-
- schema: Optional[str]
-
-
-class Table(
- DialectKWArgs, HasSchemaAttr, TableClause, inspection.Inspectable["Table"]
-):
- r"""Represent a table in a database.
-
- e.g.::
-
- mytable = Table(
- "mytable", metadata,
- Column('mytable_id', Integer, primary_key=True),
- Column('value', String(50))
- )
-
- The :class:`_schema.Table`
- object constructs a unique instance of itself based
- on its name and optional schema name within the given
- :class:`_schema.MetaData` object. Calling the :class:`_schema.Table`
- constructor with the same name and same :class:`_schema.MetaData` argument
- a second time will return the *same* :class:`_schema.Table`
- object - in this way
- the :class:`_schema.Table` constructor acts as a registry function.
-
- .. seealso::
-
- :ref:`metadata_describing` - Introduction to database metadata
-
- """
-
- __visit_name__ = "table"
-
- if TYPE_CHECKING:
-
- @util.ro_non_memoized_property
- def primary_key(self) -> PrimaryKeyConstraint: ...
-
- @util.ro_non_memoized_property
- def foreign_keys(self) -> Set[ForeignKey]: ...
-
- _columns: DedupeColumnCollection[Column[Any]]
-
- _sentinel_column: Optional[Column[Any]]
-
- constraints: Set[Constraint]
- """A collection of all :class:`_schema.Constraint` objects associated with
- this :class:`_schema.Table`.
-
- Includes :class:`_schema.PrimaryKeyConstraint`,
- :class:`_schema.ForeignKeyConstraint`, :class:`_schema.UniqueConstraint`,
- :class:`_schema.CheckConstraint`. A separate collection
- :attr:`_schema.Table.foreign_key_constraints` refers to the collection
- of all :class:`_schema.ForeignKeyConstraint` objects, and the
- :attr:`_schema.Table.primary_key` attribute refers to the single
- :class:`_schema.PrimaryKeyConstraint` associated with the
- :class:`_schema.Table`.
-
- .. seealso::
-
- :attr:`_schema.Table.constraints`
-
- :attr:`_schema.Table.primary_key`
-
- :attr:`_schema.Table.foreign_key_constraints`
-
- :attr:`_schema.Table.indexes`
-
- :class:`_reflection.Inspector`
-
-
- """
-
- indexes: Set[Index]
- """A collection of all :class:`_schema.Index` objects associated with this
- :class:`_schema.Table`.
-
- .. seealso::
-
- :meth:`_reflection.Inspector.get_indexes`
-
- """
-
- _traverse_internals: _TraverseInternalsType = (
- TableClause._traverse_internals
- + [("schema", InternalTraversal.dp_string)]
- )
-
- if TYPE_CHECKING:
-
- @util.ro_non_memoized_property
- def columns(self) -> ReadOnlyColumnCollection[str, Column[Any]]: ...
-
- @util.ro_non_memoized_property
- def exported_columns(
- self,
- ) -> ReadOnlyColumnCollection[str, Column[Any]]: ...
-
- @util.ro_non_memoized_property
- def c(self) -> ReadOnlyColumnCollection[str, Column[Any]]: ...
-
- def _gen_cache_key(
- self, anon_map: anon_map, bindparams: List[BindParameter[Any]]
- ) -> Tuple[Any, ...]:
- if self._annotations:
- return (self,) + self._annotations_cache_key
- else:
- return (self,)
-
- if not typing.TYPE_CHECKING:
- # typing tools seem to be inconsistent in how they handle
- # __new__, so suggest this pattern for classes that use
- # __new__. apply typing to the __init__ method normally
- @util.deprecated_params(
- mustexist=(
- "1.4",
- "Deprecated alias of :paramref:`_schema.Table.must_exist`",
- ),
- )
- def __new__(cls, *args: Any, **kw: Any) -> Any:
- return cls._new(*args, **kw)
-
- @classmethod
- def _new(cls, *args: Any, **kw: Any) -> Any:
- if not args and not kw:
- # python3k pickle seems to call this
- return object.__new__(cls)
-
- try:
- name, metadata, args = args[0], args[1], args[2:]
- except IndexError:
- raise TypeError(
- "Table() takes at least two positional-only "
- "arguments 'name' and 'metadata'"
- )
-
- schema = kw.get("schema", None)
- if schema is None:
- schema = metadata.schema
- elif schema is BLANK_SCHEMA:
- schema = None
- keep_existing = kw.get("keep_existing", False)
- extend_existing = kw.get("extend_existing", False)
-
- if keep_existing and extend_existing:
- msg = "keep_existing and extend_existing are mutually exclusive."
- raise exc.ArgumentError(msg)
-
- must_exist = kw.pop("must_exist", kw.pop("mustexist", False))
- key = _get_table_key(name, schema)
- if key in metadata.tables:
- if not keep_existing and not extend_existing and bool(args):
- raise exc.InvalidRequestError(
- f"Table '{key}' is already defined for this MetaData "
- "instance. Specify 'extend_existing=True' "
- "to redefine "
- "options and columns on an "
- "existing Table object."
- )
- table = metadata.tables[key]
- if extend_existing:
- table._init_existing(*args, **kw)
- return table
- else:
- if must_exist:
- raise exc.InvalidRequestError(f"Table '{key}' not defined")
- table = object.__new__(cls)
- table.dispatch.before_parent_attach(table, metadata)
- metadata._add_table(name, schema, table)
- try:
- table.__init__(name, metadata, *args, _no_init=False, **kw)
- table.dispatch.after_parent_attach(table, metadata)
- return table
- except Exception:
- with util.safe_reraise():
- metadata._remove_table(name, schema)
-
- def __init__(
- self,
- name: str,
- metadata: MetaData,
- *args: SchemaItem,
- schema: Optional[Union[str, Literal[SchemaConst.BLANK_SCHEMA]]] = None,
- quote: Optional[bool] = None,
- quote_schema: Optional[bool] = None,
- autoload_with: Optional[Union[Engine, Connection]] = None,
- autoload_replace: bool = True,
- keep_existing: bool = False,
- extend_existing: bool = False,
- resolve_fks: bool = True,
- include_columns: Optional[Collection[str]] = None,
- implicit_returning: bool = True,
- comment: Optional[str] = None,
- info: Optional[Dict[Any, Any]] = None,
- listeners: Optional[
- _typing_Sequence[Tuple[str, Callable[..., Any]]]
- ] = None,
- prefixes: Optional[_typing_Sequence[str]] = None,
- # used internally in the metadata.reflect() process
- _extend_on: Optional[Set[Table]] = None,
- # used by __new__ to bypass __init__
- _no_init: bool = True,
- # dialect-specific keyword args
- **kw: Any,
- ) -> None:
- r"""Constructor for :class:`_schema.Table`.
-
-
- :param name: The name of this table as represented in the database.
-
- The table name, along with the value of the ``schema`` parameter,
- forms a key which uniquely identifies this :class:`_schema.Table`
- within
- the owning :class:`_schema.MetaData` collection.
- Additional calls to :class:`_schema.Table` with the same name,
- metadata,
- and schema name will return the same :class:`_schema.Table` object.
-
- Names which contain no upper case characters
- will be treated as case insensitive names, and will not be quoted
- unless they are a reserved word or contain special characters.
- A name with any number of upper case characters is considered
- to be case sensitive, and will be sent as quoted.
-
- To enable unconditional quoting for the table name, specify the flag
- ``quote=True`` to the constructor, or use the :class:`.quoted_name`
- construct to specify the name.
-
- :param metadata: a :class:`_schema.MetaData`
- object which will contain this
- table. The metadata is used as a point of association of this table
- with other tables which are referenced via foreign key. It also
- may be used to associate this table with a particular
- :class:`.Connection` or :class:`.Engine`.
-
- :param \*args: Additional positional arguments are used primarily
- to add the list of :class:`_schema.Column`
- objects contained within this
- table. Similar to the style of a CREATE TABLE statement, other
- :class:`.SchemaItem` constructs may be added here, including
- :class:`.PrimaryKeyConstraint`, and
- :class:`_schema.ForeignKeyConstraint`.
-
- :param autoload_replace: Defaults to ``True``; when using
- :paramref:`_schema.Table.autoload_with`
- in conjunction with :paramref:`_schema.Table.extend_existing`,
- indicates
- that :class:`_schema.Column` objects present in the already-existing
- :class:`_schema.Table`
- object should be replaced with columns of the same
- name retrieved from the autoload process. When ``False``, columns
- already present under existing names will be omitted from the
- reflection process.
-
- Note that this setting does not impact :class:`_schema.Column` objects
- specified programmatically within the call to :class:`_schema.Table`
- that
- also is autoloading; those :class:`_schema.Column` objects will always
- replace existing columns of the same name when
- :paramref:`_schema.Table.extend_existing` is ``True``.
-
- .. seealso::
-
- :paramref:`_schema.Table.autoload_with`
-
- :paramref:`_schema.Table.extend_existing`
-
- :param autoload_with: An :class:`_engine.Engine` or
- :class:`_engine.Connection` object,
- or a :class:`_reflection.Inspector` object as returned by
- :func:`_sa.inspect`
- against one, with which this :class:`_schema.Table`
- object will be reflected.
- When set to a non-None value, the autoload process will take place
- for this table against the given engine or connection.
-
- .. seealso::
-
- :ref:`metadata_reflection_toplevel`
-
- :meth:`_events.DDLEvents.column_reflect`
-
- :ref:`metadata_reflection_dbagnostic_types`
-
- :param extend_existing: When ``True``, indicates that if this
- :class:`_schema.Table` is already present in the given
- :class:`_schema.MetaData`,
- apply further arguments within the constructor to the existing
- :class:`_schema.Table`.
-
- If :paramref:`_schema.Table.extend_existing` or
- :paramref:`_schema.Table.keep_existing` are not set,
- and the given name
- of the new :class:`_schema.Table` refers to a :class:`_schema.Table`
- that is
- already present in the target :class:`_schema.MetaData` collection,
- and
- this :class:`_schema.Table`
- specifies additional columns or other constructs
- or flags that modify the table's state, an
- error is raised. The purpose of these two mutually-exclusive flags
- is to specify what action should be taken when a
- :class:`_schema.Table`
- is specified that matches an existing :class:`_schema.Table`,
- yet specifies
- additional constructs.
-
- :paramref:`_schema.Table.extend_existing`
- will also work in conjunction
- with :paramref:`_schema.Table.autoload_with` to run a new reflection
- operation against the database, even if a :class:`_schema.Table`
- of the same name is already present in the target
- :class:`_schema.MetaData`; newly reflected :class:`_schema.Column`
- objects
- and other options will be added into the state of the
- :class:`_schema.Table`, potentially overwriting existing columns
- and options of the same name.
-
- As is always the case with :paramref:`_schema.Table.autoload_with`,
- :class:`_schema.Column` objects can be specified in the same
- :class:`_schema.Table`
- constructor, which will take precedence. Below, the existing
- table ``mytable`` will be augmented with :class:`_schema.Column`
- objects
- both reflected from the database, as well as the given
- :class:`_schema.Column`
- named "y"::
-
- Table("mytable", metadata,
- Column('y', Integer),
- extend_existing=True,
- autoload_with=engine
- )
-
- .. seealso::
-
- :paramref:`_schema.Table.autoload_with`
-
- :paramref:`_schema.Table.autoload_replace`
-
- :paramref:`_schema.Table.keep_existing`
-
-
- :param implicit_returning: True by default - indicates that
- RETURNING can be used, typically by the ORM, in order to fetch
- server-generated values such as primary key values and
- server side defaults, on those backends which support RETURNING.
-
- In modern SQLAlchemy there is generally no reason to alter this
- setting, except for some backend specific cases
- (see :ref:`mssql_triggers` in the SQL Server dialect documentation
- for one such example).
-
- :param include_columns: A list of strings indicating a subset of
- columns to be loaded via the ``autoload`` operation; table columns who
- aren't present in this list will not be represented on the resulting
- ``Table`` object. Defaults to ``None`` which indicates all columns
- should be reflected.
-
- :param resolve_fks: Whether or not to reflect :class:`_schema.Table`
- objects
- related to this one via :class:`_schema.ForeignKey` objects, when
- :paramref:`_schema.Table.autoload_with` is
- specified. Defaults to True. Set to False to disable reflection of
- related tables as :class:`_schema.ForeignKey`
- objects are encountered; may be
- used either to save on SQL calls or to avoid issues with related tables
- that can't be accessed. Note that if a related table is already present
- in the :class:`_schema.MetaData` collection, or becomes present later,
- a
- :class:`_schema.ForeignKey` object associated with this
- :class:`_schema.Table` will
- resolve to that table normally.
-
- .. versionadded:: 1.3
-
- .. seealso::
-
- :paramref:`.MetaData.reflect.resolve_fks`
-
-
- :param info: Optional data dictionary which will be populated into the
- :attr:`.SchemaItem.info` attribute of this object.
-
- :param keep_existing: When ``True``, indicates that if this Table
- is already present in the given :class:`_schema.MetaData`, ignore
- further arguments within the constructor to the existing
- :class:`_schema.Table`, and return the :class:`_schema.Table`
- object as
- originally created. This is to allow a function that wishes
- to define a new :class:`_schema.Table` on first call, but on
- subsequent calls will return the same :class:`_schema.Table`,
- without any of the declarations (particularly constraints)
- being applied a second time.
-
- If :paramref:`_schema.Table.extend_existing` or
- :paramref:`_schema.Table.keep_existing` are not set,
- and the given name
- of the new :class:`_schema.Table` refers to a :class:`_schema.Table`
- that is
- already present in the target :class:`_schema.MetaData` collection,
- and
- this :class:`_schema.Table`
- specifies additional columns or other constructs
- or flags that modify the table's state, an
- error is raised. The purpose of these two mutually-exclusive flags
- is to specify what action should be taken when a
- :class:`_schema.Table`
- is specified that matches an existing :class:`_schema.Table`,
- yet specifies
- additional constructs.
-
- .. seealso::
-
- :paramref:`_schema.Table.extend_existing`
-
- :param listeners: A list of tuples of the form ``(<eventname>, <fn>)``
- which will be passed to :func:`.event.listen` upon construction.
- This alternate hook to :func:`.event.listen` allows the establishment
- of a listener function specific to this :class:`_schema.Table` before
- the "autoload" process begins. Historically this has been intended
- for use with the :meth:`.DDLEvents.column_reflect` event, however
- note that this event hook may now be associated with the
- :class:`_schema.MetaData` object directly::
-
- def listen_for_reflect(table, column_info):
- "handle the column reflection event"
- # ...
-
- t = Table(
- 'sometable',
- autoload_with=engine,
- listeners=[
- ('column_reflect', listen_for_reflect)
- ])
-
- .. seealso::
-
- :meth:`_events.DDLEvents.column_reflect`
-
- :param must_exist: When ``True``, indicates that this Table must already
- be present in the given :class:`_schema.MetaData` collection, else
- an exception is raised.
-
- :param prefixes:
- A list of strings to insert after CREATE in the CREATE TABLE
- statement. They will be separated by spaces.
-
- :param quote: Force quoting of this table's name on or off, corresponding
- to ``True`` or ``False``. When left at its default of ``None``,
- the column identifier will be quoted according to whether the name is
- case sensitive (identifiers with at least one upper case character are
- treated as case sensitive), or if it's a reserved word. This flag
- is only needed to force quoting of a reserved word which is not known
- by the SQLAlchemy dialect.
-
- .. note:: setting this flag to ``False`` will not provide
- case-insensitive behavior for table reflection; table reflection
- will always search for a mixed-case name in a case sensitive
- fashion. Case insensitive names are specified in SQLAlchemy only
- by stating the name with all lower case characters.
-
- :param quote_schema: same as 'quote' but applies to the schema identifier.
-
- :param schema: The schema name for this table, which is required if
- the table resides in a schema other than the default selected schema
- for the engine's database connection. Defaults to ``None``.
-
- If the owning :class:`_schema.MetaData` of this :class:`_schema.Table`
- specifies its
- own :paramref:`_schema.MetaData.schema` parameter,
- then that schema name will
- be applied to this :class:`_schema.Table`
- if the schema parameter here is set
- to ``None``. To set a blank schema name on a :class:`_schema.Table`
- that
- would otherwise use the schema set on the owning
- :class:`_schema.MetaData`,
- specify the special symbol :attr:`.BLANK_SCHEMA`.
-
- The quoting rules for the schema name are the same as those for the
- ``name`` parameter, in that quoting is applied for reserved words or
- case-sensitive names; to enable unconditional quoting for the schema
- name, specify the flag ``quote_schema=True`` to the constructor, or use
- the :class:`.quoted_name` construct to specify the name.
-
- :param comment: Optional string that will render an SQL comment on table
- creation.
-
- .. versionadded:: 1.2 Added the :paramref:`_schema.Table.comment`
- parameter
- to :class:`_schema.Table`.
-
- :param \**kw: Additional keyword arguments not mentioned above are
- dialect specific, and passed in the form ``<dialectname>_<argname>``.
- See the documentation regarding an individual dialect at
- :ref:`dialect_toplevel` for detail on documented arguments.
-
- """ # noqa: E501
- if _no_init:
- # don't run __init__ from __new__ by default;
- # __new__ has a specific place that __init__ is called
- return
-
- super().__init__(quoted_name(name, quote))
- self.metadata = metadata
-
- if schema is None:
- self.schema = metadata.schema
- elif schema is BLANK_SCHEMA:
- self.schema = None
- else:
- quote_schema = quote_schema
- assert isinstance(schema, str)
- self.schema = quoted_name(schema, quote_schema)
-
- self._sentinel_column = None
-
- self.indexes = set()
- self.constraints = set()
- PrimaryKeyConstraint(
- _implicit_generated=True
- )._set_parent_with_dispatch(self)
- self.foreign_keys = set() # type: ignore
- self._extra_dependencies: Set[Table] = set()
- if self.schema is not None:
- self.fullname = "%s.%s" % (self.schema, self.name)
- else:
- self.fullname = self.name
-
- self.implicit_returning = implicit_returning
- _reflect_info = kw.pop("_reflect_info", None)
-
- self.comment = comment
-
- if info is not None:
- self.info = info
-
- if listeners is not None:
- for evt, fn in listeners:
- event.listen(self, evt, fn)
-
- self._prefixes = prefixes if prefixes else []
-
- self._extra_kwargs(**kw)
-
- # load column definitions from the database if 'autoload' is defined
- # we do it after the table is in the singleton dictionary to support
- # circular foreign keys
- if autoload_with is not None:
- self._autoload(
- metadata,
- autoload_with,
- include_columns,
- _extend_on=_extend_on,
- _reflect_info=_reflect_info,
- resolve_fks=resolve_fks,
- )
-
- # initialize all the column, etc. objects. done after reflection to
- # allow user-overrides
-
- self._init_items(
- *args,
- allow_replacements=extend_existing
- or keep_existing
- or autoload_with,
- all_names={},
- )
-
- def _autoload(
- self,
- metadata: MetaData,
- autoload_with: Union[Engine, Connection],
- include_columns: Optional[Collection[str]],
- exclude_columns: Collection[str] = (),
- resolve_fks: bool = True,
- _extend_on: Optional[Set[Table]] = None,
- _reflect_info: _ReflectionInfo | None = None,
- ) -> None:
- insp = inspection.inspect(autoload_with)
- with insp._inspection_context() as conn_insp:
- conn_insp.reflect_table(
- self,
- include_columns,
- exclude_columns,
- resolve_fks,
- _extend_on=_extend_on,
- _reflect_info=_reflect_info,
- )
-
- @property
- def _sorted_constraints(self) -> List[Constraint]:
- """Return the set of constraints as a list, sorted by creation
- order.
-
- """
-
- return sorted(self.constraints, key=lambda c: c._creation_order)
-
- @property
- def foreign_key_constraints(self) -> Set[ForeignKeyConstraint]:
- """:class:`_schema.ForeignKeyConstraint` objects referred to by this
- :class:`_schema.Table`.
-
- This list is produced from the collection of
- :class:`_schema.ForeignKey`
- objects currently associated.
-
-
- .. seealso::
-
- :attr:`_schema.Table.constraints`
-
- :attr:`_schema.Table.foreign_keys`
-
- :attr:`_schema.Table.indexes`
-
- """
- return {
- fkc.constraint
- for fkc in self.foreign_keys
- if fkc.constraint is not None
- }
-
- def _init_existing(self, *args: Any, **kwargs: Any) -> None:
- autoload_with = kwargs.pop("autoload_with", None)
- autoload = kwargs.pop("autoload", autoload_with is not None)
- autoload_replace = kwargs.pop("autoload_replace", True)
- schema = kwargs.pop("schema", None)
- _extend_on = kwargs.pop("_extend_on", None)
- _reflect_info = kwargs.pop("_reflect_info", None)
-
- # these arguments are only used with _init()
- extend_existing = kwargs.pop("extend_existing", False)
- keep_existing = kwargs.pop("keep_existing", False)
-
- assert extend_existing
- assert not keep_existing
-
- if schema and schema != self.schema:
- raise exc.ArgumentError(
- f"Can't change schema of existing table "
- f"from '{self.schema}' to '{schema}'",
- )
-
- include_columns = kwargs.pop("include_columns", None)
- if include_columns is not None:
- for c in self.c:
- if c.name not in include_columns:
- self._columns.remove(c)
-
- resolve_fks = kwargs.pop("resolve_fks", True)
-
- for key in ("quote", "quote_schema"):
- if key in kwargs:
- raise exc.ArgumentError(
- "Can't redefine 'quote' or 'quote_schema' arguments"
- )
-
- # update `self` with these kwargs, if provided
- self.comment = kwargs.pop("comment", self.comment)
- self.implicit_returning = kwargs.pop(
- "implicit_returning", self.implicit_returning
- )
- self.info = kwargs.pop("info", self.info)
-
- exclude_columns: _typing_Sequence[str]
-
- if autoload:
- if not autoload_replace:
- # don't replace columns already present.
- # we'd like to do this for constraints also however we don't
- # have simple de-duping for unnamed constraints.
- exclude_columns = [c.name for c in self.c]
- else:
- exclude_columns = ()
- self._autoload(
- self.metadata,
- autoload_with,
- include_columns,
- exclude_columns,
- resolve_fks,
- _extend_on=_extend_on,
- _reflect_info=_reflect_info,
- )
-
- all_names = {c.name: c for c in self.c}
- self._extra_kwargs(**kwargs)
- self._init_items(*args, allow_replacements=True, all_names=all_names)
-
- def _extra_kwargs(self, **kwargs: Any) -> None:
- self._validate_dialect_kwargs(kwargs)
-
- def _init_collections(self) -> None:
- pass
-
- def _reset_exported(self) -> None:
- pass
-
- @util.ro_non_memoized_property
- def _autoincrement_column(self) -> Optional[Column[int]]:
- return self.primary_key._autoincrement_column
-
- @util.ro_memoized_property
- def _sentinel_column_characteristics(
- self,
- ) -> _SentinelColumnCharacterization:
- """determine a candidate column (or columns, in case of a client
- generated composite primary key) which can be used as an
- "insert sentinel" for an INSERT statement.
-
- The returned structure, :class:`_SentinelColumnCharacterization`,
- includes all the details needed by :class:`.Dialect` and
- :class:`.SQLCompiler` to determine if these column(s) can be used
- as an INSERT..RETURNING sentinel for a particular database
- dialect.
-
- .. versionadded:: 2.0.10
-
- """
-
- sentinel_is_explicit = False
- sentinel_is_autoinc = False
- the_sentinel: Optional[_typing_Sequence[Column[Any]]] = None
-
- # see if a column was explicitly marked "insert_sentinel=True".
- explicit_sentinel_col = self._sentinel_column
-
- if explicit_sentinel_col is not None:
- the_sentinel = (explicit_sentinel_col,)
- sentinel_is_explicit = True
-
- autoinc_col = self._autoincrement_column
- if sentinel_is_explicit and explicit_sentinel_col is autoinc_col:
- assert autoinc_col is not None
- sentinel_is_autoinc = True
- elif explicit_sentinel_col is None and autoinc_col is not None:
- the_sentinel = (autoinc_col,)
- sentinel_is_autoinc = True
-
- default_characterization = _SentinelDefaultCharacterization.UNKNOWN
-
- if the_sentinel:
- the_sentinel_zero = the_sentinel[0]
- if the_sentinel_zero.identity:
- if the_sentinel_zero.identity._increment_is_negative:
- if sentinel_is_explicit:
- raise exc.InvalidRequestError(
- "Can't use IDENTITY default with negative "
- "increment as an explicit sentinel column"
- )
- else:
- if sentinel_is_autoinc:
- autoinc_col = None
- sentinel_is_autoinc = False
- the_sentinel = None
- else:
- default_characterization = (
- _SentinelDefaultCharacterization.IDENTITY
- )
- elif (
- the_sentinel_zero.default is None
- and the_sentinel_zero.server_default is None
- ):
- if the_sentinel_zero.nullable:
- raise exc.InvalidRequestError(
- f"Column {the_sentinel_zero} has been marked as a "
- "sentinel "
- "column with no default generation function; it "
- "at least needs to be marked nullable=False assuming "
- "user-populated sentinel values will be used."
- )
- default_characterization = (
- _SentinelDefaultCharacterization.NONE
- )
- elif the_sentinel_zero.default is not None:
- if the_sentinel_zero.default.is_sentinel:
- default_characterization = (
- _SentinelDefaultCharacterization.SENTINEL_DEFAULT
- )
- elif default_is_sequence(the_sentinel_zero.default):
- if the_sentinel_zero.default._increment_is_negative:
- if sentinel_is_explicit:
- raise exc.InvalidRequestError(
- "Can't use SEQUENCE default with negative "
- "increment as an explicit sentinel column"
- )
- else:
- if sentinel_is_autoinc:
- autoinc_col = None
- sentinel_is_autoinc = False
- the_sentinel = None
-
- default_characterization = (
- _SentinelDefaultCharacterization.SEQUENCE
- )
- elif the_sentinel_zero.default.is_callable:
- default_characterization = (
- _SentinelDefaultCharacterization.CLIENTSIDE
- )
- elif the_sentinel_zero.server_default is not None:
- if sentinel_is_explicit:
- raise exc.InvalidRequestError(
- f"Column {the_sentinel[0]} can't be a sentinel column "
- "because it uses an explicit server side default "
- "that's not the Identity() default."
- )
-
- default_characterization = (
- _SentinelDefaultCharacterization.SERVERSIDE
- )
-
- if the_sentinel is None and self.primary_key:
- assert autoinc_col is None
-
- # determine for non-autoincrement pk if all elements are
- # client side
- for _pkc in self.primary_key:
- if _pkc.server_default is not None or (
- _pkc.default and not _pkc.default.is_callable
- ):
- break
- else:
- the_sentinel = tuple(self.primary_key)
- default_characterization = (
- _SentinelDefaultCharacterization.CLIENTSIDE
- )
-
- return _SentinelColumnCharacterization(
- the_sentinel,
- sentinel_is_explicit,
- sentinel_is_autoinc,
- default_characterization,
- )
-
- @property
- def autoincrement_column(self) -> Optional[Column[int]]:
- """Returns the :class:`.Column` object which currently represents
- the "auto increment" column, if any, else returns None.
-
- This is based on the rules for :class:`.Column` as defined by the
- :paramref:`.Column.autoincrement` parameter, which generally means the
- column within a single integer column primary key constraint that is
- not constrained by a foreign key. If the table does not have such
- a primary key constraint, then there's no "autoincrement" column.
- A :class:`.Table` may have only one column defined as the
- "autoincrement" column.
-
- .. versionadded:: 2.0.4
-
- .. seealso::
-
- :paramref:`.Column.autoincrement`
-
- """
- return self._autoincrement_column
-
- @property
- def key(self) -> str:
- """Return the 'key' for this :class:`_schema.Table`.
-
- This value is used as the dictionary key within the
- :attr:`_schema.MetaData.tables` collection. It is typically the same
- as that of :attr:`_schema.Table.name` for a table with no
- :attr:`_schema.Table.schema`
- set; otherwise it is typically of the form
- ``schemaname.tablename``.
-
- """
- return _get_table_key(self.name, self.schema)
-
- def __repr__(self) -> str:
- return "Table(%s)" % ", ".join(
- [repr(self.name)]
- + [repr(self.metadata)]
- + [repr(x) for x in self.columns]
- + ["%s=%s" % (k, repr(getattr(self, k))) for k in ["schema"]]
- )
-
- def __str__(self) -> str:
- return _get_table_key(self.description, self.schema)
-
- def add_is_dependent_on(self, table: Table) -> None:
- """Add a 'dependency' for this Table.
-
- This is another Table object which must be created
- first before this one can, or dropped after this one.
-
- Usually, dependencies between tables are determined via
- ForeignKey objects. However, for other situations that
- create dependencies outside of foreign keys (rules, inheriting),
- this method can manually establish such a link.
-
- """
- self._extra_dependencies.add(table)
-
- def append_column(
- self, column: ColumnClause[Any], replace_existing: bool = False
- ) -> None:
- """Append a :class:`_schema.Column` to this :class:`_schema.Table`.
-
- The "key" of the newly added :class:`_schema.Column`, i.e. the
- value of its ``.key`` attribute, will then be available
- in the ``.c`` collection of this :class:`_schema.Table`, and the
- column definition will be included in any CREATE TABLE, SELECT,
- UPDATE, etc. statements generated from this :class:`_schema.Table`
- construct.
-
- Note that this does **not** change the definition of the table
- as it exists within any underlying database, assuming that
- table has already been created in the database. Relational
- databases support the addition of columns to existing tables
- using the SQL ALTER command, which would need to be
- emitted for an already-existing table that doesn't contain
- the newly added column.
-
- :param replace_existing: When ``True``, allows replacing existing
- columns. When ``False``, the default, an warning will be raised
- if a column with the same ``.key`` already exists. A future
- version of sqlalchemy will instead rise a warning.
-
- .. versionadded:: 1.4.0
- """
-
- try:
- column._set_parent_with_dispatch(
- self,
- allow_replacements=replace_existing,
- all_names={c.name: c for c in self.c},
- )
- except exc.DuplicateColumnError as de:
- raise exc.DuplicateColumnError(
- f"{de.args[0]} Specify replace_existing=True to "
- "Table.append_column() to replace an "
- "existing column."
- ) from de
-
- def append_constraint(self, constraint: Union[Index, Constraint]) -> None:
- """Append a :class:`_schema.Constraint` to this
- :class:`_schema.Table`.
-
- This has the effect of the constraint being included in any
- future CREATE TABLE statement, assuming specific DDL creation
- events have not been associated with the given
- :class:`_schema.Constraint` object.
-
- Note that this does **not** produce the constraint within the
- relational database automatically, for a table that already exists
- in the database. To add a constraint to an
- existing relational database table, the SQL ALTER command must
- be used. SQLAlchemy also provides the
- :class:`.AddConstraint` construct which can produce this SQL when
- invoked as an executable clause.
-
- """
-
- constraint._set_parent_with_dispatch(self)
-
- def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
- metadata = parent
- assert isinstance(metadata, MetaData)
- metadata._add_table(self.name, self.schema, self)
- self.metadata = metadata
-
- def create(self, bind: _CreateDropBind, checkfirst: bool = False) -> None:
- """Issue a ``CREATE`` statement for this
- :class:`_schema.Table`, using the given
- :class:`.Connection` or :class:`.Engine`
- for connectivity.
-
- .. seealso::
-
- :meth:`_schema.MetaData.create_all`.
-
- """
-
- bind._run_ddl_visitor(ddl.SchemaGenerator, self, checkfirst=checkfirst)
-
- def drop(self, bind: _CreateDropBind, checkfirst: bool = False) -> None:
- """Issue a ``DROP`` statement for this
- :class:`_schema.Table`, using the given
- :class:`.Connection` or :class:`.Engine` for connectivity.
-
- .. seealso::
-
- :meth:`_schema.MetaData.drop_all`.
-
- """
- bind._run_ddl_visitor(ddl.SchemaDropper, self, checkfirst=checkfirst)
-
- @util.deprecated(
- "1.4",
- ":meth:`_schema.Table.tometadata` is renamed to "
- ":meth:`_schema.Table.to_metadata`",
- )
- def tometadata(
- self,
- metadata: MetaData,
- schema: Union[str, Literal[SchemaConst.RETAIN_SCHEMA]] = RETAIN_SCHEMA,
- referred_schema_fn: Optional[
- Callable[
- [Table, Optional[str], ForeignKeyConstraint, Optional[str]],
- Optional[str],
- ]
- ] = None,
- name: Optional[str] = None,
- ) -> Table:
- """Return a copy of this :class:`_schema.Table`
- associated with a different
- :class:`_schema.MetaData`.
-
- See :meth:`_schema.Table.to_metadata` for a full description.
-
- """
- return self.to_metadata(
- metadata,
- schema=schema,
- referred_schema_fn=referred_schema_fn,
- name=name,
- )
-
- def to_metadata(
- self,
- metadata: MetaData,
- schema: Union[str, Literal[SchemaConst.RETAIN_SCHEMA]] = RETAIN_SCHEMA,
- referred_schema_fn: Optional[
- Callable[
- [Table, Optional[str], ForeignKeyConstraint, Optional[str]],
- Optional[str],
- ]
- ] = None,
- name: Optional[str] = None,
- ) -> Table:
- """Return a copy of this :class:`_schema.Table` associated with a
- different :class:`_schema.MetaData`.
-
- E.g.::
-
- m1 = MetaData()
-
- user = Table('user', m1, Column('id', Integer, primary_key=True))
-
- m2 = MetaData()
- user_copy = user.to_metadata(m2)
-
- .. versionchanged:: 1.4 The :meth:`_schema.Table.to_metadata` function
- was renamed from :meth:`_schema.Table.tometadata`.
-
-
- :param metadata: Target :class:`_schema.MetaData` object,
- into which the
- new :class:`_schema.Table` object will be created.
-
- :param schema: optional string name indicating the target schema.
- Defaults to the special symbol :attr:`.RETAIN_SCHEMA` which indicates
- that no change to the schema name should be made in the new
- :class:`_schema.Table`. If set to a string name, the new
- :class:`_schema.Table`
- will have this new name as the ``.schema``. If set to ``None``, the
- schema will be set to that of the schema set on the target
- :class:`_schema.MetaData`, which is typically ``None`` as well,
- unless
- set explicitly::
-
- m2 = MetaData(schema='newschema')
-
- # user_copy_one will have "newschema" as the schema name
- user_copy_one = user.to_metadata(m2, schema=None)
-
- m3 = MetaData() # schema defaults to None
-
- # user_copy_two will have None as the schema name
- user_copy_two = user.to_metadata(m3, schema=None)
-
- :param referred_schema_fn: optional callable which can be supplied
- in order to provide for the schema name that should be assigned
- to the referenced table of a :class:`_schema.ForeignKeyConstraint`.
- The callable accepts this parent :class:`_schema.Table`, the
- target schema that we are changing to, the
- :class:`_schema.ForeignKeyConstraint` object, and the existing
- "target schema" of that constraint. The function should return the
- string schema name that should be applied. To reset the schema
- to "none", return the symbol :data:`.BLANK_SCHEMA`. To effect no
- change, return ``None`` or :data:`.RETAIN_SCHEMA`.
-
- .. versionchanged:: 1.4.33 The ``referred_schema_fn`` function
- may return the :data:`.BLANK_SCHEMA` or :data:`.RETAIN_SCHEMA`
- symbols.
-
- E.g.::
-
- def referred_schema_fn(table, to_schema,
- constraint, referred_schema):
- if referred_schema == 'base_tables':
- return referred_schema
- else:
- return to_schema
-
- new_table = table.to_metadata(m2, schema="alt_schema",
- referred_schema_fn=referred_schema_fn)
-
- :param name: optional string name indicating the target table name.
- If not specified or None, the table name is retained. This allows
- a :class:`_schema.Table` to be copied to the same
- :class:`_schema.MetaData` target
- with a new name.
-
- """
- if name is None:
- name = self.name
-
- actual_schema: Optional[str]
-
- if schema is RETAIN_SCHEMA:
- actual_schema = self.schema
- elif schema is None:
- actual_schema = metadata.schema
- else:
- actual_schema = schema
- key = _get_table_key(name, actual_schema)
- if key in metadata.tables:
- util.warn(
- f"Table '{self.description}' already exists within the given "
- "MetaData - not copying."
- )
- return metadata.tables[key]
-
- args = []
- for col in self.columns:
- args.append(col._copy(schema=actual_schema))
- table = Table(
- name,
- metadata,
- schema=actual_schema,
- comment=self.comment,
- *args,
- **self.kwargs,
- )
- for const in self.constraints:
- if isinstance(const, ForeignKeyConstraint):
- referred_schema = const._referred_schema
- if referred_schema_fn:
- fk_constraint_schema = referred_schema_fn(
- self, actual_schema, const, referred_schema
- )
- else:
- fk_constraint_schema = (
- actual_schema
- if referred_schema == self.schema
- else None
- )
- table.append_constraint(
- const._copy(
- schema=fk_constraint_schema, target_table=table
- )
- )
- elif not const._type_bound:
- # skip unique constraints that would be generated
- # by the 'unique' flag on Column
- if const._column_flag:
- continue
-
- table.append_constraint(
- const._copy(schema=actual_schema, target_table=table)
- )
- for index in self.indexes:
- # skip indexes that would be generated
- # by the 'index' flag on Column
- if index._column_flag:
- continue
- Index(
- index.name,
- unique=index.unique,
- *[
- _copy_expression(expr, self, table)
- for expr in index._table_bound_expressions
- ],
- _table=table,
- **index.kwargs,
- )
- return self._schema_item_copy(table)
-
-
-class Column(DialectKWArgs, SchemaItem, ColumnClause[_T]):
- """Represents a column in a database table."""
-
- __visit_name__ = "column"
-
- inherit_cache = True
- key: str
-
- server_default: Optional[FetchedValue]
-
- def __init__(
- self,
- __name_pos: Optional[
- Union[str, _TypeEngineArgument[_T], SchemaEventTarget]
- ] = None,
- __type_pos: Optional[
- Union[_TypeEngineArgument[_T], SchemaEventTarget]
- ] = None,
- *args: SchemaEventTarget,
- name: Optional[str] = None,
- type_: Optional[_TypeEngineArgument[_T]] = None,
- autoincrement: _AutoIncrementType = "auto",
- default: Optional[Any] = None,
- doc: Optional[str] = None,
- key: Optional[str] = None,
- index: Optional[bool] = None,
- unique: Optional[bool] = None,
- info: Optional[_InfoType] = None,
- nullable: Optional[
- Union[bool, Literal[SchemaConst.NULL_UNSPECIFIED]]
- ] = SchemaConst.NULL_UNSPECIFIED,
- onupdate: Optional[Any] = None,
- primary_key: bool = False,
- server_default: Optional[_ServerDefaultArgument] = None,
- server_onupdate: Optional[FetchedValue] = None,
- quote: Optional[bool] = None,
- system: bool = False,
- comment: Optional[str] = None,
- insert_sentinel: bool = False,
- _omit_from_statements: bool = False,
- _proxies: Optional[Any] = None,
- **dialect_kwargs: Any,
- ):
- r"""
- Construct a new ``Column`` object.
-
- :param name: The name of this column as represented in the database.
- This argument may be the first positional argument, or specified
- via keyword.
-
- Names which contain no upper case characters
- will be treated as case insensitive names, and will not be quoted
- unless they are a reserved word. Names with any number of upper
- case characters will be quoted and sent exactly. Note that this
- behavior applies even for databases which standardize upper
- case names as case insensitive such as Oracle.
-
- The name field may be omitted at construction time and applied
- later, at any time before the Column is associated with a
- :class:`_schema.Table`. This is to support convenient
- usage within the :mod:`~sqlalchemy.ext.declarative` extension.
-
- :param type\_: The column's type, indicated using an instance which
- subclasses :class:`~sqlalchemy.types.TypeEngine`. If no arguments
- are required for the type, the class of the type can be sent
- as well, e.g.::
-
- # use a type with arguments
- Column('data', String(50))
-
- # use no arguments
- Column('level', Integer)
-
- The ``type`` argument may be the second positional argument
- or specified by keyword.
-
- If the ``type`` is ``None`` or is omitted, it will first default to
- the special type :class:`.NullType`. If and when this
- :class:`_schema.Column` is made to refer to another column using
- :class:`_schema.ForeignKey` and/or
- :class:`_schema.ForeignKeyConstraint`, the type
- of the remote-referenced column will be copied to this column as
- well, at the moment that the foreign key is resolved against that
- remote :class:`_schema.Column` object.
-
- :param \*args: Additional positional arguments include various
- :class:`.SchemaItem` derived constructs which will be applied
- as options to the column. These include instances of
- :class:`.Constraint`, :class:`_schema.ForeignKey`,
- :class:`.ColumnDefault`, :class:`.Sequence`, :class:`.Computed`
- :class:`.Identity`. In some cases an
- equivalent keyword argument is available such as ``server_default``,
- ``default`` and ``unique``.
-
- :param autoincrement: Set up "auto increment" semantics for an
- **integer primary key column with no foreign key dependencies**
- (see later in this docstring for a more specific definition).
- This may influence the :term:`DDL` that will be emitted for
- this column during a table create, as well as how the column
- will be considered when INSERT statements are compiled and
- executed.
-
- The default value is the string ``"auto"``,
- which indicates that a single-column (i.e. non-composite) primary key
- that is of an INTEGER type with no other client-side or server-side
- default constructs indicated should receive auto increment semantics
- automatically. Other values include ``True`` (force this column to
- have auto-increment semantics for a :term:`composite primary key` as
- well), ``False`` (this column should never have auto-increment
- semantics), and the string ``"ignore_fk"`` (special-case for foreign
- key columns, see below).
-
- The term "auto increment semantics" refers both to the kind of DDL
- that will be emitted for the column within a CREATE TABLE statement,
- when methods such as :meth:`.MetaData.create_all` and
- :meth:`.Table.create` are invoked, as well as how the column will be
- considered when an INSERT statement is compiled and emitted to the
- database:
-
- * **DDL rendering** (i.e. :meth:`.MetaData.create_all`,
- :meth:`.Table.create`): When used on a :class:`.Column` that has
- no other
- default-generating construct associated with it (such as a
- :class:`.Sequence` or :class:`.Identity` construct), the parameter
- will imply that database-specific keywords such as PostgreSQL
- ``SERIAL``, MySQL ``AUTO_INCREMENT``, or ``IDENTITY`` on SQL Server
- should also be rendered. Not every database backend has an
- "implied" default generator available; for example the Oracle
- backend always needs an explicit construct such as
- :class:`.Identity` to be included with a :class:`.Column` in order
- for the DDL rendered to include auto-generating constructs to also
- be produced in the database.
-
- * **INSERT semantics** (i.e. when a :func:`_sql.insert` construct is
- compiled into a SQL string and is then executed on a database using
- :meth:`_engine.Connection.execute` or equivalent): A single-row
- INSERT statement will be known to produce a new integer primary key
- value automatically for this column, which will be accessible
- after the statement is invoked via the
- :attr:`.CursorResult.inserted_primary_key` attribute upon the
- :class:`_result.Result` object. This also applies towards use of the
- ORM when ORM-mapped objects are persisted to the database,
- indicating that a new integer primary key will be available to
- become part of the :term:`identity key` for that object. This
- behavior takes place regardless of what DDL constructs are
- associated with the :class:`_schema.Column` and is independent
- of the "DDL Rendering" behavior discussed in the previous note
- above.
-
- The parameter may be set to ``True`` to indicate that a column which
- is part of a composite (i.e. multi-column) primary key should
- have autoincrement semantics, though note that only one column
- within a primary key may have this setting. It can also
- be set to ``True`` to indicate autoincrement semantics on a
- column that has a client-side or server-side default configured,
- however note that not all dialects can accommodate all styles
- of default as an "autoincrement". It can also be
- set to ``False`` on a single-column primary key that has a
- datatype of INTEGER in order to disable auto increment semantics
- for that column.
-
- The setting *only* has an effect for columns which are:
-
- * Integer derived (i.e. INT, SMALLINT, BIGINT).
-
- * Part of the primary key
-
- * Not referring to another column via :class:`_schema.ForeignKey`,
- unless
- the value is specified as ``'ignore_fk'``::
-
- # turn on autoincrement for this column despite
- # the ForeignKey()
- Column('id', ForeignKey('other.id'),
- primary_key=True, autoincrement='ignore_fk')
-
- It is typically not desirable to have "autoincrement" enabled on a
- column that refers to another via foreign key, as such a column is
- required to refer to a value that originates from elsewhere.
-
- The setting has these effects on columns that meet the
- above criteria:
-
- * DDL issued for the column, if the column does not already include
- a default generating construct supported by the backend such as
- :class:`.Identity`, will include database-specific
- keywords intended to signify this column as an
- "autoincrement" column for specific backends. Behavior for
- primary SQLAlchemy dialects includes:
-
- * AUTO INCREMENT on MySQL and MariaDB
- * SERIAL on PostgreSQL
- * IDENTITY on MS-SQL - this occurs even without the
- :class:`.Identity` construct as the
- :paramref:`.Column.autoincrement` parameter pre-dates this
- construct.
- * SQLite - SQLite integer primary key columns are implicitly
- "auto incrementing" and no additional keywords are rendered;
- to render the special SQLite keyword ``AUTOINCREMENT``
- is not included as this is unnecessary and not recommended
- by the database vendor. See the section
- :ref:`sqlite_autoincrement` for more background.
- * Oracle - The Oracle dialect has no default "autoincrement"
- feature available at this time, instead the :class:`.Identity`
- construct is recommended to achieve this (the :class:`.Sequence`
- construct may also be used).
- * Third-party dialects - consult those dialects' documentation
- for details on their specific behaviors.
-
- * When a single-row :func:`_sql.insert` construct is compiled and
- executed, which does not set the :meth:`_sql.Insert.inline`
- modifier, newly generated primary key values for this column
- will be automatically retrieved upon statement execution
- using a method specific to the database driver in use:
-
- * MySQL, SQLite - calling upon ``cursor.lastrowid()``
- (see
- `https://www.python.org/dev/peps/pep-0249/#lastrowid
- <https://www.python.org/dev/peps/pep-0249/#lastrowid>`_)
- * PostgreSQL, SQL Server, Oracle - use RETURNING or an equivalent
- construct when rendering an INSERT statement, and then retrieving
- the newly generated primary key values after execution
- * PostgreSQL, Oracle for :class:`_schema.Table` objects that
- set :paramref:`_schema.Table.implicit_returning` to False -
- for a :class:`.Sequence` only, the :class:`.Sequence` is invoked
- explicitly before the INSERT statement takes place so that the
- newly generated primary key value is available to the client
- * SQL Server for :class:`_schema.Table` objects that
- set :paramref:`_schema.Table.implicit_returning` to False -
- the ``SELECT scope_identity()`` construct is used after the
- INSERT statement is invoked to retrieve the newly generated
- primary key value.
- * Third-party dialects - consult those dialects' documentation
- for details on their specific behaviors.
-
- * For multiple-row :func:`_sql.insert` constructs invoked with
- a list of parameters (i.e. "executemany" semantics), primary-key
- retrieving behaviors are generally disabled, however there may
- be special APIs that may be used to retrieve lists of new
- primary key values for an "executemany", such as the psycopg2
- "fast insertmany" feature. Such features are very new and
- may not yet be well covered in documentation.
-
- :param default: A scalar, Python callable, or
- :class:`_expression.ColumnElement` expression representing the
- *default value* for this column, which will be invoked upon insert
- if this column is otherwise not specified in the VALUES clause of
- the insert. This is a shortcut to using :class:`.ColumnDefault` as
- a positional argument; see that class for full detail on the
- structure of the argument.
-
- Contrast this argument to
- :paramref:`_schema.Column.server_default`
- which creates a default generator on the database side.
-
- .. seealso::
-
- :ref:`metadata_defaults_toplevel`
-
- :param doc: optional String that can be used by the ORM or similar
- to document attributes on the Python side. This attribute does
- **not** render SQL comments; use the
- :paramref:`_schema.Column.comment`
- parameter for this purpose.
-
- :param key: An optional string identifier which will identify this
- ``Column`` object on the :class:`_schema.Table`.
- When a key is provided,
- this is the only identifier referencing the ``Column`` within the
- application, including ORM attribute mapping; the ``name`` field
- is used only when rendering SQL.
-
- :param index: When ``True``, indicates that a :class:`_schema.Index`
- construct will be automatically generated for this
- :class:`_schema.Column`, which will result in a "CREATE INDEX"
- statement being emitted for the :class:`_schema.Table` when the DDL
- create operation is invoked.
-
- Using this flag is equivalent to making use of the
- :class:`_schema.Index` construct explicitly at the level of the
- :class:`_schema.Table` construct itself::
-
- Table(
- "some_table",
- metadata,
- Column("x", Integer),
- Index("ix_some_table_x", "x")
- )
-
- To add the :paramref:`_schema.Index.unique` flag to the
- :class:`_schema.Index`, set both the
- :paramref:`_schema.Column.unique` and
- :paramref:`_schema.Column.index` flags to True simultaneously,
- which will have the effect of rendering the "CREATE UNIQUE INDEX"
- DDL instruction instead of "CREATE INDEX".
-
- The name of the index is generated using the
- :ref:`default naming convention <constraint_default_naming_convention>`
- which for the :class:`_schema.Index` construct is of the form
- ``ix_<tablename>_<columnname>``.
-
- As this flag is intended only as a convenience for the common case
- of adding a single-column, default configured index to a table
- definition, explicit use of the :class:`_schema.Index` construct
- should be preferred for most use cases, including composite indexes
- that encompass more than one column, indexes with SQL expressions
- or ordering, backend-specific index configuration options, and
- indexes that use a specific name.
-
- .. note:: the :attr:`_schema.Column.index` attribute on
- :class:`_schema.Column`
- **does not indicate** if this column is indexed or not, only
- if this flag was explicitly set here. To view indexes on
- a column, view the :attr:`_schema.Table.indexes` collection
- or use :meth:`_reflection.Inspector.get_indexes`.
-
- .. seealso::
-
- :ref:`schema_indexes`
-
- :ref:`constraint_naming_conventions`
-
- :paramref:`_schema.Column.unique`
-
- :param info: Optional data dictionary which will be populated into the
- :attr:`.SchemaItem.info` attribute of this object.
-
- :param nullable: When set to ``False``, will cause the "NOT NULL"
- phrase to be added when generating DDL for the column. When
- ``True``, will normally generate nothing (in SQL this defaults to
- "NULL"), except in some very specific backend-specific edge cases
- where "NULL" may render explicitly.
- Defaults to ``True`` unless :paramref:`_schema.Column.primary_key`
- is also ``True`` or the column specifies a :class:`_sql.Identity`,
- in which case it defaults to ``False``.
- This parameter is only used when issuing CREATE TABLE statements.
-
- .. note::
-
- When the column specifies a :class:`_sql.Identity` this
- parameter is in general ignored by the DDL compiler. The
- PostgreSQL database allows nullable identity column by
- setting this parameter to ``True`` explicitly.
-
- :param onupdate: A scalar, Python callable, or
- :class:`~sqlalchemy.sql.expression.ClauseElement` representing a
- default value to be applied to the column within UPDATE
- statements, which will be invoked upon update if this column is not
- present in the SET clause of the update. This is a shortcut to
- using :class:`.ColumnDefault` as a positional argument with
- ``for_update=True``.
-
- .. seealso::
-
- :ref:`metadata_defaults` - complete discussion of onupdate
-
- :param primary_key: If ``True``, marks this column as a primary key
- column. Multiple columns can have this flag set to specify
- composite primary keys. As an alternative, the primary key of a
- :class:`_schema.Table` can be specified via an explicit
- :class:`.PrimaryKeyConstraint` object.
-
- :param server_default: A :class:`.FetchedValue` instance, str, Unicode
- or :func:`~sqlalchemy.sql.expression.text` construct representing
- the DDL DEFAULT value for the column.
-
- String types will be emitted as-is, surrounded by single quotes::
-
- Column('x', Text, server_default="val")
-
- x TEXT DEFAULT 'val'
-
- A :func:`~sqlalchemy.sql.expression.text` expression will be
- rendered as-is, without quotes::
-
- Column('y', DateTime, server_default=text('NOW()'))
-
- y DATETIME DEFAULT NOW()
-
- Strings and text() will be converted into a
- :class:`.DefaultClause` object upon initialization.
-
- This parameter can also accept complex combinations of contextually
- valid SQLAlchemy expressions or constructs::
-
- from sqlalchemy import create_engine
- from sqlalchemy import Table, Column, MetaData, ARRAY, Text
- from sqlalchemy.dialects.postgresql import array
-
- engine = create_engine(
- 'postgresql+psycopg2://scott:tiger@localhost/mydatabase'
- )
- metadata_obj = MetaData()
- tbl = Table(
- "foo",
- metadata_obj,
- Column("bar",
- ARRAY(Text),
- server_default=array(["biz", "bang", "bash"])
- )
- )
- metadata_obj.create_all(engine)
-
- The above results in a table created with the following SQL::
-
- CREATE TABLE foo (
- bar TEXT[] DEFAULT ARRAY['biz', 'bang', 'bash']
- )
-
- Use :class:`.FetchedValue` to indicate that an already-existing
- column will generate a default value on the database side which
- will be available to SQLAlchemy for post-fetch after inserts. This
- construct does not specify any DDL and the implementation is left
- to the database, such as via a trigger.
-
- .. seealso::
-
- :ref:`server_defaults` - complete discussion of server side
- defaults
-
- :param server_onupdate: A :class:`.FetchedValue` instance
- representing a database-side default generation function,
- such as a trigger. This
- indicates to SQLAlchemy that a newly generated value will be
- available after updates. This construct does not actually
- implement any kind of generation function within the database,
- which instead must be specified separately.
-
-
- .. warning:: This directive **does not** currently produce MySQL's
- "ON UPDATE CURRENT_TIMESTAMP()" clause. See
- :ref:`mysql_timestamp_onupdate` for background on how to
- produce this clause.
-
- .. seealso::
-
- :ref:`triggered_columns`
-
- :param quote: Force quoting of this column's name on or off,
- corresponding to ``True`` or ``False``. When left at its default
- of ``None``, the column identifier will be quoted according to
- whether the name is case sensitive (identifiers with at least one
- upper case character are treated as case sensitive), or if it's a
- reserved word. This flag is only needed to force quoting of a
- reserved word which is not known by the SQLAlchemy dialect.
-
- :param unique: When ``True``, and the :paramref:`_schema.Column.index`
- parameter is left at its default value of ``False``,
- indicates that a :class:`_schema.UniqueConstraint`
- construct will be automatically generated for this
- :class:`_schema.Column`,
- which will result in a "UNIQUE CONSTRAINT" clause referring
- to this column being included
- in the ``CREATE TABLE`` statement emitted, when the DDL create
- operation for the :class:`_schema.Table` object is invoked.
-
- When this flag is ``True`` while the
- :paramref:`_schema.Column.index` parameter is simultaneously
- set to ``True``, the effect instead is that a
- :class:`_schema.Index` construct which includes the
- :paramref:`_schema.Index.unique` parameter set to ``True``
- is generated. See the documentation for
- :paramref:`_schema.Column.index` for additional detail.
-
- Using this flag is equivalent to making use of the
- :class:`_schema.UniqueConstraint` construct explicitly at the
- level of the :class:`_schema.Table` construct itself::
-
- Table(
- "some_table",
- metadata,
- Column("x", Integer),
- UniqueConstraint("x")
- )
-
- The :paramref:`_schema.UniqueConstraint.name` parameter
- of the unique constraint object is left at its default value
- of ``None``; in the absence of a :ref:`naming convention <constraint_naming_conventions>`
- for the enclosing :class:`_schema.MetaData`, the UNIQUE CONSTRAINT
- construct will be emitted as unnamed, which typically invokes
- a database-specific naming convention to take place.
-
- As this flag is intended only as a convenience for the common case
- of adding a single-column, default configured unique constraint to a table
- definition, explicit use of the :class:`_schema.UniqueConstraint` construct
- should be preferred for most use cases, including composite constraints
- that encompass more than one column, backend-specific index configuration options, and
- constraints that use a specific name.
-
- .. note:: the :attr:`_schema.Column.unique` attribute on
- :class:`_schema.Column`
- **does not indicate** if this column has a unique constraint or
- not, only if this flag was explicitly set here. To view
- indexes and unique constraints that may involve this column,
- view the
- :attr:`_schema.Table.indexes` and/or
- :attr:`_schema.Table.constraints` collections or use
- :meth:`_reflection.Inspector.get_indexes` and/or
- :meth:`_reflection.Inspector.get_unique_constraints`
-
- .. seealso::
-
- :ref:`schema_unique_constraint`
-
- :ref:`constraint_naming_conventions`
-
- :paramref:`_schema.Column.index`
-
- :param system: When ``True``, indicates this is a "system" column,
- that is a column which is automatically made available by the
- database, and should not be included in the columns list for a
- ``CREATE TABLE`` statement.
-
- For more elaborate scenarios where columns should be
- conditionally rendered differently on different backends,
- consider custom compilation rules for :class:`.CreateColumn`.
-
- :param comment: Optional string that will render an SQL comment on
- table creation.
-
- .. versionadded:: 1.2 Added the
- :paramref:`_schema.Column.comment`
- parameter to :class:`_schema.Column`.
-
- :param insert_sentinel: Marks this :class:`_schema.Column` as an
- :term:`insert sentinel` used for optimizing the performance of the
- :term:`insertmanyvalues` feature for tables that don't
- otherwise have qualifying primary key configurations.
-
- .. versionadded:: 2.0.10
-
- .. seealso::
-
- :func:`_schema.insert_sentinel` - all in one helper for declaring
- sentinel columns
-
- :ref:`engine_insertmanyvalues`
-
- :ref:`engine_insertmanyvalues_sentinel_columns`
-
-
- """ # noqa: E501, RST201, RST202
-
- l_args = [__name_pos, __type_pos] + list(args)
- del args
-
- if l_args:
- if isinstance(l_args[0], str):
- if name is not None:
- raise exc.ArgumentError(
- "May not pass name positionally and as a keyword."
- )
- name = l_args.pop(0) # type: ignore
- elif l_args[0] is None:
- l_args.pop(0)
- if l_args:
- coltype = l_args[0]
-
- if hasattr(coltype, "_sqla_type"):
- if type_ is not None:
- raise exc.ArgumentError(
- "May not pass type_ positionally and as a keyword."
- )
- type_ = l_args.pop(0) # type: ignore
- elif l_args[0] is None:
- l_args.pop(0)
-
- if name is not None:
- name = quoted_name(name, quote)
- elif quote is not None:
- raise exc.ArgumentError(
- "Explicit 'name' is required when sending 'quote' argument"
- )
-
- # name = None is expected to be an interim state
- # note this use case is legacy now that ORM declarative has a
- # dedicated "column" construct local to the ORM
- super().__init__(name, type_) # type: ignore
-
- self.key = key if key is not None else name # type: ignore
- self.primary_key = primary_key
- self._insert_sentinel = insert_sentinel
- self._omit_from_statements = _omit_from_statements
- self._user_defined_nullable = udn = nullable
- if udn is not NULL_UNSPECIFIED:
- self.nullable = udn
- else:
- self.nullable = not primary_key
-
- # these default to None because .index and .unique is *not*
- # an informational flag about Column - there can still be an
- # Index or UniqueConstraint referring to this Column.
- self.index = index
- self.unique = unique
-
- self.system = system
- self.doc = doc
- self.autoincrement: _AutoIncrementType = autoincrement
- self.constraints = set()
- self.foreign_keys = set()
- self.comment = comment
- self.computed = None
- self.identity = None
-
- # check if this Column is proxying another column
-
- if _proxies is not None:
- self._proxies = _proxies
- else:
- # otherwise, add DDL-related events
- self._set_type(self.type)
-
- if default is not None:
- if not isinstance(default, (ColumnDefault, Sequence)):
- default = ColumnDefault(default)
-
- self.default = default
- l_args.append(default)
- else:
- self.default = None
-
- if onupdate is not None:
- if not isinstance(onupdate, (ColumnDefault, Sequence)):
- onupdate = ColumnDefault(onupdate, for_update=True)
-
- self.onupdate = onupdate
- l_args.append(onupdate)
- else:
- self.onupdate = None
-
- if server_default is not None:
- if isinstance(server_default, FetchedValue):
- server_default = server_default._as_for_update(False)
- l_args.append(server_default)
- else:
- server_default = DefaultClause(server_default)
- l_args.append(server_default)
- self.server_default = server_default
-
- if server_onupdate is not None:
- if isinstance(server_onupdate, FetchedValue):
- server_onupdate = server_onupdate._as_for_update(True)
- l_args.append(server_onupdate)
- else:
- server_onupdate = DefaultClause(
- server_onupdate, for_update=True
- )
- l_args.append(server_onupdate)
- self.server_onupdate = server_onupdate
-
- self._init_items(*cast(_typing_Sequence[SchemaItem], l_args))
-
- util.set_creation_order(self)
-
- if info is not None:
- self.info = info
-
- self._extra_kwargs(**dialect_kwargs)
-
- table: Table
-
- constraints: Set[Constraint]
-
- foreign_keys: Set[ForeignKey]
- """A collection of all :class:`_schema.ForeignKey` marker objects
- associated with this :class:`_schema.Column`.
-
- Each object is a member of a :class:`_schema.Table`-wide
- :class:`_schema.ForeignKeyConstraint`.
-
- .. seealso::
-
- :attr:`_schema.Table.foreign_keys`
-
- """
-
- index: Optional[bool]
- """The value of the :paramref:`_schema.Column.index` parameter.
-
- Does not indicate if this :class:`_schema.Column` is actually indexed
- or not; use :attr:`_schema.Table.indexes`.
-
- .. seealso::
-
- :attr:`_schema.Table.indexes`
- """
-
- unique: Optional[bool]
- """The value of the :paramref:`_schema.Column.unique` parameter.
-
- Does not indicate if this :class:`_schema.Column` is actually subject to
- a unique constraint or not; use :attr:`_schema.Table.indexes` and
- :attr:`_schema.Table.constraints`.
-
- .. seealso::
-
- :attr:`_schema.Table.indexes`
-
- :attr:`_schema.Table.constraints`.
-
- """
-
- computed: Optional[Computed]
-
- identity: Optional[Identity]
-
- def _set_type(self, type_: TypeEngine[Any]) -> None:
- assert self.type._isnull or type_ is self.type
-
- self.type = type_
- if isinstance(self.type, SchemaEventTarget):
- self.type._set_parent_with_dispatch(self)
- for impl in self.type._variant_mapping.values():
- if isinstance(impl, SchemaEventTarget):
- impl._set_parent_with_dispatch(self)
-
- @HasMemoized.memoized_attribute
- def _default_description_tuple(self) -> _DefaultDescriptionTuple:
- """used by default.py -> _process_execute_defaults()"""
-
- return _DefaultDescriptionTuple._from_column_default(self.default)
-
- @HasMemoized.memoized_attribute
- def _onupdate_description_tuple(self) -> _DefaultDescriptionTuple:
- """used by default.py -> _process_execute_defaults()"""
- return _DefaultDescriptionTuple._from_column_default(self.onupdate)
-
- @util.memoized_property
- def _gen_static_annotations_cache_key(self) -> bool: # type: ignore
- """special attribute used by cache key gen, if true, we will
- use a static cache key for the annotations dictionary, else we
- will generate a new cache key for annotations each time.
-
- Added for #8790
-
- """
- return self.table is not None and self.table._is_table
-
- def _extra_kwargs(self, **kwargs: Any) -> None:
- self._validate_dialect_kwargs(kwargs)
-
- def __str__(self) -> str:
- if self.name is None:
- return "(no name)"
- elif self.table is not None:
- if self.table.named_with_column:
- return self.table.description + "." + self.description
- else:
- return self.description
- else:
- return self.description
-
- def references(self, column: Column[Any]) -> bool:
- """Return True if this Column references the given column via foreign
- key."""
-
- for fk in self.foreign_keys:
- if fk.column.proxy_set.intersection(column.proxy_set):
- return True
- else:
- return False
-
- def append_foreign_key(self, fk: ForeignKey) -> None:
- fk._set_parent_with_dispatch(self)
-
- def __repr__(self) -> str:
- kwarg = []
- if self.key != self.name:
- kwarg.append("key")
- if self.primary_key:
- kwarg.append("primary_key")
- if not self.nullable:
- kwarg.append("nullable")
- if self.onupdate:
- kwarg.append("onupdate")
- if self.default:
- kwarg.append("default")
- if self.server_default:
- kwarg.append("server_default")
- if self.comment:
- kwarg.append("comment")
- return "Column(%s)" % ", ".join(
- [repr(self.name)]
- + [repr(self.type)]
- + [repr(x) for x in self.foreign_keys if x is not None]
- + [repr(x) for x in self.constraints]
- + [
- (
- self.table is not None
- and "table=<%s>" % self.table.description
- or "table=None"
- )
- ]
- + ["%s=%s" % (k, repr(getattr(self, k))) for k in kwarg]
- )
-
- def _set_parent( # type: ignore[override]
- self,
- parent: SchemaEventTarget,
- *,
- all_names: Dict[str, Column[Any]],
- allow_replacements: bool,
- **kw: Any,
- ) -> None:
- table = parent
- assert isinstance(table, Table)
- if not self.name:
- raise exc.ArgumentError(
- "Column must be constructed with a non-blank name or "
- "assign a non-blank .name before adding to a Table."
- )
-
- self._reset_memoizations()
-
- if self.key is None:
- self.key = self.name
-
- existing = getattr(self, "table", None)
- if existing is not None and existing is not table:
- raise exc.ArgumentError(
- f"Column object '{self.key}' already "
- f"assigned to Table '{existing.description}'"
- )
-
- extra_remove = None
- existing_col = None
- conflicts_on = ""
-
- if self.key in table._columns:
- existing_col = table._columns[self.key]
- if self.key == self.name:
- conflicts_on = "name"
- else:
- conflicts_on = "key"
- elif self.name in all_names:
- existing_col = all_names[self.name]
- extra_remove = {existing_col}
- conflicts_on = "name"
-
- if existing_col is not None:
- if existing_col is not self:
- if not allow_replacements:
- raise exc.DuplicateColumnError(
- f"A column with {conflicts_on} "
- f"""'{
- self.key if conflicts_on == 'key' else self.name
- }' """
- f"is already present in table '{table.name}'."
- )
- for fk in existing_col.foreign_keys:
- table.foreign_keys.remove(fk)
- if fk.constraint in table.constraints:
- # this might have been removed
- # already, if it's a composite constraint
- # and more than one col being replaced
- table.constraints.remove(fk.constraint)
-
- if extra_remove and existing_col is not None and self.key == self.name:
- util.warn(
- f'Column with user-specified key "{existing_col.key}" is '
- "being replaced with "
- f'plain named column "{self.name}", '
- f'key "{existing_col.key}" is being removed. If this is a '
- "reflection operation, specify autoload_replace=False to "
- "prevent this replacement."
- )
- table._columns.replace(self, extra_remove=extra_remove)
- all_names[self.name] = self
- self.table = table
-
- if self._insert_sentinel:
- if self.table._sentinel_column is not None:
- raise exc.ArgumentError(
- "a Table may have only one explicit sentinel column"
- )
- self.table._sentinel_column = self
-
- if self.primary_key:
- table.primary_key._replace(self)
- elif self.key in table.primary_key:
- raise exc.ArgumentError(
- f"Trying to redefine primary-key column '{self.key}' as a "
- f"non-primary-key column on table '{table.fullname}'"
- )
-
- if self.index:
- if isinstance(self.index, str):
- raise exc.ArgumentError(
- "The 'index' keyword argument on Column is boolean only. "
- "To create indexes with a specific name, create an "
- "explicit Index object external to the Table."
- )
- table.append_constraint(
- Index(
- None, self.key, unique=bool(self.unique), _column_flag=True
- )
- )
-
- elif self.unique:
- if isinstance(self.unique, str):
- raise exc.ArgumentError(
- "The 'unique' keyword argument on Column is boolean "
- "only. To create unique constraints or indexes with a "
- "specific name, append an explicit UniqueConstraint to "
- "the Table's list of elements, or create an explicit "
- "Index object external to the Table."
- )
- table.append_constraint(
- UniqueConstraint(self.key, _column_flag=True)
- )
-
- self._setup_on_memoized_fks(lambda fk: fk._set_remote_table(table))
-
- if self.identity and (
- isinstance(self.default, Sequence)
- or isinstance(self.onupdate, Sequence)
- ):
- raise exc.ArgumentError(
- "An column cannot specify both Identity and Sequence."
- )
-
- def _setup_on_memoized_fks(self, fn: Callable[..., Any]) -> None:
- fk_keys = [
- ((self.table.key, self.key), False),
- ((self.table.key, self.name), True),
- ]
- for fk_key, link_to_name in fk_keys:
- if fk_key in self.table.metadata._fk_memos:
- for fk in self.table.metadata._fk_memos[fk_key]:
- if fk.link_to_name is link_to_name:
- fn(fk)
-
- def _on_table_attach(self, fn: Callable[..., Any]) -> None:
- if self.table is not None:
- fn(self, self.table)
- else:
- event.listen(self, "after_parent_attach", fn)
-
- @util.deprecated(
- "1.4",
- "The :meth:`_schema.Column.copy` method is deprecated "
- "and will be removed in a future release.",
- )
- def copy(self, **kw: Any) -> Column[Any]:
- return self._copy(**kw)
-
- def _copy(self, **kw: Any) -> Column[Any]:
- """Create a copy of this ``Column``, uninitialized.
-
- This is used in :meth:`_schema.Table.to_metadata` and by the ORM.
-
- """
-
- # Constraint objects plus non-constraint-bound ForeignKey objects
- args: List[SchemaItem] = [
- c._copy(**kw) for c in self.constraints if not c._type_bound
- ] + [c._copy(**kw) for c in self.foreign_keys if not c.constraint]
-
- # ticket #5276
- column_kwargs = {}
- for dialect_name in self.dialect_options:
- dialect_options = self.dialect_options[dialect_name]._non_defaults
- for (
- dialect_option_key,
- dialect_option_value,
- ) in dialect_options.items():
- column_kwargs[dialect_name + "_" + dialect_option_key] = (
- dialect_option_value
- )
-
- server_default = self.server_default
- server_onupdate = self.server_onupdate
- if isinstance(server_default, (Computed, Identity)):
- # TODO: likely should be copied in all cases
- args.append(server_default._copy(**kw))
- server_default = server_onupdate = None
-
- type_ = self.type
- if isinstance(type_, SchemaEventTarget):
- type_ = type_.copy(**kw)
-
- # TODO: DefaultGenerator is not copied here! it's just used again
- # with _set_parent() pointing to the old column. see the new
- # use of _copy() in the new _merge() method
-
- c = self._constructor(
- name=self.name,
- type_=type_,
- key=self.key,
- primary_key=self.primary_key,
- unique=self.unique,
- system=self.system,
- # quote=self.quote, # disabled 2013-08-27 (commit 031ef080)
- index=self.index,
- autoincrement=self.autoincrement,
- default=self.default,
- server_default=server_default,
- onupdate=self.onupdate,
- server_onupdate=server_onupdate,
- doc=self.doc,
- comment=self.comment,
- _omit_from_statements=self._omit_from_statements,
- insert_sentinel=self._insert_sentinel,
- *args,
- **column_kwargs,
- )
-
- # copy the state of "nullable" exactly, to accommodate for
- # ORM flipping the .nullable flag directly
- c.nullable = self.nullable
- c._user_defined_nullable = self._user_defined_nullable
-
- return self._schema_item_copy(c)
-
- def _merge(self, other: Column[Any]) -> None:
- """merge the elements of another column into this one.
-
- this is used by ORM pep-593 merge and will likely need a lot
- of fixes.
-
-
- """
-
- if self.primary_key:
- other.primary_key = True
-
- if self.autoincrement != "auto" and other.autoincrement == "auto":
- other.autoincrement = self.autoincrement
-
- if self.system:
- other.system = self.system
-
- if self.info:
- other.info.update(self.info)
-
- type_ = self.type
- if not type_._isnull and other.type._isnull:
- if isinstance(type_, SchemaEventTarget):
- type_ = type_.copy()
-
- other.type = type_
-
- if isinstance(type_, SchemaEventTarget):
- type_._set_parent_with_dispatch(other)
-
- for impl in type_._variant_mapping.values():
- if isinstance(impl, SchemaEventTarget):
- impl._set_parent_with_dispatch(other)
-
- if (
- self._user_defined_nullable is not NULL_UNSPECIFIED
- and other._user_defined_nullable is NULL_UNSPECIFIED
- ):
- other.nullable = self.nullable
- other._user_defined_nullable = self._user_defined_nullable
-
- if self.default is not None and other.default is None:
- new_default = self.default._copy()
- new_default._set_parent(other)
-
- if self.server_default and other.server_default is None:
- new_server_default = self.server_default
- if isinstance(new_server_default, FetchedValue):
- new_server_default = new_server_default._copy()
- new_server_default._set_parent(other)
- else:
- other.server_default = new_server_default
-
- if self.server_onupdate and other.server_onupdate is None:
- new_server_onupdate = self.server_onupdate
- new_server_onupdate = new_server_onupdate._copy()
- new_server_onupdate._set_parent(other)
-
- if self.onupdate and other.onupdate is None:
- new_onupdate = self.onupdate._copy()
- new_onupdate._set_parent(other)
-
- if self.index in (True, False) and other.index is None:
- other.index = self.index
-
- if self.unique in (True, False) and other.unique is None:
- other.unique = self.unique
-
- if self.doc and other.doc is None:
- other.doc = self.doc
-
- if self.comment and other.comment is None:
- other.comment = self.comment
-
- for const in self.constraints:
- if not const._type_bound:
- new_const = const._copy()
- new_const._set_parent(other)
-
- for fk in self.foreign_keys:
- if not fk.constraint:
- new_fk = fk._copy()
- new_fk._set_parent(other)
-
- def _make_proxy(
- self,
- selectable: FromClause,
- name: Optional[str] = None,
- key: Optional[str] = None,
- name_is_truncatable: bool = False,
- compound_select_cols: Optional[
- _typing_Sequence[ColumnElement[Any]]
- ] = None,
- **kw: Any,
- ) -> Tuple[str, ColumnClause[_T]]:
- """Create a *proxy* for this column.
-
- This is a copy of this ``Column`` referenced by a different parent
- (such as an alias or select statement). The column should
- be used only in select scenarios, as its full DDL/default
- information is not transferred.
-
- """
-
- fk = [
- ForeignKey(
- col if col is not None else f._colspec,
- _unresolvable=col is None,
- _constraint=f.constraint,
- )
- for f, col in [
- (fk, fk._resolve_column(raiseerr=False))
- for fk in self.foreign_keys
- ]
- ]
-
- if name is None and self.name is None:
- raise exc.InvalidRequestError(
- "Cannot initialize a sub-selectable"
- " with this Column object until its 'name' has "
- "been assigned."
- )
- try:
- c = self._constructor(
- (
- coercions.expect(
- roles.TruncatedLabelRole, name if name else self.name
- )
- if name_is_truncatable
- else (name or self.name)
- ),
- self.type,
- # this may actually be ._proxy_key when the key is incoming
- key=key if key else name if name else self.key,
- primary_key=self.primary_key,
- nullable=self.nullable,
- _proxies=(
- list(compound_select_cols)
- if compound_select_cols
- else [self]
- ),
- *fk,
- )
- except TypeError as err:
- raise TypeError(
- "Could not create a copy of this %r object. "
- "Ensure the class includes a _constructor() "
- "attribute or method which accepts the "
- "standard Column constructor arguments, or "
- "references the Column class itself." % self.__class__
- ) from err
-
- c.table = selectable
- c._propagate_attrs = selectable._propagate_attrs
- if selectable._is_clone_of is not None:
- c._is_clone_of = selectable._is_clone_of.columns.get(c.key)
- if self.primary_key:
- selectable.primary_key.add(c) # type: ignore
- if fk:
- selectable.foreign_keys.update(fk) # type: ignore
- return c.key, c
-
-
-def insert_sentinel(
- name: Optional[str] = None,
- type_: Optional[_TypeEngineArgument[_T]] = None,
- *,
- default: Optional[Any] = None,
- omit_from_statements: bool = True,
-) -> Column[Any]:
- """Provides a surrogate :class:`_schema.Column` that will act as a
- dedicated insert :term:`sentinel` column, allowing efficient bulk
- inserts with deterministic RETURNING sorting for tables that
- don't otherwise have qualifying primary key configurations.
-
- Adding this column to a :class:`.Table` object requires that a
- corresponding database table actually has this column present, so if adding
- it to an existing model, existing database tables would need to be migrated
- (e.g. using ALTER TABLE or similar) to include this column.
-
- For background on how this object is used, see the section
- :ref:`engine_insertmanyvalues_sentinel_columns` as part of the
- section :ref:`engine_insertmanyvalues`.
-
- The :class:`_schema.Column` returned will be a nullable integer column by
- default and make use of a sentinel-specific default generator used only in
- "insertmanyvalues" operations.
-
- .. seealso::
-
- :func:`_orm.orm_insert_sentinel`
-
- :paramref:`_schema.Column.insert_sentinel`
-
- :ref:`engine_insertmanyvalues`
-
- :ref:`engine_insertmanyvalues_sentinel_columns`
-
-
- .. versionadded:: 2.0.10
-
- """
- return Column(
- name=name,
- type_=type_api.INTEGERTYPE if type_ is None else type_,
- default=(
- default if default is not None else _InsertSentinelColumnDefault()
- ),
- _omit_from_statements=omit_from_statements,
- insert_sentinel=True,
- )
-
-
-class ForeignKey(DialectKWArgs, SchemaItem):
- """Defines a dependency between two columns.
-
- ``ForeignKey`` is specified as an argument to a :class:`_schema.Column`
- object,
- e.g.::
-
- t = Table("remote_table", metadata,
- Column("remote_id", ForeignKey("main_table.id"))
- )
-
- Note that ``ForeignKey`` is only a marker object that defines
- a dependency between two columns. The actual constraint
- is in all cases represented by the :class:`_schema.ForeignKeyConstraint`
- object. This object will be generated automatically when
- a ``ForeignKey`` is associated with a :class:`_schema.Column` which
- in turn is associated with a :class:`_schema.Table`. Conversely,
- when :class:`_schema.ForeignKeyConstraint` is applied to a
- :class:`_schema.Table`,
- ``ForeignKey`` markers are automatically generated to be
- present on each associated :class:`_schema.Column`, which are also
- associated with the constraint object.
-
- Note that you cannot define a "composite" foreign key constraint,
- that is a constraint between a grouping of multiple parent/child
- columns, using ``ForeignKey`` objects. To define this grouping,
- the :class:`_schema.ForeignKeyConstraint` object must be used, and applied
- to the :class:`_schema.Table`. The associated ``ForeignKey`` objects
- are created automatically.
-
- The ``ForeignKey`` objects associated with an individual
- :class:`_schema.Column`
- object are available in the `foreign_keys` collection
- of that column.
-
- Further examples of foreign key configuration are in
- :ref:`metadata_foreignkeys`.
-
- """
-
- __visit_name__ = "foreign_key"
-
- parent: Column[Any]
-
- _table_column: Optional[Column[Any]]
-
- def __init__(
- self,
- column: _DDLColumnArgument,
- _constraint: Optional[ForeignKeyConstraint] = None,
- use_alter: bool = False,
- name: _ConstraintNameArgument = None,
- onupdate: Optional[str] = None,
- ondelete: Optional[str] = None,
- deferrable: Optional[bool] = None,
- initially: Optional[str] = None,
- link_to_name: bool = False,
- match: Optional[str] = None,
- info: Optional[_InfoType] = None,
- comment: Optional[str] = None,
- _unresolvable: bool = False,
- **dialect_kw: Any,
- ):
- r"""
- Construct a column-level FOREIGN KEY.
-
- The :class:`_schema.ForeignKey` object when constructed generates a
- :class:`_schema.ForeignKeyConstraint`
- which is associated with the parent
- :class:`_schema.Table` object's collection of constraints.
-
- :param column: A single target column for the key relationship. A
- :class:`_schema.Column` object or a column name as a string:
- ``tablename.columnkey`` or ``schema.tablename.columnkey``.
- ``columnkey`` is the ``key`` which has been assigned to the column
- (defaults to the column name itself), unless ``link_to_name`` is
- ``True`` in which case the rendered name of the column is used.
-
- :param name: Optional string. An in-database name for the key if
- `constraint` is not provided.
-
- :param onupdate: Optional string. If set, emit ON UPDATE <value> when
- issuing DDL for this constraint. Typical values include CASCADE,
- DELETE and RESTRICT.
-
- :param ondelete: Optional string. If set, emit ON DELETE <value> when
- issuing DDL for this constraint. Typical values include CASCADE,
- DELETE and RESTRICT.
-
- :param deferrable: Optional bool. If set, emit DEFERRABLE or NOT
- DEFERRABLE when issuing DDL for this constraint.
-
- :param initially: Optional string. If set, emit INITIALLY <value> when
- issuing DDL for this constraint.
-
- :param link_to_name: if True, the string name given in ``column`` is
- the rendered name of the referenced column, not its locally
- assigned ``key``.
-
- :param use_alter: passed to the underlying
- :class:`_schema.ForeignKeyConstraint`
- to indicate the constraint should
- be generated/dropped externally from the CREATE TABLE/ DROP TABLE
- statement. See :paramref:`_schema.ForeignKeyConstraint.use_alter`
- for further description.
-
- .. seealso::
-
- :paramref:`_schema.ForeignKeyConstraint.use_alter`
-
- :ref:`use_alter`
-
- :param match: Optional string. If set, emit MATCH <value> when issuing
- DDL for this constraint. Typical values include SIMPLE, PARTIAL
- and FULL.
-
- :param info: Optional data dictionary which will be populated into the
- :attr:`.SchemaItem.info` attribute of this object.
-
- :param comment: Optional string that will render an SQL comment on
- foreign key constraint creation.
-
- .. versionadded:: 2.0
-
- :param \**dialect_kw: Additional keyword arguments are dialect
- specific, and passed in the form ``<dialectname>_<argname>``. The
- arguments are ultimately handled by a corresponding
- :class:`_schema.ForeignKeyConstraint`.
- See the documentation regarding
- an individual dialect at :ref:`dialect_toplevel` for detail on
- documented arguments.
-
- """
-
- self._colspec = coercions.expect(roles.DDLReferredColumnRole, column)
- self._unresolvable = _unresolvable
-
- if isinstance(self._colspec, str):
- self._table_column = None
- else:
- self._table_column = self._colspec
-
- if not isinstance(
- self._table_column.table, (type(None), TableClause)
- ):
- raise exc.ArgumentError(
- "ForeignKey received Column not bound "
- "to a Table, got: %r" % self._table_column.table
- )
-
- # the linked ForeignKeyConstraint.
- # ForeignKey will create this when parent Column
- # is attached to a Table, *or* ForeignKeyConstraint
- # object passes itself in when creating ForeignKey
- # markers.
- self.constraint = _constraint
-
- # .parent is not Optional under normal use
- self.parent = None # type: ignore
-
- self.use_alter = use_alter
- self.name = name
- self.onupdate = onupdate
- self.ondelete = ondelete
- self.deferrable = deferrable
- self.initially = initially
- self.link_to_name = link_to_name
- self.match = match
- self.comment = comment
- if info:
- self.info = info
- self._unvalidated_dialect_kw = dialect_kw
-
- def __repr__(self) -> str:
- return "ForeignKey(%r)" % self._get_colspec()
-
- @util.deprecated(
- "1.4",
- "The :meth:`_schema.ForeignKey.copy` method is deprecated "
- "and will be removed in a future release.",
- )
- def copy(self, *, schema: Optional[str] = None, **kw: Any) -> ForeignKey:
- return self._copy(schema=schema, **kw)
-
- def _copy(self, *, schema: Optional[str] = None, **kw: Any) -> ForeignKey:
- """Produce a copy of this :class:`_schema.ForeignKey` object.
-
- The new :class:`_schema.ForeignKey` will not be bound
- to any :class:`_schema.Column`.
-
- This method is usually used by the internal
- copy procedures of :class:`_schema.Column`, :class:`_schema.Table`,
- and :class:`_schema.MetaData`.
-
- :param schema: The returned :class:`_schema.ForeignKey` will
- reference the original table and column name, qualified
- by the given string schema name.
-
- """
- fk = ForeignKey(
- self._get_colspec(schema=schema),
- use_alter=self.use_alter,
- name=self.name,
- onupdate=self.onupdate,
- ondelete=self.ondelete,
- deferrable=self.deferrable,
- initially=self.initially,
- link_to_name=self.link_to_name,
- match=self.match,
- comment=self.comment,
- **self._unvalidated_dialect_kw,
- )
- return self._schema_item_copy(fk)
-
- def _get_colspec(
- self,
- schema: Optional[
- Union[
- str,
- Literal[SchemaConst.RETAIN_SCHEMA, SchemaConst.BLANK_SCHEMA],
- ]
- ] = None,
- table_name: Optional[str] = None,
- _is_copy: bool = False,
- ) -> str:
- """Return a string based 'column specification' for this
- :class:`_schema.ForeignKey`.
-
- This is usually the equivalent of the string-based "tablename.colname"
- argument first passed to the object's constructor.
-
- """
- if schema not in (None, RETAIN_SCHEMA):
- _schema, tname, colname = self._column_tokens
- if table_name is not None:
- tname = table_name
- if schema is BLANK_SCHEMA:
- return "%s.%s" % (tname, colname)
- else:
- return "%s.%s.%s" % (schema, tname, colname)
- elif table_name:
- schema, tname, colname = self._column_tokens
- if schema:
- return "%s.%s.%s" % (schema, table_name, colname)
- else:
- return "%s.%s" % (table_name, colname)
- elif self._table_column is not None:
- if self._table_column.table is None:
- if _is_copy:
- raise exc.InvalidRequestError(
- f"Can't copy ForeignKey object which refers to "
- f"non-table bound Column {self._table_column!r}"
- )
- else:
- return self._table_column.key
- return "%s.%s" % (
- self._table_column.table.fullname,
- self._table_column.key,
- )
- else:
- assert isinstance(self._colspec, str)
- return self._colspec
-
- @property
- def _referred_schema(self) -> Optional[str]:
- return self._column_tokens[0]
-
- def _table_key(self) -> Any:
- if self._table_column is not None:
- if self._table_column.table is None:
- return None
- else:
- return self._table_column.table.key
- else:
- schema, tname, colname = self._column_tokens
- return _get_table_key(tname, schema)
-
- target_fullname = property(_get_colspec)
-
- def references(self, table: Table) -> bool:
- """Return True if the given :class:`_schema.Table`
- is referenced by this
- :class:`_schema.ForeignKey`."""
-
- return table.corresponding_column(self.column) is not None
-
- def get_referent(self, table: FromClause) -> Optional[Column[Any]]:
- """Return the :class:`_schema.Column` in the given
- :class:`_schema.Table` (or any :class:`.FromClause`)
- referenced by this :class:`_schema.ForeignKey`.
-
- Returns None if this :class:`_schema.ForeignKey`
- does not reference the given
- :class:`_schema.Table`.
-
- """
- # our column is a Column, and any subquery etc. proxying us
- # would be doing so via another Column, so that's what would
- # be returned here
- return table.columns.corresponding_column(self.column) # type: ignore
-
- @util.memoized_property
- def _column_tokens(self) -> Tuple[Optional[str], str, Optional[str]]:
- """parse a string-based _colspec into its component parts."""
-
- m = self._get_colspec().split(".")
- if m is None:
- raise exc.ArgumentError(
- f"Invalid foreign key column specification: {self._colspec}"
- )
- if len(m) == 1:
- tname = m.pop()
- colname = None
- else:
- colname = m.pop()
- tname = m.pop()
-
- # A FK between column 'bar' and table 'foo' can be
- # specified as 'foo', 'foo.bar', 'dbo.foo.bar',
- # 'otherdb.dbo.foo.bar'. Once we have the column name and
- # the table name, treat everything else as the schema
- # name. Some databases (e.g. Sybase) support
- # inter-database foreign keys. See tickets#1341 and --
- # indirectly related -- Ticket #594. This assumes that '.'
- # will never appear *within* any component of the FK.
-
- if len(m) > 0:
- schema = ".".join(m)
- else:
- schema = None
- return schema, tname, colname
-
- def _resolve_col_tokens(self) -> Tuple[Table, str, Optional[str]]:
- if self.parent is None:
- raise exc.InvalidRequestError(
- "this ForeignKey object does not yet have a "
- "parent Column associated with it."
- )
-
- elif self.parent.table is None:
- raise exc.InvalidRequestError(
- "this ForeignKey's parent column is not yet associated "
- "with a Table."
- )
-
- parenttable = self.parent.table
-
- if self._unresolvable:
- schema, tname, colname = self._column_tokens
- tablekey = _get_table_key(tname, schema)
- return parenttable, tablekey, colname
-
- # assertion
- # basically Column._make_proxy() sends the actual
- # target Column to the ForeignKey object, so the
- # string resolution here is never called.
- for c in self.parent.base_columns:
- if isinstance(c, Column):
- assert c.table is parenttable
- break
- else:
- assert False
- ######################
-
- schema, tname, colname = self._column_tokens
-
- if schema is None and parenttable.metadata.schema is not None:
- schema = parenttable.metadata.schema
-
- tablekey = _get_table_key(tname, schema)
- return parenttable, tablekey, colname
-
- def _link_to_col_by_colstring(
- self, parenttable: Table, table: Table, colname: Optional[str]
- ) -> Column[Any]:
- _column = None
- if colname is None:
- # colname is None in the case that ForeignKey argument
- # was specified as table name only, in which case we
- # match the column name to the same column on the
- # parent.
- # this use case wasn't working in later 1.x series
- # as it had no test coverage; fixed in 2.0
- parent = self.parent
- assert parent is not None
- key = parent.key
- _column = table.c.get(key, None)
- elif self.link_to_name:
- key = colname
- for c in table.c:
- if c.name == colname:
- _column = c
- else:
- key = colname
- _column = table.c.get(colname, None)
-
- if _column is None:
- raise exc.NoReferencedColumnError(
- "Could not initialize target column "
- f"for ForeignKey '{self._colspec}' "
- f"on table '{parenttable.name}': "
- f"table '{table.name}' has no column named '{key}'",
- table.name,
- key,
- )
-
- return _column
-
- def _set_target_column(self, column: Column[Any]) -> None:
- assert self.parent is not None
-
- # propagate TypeEngine to parent if it didn't have one
- if self.parent.type._isnull:
- self.parent.type = column.type
-
- # super-edgy case, if other FKs point to our column,
- # they'd get the type propagated out also.
-
- def set_type(fk: ForeignKey) -> None:
- if fk.parent.type._isnull:
- fk.parent.type = column.type
-
- self.parent._setup_on_memoized_fks(set_type)
-
- self.column = column # type: ignore
-
- @util.ro_memoized_property
- def column(self) -> Column[Any]:
- """Return the target :class:`_schema.Column` referenced by this
- :class:`_schema.ForeignKey`.
-
- If no target column has been established, an exception
- is raised.
-
- """
-
- return self._resolve_column()
-
- @overload
- def _resolve_column(
- self, *, raiseerr: Literal[True] = ...
- ) -> Column[Any]: ...
-
- @overload
- def _resolve_column(
- self, *, raiseerr: bool = ...
- ) -> Optional[Column[Any]]: ...
-
- def _resolve_column(
- self, *, raiseerr: bool = True
- ) -> Optional[Column[Any]]:
- _column: Column[Any]
-
- if isinstance(self._colspec, str):
- parenttable, tablekey, colname = self._resolve_col_tokens()
-
- if self._unresolvable or tablekey not in parenttable.metadata:
- if not raiseerr:
- return None
- raise exc.NoReferencedTableError(
- f"Foreign key associated with column "
- f"'{self.parent}' could not find "
- f"table '{tablekey}' with which to generate a "
- f"foreign key to target column '{colname}'",
- tablekey,
- )
- elif parenttable.key not in parenttable.metadata:
- if not raiseerr:
- return None
- raise exc.InvalidRequestError(
- f"Table {parenttable} is no longer associated with its "
- "parent MetaData"
- )
- else:
- table = parenttable.metadata.tables[tablekey]
- return self._link_to_col_by_colstring(
- parenttable, table, colname
- )
-
- elif hasattr(self._colspec, "__clause_element__"):
- _column = self._colspec.__clause_element__()
- return _column
- else:
- _column = self._colspec
- return _column
-
- def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
- assert isinstance(parent, Column)
-
- if self.parent is not None and self.parent is not parent:
- raise exc.InvalidRequestError(
- "This ForeignKey already has a parent !"
- )
- self.parent = parent
- self.parent.foreign_keys.add(self)
- self.parent._on_table_attach(self._set_table)
-
- def _set_remote_table(self, table: Table) -> None:
- parenttable, _, colname = self._resolve_col_tokens()
- _column = self._link_to_col_by_colstring(parenttable, table, colname)
- self._set_target_column(_column)
- assert self.constraint is not None
- self.constraint._validate_dest_table(table)
-
- def _remove_from_metadata(self, metadata: MetaData) -> None:
- parenttable, table_key, colname = self._resolve_col_tokens()
- fk_key = (table_key, colname)
-
- if self in metadata._fk_memos[fk_key]:
- # TODO: no test coverage for self not in memos
- metadata._fk_memos[fk_key].remove(self)
-
- def _set_table(self, column: Column[Any], table: Table) -> None:
- # standalone ForeignKey - create ForeignKeyConstraint
- # on the hosting Table when attached to the Table.
- assert isinstance(table, Table)
- if self.constraint is None:
- self.constraint = ForeignKeyConstraint(
- [],
- [],
- use_alter=self.use_alter,
- name=self.name,
- onupdate=self.onupdate,
- ondelete=self.ondelete,
- deferrable=self.deferrable,
- initially=self.initially,
- match=self.match,
- comment=self.comment,
- **self._unvalidated_dialect_kw,
- )
- self.constraint._append_element(column, self)
- self.constraint._set_parent_with_dispatch(table)
- table.foreign_keys.add(self)
- # set up remote ".column" attribute, or a note to pick it
- # up when the other Table/Column shows up
- if isinstance(self._colspec, str):
- parenttable, table_key, colname = self._resolve_col_tokens()
- fk_key = (table_key, colname)
- if table_key in parenttable.metadata.tables:
- table = parenttable.metadata.tables[table_key]
- try:
- _column = self._link_to_col_by_colstring(
- parenttable, table, colname
- )
- except exc.NoReferencedColumnError:
- # this is OK, we'll try later
- pass
- else:
- self._set_target_column(_column)
-
- parenttable.metadata._fk_memos[fk_key].append(self)
- elif hasattr(self._colspec, "__clause_element__"):
- _column = self._colspec.__clause_element__()
- self._set_target_column(_column)
- else:
- _column = self._colspec
- self._set_target_column(_column)
-
-
-if TYPE_CHECKING:
-
- def default_is_sequence(
- obj: Optional[DefaultGenerator],
- ) -> TypeGuard[Sequence]: ...
-
- def default_is_clause_element(
- obj: Optional[DefaultGenerator],
- ) -> TypeGuard[ColumnElementColumnDefault]: ...
-
- def default_is_scalar(
- obj: Optional[DefaultGenerator],
- ) -> TypeGuard[ScalarElementColumnDefault]: ...
-
-else:
- default_is_sequence = operator.attrgetter("is_sequence")
-
- default_is_clause_element = operator.attrgetter("is_clause_element")
-
- default_is_scalar = operator.attrgetter("is_scalar")
-
-
-class DefaultGenerator(Executable, SchemaItem):
- """Base class for column *default* values.
-
- This object is only present on column.default or column.onupdate.
- It's not valid as a server default.
-
- """
-
- __visit_name__ = "default_generator"
-
- _is_default_generator = True
- is_sequence = False
- is_identity = False
- is_server_default = False
- is_clause_element = False
- is_callable = False
- is_scalar = False
- has_arg = False
- is_sentinel = False
- column: Optional[Column[Any]]
-
- def __init__(self, for_update: bool = False) -> None:
- self.for_update = for_update
-
- def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
- if TYPE_CHECKING:
- assert isinstance(parent, Column)
- self.column = parent
- if self.for_update:
- self.column.onupdate = self
- else:
- self.column.default = self
-
- def _copy(self) -> DefaultGenerator:
- raise NotImplementedError()
-
- def _execute_on_connection(
- self,
- connection: Connection,
- distilled_params: _CoreMultiExecuteParams,
- execution_options: CoreExecuteOptionsParameter,
- ) -> Any:
- util.warn_deprecated(
- "Using the .execute() method to invoke a "
- "DefaultGenerator object is deprecated; please use "
- "the .scalar() method.",
- "2.0",
- )
- return self._execute_on_scalar(
- connection, distilled_params, execution_options
- )
-
- def _execute_on_scalar(
- self,
- connection: Connection,
- distilled_params: _CoreMultiExecuteParams,
- execution_options: CoreExecuteOptionsParameter,
- ) -> Any:
- return connection._execute_default(
- self, distilled_params, execution_options
- )
-
-
-class ColumnDefault(DefaultGenerator, ABC):
- """A plain default value on a column.
-
- This could correspond to a constant, a callable function,
- or a SQL clause.
-
- :class:`.ColumnDefault` is generated automatically
- whenever the ``default``, ``onupdate`` arguments of
- :class:`_schema.Column` are used. A :class:`.ColumnDefault`
- can be passed positionally as well.
-
- For example, the following::
-
- Column('foo', Integer, default=50)
-
- Is equivalent to::
-
- Column('foo', Integer, ColumnDefault(50))
-
-
- """
-
- arg: Any
-
- @overload
- def __new__(
- cls, arg: Callable[..., Any], for_update: bool = ...
- ) -> CallableColumnDefault: ...
-
- @overload
- def __new__(
- cls, arg: ColumnElement[Any], for_update: bool = ...
- ) -> ColumnElementColumnDefault: ...
-
- # if I return ScalarElementColumnDefault here, which is what's actually
- # returned, mypy complains that
- # overloads overlap w/ incompatible return types.
- @overload
- def __new__(cls, arg: object, for_update: bool = ...) -> ColumnDefault: ...
-
- def __new__(
- cls, arg: Any = None, for_update: bool = False
- ) -> ColumnDefault:
- """Construct a new :class:`.ColumnDefault`.
-
-
- :param arg: argument representing the default value.
- May be one of the following:
-
- * a plain non-callable Python value, such as a
- string, integer, boolean, or other simple type.
- The default value will be used as is each time.
- * a SQL expression, that is one which derives from
- :class:`_expression.ColumnElement`. The SQL expression will
- be rendered into the INSERT or UPDATE statement,
- or in the case of a primary key column when
- RETURNING is not used may be
- pre-executed before an INSERT within a SELECT.
- * A Python callable. The function will be invoked for each
- new row subject to an INSERT or UPDATE.
- The callable must accept exactly
- zero or one positional arguments. The one-argument form
- will receive an instance of the :class:`.ExecutionContext`,
- which provides contextual information as to the current
- :class:`_engine.Connection` in use as well as the current
- statement and parameters.
-
- """
-
- if isinstance(arg, FetchedValue):
- raise exc.ArgumentError(
- "ColumnDefault may not be a server-side default type."
- )
- elif callable(arg):
- cls = CallableColumnDefault
- elif isinstance(arg, ClauseElement):
- cls = ColumnElementColumnDefault
- elif arg is not None:
- cls = ScalarElementColumnDefault
-
- return object.__new__(cls)
-
- def __repr__(self) -> str:
- return f"{self.__class__.__name__}({self.arg!r})"
-
-
-class ScalarElementColumnDefault(ColumnDefault):
- """default generator for a fixed scalar Python value
-
- .. versionadded: 2.0
-
- """
-
- is_scalar = True
- has_arg = True
-
- def __init__(self, arg: Any, for_update: bool = False) -> None:
- self.for_update = for_update
- self.arg = arg
-
- def _copy(self) -> ScalarElementColumnDefault:
- return ScalarElementColumnDefault(
- arg=self.arg, for_update=self.for_update
- )
-
-
-class _InsertSentinelColumnDefault(ColumnDefault):
- """Default generator that's specific to the use of a "sentinel" column
- when using the insertmanyvalues feature.
-
- This default is used as part of the :func:`_schema.insert_sentinel`
- construct.
-
- """
-
- is_sentinel = True
- for_update = False
- arg = None
-
- def __new__(cls) -> _InsertSentinelColumnDefault:
- return object.__new__(cls)
-
- def __init__(self) -> None:
- pass
-
- def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
- col = cast("Column[Any]", parent)
- if not col._insert_sentinel:
- raise exc.ArgumentError(
- "The _InsertSentinelColumnDefault may only be applied to a "
- "Column marked as insert_sentinel=True"
- )
- elif not col.nullable:
- raise exc.ArgumentError(
- "The _InsertSentinelColumnDefault may only be applied to a "
- "Column that is nullable"
- )
-
- super()._set_parent(parent, **kw)
-
- def _copy(self) -> _InsertSentinelColumnDefault:
- return _InsertSentinelColumnDefault()
-
-
-_SQLExprDefault = Union["ColumnElement[Any]", "TextClause"]
-
-
-class ColumnElementColumnDefault(ColumnDefault):
- """default generator for a SQL expression
-
- .. versionadded:: 2.0
-
- """
-
- is_clause_element = True
- has_arg = True
- arg: _SQLExprDefault
-
- def __init__(
- self,
- arg: _SQLExprDefault,
- for_update: bool = False,
- ) -> None:
- self.for_update = for_update
- self.arg = arg
-
- def _copy(self) -> ColumnElementColumnDefault:
- return ColumnElementColumnDefault(
- arg=self.arg, for_update=self.for_update
- )
-
- @util.memoized_property
- @util.preload_module("sqlalchemy.sql.sqltypes")
- def _arg_is_typed(self) -> bool:
- sqltypes = util.preloaded.sql_sqltypes
-
- return not isinstance(self.arg.type, sqltypes.NullType)
-
-
-class _CallableColumnDefaultProtocol(Protocol):
- def __call__(self, context: ExecutionContext) -> Any: ...
-
-
-class CallableColumnDefault(ColumnDefault):
- """default generator for a callable Python function
-
- .. versionadded:: 2.0
-
- """
-
- is_callable = True
- arg: _CallableColumnDefaultProtocol
- has_arg = True
-
- def __init__(
- self,
- arg: Union[_CallableColumnDefaultProtocol, Callable[[], Any]],
- for_update: bool = False,
- ) -> None:
- self.for_update = for_update
- self.arg = self._maybe_wrap_callable(arg)
-
- def _copy(self) -> CallableColumnDefault:
- return CallableColumnDefault(arg=self.arg, for_update=self.for_update)
-
- def _maybe_wrap_callable(
- self, fn: Union[_CallableColumnDefaultProtocol, Callable[[], Any]]
- ) -> _CallableColumnDefaultProtocol:
- """Wrap callables that don't accept a context.
-
- This is to allow easy compatibility with default callables
- that aren't specific to accepting of a context.
-
- """
-
- try:
- argspec = util.get_callable_argspec(fn, no_self=True)
- except TypeError:
- return util.wrap_callable(lambda ctx: fn(), fn) # type: ignore
-
- defaulted = argspec[3] is not None and len(argspec[3]) or 0
- positionals = len(argspec[0]) - defaulted
-
- if positionals == 0:
- return util.wrap_callable(lambda ctx: fn(), fn) # type: ignore
-
- elif positionals == 1:
- return fn # type: ignore
- else:
- raise exc.ArgumentError(
- "ColumnDefault Python function takes zero or one "
- "positional arguments"
- )
-
-
-class IdentityOptions:
- """Defines options for a named database sequence or an identity column.
-
- .. versionadded:: 1.3.18
-
- .. seealso::
-
- :class:`.Sequence`
-
- """
-
- def __init__(
- self,
- start: Optional[int] = None,
- increment: Optional[int] = None,
- minvalue: Optional[int] = None,
- maxvalue: Optional[int] = None,
- nominvalue: Optional[bool] = None,
- nomaxvalue: Optional[bool] = None,
- cycle: Optional[bool] = None,
- cache: Optional[int] = None,
- order: Optional[bool] = None,
- ) -> None:
- """Construct a :class:`.IdentityOptions` object.
-
- See the :class:`.Sequence` documentation for a complete description
- of the parameters.
-
- :param start: the starting index of the sequence.
- :param increment: the increment value of the sequence.
- :param minvalue: the minimum value of the sequence.
- :param maxvalue: the maximum value of the sequence.
- :param nominvalue: no minimum value of the sequence.
- :param nomaxvalue: no maximum value of the sequence.
- :param cycle: allows the sequence to wrap around when the maxvalue
- or minvalue has been reached.
- :param cache: optional integer value; number of future values in the
- sequence which are calculated in advance.
- :param order: optional boolean value; if ``True``, renders the
- ORDER keyword.
-
- """
- self.start = start
- self.increment = increment
- self.minvalue = minvalue
- self.maxvalue = maxvalue
- self.nominvalue = nominvalue
- self.nomaxvalue = nomaxvalue
- self.cycle = cycle
- self.cache = cache
- self.order = order
-
- @property
- def _increment_is_negative(self) -> bool:
- return self.increment is not None and self.increment < 0
-
-
-class Sequence(HasSchemaAttr, IdentityOptions, DefaultGenerator):
- """Represents a named database sequence.
-
- The :class:`.Sequence` object represents the name and configurational
- parameters of a database sequence. It also represents
- a construct that can be "executed" by a SQLAlchemy :class:`_engine.Engine`
- or :class:`_engine.Connection`,
- rendering the appropriate "next value" function
- for the target database and returning a result.
-
- The :class:`.Sequence` is typically associated with a primary key column::
-
- some_table = Table(
- 'some_table', metadata,
- Column('id', Integer, Sequence('some_table_seq', start=1),
- primary_key=True)
- )
-
- When CREATE TABLE is emitted for the above :class:`_schema.Table`, if the
- target platform supports sequences, a CREATE SEQUENCE statement will
- be emitted as well. For platforms that don't support sequences,
- the :class:`.Sequence` construct is ignored.
-
- .. seealso::
-
- :ref:`defaults_sequences`
-
- :class:`.CreateSequence`
-
- :class:`.DropSequence`
-
- """
-
- __visit_name__ = "sequence"
-
- is_sequence = True
-
- column: Optional[Column[Any]]
- data_type: Optional[TypeEngine[int]]
-
- def __init__(
- self,
- name: str,
- start: Optional[int] = None,
- increment: Optional[int] = None,
- minvalue: Optional[int] = None,
- maxvalue: Optional[int] = None,
- nominvalue: Optional[bool] = None,
- nomaxvalue: Optional[bool] = None,
- cycle: Optional[bool] = None,
- schema: Optional[Union[str, Literal[SchemaConst.BLANK_SCHEMA]]] = None,
- cache: Optional[int] = None,
- order: Optional[bool] = None,
- data_type: Optional[_TypeEngineArgument[int]] = None,
- optional: bool = False,
- quote: Optional[bool] = None,
- metadata: Optional[MetaData] = None,
- quote_schema: Optional[bool] = None,
- for_update: bool = False,
- ) -> None:
- """Construct a :class:`.Sequence` object.
-
- :param name: the name of the sequence.
-
- :param start: the starting index of the sequence. This value is
- used when the CREATE SEQUENCE command is emitted to the database
- as the value of the "START WITH" clause. If ``None``, the
- clause is omitted, which on most platforms indicates a starting
- value of 1.
-
- .. versionchanged:: 2.0 The :paramref:`.Sequence.start` parameter
- is required in order to have DDL emit "START WITH". This is a
- reversal of a change made in version 1.4 which would implicitly
- render "START WITH 1" if the :paramref:`.Sequence.start` were
- not included. See :ref:`change_7211` for more detail.
-
- :param increment: the increment value of the sequence. This
- value is used when the CREATE SEQUENCE command is emitted to
- the database as the value of the "INCREMENT BY" clause. If ``None``,
- the clause is omitted, which on most platforms indicates an
- increment of 1.
- :param minvalue: the minimum value of the sequence. This
- value is used when the CREATE SEQUENCE command is emitted to
- the database as the value of the "MINVALUE" clause. If ``None``,
- the clause is omitted, which on most platforms indicates a
- minvalue of 1 and -2^63-1 for ascending and descending sequences,
- respectively.
-
- :param maxvalue: the maximum value of the sequence. This
- value is used when the CREATE SEQUENCE command is emitted to
- the database as the value of the "MAXVALUE" clause. If ``None``,
- the clause is omitted, which on most platforms indicates a
- maxvalue of 2^63-1 and -1 for ascending and descending sequences,
- respectively.
-
- :param nominvalue: no minimum value of the sequence. This
- value is used when the CREATE SEQUENCE command is emitted to
- the database as the value of the "NO MINVALUE" clause. If ``None``,
- the clause is omitted, which on most platforms indicates a
- minvalue of 1 and -2^63-1 for ascending and descending sequences,
- respectively.
-
- :param nomaxvalue: no maximum value of the sequence. This
- value is used when the CREATE SEQUENCE command is emitted to
- the database as the value of the "NO MAXVALUE" clause. If ``None``,
- the clause is omitted, which on most platforms indicates a
- maxvalue of 2^63-1 and -1 for ascending and descending sequences,
- respectively.
-
- :param cycle: allows the sequence to wrap around when the maxvalue
- or minvalue has been reached by an ascending or descending sequence
- respectively. This value is used when the CREATE SEQUENCE command
- is emitted to the database as the "CYCLE" clause. If the limit is
- reached, the next number generated will be the minvalue or maxvalue,
- respectively. If cycle=False (the default) any calls to nextval
- after the sequence has reached its maximum value will return an
- error.
-
- :param schema: optional schema name for the sequence, if located
- in a schema other than the default. The rules for selecting the
- schema name when a :class:`_schema.MetaData`
- is also present are the same
- as that of :paramref:`_schema.Table.schema`.
-
- :param cache: optional integer value; number of future values in the
- sequence which are calculated in advance. Renders the CACHE keyword
- understood by Oracle and PostgreSQL.
-
- :param order: optional boolean value; if ``True``, renders the
- ORDER keyword, understood by Oracle, indicating the sequence is
- definitively ordered. May be necessary to provide deterministic
- ordering using Oracle RAC.
-
- :param data_type: The type to be returned by the sequence, for
- dialects that allow us to choose between INTEGER, BIGINT, etc.
- (e.g., mssql).
-
- .. versionadded:: 1.4.0
-
- :param optional: boolean value, when ``True``, indicates that this
- :class:`.Sequence` object only needs to be explicitly generated
- on backends that don't provide another way to generate primary
- key identifiers. Currently, it essentially means, "don't create
- this sequence on the PostgreSQL backend, where the SERIAL keyword
- creates a sequence for us automatically".
- :param quote: boolean value, when ``True`` or ``False``, explicitly
- forces quoting of the :paramref:`_schema.Sequence.name` on or off.
- When left at its default of ``None``, normal quoting rules based
- on casing and reserved words take place.
- :param quote_schema: Set the quoting preferences for the ``schema``
- name.
-
- :param metadata: optional :class:`_schema.MetaData` object which this
- :class:`.Sequence` will be associated with. A :class:`.Sequence`
- that is associated with a :class:`_schema.MetaData`
- gains the following
- capabilities:
-
- * The :class:`.Sequence` will inherit the
- :paramref:`_schema.MetaData.schema`
- parameter specified to the target :class:`_schema.MetaData`, which
- affects the production of CREATE / DROP DDL, if any.
-
- * The :meth:`.Sequence.create` and :meth:`.Sequence.drop` methods
- automatically use the engine bound to the :class:`_schema.MetaData`
- object, if any.
-
- * The :meth:`_schema.MetaData.create_all` and
- :meth:`_schema.MetaData.drop_all`
- methods will emit CREATE / DROP for this :class:`.Sequence`,
- even if the :class:`.Sequence` is not associated with any
- :class:`_schema.Table` / :class:`_schema.Column`
- that's a member of this
- :class:`_schema.MetaData`.
-
- The above behaviors can only occur if the :class:`.Sequence` is
- explicitly associated with the :class:`_schema.MetaData`
- via this parameter.
-
- .. seealso::
-
- :ref:`sequence_metadata` - full discussion of the
- :paramref:`.Sequence.metadata` parameter.
-
- :param for_update: Indicates this :class:`.Sequence`, when associated
- with a :class:`_schema.Column`,
- should be invoked for UPDATE statements
- on that column's table, rather than for INSERT statements, when
- no value is otherwise present for that column in the statement.
-
- """
- DefaultGenerator.__init__(self, for_update=for_update)
- IdentityOptions.__init__(
- self,
- start=start,
- increment=increment,
- minvalue=minvalue,
- maxvalue=maxvalue,
- nominvalue=nominvalue,
- nomaxvalue=nomaxvalue,
- cycle=cycle,
- cache=cache,
- order=order,
- )
- self.column = None
- self.name = quoted_name(name, quote)
- self.optional = optional
- if schema is BLANK_SCHEMA:
- self.schema = schema = None
- elif metadata is not None and schema is None and metadata.schema:
- self.schema = schema = metadata.schema
- else:
- self.schema = quoted_name.construct(schema, quote_schema)
- self.metadata = metadata
- self._key = _get_table_key(name, schema)
- if metadata:
- self._set_metadata(metadata)
- if data_type is not None:
- self.data_type = to_instance(data_type)
- else:
- self.data_type = None
-
- @util.preload_module("sqlalchemy.sql.functions")
- def next_value(self) -> Function[int]:
- """Return a :class:`.next_value` function element
- which will render the appropriate increment function
- for this :class:`.Sequence` within any SQL expression.
-
- """
- return util.preloaded.sql_functions.func.next_value(self)
-
- def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
- column = parent
- assert isinstance(column, Column)
- super()._set_parent(column)
- column._on_table_attach(self._set_table)
-
- def _copy(self) -> Sequence:
- return Sequence(
- name=self.name,
- start=self.start,
- increment=self.increment,
- minvalue=self.minvalue,
- maxvalue=self.maxvalue,
- nominvalue=self.nominvalue,
- nomaxvalue=self.nomaxvalue,
- cycle=self.cycle,
- schema=self.schema,
- cache=self.cache,
- order=self.order,
- data_type=self.data_type,
- optional=self.optional,
- metadata=self.metadata,
- for_update=self.for_update,
- )
-
- def _set_table(self, column: Column[Any], table: Table) -> None:
- self._set_metadata(table.metadata)
-
- def _set_metadata(self, metadata: MetaData) -> None:
- self.metadata = metadata
- self.metadata._sequences[self._key] = self
-
- def create(self, bind: _CreateDropBind, checkfirst: bool = True) -> None:
- """Creates this sequence in the database."""
-
- bind._run_ddl_visitor(ddl.SchemaGenerator, self, checkfirst=checkfirst)
-
- def drop(self, bind: _CreateDropBind, checkfirst: bool = True) -> None:
- """Drops this sequence from the database."""
-
- bind._run_ddl_visitor(ddl.SchemaDropper, self, checkfirst=checkfirst)
-
- def _not_a_column_expr(self) -> NoReturn:
- raise exc.InvalidRequestError(
- f"This {self.__class__.__name__} cannot be used directly "
- "as a column expression. Use func.next_value(sequence) "
- "to produce a 'next value' function that's usable "
- "as a column element."
- )
-
-
-@inspection._self_inspects
-class FetchedValue(SchemaEventTarget):
- """A marker for a transparent database-side default.
-
- Use :class:`.FetchedValue` when the database is configured
- to provide some automatic default for a column.
-
- E.g.::
-
- Column('foo', Integer, FetchedValue())
-
- Would indicate that some trigger or default generator
- will create a new value for the ``foo`` column during an
- INSERT.
-
- .. seealso::
-
- :ref:`triggered_columns`
-
- """
-
- is_server_default = True
- reflected = False
- has_argument = False
- is_clause_element = False
- is_identity = False
-
- column: Optional[Column[Any]]
-
- def __init__(self, for_update: bool = False) -> None:
- self.for_update = for_update
-
- def _as_for_update(self, for_update: bool) -> FetchedValue:
- if for_update == self.for_update:
- return self
- else:
- return self._clone(for_update)
-
- def _copy(self) -> FetchedValue:
- return FetchedValue(self.for_update)
-
- def _clone(self, for_update: bool) -> Self:
- n = self.__class__.__new__(self.__class__)
- n.__dict__.update(self.__dict__)
- n.__dict__.pop("column", None)
- n.for_update = for_update
- return n
-
- def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
- column = parent
- assert isinstance(column, Column)
- self.column = column
- if self.for_update:
- self.column.server_onupdate = self
- else:
- self.column.server_default = self
-
- def __repr__(self) -> str:
- return util.generic_repr(self)
-
-
-class DefaultClause(FetchedValue):
- """A DDL-specified DEFAULT column value.
-
- :class:`.DefaultClause` is a :class:`.FetchedValue`
- that also generates a "DEFAULT" clause when
- "CREATE TABLE" is emitted.
-
- :class:`.DefaultClause` is generated automatically
- whenever the ``server_default``, ``server_onupdate`` arguments of
- :class:`_schema.Column` are used. A :class:`.DefaultClause`
- can be passed positionally as well.
-
- For example, the following::
-
- Column('foo', Integer, server_default="50")
-
- Is equivalent to::
-
- Column('foo', Integer, DefaultClause("50"))
-
- """
-
- has_argument = True
-
- def __init__(
- self,
- arg: Union[str, ClauseElement, TextClause],
- for_update: bool = False,
- _reflected: bool = False,
- ) -> None:
- util.assert_arg_type(arg, (str, ClauseElement, TextClause), "arg")
- super().__init__(for_update)
- self.arg = arg
- self.reflected = _reflected
-
- def _copy(self) -> DefaultClause:
- return DefaultClause(
- arg=self.arg, for_update=self.for_update, _reflected=self.reflected
- )
-
- def __repr__(self) -> str:
- return "DefaultClause(%r, for_update=%r)" % (self.arg, self.for_update)
-
-
-class Constraint(DialectKWArgs, HasConditionalDDL, SchemaItem):
- """A table-level SQL constraint.
-
- :class:`_schema.Constraint` serves as the base class for the series of
- constraint objects that can be associated with :class:`_schema.Table`
- objects, including :class:`_schema.PrimaryKeyConstraint`,
- :class:`_schema.ForeignKeyConstraint`
- :class:`_schema.UniqueConstraint`, and
- :class:`_schema.CheckConstraint`.
-
- """
-
- __visit_name__ = "constraint"
-
- _creation_order: int
- _column_flag: bool
-
- def __init__(
- self,
- name: _ConstraintNameArgument = None,
- deferrable: Optional[bool] = None,
- initially: Optional[str] = None,
- info: Optional[_InfoType] = None,
- comment: Optional[str] = None,
- _create_rule: Optional[Any] = None,
- _type_bound: bool = False,
- **dialect_kw: Any,
- ) -> None:
- r"""Create a SQL constraint.
-
- :param name:
- Optional, the in-database name of this ``Constraint``.
-
- :param deferrable:
- Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when
- issuing DDL for this constraint.
-
- :param initially:
- Optional string. If set, emit INITIALLY <value> when issuing DDL
- for this constraint.
-
- :param info: Optional data dictionary which will be populated into the
- :attr:`.SchemaItem.info` attribute of this object.
-
- :param comment: Optional string that will render an SQL comment on
- foreign key constraint creation.
-
- .. versionadded:: 2.0
-
- :param \**dialect_kw: Additional keyword arguments are dialect
- specific, and passed in the form ``<dialectname>_<argname>``. See
- the documentation regarding an individual dialect at
- :ref:`dialect_toplevel` for detail on documented arguments.
-
- :param _create_rule:
- used internally by some datatypes that also create constraints.
-
- :param _type_bound:
- used internally to indicate that this constraint is associated with
- a specific datatype.
-
- """
-
- self.name = name
- self.deferrable = deferrable
- self.initially = initially
- if info:
- self.info = info
- self._create_rule = _create_rule
- self._type_bound = _type_bound
- util.set_creation_order(self)
- self._validate_dialect_kwargs(dialect_kw)
- self.comment = comment
-
- def _should_create_for_compiler(
- self, compiler: DDLCompiler, **kw: Any
- ) -> bool:
- if self._create_rule is not None and not self._create_rule(compiler):
- return False
- elif self._ddl_if is not None:
- return self._ddl_if._should_execute(
- ddl.CreateConstraint(self), self, None, compiler=compiler, **kw
- )
- else:
- return True
-
- @property
- def table(self) -> Table:
- try:
- if isinstance(self.parent, Table):
- return self.parent
- except AttributeError:
- pass
- raise exc.InvalidRequestError(
- "This constraint is not bound to a table. Did you "
- "mean to call table.append_constraint(constraint) ?"
- )
-
- def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
- assert isinstance(parent, (Table, Column))
- self.parent = parent
- parent.constraints.add(self)
-
- @util.deprecated(
- "1.4",
- "The :meth:`_schema.Constraint.copy` method is deprecated "
- "and will be removed in a future release.",
- )
- def copy(self, **kw: Any) -> Self:
- return self._copy(**kw)
-
- def _copy(self, **kw: Any) -> Self:
- raise NotImplementedError()
-
-
-class ColumnCollectionMixin:
- """A :class:`_expression.ColumnCollection` of :class:`_schema.Column`
- objects.
-
- This collection represents the columns which are referred to by
- this object.
-
- """
-
- _columns: DedupeColumnCollection[Column[Any]]
-
- _allow_multiple_tables = False
-
- _pending_colargs: List[Optional[Union[str, Column[Any]]]]
-
- if TYPE_CHECKING:
-
- def _set_parent_with_dispatch(
- self, parent: SchemaEventTarget, **kw: Any
- ) -> None: ...
-
- def __init__(
- self,
- *columns: _DDLColumnArgument,
- _autoattach: bool = True,
- _column_flag: bool = False,
- _gather_expressions: Optional[
- List[Union[str, ColumnElement[Any]]]
- ] = None,
- ) -> None:
- self._column_flag = _column_flag
- self._columns = DedupeColumnCollection()
-
- processed_expressions: Optional[
- List[Union[ColumnElement[Any], str]]
- ] = _gather_expressions
-
- if processed_expressions is not None:
- self._pending_colargs = []
- for (
- expr,
- _,
- _,
- add_element,
- ) in coercions.expect_col_expression_collection(
- roles.DDLConstraintColumnRole, columns
- ):
- self._pending_colargs.append(add_element)
- processed_expressions.append(expr)
- else:
- self._pending_colargs = [
- coercions.expect(roles.DDLConstraintColumnRole, column)
- for column in columns
- ]
-
- if _autoattach and self._pending_colargs:
- self._check_attach()
-
- def _check_attach(self, evt: bool = False) -> None:
- col_objs = [c for c in self._pending_colargs if isinstance(c, Column)]
-
- cols_w_table = [c for c in col_objs if isinstance(c.table, Table)]
-
- cols_wo_table = set(col_objs).difference(cols_w_table)
- if cols_wo_table:
- # feature #3341 - place event listeners for Column objects
- # such that when all those cols are attached, we autoattach.
- assert not evt, "Should not reach here on event call"
-
- # issue #3411 - don't do the per-column auto-attach if some of the
- # columns are specified as strings.
- has_string_cols = {
- c for c in self._pending_colargs if c is not None
- }.difference(col_objs)
- if not has_string_cols:
-
- def _col_attached(column: Column[Any], table: Table) -> None:
- # this isinstance() corresponds with the
- # isinstance() above; only want to count Table-bound
- # columns
- if isinstance(table, Table):
- cols_wo_table.discard(column)
- if not cols_wo_table:
- self._check_attach(evt=True)
-
- self._cols_wo_table = cols_wo_table
- for col in cols_wo_table:
- col._on_table_attach(_col_attached)
- return
-
- columns = cols_w_table
-
- tables = {c.table for c in columns}
- if len(tables) == 1:
- self._set_parent_with_dispatch(tables.pop())
- elif len(tables) > 1 and not self._allow_multiple_tables:
- table = columns[0].table
- others = [c for c in columns[1:] if c.table is not table]
- if others:
- # black could not format this inline
- other_str = ", ".join("'%s'" % c for c in others)
- raise exc.ArgumentError(
- f"Column(s) {other_str} "
- f"are not part of table '{table.description}'."
- )
-
- @util.ro_memoized_property
- def columns(self) -> ReadOnlyColumnCollection[str, Column[Any]]:
- return self._columns.as_readonly()
-
- @util.ro_memoized_property
- def c(self) -> ReadOnlyColumnCollection[str, Column[Any]]:
- return self._columns.as_readonly()
-
- def _col_expressions(
- self, parent: Union[Table, Column[Any]]
- ) -> List[Optional[Column[Any]]]:
- if isinstance(parent, Column):
- result: List[Optional[Column[Any]]] = [
- c for c in self._pending_colargs if isinstance(c, Column)
- ]
- assert len(result) == len(self._pending_colargs)
- return result
- else:
- try:
- return [
- parent.c[col] if isinstance(col, str) else col
- for col in self._pending_colargs
- ]
- except KeyError as ke:
- raise exc.ConstraintColumnNotFoundError(
- f"Can't create {self.__class__.__name__} "
- f"on table '{parent.description}': no column "
- f"named '{ke.args[0]}' is present."
- ) from ke
-
- def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
- assert isinstance(parent, (Table, Column))
-
- for col in self._col_expressions(parent):
- if col is not None:
- self._columns.add(col)
-
-
-class ColumnCollectionConstraint(ColumnCollectionMixin, Constraint):
- """A constraint that proxies a ColumnCollection."""
-
- def __init__(
- self,
- *columns: _DDLColumnArgument,
- name: _ConstraintNameArgument = None,
- deferrable: Optional[bool] = None,
- initially: Optional[str] = None,
- info: Optional[_InfoType] = None,
- _autoattach: bool = True,
- _column_flag: bool = False,
- _gather_expressions: Optional[List[_DDLColumnArgument]] = None,
- **dialect_kw: Any,
- ) -> None:
- r"""
- :param \*columns:
- A sequence of column names or Column objects.
-
- :param name:
- Optional, the in-database name of this constraint.
-
- :param deferrable:
- Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when
- issuing DDL for this constraint.
-
- :param initially:
- Optional string. If set, emit INITIALLY <value> when issuing DDL
- for this constraint.
-
- :param \**dialect_kw: other keyword arguments including
- dialect-specific arguments are propagated to the :class:`.Constraint`
- superclass.
-
- """
- Constraint.__init__(
- self,
- name=name,
- deferrable=deferrable,
- initially=initially,
- info=info,
- **dialect_kw,
- )
- ColumnCollectionMixin.__init__(
- self, *columns, _autoattach=_autoattach, _column_flag=_column_flag
- )
-
- columns: ReadOnlyColumnCollection[str, Column[Any]]
- """A :class:`_expression.ColumnCollection` representing the set of columns
- for this constraint.
-
- """
-
- def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
- assert isinstance(parent, (Column, Table))
- Constraint._set_parent(self, parent)
- ColumnCollectionMixin._set_parent(self, parent)
-
- def __contains__(self, x: Any) -> bool:
- return x in self._columns
-
- @util.deprecated(
- "1.4",
- "The :meth:`_schema.ColumnCollectionConstraint.copy` method "
- "is deprecated and will be removed in a future release.",
- )
- def copy(
- self,
- *,
- target_table: Optional[Table] = None,
- **kw: Any,
- ) -> ColumnCollectionConstraint:
- return self._copy(target_table=target_table, **kw)
-
- def _copy(
- self,
- *,
- target_table: Optional[Table] = None,
- **kw: Any,
- ) -> ColumnCollectionConstraint:
- # ticket #5276
- constraint_kwargs = {}
- for dialect_name in self.dialect_options:
- dialect_options = self.dialect_options[dialect_name]._non_defaults
- for (
- dialect_option_key,
- dialect_option_value,
- ) in dialect_options.items():
- constraint_kwargs[dialect_name + "_" + dialect_option_key] = (
- dialect_option_value
- )
-
- assert isinstance(self.parent, Table)
- c = self.__class__(
- name=self.name,
- deferrable=self.deferrable,
- initially=self.initially,
- *[
- _copy_expression(expr, self.parent, target_table)
- for expr in self._columns
- ],
- comment=self.comment,
- **constraint_kwargs,
- )
- return self._schema_item_copy(c)
-
- def contains_column(self, col: Column[Any]) -> bool:
- """Return True if this constraint contains the given column.
-
- Note that this object also contains an attribute ``.columns``
- which is a :class:`_expression.ColumnCollection` of
- :class:`_schema.Column` objects.
-
- """
-
- return self._columns.contains_column(col)
-
- def __iter__(self) -> Iterator[Column[Any]]:
- return iter(self._columns)
-
- def __len__(self) -> int:
- return len(self._columns)
-
-
-class CheckConstraint(ColumnCollectionConstraint):
- """A table- or column-level CHECK constraint.
-
- Can be included in the definition of a Table or Column.
- """
-
- _allow_multiple_tables = True
-
- __visit_name__ = "table_or_column_check_constraint"
-
- @_document_text_coercion(
- "sqltext",
- ":class:`.CheckConstraint`",
- ":paramref:`.CheckConstraint.sqltext`",
- )
- def __init__(
- self,
- sqltext: _TextCoercedExpressionArgument[Any],
- name: _ConstraintNameArgument = None,
- deferrable: Optional[bool] = None,
- initially: Optional[str] = None,
- table: Optional[Table] = None,
- info: Optional[_InfoType] = None,
- _create_rule: Optional[Any] = None,
- _autoattach: bool = True,
- _type_bound: bool = False,
- **dialect_kw: Any,
- ) -> None:
- r"""Construct a CHECK constraint.
-
- :param sqltext:
- A string containing the constraint definition, which will be used
- verbatim, or a SQL expression construct. If given as a string,
- the object is converted to a :func:`_expression.text` object.
- If the textual
- string includes a colon character, escape this using a backslash::
-
- CheckConstraint(r"foo ~ E'a(?\:b|c)d")
-
- :param name:
- Optional, the in-database name of the constraint.
-
- :param deferrable:
- Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when
- issuing DDL for this constraint.
-
- :param initially:
- Optional string. If set, emit INITIALLY <value> when issuing DDL
- for this constraint.
-
- :param info: Optional data dictionary which will be populated into the
- :attr:`.SchemaItem.info` attribute of this object.
-
- """
-
- self.sqltext = coercions.expect(roles.DDLExpressionRole, sqltext)
- columns: List[Column[Any]] = []
- visitors.traverse(self.sqltext, {}, {"column": columns.append})
-
- super().__init__(
- name=name,
- deferrable=deferrable,
- initially=initially,
- _create_rule=_create_rule,
- info=info,
- _type_bound=_type_bound,
- _autoattach=_autoattach,
- *columns,
- **dialect_kw,
- )
- if table is not None:
- self._set_parent_with_dispatch(table)
-
- @property
- def is_column_level(self) -> bool:
- return not isinstance(self.parent, Table)
-
- @util.deprecated(
- "1.4",
- "The :meth:`_schema.CheckConstraint.copy` method is deprecated "
- "and will be removed in a future release.",
- )
- def copy(
- self, *, target_table: Optional[Table] = None, **kw: Any
- ) -> CheckConstraint:
- return self._copy(target_table=target_table, **kw)
-
- def _copy(
- self, *, target_table: Optional[Table] = None, **kw: Any
- ) -> CheckConstraint:
- if target_table is not None:
- # note that target_table is None for the copy process of
- # a column-bound CheckConstraint, so this path is not reached
- # in that case.
- sqltext = _copy_expression(self.sqltext, self.table, target_table)
- else:
- sqltext = self.sqltext
- c = CheckConstraint(
- sqltext,
- name=self.name,
- initially=self.initially,
- deferrable=self.deferrable,
- _create_rule=self._create_rule,
- table=target_table,
- comment=self.comment,
- _autoattach=False,
- _type_bound=self._type_bound,
- )
- return self._schema_item_copy(c)
-
-
-class ForeignKeyConstraint(ColumnCollectionConstraint):
- """A table-level FOREIGN KEY constraint.
-
- Defines a single column or composite FOREIGN KEY ... REFERENCES
- constraint. For a no-frills, single column foreign key, adding a
- :class:`_schema.ForeignKey` to the definition of a :class:`_schema.Column`
- is a
- shorthand equivalent for an unnamed, single column
- :class:`_schema.ForeignKeyConstraint`.
-
- Examples of foreign key configuration are in :ref:`metadata_foreignkeys`.
-
- """
-
- __visit_name__ = "foreign_key_constraint"
-
- def __init__(
- self,
- columns: _typing_Sequence[_DDLColumnArgument],
- refcolumns: _typing_Sequence[_DDLColumnArgument],
- name: _ConstraintNameArgument = None,
- onupdate: Optional[str] = None,
- ondelete: Optional[str] = None,
- deferrable: Optional[bool] = None,
- initially: Optional[str] = None,
- use_alter: bool = False,
- link_to_name: bool = False,
- match: Optional[str] = None,
- table: Optional[Table] = None,
- info: Optional[_InfoType] = None,
- comment: Optional[str] = None,
- **dialect_kw: Any,
- ) -> None:
- r"""Construct a composite-capable FOREIGN KEY.
-
- :param columns: A sequence of local column names. The named columns
- must be defined and present in the parent Table. The names should
- match the ``key`` given to each column (defaults to the name) unless
- ``link_to_name`` is True.
-
- :param refcolumns: A sequence of foreign column names or Column
- objects. The columns must all be located within the same Table.
-
- :param name: Optional, the in-database name of the key.
-
- :param onupdate: Optional string. If set, emit ON UPDATE <value> when
- issuing DDL for this constraint. Typical values include CASCADE,
- DELETE and RESTRICT.
-
- :param ondelete: Optional string. If set, emit ON DELETE <value> when
- issuing DDL for this constraint. Typical values include CASCADE,
- DELETE and RESTRICT.
-
- :param deferrable: Optional bool. If set, emit DEFERRABLE or NOT
- DEFERRABLE when issuing DDL for this constraint.
-
- :param initially: Optional string. If set, emit INITIALLY <value> when
- issuing DDL for this constraint.
-
- :param link_to_name: if True, the string name given in ``column`` is
- the rendered name of the referenced column, not its locally assigned
- ``key``.
-
- :param use_alter: If True, do not emit the DDL for this constraint as
- part of the CREATE TABLE definition. Instead, generate it via an
- ALTER TABLE statement issued after the full collection of tables
- have been created, and drop it via an ALTER TABLE statement before
- the full collection of tables are dropped.
-
- The use of :paramref:`_schema.ForeignKeyConstraint.use_alter` is
- particularly geared towards the case where two or more tables
- are established within a mutually-dependent foreign key constraint
- relationship; however, the :meth:`_schema.MetaData.create_all` and
- :meth:`_schema.MetaData.drop_all`
- methods will perform this resolution
- automatically, so the flag is normally not needed.
-
- .. seealso::
-
- :ref:`use_alter`
-
- :param match: Optional string. If set, emit MATCH <value> when issuing
- DDL for this constraint. Typical values include SIMPLE, PARTIAL
- and FULL.
-
- :param info: Optional data dictionary which will be populated into the
- :attr:`.SchemaItem.info` attribute of this object.
-
- :param comment: Optional string that will render an SQL comment on
- foreign key constraint creation.
-
- .. versionadded:: 2.0
-
- :param \**dialect_kw: Additional keyword arguments are dialect
- specific, and passed in the form ``<dialectname>_<argname>``. See
- the documentation regarding an individual dialect at
- :ref:`dialect_toplevel` for detail on documented arguments.
-
- """
-
- Constraint.__init__(
- self,
- name=name,
- deferrable=deferrable,
- initially=initially,
- info=info,
- comment=comment,
- **dialect_kw,
- )
- self.onupdate = onupdate
- self.ondelete = ondelete
- self.link_to_name = link_to_name
- self.use_alter = use_alter
- self.match = match
-
- if len(set(columns)) != len(refcolumns):
- if len(set(columns)) != len(columns):
- # e.g. FOREIGN KEY (a, a) REFERENCES r (b, c)
- raise exc.ArgumentError(
- "ForeignKeyConstraint with duplicate source column "
- "references are not supported."
- )
- else:
- # e.g. FOREIGN KEY (a) REFERENCES r (b, c)
- # paraphrasing
- # https://www.postgresql.org/docs/current/static/ddl-constraints.html
- raise exc.ArgumentError(
- "ForeignKeyConstraint number "
- "of constrained columns must match the number of "
- "referenced columns."
- )
-
- # standalone ForeignKeyConstraint - create
- # associated ForeignKey objects which will be applied to hosted
- # Column objects (in col.foreign_keys), either now or when attached
- # to the Table for string-specified names
- self.elements = [
- ForeignKey(
- refcol,
- _constraint=self,
- name=self.name,
- onupdate=self.onupdate,
- ondelete=self.ondelete,
- use_alter=self.use_alter,
- link_to_name=self.link_to_name,
- match=self.match,
- deferrable=self.deferrable,
- initially=self.initially,
- **self.dialect_kwargs,
- )
- for refcol in refcolumns
- ]
-
- ColumnCollectionMixin.__init__(self, *columns)
- if table is not None:
- if hasattr(self, "parent"):
- assert table is self.parent
- self._set_parent_with_dispatch(table)
-
- def _append_element(self, column: Column[Any], fk: ForeignKey) -> None:
- self._columns.add(column)
- self.elements.append(fk)
-
- columns: ReadOnlyColumnCollection[str, Column[Any]]
- """A :class:`_expression.ColumnCollection` representing the set of columns
- for this constraint.
-
- """
-
- elements: List[ForeignKey]
- """A sequence of :class:`_schema.ForeignKey` objects.
-
- Each :class:`_schema.ForeignKey`
- represents a single referring column/referred
- column pair.
-
- This collection is intended to be read-only.
-
- """
-
- @property
- def _elements(self) -> util.OrderedDict[str, ForeignKey]:
- # legacy - provide a dictionary view of (column_key, fk)
- return util.OrderedDict(zip(self.column_keys, self.elements))
-
- @property
- def _referred_schema(self) -> Optional[str]:
- for elem in self.elements:
- return elem._referred_schema
- else:
- return None
-
- @property
- def referred_table(self) -> Table:
- """The :class:`_schema.Table` object to which this
- :class:`_schema.ForeignKeyConstraint` references.
-
- This is a dynamically calculated attribute which may not be available
- if the constraint and/or parent table is not yet associated with
- a metadata collection that contains the referred table.
-
- """
- return self.elements[0].column.table
-
- def _validate_dest_table(self, table: Table) -> None:
- table_keys = {elem._table_key() for elem in self.elements}
- if None not in table_keys and len(table_keys) > 1:
- elem0, elem1 = sorted(table_keys)[0:2]
- raise exc.ArgumentError(
- f"ForeignKeyConstraint on "
- f"{table.fullname}({self._col_description}) refers to "
- f"multiple remote tables: {elem0} and {elem1}"
- )
-
- @property
- def column_keys(self) -> _typing_Sequence[str]:
- """Return a list of string keys representing the local
- columns in this :class:`_schema.ForeignKeyConstraint`.
-
- This list is either the original string arguments sent
- to the constructor of the :class:`_schema.ForeignKeyConstraint`,
- or if the constraint has been initialized with :class:`_schema.Column`
- objects, is the string ``.key`` of each element.
-
- """
- if hasattr(self, "parent"):
- return self._columns.keys()
- else:
- return [
- col.key if isinstance(col, ColumnElement) else str(col)
- for col in self._pending_colargs
- ]
-
- @property
- def _col_description(self) -> str:
- return ", ".join(self.column_keys)
-
- def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
- table = parent
- assert isinstance(table, Table)
- Constraint._set_parent(self, table)
-
- ColumnCollectionConstraint._set_parent(self, table)
-
- for col, fk in zip(self._columns, self.elements):
- if not hasattr(fk, "parent") or fk.parent is not col:
- fk._set_parent_with_dispatch(col)
-
- self._validate_dest_table(table)
-
- @util.deprecated(
- "1.4",
- "The :meth:`_schema.ForeignKeyConstraint.copy` method is deprecated "
- "and will be removed in a future release.",
- )
- def copy(
- self,
- *,
- schema: Optional[str] = None,
- target_table: Optional[Table] = None,
- **kw: Any,
- ) -> ForeignKeyConstraint:
- return self._copy(schema=schema, target_table=target_table, **kw)
-
- def _copy(
- self,
- *,
- schema: Optional[str] = None,
- target_table: Optional[Table] = None,
- **kw: Any,
- ) -> ForeignKeyConstraint:
- fkc = ForeignKeyConstraint(
- [x.parent.key for x in self.elements],
- [
- x._get_colspec(
- schema=schema,
- table_name=(
- target_table.name
- if target_table is not None
- and x._table_key() == x.parent.table.key
- else None
- ),
- _is_copy=True,
- )
- for x in self.elements
- ],
- name=self.name,
- onupdate=self.onupdate,
- ondelete=self.ondelete,
- use_alter=self.use_alter,
- deferrable=self.deferrable,
- initially=self.initially,
- link_to_name=self.link_to_name,
- match=self.match,
- comment=self.comment,
- )
- for self_fk, other_fk in zip(self.elements, fkc.elements):
- self_fk._schema_item_copy(other_fk)
- return self._schema_item_copy(fkc)
-
-
-class PrimaryKeyConstraint(ColumnCollectionConstraint):
- """A table-level PRIMARY KEY constraint.
-
- The :class:`.PrimaryKeyConstraint` object is present automatically
- on any :class:`_schema.Table` object; it is assigned a set of
- :class:`_schema.Column` objects corresponding to those marked with
- the :paramref:`_schema.Column.primary_key` flag::
-
- >>> my_table = Table('mytable', metadata,
- ... Column('id', Integer, primary_key=True),
- ... Column('version_id', Integer, primary_key=True),
- ... Column('data', String(50))
- ... )
- >>> my_table.primary_key
- PrimaryKeyConstraint(
- Column('id', Integer(), table=<mytable>,
- primary_key=True, nullable=False),
- Column('version_id', Integer(), table=<mytable>,
- primary_key=True, nullable=False)
- )
-
- The primary key of a :class:`_schema.Table` can also be specified by using
- a :class:`.PrimaryKeyConstraint` object explicitly; in this mode of usage,
- the "name" of the constraint can also be specified, as well as other
- options which may be recognized by dialects::
-
- my_table = Table('mytable', metadata,
- Column('id', Integer),
- Column('version_id', Integer),
- Column('data', String(50)),
- PrimaryKeyConstraint('id', 'version_id',
- name='mytable_pk')
- )
-
- The two styles of column-specification should generally not be mixed.
- An warning is emitted if the columns present in the
- :class:`.PrimaryKeyConstraint`
- don't match the columns that were marked as ``primary_key=True``, if both
- are present; in this case, the columns are taken strictly from the
- :class:`.PrimaryKeyConstraint` declaration, and those columns otherwise
- marked as ``primary_key=True`` are ignored. This behavior is intended to
- be backwards compatible with previous behavior.
-
- For the use case where specific options are to be specified on the
- :class:`.PrimaryKeyConstraint`, but the usual style of using
- ``primary_key=True`` flags is still desirable, an empty
- :class:`.PrimaryKeyConstraint` may be specified, which will take on the
- primary key column collection from the :class:`_schema.Table` based on the
- flags::
-
- my_table = Table('mytable', metadata,
- Column('id', Integer, primary_key=True),
- Column('version_id', Integer, primary_key=True),
- Column('data', String(50)),
- PrimaryKeyConstraint(name='mytable_pk',
- mssql_clustered=True)
- )
-
- """
-
- __visit_name__ = "primary_key_constraint"
-
- def __init__(
- self,
- *columns: _DDLColumnArgument,
- name: Optional[str] = None,
- deferrable: Optional[bool] = None,
- initially: Optional[str] = None,
- info: Optional[_InfoType] = None,
- _implicit_generated: bool = False,
- **dialect_kw: Any,
- ) -> None:
- self._implicit_generated = _implicit_generated
- super().__init__(
- *columns,
- name=name,
- deferrable=deferrable,
- initially=initially,
- info=info,
- **dialect_kw,
- )
-
- def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
- table = parent
- assert isinstance(table, Table)
- super()._set_parent(table)
-
- if table.primary_key is not self:
- table.constraints.discard(table.primary_key)
- table.primary_key = self # type: ignore
- table.constraints.add(self)
-
- table_pks = [c for c in table.c if c.primary_key]
- if (
- self._columns
- and table_pks
- and set(table_pks) != set(self._columns)
- ):
- # black could not format these inline
- table_pk_str = ", ".join("'%s'" % c.name for c in table_pks)
- col_str = ", ".join("'%s'" % c.name for c in self._columns)
-
- util.warn(
- f"Table '{table.name}' specifies columns "
- f"{table_pk_str} as "
- f"primary_key=True, "
- f"not matching locally specified columns {col_str}; "
- f"setting the "
- f"current primary key columns to "
- f"{col_str}. "
- f"This warning "
- f"may become an exception in a future release"
- )
- table_pks[:] = []
-
- for c in self._columns:
- c.primary_key = True
- if c._user_defined_nullable is NULL_UNSPECIFIED:
- c.nullable = False
- if table_pks:
- self._columns.extend(table_pks)
-
- def _reload(self, columns: Iterable[Column[Any]]) -> None:
- """repopulate this :class:`.PrimaryKeyConstraint` given
- a set of columns.
-
- Existing columns in the table that are marked as primary_key=True
- are maintained.
-
- Also fires a new event.
-
- This is basically like putting a whole new
- :class:`.PrimaryKeyConstraint` object on the parent
- :class:`_schema.Table` object without actually replacing the object.
-
- The ordering of the given list of columns is also maintained; these
- columns will be appended to the list of columns after any which
- are already present.
-
- """
- # set the primary key flag on new columns.
- # note any existing PK cols on the table also have their
- # flag still set.
- for col in columns:
- col.primary_key = True
-
- self._columns.extend(columns)
-
- PrimaryKeyConstraint._autoincrement_column._reset(self) # type: ignore
- self._set_parent_with_dispatch(self.table)
-
- def _replace(self, col: Column[Any]) -> None:
- PrimaryKeyConstraint._autoincrement_column._reset(self) # type: ignore
- self._columns.replace(col)
-
- self.dispatch._sa_event_column_added_to_pk_constraint(self, col)
-
- @property
- def columns_autoinc_first(self) -> List[Column[Any]]:
- autoinc = self._autoincrement_column
-
- if autoinc is not None:
- return [autoinc] + [c for c in self._columns if c is not autoinc]
- else:
- return list(self._columns)
-
- @util.ro_memoized_property
- def _autoincrement_column(self) -> Optional[Column[int]]:
- def _validate_autoinc(col: Column[Any], autoinc_true: bool) -> bool:
- if col.type._type_affinity is None or not issubclass(
- col.type._type_affinity,
- (
- type_api.INTEGERTYPE._type_affinity,
- type_api.NUMERICTYPE._type_affinity,
- ),
- ):
- if autoinc_true:
- raise exc.ArgumentError(
- f"Column type {col.type} on column '{col}' is not "
- f"compatible with autoincrement=True"
- )
- else:
- return False
- elif (
- not isinstance(col.default, (type(None), Sequence))
- and not autoinc_true
- ):
- return False
- elif (
- col.server_default is not None
- and not isinstance(col.server_default, Identity)
- and not autoinc_true
- ):
- return False
- elif col.foreign_keys and col.autoincrement not in (
- True,
- "ignore_fk",
- ):
- return False
- return True
-
- if len(self._columns) == 1:
- col = list(self._columns)[0]
-
- if col.autoincrement is True:
- _validate_autoinc(col, True)
- return col
- elif col.autoincrement in (
- "auto",
- "ignore_fk",
- ) and _validate_autoinc(col, False):
- return col
- else:
- return None
-
- else:
- autoinc = None
- for col in self._columns:
- if col.autoincrement is True:
- _validate_autoinc(col, True)
- if autoinc is not None:
- raise exc.ArgumentError(
- f"Only one Column may be marked "
- f"autoincrement=True, found both "
- f"{col.name} and {autoinc.name}."
- )
- else:
- autoinc = col
-
- return autoinc
-
-
-class UniqueConstraint(ColumnCollectionConstraint):
- """A table-level UNIQUE constraint.
-
- Defines a single column or composite UNIQUE constraint. For a no-frills,
- single column constraint, adding ``unique=True`` to the ``Column``
- definition is a shorthand equivalent for an unnamed, single column
- UniqueConstraint.
- """
-
- __visit_name__ = "unique_constraint"
-
-
-class Index(
- DialectKWArgs, ColumnCollectionMixin, HasConditionalDDL, SchemaItem
-):
- """A table-level INDEX.
-
- Defines a composite (one or more column) INDEX.
-
- E.g.::
-
- sometable = Table("sometable", metadata,
- Column("name", String(50)),
- Column("address", String(100))
- )
-
- Index("some_index", sometable.c.name)
-
- For a no-frills, single column index, adding
- :class:`_schema.Column` also supports ``index=True``::
-
- sometable = Table("sometable", metadata,
- Column("name", String(50), index=True)
- )
-
- For a composite index, multiple columns can be specified::
-
- Index("some_index", sometable.c.name, sometable.c.address)
-
- Functional indexes are supported as well, typically by using the
- :data:`.func` construct in conjunction with table-bound
- :class:`_schema.Column` objects::
-
- Index("some_index", func.lower(sometable.c.name))
-
- An :class:`.Index` can also be manually associated with a
- :class:`_schema.Table`,
- either through inline declaration or using
- :meth:`_schema.Table.append_constraint`. When this approach is used,
- the names
- of the indexed columns can be specified as strings::
-
- Table("sometable", metadata,
- Column("name", String(50)),
- Column("address", String(100)),
- Index("some_index", "name", "address")
- )
-
- To support functional or expression-based indexes in this form, the
- :func:`_expression.text` construct may be used::
-
- from sqlalchemy import text
-
- Table("sometable", metadata,
- Column("name", String(50)),
- Column("address", String(100)),
- Index("some_index", text("lower(name)"))
- )
-
- .. seealso::
-
- :ref:`schema_indexes` - General information on :class:`.Index`.
-
- :ref:`postgresql_indexes` - PostgreSQL-specific options available for
- the :class:`.Index` construct.
-
- :ref:`mysql_indexes` - MySQL-specific options available for the
- :class:`.Index` construct.
-
- :ref:`mssql_indexes` - MSSQL-specific options available for the
- :class:`.Index` construct.
-
- """
-
- __visit_name__ = "index"
-
- table: Optional[Table]
- expressions: _typing_Sequence[Union[str, ColumnElement[Any]]]
- _table_bound_expressions: _typing_Sequence[ColumnElement[Any]]
-
- def __init__(
- self,
- name: Optional[str],
- *expressions: _DDLColumnArgument,
- unique: bool = False,
- quote: Optional[bool] = None,
- info: Optional[_InfoType] = None,
- _table: Optional[Table] = None,
- _column_flag: bool = False,
- **dialect_kw: Any,
- ) -> None:
- r"""Construct an index object.
-
- :param name:
- The name of the index
-
- :param \*expressions:
- Column expressions to include in the index. The expressions
- are normally instances of :class:`_schema.Column`, but may also
- be arbitrary SQL expressions which ultimately refer to a
- :class:`_schema.Column`.
-
- :param unique=False:
- Keyword only argument; if True, create a unique index.
-
- :param quote=None:
- Keyword only argument; whether to apply quoting to the name of
- the index. Works in the same manner as that of
- :paramref:`_schema.Column.quote`.
-
- :param info=None: Optional data dictionary which will be populated
- into the :attr:`.SchemaItem.info` attribute of this object.
-
- :param \**dialect_kw: Additional keyword arguments not mentioned above
- are dialect specific, and passed in the form
- ``<dialectname>_<argname>``. See the documentation regarding an
- individual dialect at :ref:`dialect_toplevel` for detail on
- documented arguments.
-
- """
- self.table = table = None
-
- self.name = quoted_name.construct(name, quote)
- self.unique = unique
- if info is not None:
- self.info = info
-
- # TODO: consider "table" argument being public, but for
- # the purpose of the fix here, it starts as private.
- if _table is not None:
- table = _table
-
- self._validate_dialect_kwargs(dialect_kw)
-
- self.expressions = []
- # will call _set_parent() if table-bound column
- # objects are present
- ColumnCollectionMixin.__init__(
- self,
- *expressions,
- _column_flag=_column_flag,
- _gather_expressions=self.expressions,
- )
- if table is not None:
- self._set_parent(table)
-
- def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
- table = parent
- assert isinstance(table, Table)
- ColumnCollectionMixin._set_parent(self, table)
-
- if self.table is not None and table is not self.table:
- raise exc.ArgumentError(
- f"Index '{self.name}' is against table "
- f"'{self.table.description}', and "
- f"cannot be associated with table '{table.description}'."
- )
- self.table = table
- table.indexes.add(self)
-
- expressions = self.expressions
- col_expressions = self._col_expressions(table)
- assert len(expressions) == len(col_expressions)
-
- exprs = []
- for expr, colexpr in zip(expressions, col_expressions):
- if isinstance(expr, ClauseElement):
- exprs.append(expr)
- elif colexpr is not None:
- exprs.append(colexpr)
- else:
- assert False
- self.expressions = self._table_bound_expressions = exprs
-
- def create(self, bind: _CreateDropBind, checkfirst: bool = False) -> None:
- """Issue a ``CREATE`` statement for this
- :class:`.Index`, using the given
- :class:`.Connection` or :class:`.Engine`` for connectivity.
-
- .. seealso::
-
- :meth:`_schema.MetaData.create_all`.
-
- """
- bind._run_ddl_visitor(ddl.SchemaGenerator, self, checkfirst=checkfirst)
-
- def drop(self, bind: _CreateDropBind, checkfirst: bool = False) -> None:
- """Issue a ``DROP`` statement for this
- :class:`.Index`, using the given
- :class:`.Connection` or :class:`.Engine` for connectivity.
-
- .. seealso::
-
- :meth:`_schema.MetaData.drop_all`.
-
- """
- bind._run_ddl_visitor(ddl.SchemaDropper, self, checkfirst=checkfirst)
-
- def __repr__(self) -> str:
- exprs: _typing_Sequence[Any] # noqa: F842
-
- return "Index(%s)" % (
- ", ".join(
- [repr(self.name)]
- + [repr(e) for e in self.expressions]
- + (self.unique and ["unique=True"] or [])
- )
- )
-
-
-_NamingSchemaCallable = Callable[[Constraint, Table], str]
-_NamingSchemaDirective = Union[str, _NamingSchemaCallable]
-
-
-class _NamingSchemaTD(TypedDict, total=False):
- fk: _NamingSchemaDirective
- pk: _NamingSchemaDirective
- ix: _NamingSchemaDirective
- ck: _NamingSchemaDirective
- uq: _NamingSchemaDirective
-
-
-_NamingSchemaParameter = Union[
- # it seems like the TypedDict here is useful for pylance typeahead,
- # and not much else
- _NamingSchemaTD,
- # there is no form that allows Union[Type[Any], str] to work in all
- # cases, including breaking out Mapping[] entries for each combination
- # even, therefore keys must be `Any` (see #10264)
- Mapping[Any, _NamingSchemaDirective],
-]
-
-
-DEFAULT_NAMING_CONVENTION: _NamingSchemaParameter = util.immutabledict(
- {"ix": "ix_%(column_0_label)s"}
-)
-
-
-class MetaData(HasSchemaAttr):
- """A collection of :class:`_schema.Table`
- objects and their associated schema
- constructs.
-
- Holds a collection of :class:`_schema.Table` objects as well as
- an optional binding to an :class:`_engine.Engine` or
- :class:`_engine.Connection`. If bound, the :class:`_schema.Table` objects
- in the collection and their columns may participate in implicit SQL
- execution.
-
- The :class:`_schema.Table` objects themselves are stored in the
- :attr:`_schema.MetaData.tables` dictionary.
-
- :class:`_schema.MetaData` is a thread-safe object for read operations.
- Construction of new tables within a single :class:`_schema.MetaData`
- object,
- either explicitly or via reflection, may not be completely thread-safe.
-
- .. seealso::
-
- :ref:`metadata_describing` - Introduction to database metadata
-
- """
-
- __visit_name__ = "metadata"
-
- def __init__(
- self,
- schema: Optional[str] = None,
- quote_schema: Optional[bool] = None,
- naming_convention: Optional[_NamingSchemaParameter] = None,
- info: Optional[_InfoType] = None,
- ) -> None:
- """Create a new MetaData object.
-
- :param schema:
- The default schema to use for the :class:`_schema.Table`,
- :class:`.Sequence`, and potentially other objects associated with
- this :class:`_schema.MetaData`. Defaults to ``None``.
-
- .. seealso::
-
- :ref:`schema_metadata_schema_name` - details on how the
- :paramref:`_schema.MetaData.schema` parameter is used.
-
- :paramref:`_schema.Table.schema`
-
- :paramref:`.Sequence.schema`
-
- :param quote_schema:
- Sets the ``quote_schema`` flag for those :class:`_schema.Table`,
- :class:`.Sequence`, and other objects which make usage of the
- local ``schema`` name.
-
- :param info: Optional data dictionary which will be populated into the
- :attr:`.SchemaItem.info` attribute of this object.
-
- :param naming_convention: a dictionary referring to values which
- will establish default naming conventions for :class:`.Constraint`
- and :class:`.Index` objects, for those objects which are not given
- a name explicitly.
-
- The keys of this dictionary may be:
-
- * a constraint or Index class, e.g. the :class:`.UniqueConstraint`,
- :class:`_schema.ForeignKeyConstraint` class, the :class:`.Index`
- class
-
- * a string mnemonic for one of the known constraint classes;
- ``"fk"``, ``"pk"``, ``"ix"``, ``"ck"``, ``"uq"`` for foreign key,
- primary key, index, check, and unique constraint, respectively.
-
- * the string name of a user-defined "token" that can be used
- to define new naming tokens.
-
- The values associated with each "constraint class" or "constraint
- mnemonic" key are string naming templates, such as
- ``"uq_%(table_name)s_%(column_0_name)s"``,
- which describe how the name should be composed. The values
- associated with user-defined "token" keys should be callables of the
- form ``fn(constraint, table)``, which accepts the constraint/index
- object and :class:`_schema.Table` as arguments, returning a string
- result.
-
- The built-in names are as follows, some of which may only be
- available for certain types of constraint:
-
- * ``%(table_name)s`` - the name of the :class:`_schema.Table`
- object
- associated with the constraint.
-
- * ``%(referred_table_name)s`` - the name of the
- :class:`_schema.Table`
- object associated with the referencing target of a
- :class:`_schema.ForeignKeyConstraint`.
-
- * ``%(column_0_name)s`` - the name of the :class:`_schema.Column`
- at
- index position "0" within the constraint.
-
- * ``%(column_0N_name)s`` - the name of all :class:`_schema.Column`
- objects in order within the constraint, joined without a
- separator.
-
- * ``%(column_0_N_name)s`` - the name of all
- :class:`_schema.Column`
- objects in order within the constraint, joined with an
- underscore as a separator.
-
- * ``%(column_0_label)s``, ``%(column_0N_label)s``,
- ``%(column_0_N_label)s`` - the label of either the zeroth
- :class:`_schema.Column` or all :class:`.Columns`, separated with
- or without an underscore
-
- * ``%(column_0_key)s``, ``%(column_0N_key)s``,
- ``%(column_0_N_key)s`` - the key of either the zeroth
- :class:`_schema.Column` or all :class:`.Columns`, separated with
- or without an underscore
-
- * ``%(referred_column_0_name)s``, ``%(referred_column_0N_name)s``
- ``%(referred_column_0_N_name)s``, ``%(referred_column_0_key)s``,
- ``%(referred_column_0N_key)s``, ... column tokens which
- render the names/keys/labels of columns that are referenced
- by a :class:`_schema.ForeignKeyConstraint`.
-
- * ``%(constraint_name)s`` - a special key that refers to the
- existing name given to the constraint. When this key is
- present, the :class:`.Constraint` object's existing name will be
- replaced with one that is composed from template string that
- uses this token. When this token is present, it is required that
- the :class:`.Constraint` is given an explicit name ahead of time.
-
- * user-defined: any additional token may be implemented by passing
- it along with a ``fn(constraint, table)`` callable to the
- naming_convention dictionary.
-
- .. versionadded:: 1.3.0 - added new ``%(column_0N_name)s``,
- ``%(column_0_N_name)s``, and related tokens that produce
- concatenations of names, keys, or labels for all columns referred
- to by a given constraint.
-
- .. seealso::
-
- :ref:`constraint_naming_conventions` - for detailed usage
- examples.
-
- """
- if schema is not None and not isinstance(schema, str):
- raise exc.ArgumentError(
- "expected schema argument to be a string, "
- f"got {type(schema)}."
- )
- self.tables = util.FacadeDict()
- self.schema = quoted_name.construct(schema, quote_schema)
- self.naming_convention = (
- naming_convention
- if naming_convention
- else DEFAULT_NAMING_CONVENTION
- )
- if info:
- self.info = info
- self._schemas: Set[str] = set()
- self._sequences: Dict[str, Sequence] = {}
- self._fk_memos: Dict[Tuple[str, Optional[str]], List[ForeignKey]] = (
- collections.defaultdict(list)
- )
-
- tables: util.FacadeDict[str, Table]
- """A dictionary of :class:`_schema.Table`
- objects keyed to their name or "table key".
-
- The exact key is that determined by the :attr:`_schema.Table.key`
- attribute;
- for a table with no :attr:`_schema.Table.schema` attribute,
- this is the same
- as :attr:`_schema.Table.name`. For a table with a schema,
- it is typically of the
- form ``schemaname.tablename``.
-
- .. seealso::
-
- :attr:`_schema.MetaData.sorted_tables`
-
- """
-
- def __repr__(self) -> str:
- return "MetaData()"
-
- def __contains__(self, table_or_key: Union[str, Table]) -> bool:
- if not isinstance(table_or_key, str):
- table_or_key = table_or_key.key
- return table_or_key in self.tables
-
- def _add_table(
- self, name: str, schema: Optional[str], table: Table
- ) -> None:
- key = _get_table_key(name, schema)
- self.tables._insert_item(key, table)
- if schema:
- self._schemas.add(schema)
-
- def _remove_table(self, name: str, schema: Optional[str]) -> None:
- key = _get_table_key(name, schema)
- removed = dict.pop(self.tables, key, None)
- if removed is not None:
- for fk in removed.foreign_keys:
- fk._remove_from_metadata(self)
- if self._schemas:
- self._schemas = {
- t.schema for t in self.tables.values() if t.schema is not None
- }
-
- def __getstate__(self) -> Dict[str, Any]:
- return {
- "tables": self.tables,
- "schema": self.schema,
- "schemas": self._schemas,
- "sequences": self._sequences,
- "fk_memos": self._fk_memos,
- "naming_convention": self.naming_convention,
- }
-
- def __setstate__(self, state: Dict[str, Any]) -> None:
- self.tables = state["tables"]
- self.schema = state["schema"]
- self.naming_convention = state["naming_convention"]
- self._sequences = state["sequences"]
- self._schemas = state["schemas"]
- self._fk_memos = state["fk_memos"]
-
- def clear(self) -> None:
- """Clear all Table objects from this MetaData."""
-
- dict.clear(self.tables) # type: ignore
- self._schemas.clear()
- self._fk_memos.clear()
-
- def remove(self, table: Table) -> None:
- """Remove the given Table object from this MetaData."""
-
- self._remove_table(table.name, table.schema)
-
- @property
- def sorted_tables(self) -> List[Table]:
- """Returns a list of :class:`_schema.Table` objects sorted in order of
- foreign key dependency.
-
- The sorting will place :class:`_schema.Table`
- objects that have dependencies
- first, before the dependencies themselves, representing the
- order in which they can be created. To get the order in which
- the tables would be dropped, use the ``reversed()`` Python built-in.
-
- .. warning::
-
- The :attr:`.MetaData.sorted_tables` attribute cannot by itself
- accommodate automatic resolution of dependency cycles between
- tables, which are usually caused by mutually dependent foreign key
- constraints. When these cycles are detected, the foreign keys
- of these tables are omitted from consideration in the sort.
- A warning is emitted when this condition occurs, which will be an
- exception raise in a future release. Tables which are not part
- of the cycle will still be returned in dependency order.
-
- To resolve these cycles, the
- :paramref:`_schema.ForeignKeyConstraint.use_alter` parameter may be
- applied to those constraints which create a cycle. Alternatively,
- the :func:`_schema.sort_tables_and_constraints` function will
- automatically return foreign key constraints in a separate
- collection when cycles are detected so that they may be applied
- to a schema separately.
-
- .. versionchanged:: 1.3.17 - a warning is emitted when
- :attr:`.MetaData.sorted_tables` cannot perform a proper sort
- due to cyclical dependencies. This will be an exception in a
- future release. Additionally, the sort will continue to return
- other tables not involved in the cycle in dependency order which
- was not the case previously.
-
- .. seealso::
-
- :func:`_schema.sort_tables`
-
- :func:`_schema.sort_tables_and_constraints`
-
- :attr:`_schema.MetaData.tables`
-
- :meth:`_reflection.Inspector.get_table_names`
-
- :meth:`_reflection.Inspector.get_sorted_table_and_fkc_names`
-
-
- """
- return ddl.sort_tables(
- sorted(self.tables.values(), key=lambda t: t.key) # type: ignore
- )
-
- @util.preload_module("sqlalchemy.engine.reflection")
- def reflect(
- self,
- bind: Union[Engine, Connection],
- schema: Optional[str] = None,
- views: bool = False,
- only: Union[
- _typing_Sequence[str], Callable[[str, MetaData], bool], None
- ] = None,
- extend_existing: bool = False,
- autoload_replace: bool = True,
- resolve_fks: bool = True,
- **dialect_kwargs: Any,
- ) -> None:
- r"""Load all available table definitions from the database.
-
- Automatically creates ``Table`` entries in this ``MetaData`` for any
- table available in the database but not yet present in the
- ``MetaData``. May be called multiple times to pick up tables recently
- added to the database, however no special action is taken if a table
- in this ``MetaData`` no longer exists in the database.
-
- :param bind:
- A :class:`.Connection` or :class:`.Engine` used to access the
- database.
-
- :param schema:
- Optional, query and reflect tables from an alternate schema.
- If None, the schema associated with this :class:`_schema.MetaData`
- is used, if any.
-
- :param views:
- If True, also reflect views (materialized and plain).
-
- :param only:
- Optional. Load only a sub-set of available named tables. May be
- specified as a sequence of names or a callable.
-
- If a sequence of names is provided, only those tables will be
- reflected. An error is raised if a table is requested but not
- available. Named tables already present in this ``MetaData`` are
- ignored.
-
- If a callable is provided, it will be used as a boolean predicate to
- filter the list of potential table names. The callable is called
- with a table name and this ``MetaData`` instance as positional
- arguments and should return a true value for any table to reflect.
-
- :param extend_existing: Passed along to each :class:`_schema.Table` as
- :paramref:`_schema.Table.extend_existing`.
-
- :param autoload_replace: Passed along to each :class:`_schema.Table`
- as
- :paramref:`_schema.Table.autoload_replace`.
-
- :param resolve_fks: if True, reflect :class:`_schema.Table`
- objects linked
- to :class:`_schema.ForeignKey` objects located in each
- :class:`_schema.Table`.
- For :meth:`_schema.MetaData.reflect`,
- this has the effect of reflecting
- related tables that might otherwise not be in the list of tables
- being reflected, for example if the referenced table is in a
- different schema or is omitted via the
- :paramref:`.MetaData.reflect.only` parameter. When False,
- :class:`_schema.ForeignKey` objects are not followed to the
- :class:`_schema.Table`
- in which they link, however if the related table is also part of the
- list of tables that would be reflected in any case, the
- :class:`_schema.ForeignKey` object will still resolve to its related
- :class:`_schema.Table` after the :meth:`_schema.MetaData.reflect`
- operation is
- complete. Defaults to True.
-
- .. versionadded:: 1.3.0
-
- .. seealso::
-
- :paramref:`_schema.Table.resolve_fks`
-
- :param \**dialect_kwargs: Additional keyword arguments not mentioned
- above are dialect specific, and passed in the form
- ``<dialectname>_<argname>``. See the documentation regarding an
- individual dialect at :ref:`dialect_toplevel` for detail on
- documented arguments.
-
- .. seealso::
-
- :ref:`metadata_reflection_toplevel`
-
- :meth:`_events.DDLEvents.column_reflect` - Event used to customize
- the reflected columns. Usually used to generalize the types using
- :meth:`_types.TypeEngine.as_generic`
-
- :ref:`metadata_reflection_dbagnostic_types` - describes how to
- reflect tables using general types.
-
- """
-
- with inspection.inspect(bind)._inspection_context() as insp:
- reflect_opts: Any = {
- "autoload_with": insp,
- "extend_existing": extend_existing,
- "autoload_replace": autoload_replace,
- "resolve_fks": resolve_fks,
- "_extend_on": set(),
- }
-
- reflect_opts.update(dialect_kwargs)
-
- if schema is None:
- schema = self.schema
-
- if schema is not None:
- reflect_opts["schema"] = schema
-
- kind = util.preloaded.engine_reflection.ObjectKind.TABLE
- available: util.OrderedSet[str] = util.OrderedSet(
- insp.get_table_names(schema)
- )
- if views:
- kind = util.preloaded.engine_reflection.ObjectKind.ANY
- available.update(insp.get_view_names(schema))
- try:
- available.update(insp.get_materialized_view_names(schema))
- except NotImplementedError:
- pass
-
- if schema is not None:
- available_w_schema: util.OrderedSet[str] = util.OrderedSet(
- [f"{schema}.{name}" for name in available]
- )
- else:
- available_w_schema = available
-
- current = set(self.tables)
-
- if only is None:
- load = [
- name
- for name, schname in zip(available, available_w_schema)
- if extend_existing or schname not in current
- ]
- elif callable(only):
- load = [
- name
- for name, schname in zip(available, available_w_schema)
- if (extend_existing or schname not in current)
- and only(name, self)
- ]
- else:
- missing = [name for name in only if name not in available]
- if missing:
- s = schema and (" schema '%s'" % schema) or ""
- missing_str = ", ".join(missing)
- raise exc.InvalidRequestError(
- f"Could not reflect: requested table(s) not available "
- f"in {bind.engine!r}{s}: ({missing_str})"
- )
- load = [
- name
- for name in only
- if extend_existing or name not in current
- ]
- # pass the available tables so the inspector can
- # choose to ignore the filter_names
- _reflect_info = insp._get_reflection_info(
- schema=schema,
- filter_names=load,
- available=available,
- kind=kind,
- scope=util.preloaded.engine_reflection.ObjectScope.ANY,
- **dialect_kwargs,
- )
- reflect_opts["_reflect_info"] = _reflect_info
-
- for name in load:
- try:
- Table(name, self, **reflect_opts)
- except exc.UnreflectableTableError as uerr:
- util.warn(f"Skipping table {name}: {uerr}")
-
- def create_all(
- self,
- bind: _CreateDropBind,
- tables: Optional[_typing_Sequence[Table]] = None,
- checkfirst: bool = True,
- ) -> None:
- """Create all tables stored in this metadata.
-
- Conditional by default, will not attempt to recreate tables already
- present in the target database.
-
- :param bind:
- A :class:`.Connection` or :class:`.Engine` used to access the
- database.
-
- :param tables:
- Optional list of ``Table`` objects, which is a subset of the total
- tables in the ``MetaData`` (others are ignored).
-
- :param checkfirst:
- Defaults to True, don't issue CREATEs for tables already present
- in the target database.
-
- """
- bind._run_ddl_visitor(
- ddl.SchemaGenerator, self, checkfirst=checkfirst, tables=tables
- )
-
- def drop_all(
- self,
- bind: _CreateDropBind,
- tables: Optional[_typing_Sequence[Table]] = None,
- checkfirst: bool = True,
- ) -> None:
- """Drop all tables stored in this metadata.
-
- Conditional by default, will not attempt to drop tables not present in
- the target database.
-
- :param bind:
- A :class:`.Connection` or :class:`.Engine` used to access the
- database.
-
- :param tables:
- Optional list of ``Table`` objects, which is a subset of the
- total tables in the ``MetaData`` (others are ignored).
-
- :param checkfirst:
- Defaults to True, only issue DROPs for tables confirmed to be
- present in the target database.
-
- """
- bind._run_ddl_visitor(
- ddl.SchemaDropper, self, checkfirst=checkfirst, tables=tables
- )
-
-
-class Computed(FetchedValue, SchemaItem):
- """Defines a generated column, i.e. "GENERATED ALWAYS AS" syntax.
-
- The :class:`.Computed` construct is an inline construct added to the
- argument list of a :class:`_schema.Column` object::
-
- from sqlalchemy import Computed
-
- Table('square', metadata_obj,
- Column('side', Float, nullable=False),
- Column('area', Float, Computed('side * side'))
- )
-
- See the linked documentation below for complete details.
-
- .. versionadded:: 1.3.11
-
- .. seealso::
-
- :ref:`computed_ddl`
-
- """
-
- __visit_name__ = "computed_column"
-
- column: Optional[Column[Any]]
-
- @_document_text_coercion(
- "sqltext", ":class:`.Computed`", ":paramref:`.Computed.sqltext`"
- )
- def __init__(
- self, sqltext: _DDLColumnArgument, persisted: Optional[bool] = None
- ) -> None:
- """Construct a GENERATED ALWAYS AS DDL construct to accompany a
- :class:`_schema.Column`.
-
- :param sqltext:
- A string containing the column generation expression, which will be
- used verbatim, or a SQL expression construct, such as a
- :func:`_expression.text`
- object. If given as a string, the object is converted to a
- :func:`_expression.text` object.
-
- :param persisted:
- Optional, controls how this column should be persisted by the
- database. Possible values are:
-
- * ``None``, the default, it will use the default persistence
- defined by the database.
- * ``True``, will render ``GENERATED ALWAYS AS ... STORED``, or the
- equivalent for the target database if supported.
- * ``False``, will render ``GENERATED ALWAYS AS ... VIRTUAL``, or
- the equivalent for the target database if supported.
-
- Specifying ``True`` or ``False`` may raise an error when the DDL
- is emitted to the target database if the database does not support
- that persistence option. Leaving this parameter at its default
- of ``None`` is guaranteed to succeed for all databases that support
- ``GENERATED ALWAYS AS``.
-
- """
- self.sqltext = coercions.expect(roles.DDLExpressionRole, sqltext)
- self.persisted = persisted
- self.column = None
-
- def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
- assert isinstance(parent, Column)
-
- if not isinstance(
- parent.server_default, (type(None), Computed)
- ) or not isinstance(parent.server_onupdate, (type(None), Computed)):
- raise exc.ArgumentError(
- "A generated column cannot specify a server_default or a "
- "server_onupdate argument"
- )
- self.column = parent
- parent.computed = self
- self.column.server_onupdate = self
- self.column.server_default = self
-
- def _as_for_update(self, for_update: bool) -> FetchedValue:
- return self
-
- @util.deprecated(
- "1.4",
- "The :meth:`_schema.Computed.copy` method is deprecated "
- "and will be removed in a future release.",
- )
- def copy(
- self, *, target_table: Optional[Table] = None, **kw: Any
- ) -> Computed:
- return self._copy(target_table=target_table, **kw)
-
- def _copy(
- self, *, target_table: Optional[Table] = None, **kw: Any
- ) -> Computed:
- sqltext = _copy_expression(
- self.sqltext,
- self.column.table if self.column is not None else None,
- target_table,
- )
- g = Computed(sqltext, persisted=self.persisted)
-
- return self._schema_item_copy(g)
-
-
-class Identity(IdentityOptions, FetchedValue, SchemaItem):
- """Defines an identity column, i.e. "GENERATED { ALWAYS | BY DEFAULT }
- AS IDENTITY" syntax.
-
- The :class:`.Identity` construct is an inline construct added to the
- argument list of a :class:`_schema.Column` object::
-
- from sqlalchemy import Identity
-
- Table('foo', metadata_obj,
- Column('id', Integer, Identity())
- Column('description', Text),
- )
-
- See the linked documentation below for complete details.
-
- .. versionadded:: 1.4
-
- .. seealso::
-
- :ref:`identity_ddl`
-
- """
-
- __visit_name__ = "identity_column"
-
- is_identity = True
-
- def __init__(
- self,
- always: bool = False,
- on_null: Optional[bool] = None,
- start: Optional[int] = None,
- increment: Optional[int] = None,
- minvalue: Optional[int] = None,
- maxvalue: Optional[int] = None,
- nominvalue: Optional[bool] = None,
- nomaxvalue: Optional[bool] = None,
- cycle: Optional[bool] = None,
- cache: Optional[int] = None,
- order: Optional[bool] = None,
- ) -> None:
- """Construct a GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY DDL
- construct to accompany a :class:`_schema.Column`.
-
- See the :class:`.Sequence` documentation for a complete description
- of most parameters.
-
- .. note::
- MSSQL supports this construct as the preferred alternative to
- generate an IDENTITY on a column, but it uses non standard
- syntax that only support :paramref:`_schema.Identity.start`
- and :paramref:`_schema.Identity.increment`.
- All other parameters are ignored.
-
- :param always:
- A boolean, that indicates the type of identity column.
- If ``False`` is specified, the default, then the user-specified
- value takes precedence.
- If ``True`` is specified, a user-specified value is not accepted (
- on some backends, like PostgreSQL, OVERRIDING SYSTEM VALUE, or
- similar, may be specified in an INSERT to override the sequence
- value).
- Some backends also have a default value for this parameter,
- ``None`` can be used to omit rendering this part in the DDL. It
- will be treated as ``False`` if a backend does not have a default
- value.
-
- :param on_null:
- Set to ``True`` to specify ON NULL in conjunction with a
- ``always=False`` identity column. This option is only supported on
- some backends, like Oracle.
-
- :param start: the starting index of the sequence.
- :param increment: the increment value of the sequence.
- :param minvalue: the minimum value of the sequence.
- :param maxvalue: the maximum value of the sequence.
- :param nominvalue: no minimum value of the sequence.
- :param nomaxvalue: no maximum value of the sequence.
- :param cycle: allows the sequence to wrap around when the maxvalue
- or minvalue has been reached.
- :param cache: optional integer value; number of future values in the
- sequence which are calculated in advance.
- :param order: optional boolean value; if true, renders the
- ORDER keyword.
-
- """
- IdentityOptions.__init__(
- self,
- start=start,
- increment=increment,
- minvalue=minvalue,
- maxvalue=maxvalue,
- nominvalue=nominvalue,
- nomaxvalue=nomaxvalue,
- cycle=cycle,
- cache=cache,
- order=order,
- )
- self.always = always
- self.on_null = on_null
- self.column = None
-
- def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
- assert isinstance(parent, Column)
- if not isinstance(
- parent.server_default, (type(None), Identity)
- ) or not isinstance(parent.server_onupdate, type(None)):
- raise exc.ArgumentError(
- "A column with an Identity object cannot specify a "
- "server_default or a server_onupdate argument"
- )
- if parent.autoincrement is False:
- raise exc.ArgumentError(
- "A column with an Identity object cannot specify "
- "autoincrement=False"
- )
- self.column = parent
-
- parent.identity = self
- if parent._user_defined_nullable is NULL_UNSPECIFIED:
- parent.nullable = False
-
- parent.server_default = self
-
- def _as_for_update(self, for_update: bool) -> FetchedValue:
- return self
-
- @util.deprecated(
- "1.4",
- "The :meth:`_schema.Identity.copy` method is deprecated "
- "and will be removed in a future release.",
- )
- def copy(self, **kw: Any) -> Identity:
- return self._copy(**kw)
-
- def _copy(self, **kw: Any) -> Identity:
- i = Identity(
- always=self.always,
- on_null=self.on_null,
- start=self.start,
- increment=self.increment,
- minvalue=self.minvalue,
- maxvalue=self.maxvalue,
- nominvalue=self.nominvalue,
- nomaxvalue=self.nomaxvalue,
- cycle=self.cycle,
- cache=self.cache,
- order=self.order,
- )
-
- return self._schema_item_copy(i)